Skip to content

Commit 277c8cd

Browse files
[codebuff-cli] feat(cli): add staging binary release pipeline (#343)
1 parent d8abfe1 commit 277c8cd

File tree

11 files changed

+724
-42
lines changed

11 files changed

+724
-42
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
name: Build CLI Binary
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
binary-name:
7+
required: true
8+
type: string
9+
description: 'Name of the CLI binary to build'
10+
new-version:
11+
required: true
12+
type: string
13+
description: 'Version string for the build'
14+
artifact-name:
15+
required: false
16+
type: string
17+
description: 'Optional artifact containing staging metadata'
18+
default: ''
19+
checkout-ref:
20+
required: false
21+
type: string
22+
description: 'Git ref to checkout'
23+
default: ''
24+
env-overrides:
25+
required: false
26+
type: string
27+
description: 'JSON object of environment variable overrides'
28+
default: '{}'
29+
30+
jobs:
31+
build-binaries:
32+
strategy:
33+
matrix:
34+
include:
35+
- os: ubuntu-latest
36+
target: linux-x64
37+
bun_target: bun-linux-x64
38+
platform: linux
39+
arch: x64
40+
- os: ubuntu-latest
41+
target: linux-arm64
42+
bun_target: bun-linux-arm64
43+
platform: linux
44+
arch: arm64
45+
- os: macos-13
46+
target: darwin-x64
47+
bun_target: bun-darwin-x64
48+
platform: darwin
49+
arch: x64
50+
- os: macos-14
51+
target: darwin-arm64
52+
bun_target: bun-darwin-arm64
53+
platform: darwin
54+
arch: arm64
55+
- os: windows-latest
56+
target: win32-x64
57+
bun_target: bun-windows-x64
58+
platform: win32
59+
arch: x64
60+
runs-on: ${{ matrix.os }}
61+
steps:
62+
- uses: actions/checkout@v4
63+
with:
64+
ref: ${{ inputs.checkout-ref || github.sha }}
65+
66+
- name: Download staging metadata
67+
if: inputs.artifact-name != ''
68+
uses: actions/download-artifact@v4
69+
with:
70+
name: ${{ inputs.artifact-name }}
71+
path: cli/release-staging/
72+
73+
- name: Set up Bun
74+
uses: oven-sh/setup-bun@v2
75+
with:
76+
bun-version: '1.2.16'
77+
78+
- name: Cache dependencies
79+
uses: actions/cache@v4
80+
with:
81+
path: |
82+
node_modules
83+
*/node_modules
84+
key: ${{ runner.os }}-deps-${{ hashFiles('**/bun.lockb', '**/package.json') }}
85+
restore-keys: |
86+
${{ runner.os }}-deps-${{ hashFiles('**/bun.lockb') }}
87+
${{ runner.os }}-deps-
88+
89+
- name: Install dependencies
90+
run: bun install --frozen-lockfile
91+
92+
- name: Configure environment variables
93+
env:
94+
SECRETS_CONTEXT: ${{ toJSON(secrets) }}
95+
ENV_OVERRIDES: ${{ inputs.env-overrides }}
96+
shell: bash
97+
run: |
98+
VAR_NAMES=$(node scripts/generate-ci-env.js --prefix NEXT_PUBLIC_)
99+
100+
echo "$SECRETS_CONTEXT" | jq -r --argjson vars "$VAR_NAMES" '
101+
to_entries | .[] | select(.key as $k | $vars | index($k)) | .key + "=" + .value
102+
' >> $GITHUB_ENV
103+
echo "CODEBUFF_GITHUB_ACTIONS=true" >> $GITHUB_ENV
104+
echo "CODEBUFF_GITHUB_TOKEN=${{ secrets.CODEBUFF_GITHUB_TOKEN }}" >> $GITHUB_ENV
105+
if [ "$ENV_OVERRIDES" != "{}" ]; then
106+
echo "$ENV_OVERRIDES" | jq -r 'to_entries | .[] | .key + "=" + .value' >> $GITHUB_ENV
107+
fi
108+
109+
- name: Build binary
110+
run: bun run scripts/build-binary.ts ${{ inputs.binary-name }} ${{ inputs.new-version }}
111+
working-directory: cli
112+
shell: bash
113+
env:
114+
VERBOSE: true
115+
OVERRIDE_TARGET: ${{ matrix.bun_target }}
116+
OVERRIDE_PLATFORM: ${{ matrix.platform }}
117+
OVERRIDE_ARCH: ${{ matrix.arch }}
118+
119+
- name: Smoke test binary
120+
shell: bash
121+
run: |
122+
cd cli/bin
123+
if [[ "${{ runner.os }}" == "Windows" ]]; then
124+
./${{ inputs.binary-name }}.exe --version
125+
else
126+
./${{ inputs.binary-name }} --version
127+
fi
128+
129+
- name: Create tarball
130+
shell: bash
131+
run: |
132+
BINARY_FILE="${{ inputs.binary-name }}"
133+
if [[ "${{ runner.os }}" == "Windows" ]]; then
134+
BINARY_FILE="${{ inputs.binary-name }}.exe"
135+
fi
136+
tar -czf codebuff-cli-${{ matrix.target }}.tar.gz -C cli/bin "$BINARY_FILE"
137+
138+
- name: Upload binary artifact
139+
uses: actions/upload-artifact@v4
140+
with:
141+
name: codebuff-cli-${{ matrix.target }}
142+
path: codebuff-cli-${{ matrix.target }}.tar.gz
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
name: CLI Release Staging
2+
3+
on:
4+
pull_request:
5+
branches: ['main']
6+
7+
concurrency:
8+
group: cli-staging-release
9+
cancel-in-progress: false
10+
11+
permissions:
12+
contents: write
13+
14+
jobs:
15+
prepare-and-commit-staging:
16+
runs-on: ubuntu-latest
17+
if: contains(github.event.pull_request.title, '[codebuff-cli]')
18+
outputs:
19+
new_version: ${{ steps.bump_version.outputs.new_version }}
20+
steps:
21+
- uses: actions/checkout@v4
22+
with:
23+
token: ${{ secrets.GITHUB_TOKEN }}
24+
ref: ${{ github.event.pull_request.head.sha }}
25+
26+
- name: Set up Bun
27+
uses: oven-sh/setup-bun@v2
28+
with:
29+
bun-version: '1.2.16'
30+
31+
- name: Cache dependencies
32+
uses: actions/cache@v4
33+
with:
34+
path: |
35+
node_modules
36+
*/node_modules
37+
key: ${{ runner.os }}-deps-${{ hashFiles('**/bun.lockb', '**/package.json') }}
38+
restore-keys: |
39+
${{ runner.os }}-deps-${{ hashFiles('**/bun.lockb') }}
40+
${{ runner.os }}-deps-
41+
42+
- name: Install dependencies
43+
run: bun install --frozen-lockfile
44+
45+
- name: Calculate staging version
46+
id: bump_version
47+
env:
48+
GITHUB_TOKEN: ${{ secrets.CODEBUFF_GITHUB_TOKEN }}
49+
run: |
50+
cd cli/release-staging
51+
52+
BASE_VERSION=$(node -e "console.log(require('./package.json').version)")
53+
echo "Base version: $BASE_VERSION"
54+
55+
echo "Fetching latest CLI prerelease from GitHub..."
56+
RELEASES_JSON=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
57+
"https://api.github.com/repos/CodebuffAI/codebuff/releases?per_page=100")
58+
59+
LATEST_BETA=$(echo "$RELEASES_JSON" | jq -r '.[] | select(.prerelease == true and (.name // "" | test("Codebuff CLI v"))) | .tag_name' | sort -V | tail -n 1)
60+
61+
if [ "$LATEST_BETA" = "null" ]; then
62+
LATEST_BETA=""
63+
fi
64+
65+
if [ -z "$LATEST_BETA" ]; then
66+
echo "No existing CLI beta releases found, starting with beta.1"
67+
NEW_VERSION="${BASE_VERSION}-beta.1"
68+
else
69+
echo "Latest CLI beta tag: $LATEST_BETA"
70+
LATEST_VERSION=${LATEST_BETA#v}
71+
LATEST_BASE=$(echo "$LATEST_VERSION" | sed 's/-beta\..*$//')
72+
LATEST_BETA_NUM=$(echo "$LATEST_VERSION" | sed 's/.*-beta\.//')
73+
74+
if [ "$LATEST_BASE" = "$BASE_VERSION" ]; then
75+
NEXT=$((LATEST_BETA_NUM + 1))
76+
NEW_VERSION="${BASE_VERSION}-beta.${NEXT}"
77+
else
78+
echo "Base version changed, resetting beta counter"
79+
NEW_VERSION="${BASE_VERSION}-beta.1"
80+
fi
81+
fi
82+
83+
echo "New staging version: $NEW_VERSION"
84+
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
85+
86+
node -e "
87+
const fs = require('fs');
88+
const path = require('path');
89+
const version = '$NEW_VERSION';
90+
const stagingPath = path.join(process.cwd(), 'package.json');
91+
const stagingPkg = JSON.parse(fs.readFileSync(stagingPath, 'utf8'));
92+
stagingPkg.version = version;
93+
fs.writeFileSync(stagingPath, JSON.stringify(stagingPkg, null, 2) + '\n');
94+
const rootPkgPath = path.join(process.cwd(), '..', 'package.json');
95+
const rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf8'));
96+
rootPkg.version = version;
97+
fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n');
98+
"
99+
100+
- name: Configure git
101+
run: |
102+
git config --global user.name "github-actions[bot]"
103+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
104+
105+
- name: Commit staging release snapshot
106+
run: |
107+
git add -A
108+
git commit -m "Staging CLI Release v${{ steps.bump_version.outputs.new_version }} [codebuff-cli]
109+
110+
Captures the staged state for the CLI prerelease, including the version bump.
111+
112+
🤖 Generated with Codebuff
113+
Co-Authored-By: Codebuff <noreply@codebuff.com>"
114+
115+
- name: Create and push staging tag
116+
run: |
117+
git tag "v${{ steps.bump_version.outputs.new_version }}"
118+
git push origin "v${{ steps.bump_version.outputs.new_version }}"
119+
120+
- name: Upload staging metadata
121+
uses: actions/upload-artifact@v4
122+
with:
123+
name: cli-staging-metadata
124+
path: cli/release-staging/
125+
126+
build-staging-binaries:
127+
needs: prepare-and-commit-staging
128+
uses: ./.github/workflows/cli-release-build.yml
129+
with:
130+
binary-name: codebuff-cli
131+
new-version: ${{ needs.prepare-and-commit-staging.outputs.new_version }}
132+
artifact-name: cli-staging-metadata
133+
checkout-ref: ${{ github.event.pull_request.head.sha }}
134+
env-overrides: '{}'
135+
secrets: inherit
136+
137+
create-staging-release:
138+
needs: [prepare-and-commit-staging, build-staging-binaries]
139+
runs-on: ubuntu-latest
140+
steps:
141+
- uses: actions/checkout@v4
142+
with:
143+
ref: ${{ github.event.pull_request.head.sha }}
144+
145+
- name: Clean up old CLI prereleases
146+
run: |
147+
ONE_WEEK_AGO=$(date -d '7 days ago' -u +%Y-%m-%dT%H:%M:%SZ)
148+
echo "Removing CLI prereleases older than: $ONE_WEEK_AGO"
149+
150+
RELEASES=$(curl -s -H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \
151+
"https://api.github.com/repos/CodebuffAI/codebuff/releases?per_page=100")
152+
153+
if echo "$RELEASES" | jq -e . >/dev/null 2>&1; then
154+
OLD=$(echo "$RELEASES" | jq -r '.[] | select(.prerelease == true and .created_at < "'$ONE_WEEK_AGO'" and (.tag_name | test("^v[0-9].*-beta\\.[0-9]+$"))) | "\(.id):\(.tag_name)"')
155+
156+
if [ -n "$OLD" ]; then
157+
echo "Deleting old prereleases:"
158+
echo "$OLD"
159+
echo "$OLD" | while IFS=: read -r RELEASE_ID TAG_NAME; do
160+
curl -s -X DELETE \
161+
-H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \
162+
"https://api.github.com/repos/CodebuffAI/codebuff/releases/$RELEASE_ID"
163+
done
164+
else
165+
echo "No stale prereleases found."
166+
fi
167+
else
168+
echo "Failed to parse releases response:"
169+
echo "$RELEASES" | head -20
170+
fi
171+
172+
- name: Download all binary artifacts
173+
uses: actions/download-artifact@v4
174+
with:
175+
path: binaries/
176+
177+
- name: Download staging metadata
178+
uses: actions/download-artifact@v4
179+
with:
180+
name: cli-staging-metadata
181+
path: cli/release-staging/
182+
183+
- name: Create GitHub prerelease
184+
env:
185+
VERSION: ${{ needs.prepare-and-commit-staging.outputs.new_version }}
186+
run: |
187+
CURRENT_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
188+
RELEASE_BODY=$(cat <<EOF
189+
## Codebuff CLI v${VERSION} (Staging)
190+
191+
**⚠️ This is a staging build intended for internal testing.**
192+
193+
### Included Binaries
194+
- \`codebuff-cli-linux-x64.tar.gz\`
195+
- \`codebuff-cli-linux-arm64.tar.gz\`
196+
- \`codebuff-cli-darwin-x64.tar.gz\`
197+
- \`codebuff-cli-darwin-arm64.tar.gz\`
198+
- \`codebuff-cli-win32-x64.tar.gz\`
199+
200+
After downloading, extract the tarball, add the binary to your PATH, and run \`codebuff-cli --help\` for usage.
201+
EOF
202+
)
203+
204+
curl -s -X POST \
205+
-H "Accept: application/vnd.github.v3+json" \
206+
-H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \
207+
-H "Content-Type: application/json" \
208+
https://api.github.com/repos/CodebuffAI/codebuff/releases \
209+
-d "{
210+
\"tag_name\": \"v${VERSION}\",
211+
\"name\": \"Codebuff CLI v${VERSION} (Staging)\",
212+
\"body\": \"${RELEASE_BODY//$'\n'/\\n}\",
213+
\"prerelease\": true,
214+
\"published_at\": \"$CURRENT_TIME\"
215+
}"
216+
217+
- name: Upload release assets
218+
env:
219+
VERSION: ${{ needs.prepare-and-commit-staging.outputs.new_version }}
220+
run: |
221+
RELEASE_ID=$(curl -s -H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \
222+
"https://api.github.com/repos/CodebuffAI/codebuff/releases/tags/v${VERSION}" | jq -r '.id')
223+
224+
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
225+
echo "Failed to resolve release ID for v${VERSION}"
226+
exit 1
227+
fi
228+
229+
for file in binaries/*/codebuff-cli-*; do
230+
if [ -f "$file" ]; then
231+
FILENAME=$(basename "$file")
232+
echo "Uploading $FILENAME"
233+
curl -s -X POST \
234+
-H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \
235+
-H "Content-Type: application/octet-stream" \
236+
--data-binary @"$file" \
237+
"https://uploads.github.com/repos/CodebuffAI/codebuff/releases/$RELEASE_ID/assets?name=$FILENAME"
238+
fi
239+
done

0 commit comments

Comments
 (0)