[codecane] feat: deploy new cli app #58
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CLI Release Staging | |
| on: | |
| pull_request: | |
| branches: ['main'] | |
| concurrency: | |
| group: cli-staging-release | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| jobs: | |
| prepare-and-commit-staging: | |
| runs-on: ubuntu-latest | |
| if: contains(github.event.pull_request.title, '[codecane]') | |
| outputs: | |
| new_version: ${{ steps.bump_version.outputs.new_version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| - name: Set up Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: '1.2.16' | |
| - name: Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| node_modules | |
| */node_modules | |
| key: ${{ runner.os }}-deps-${{ hashFiles('**/bun.lockb', '**/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-deps-${{ hashFiles('**/bun.lockb') }} | |
| ${{ runner.os }}-deps- | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Calculate staging version | |
| id: bump_version | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.CODEBUFF_GITHUB_TOKEN }} | |
| run: | | |
| cd cli/release-staging | |
| BASE_VERSION=$(node -e "console.log(require('./package.json').version)") | |
| echo "Base version: $BASE_VERSION" | |
| echo "Fetching latest CLI prerelease from GitHub..." | |
| RELEASES_JSON=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \ | |
| "https://api.github.com/repos/CodebuffAI/codebuff/releases?per_page=100") | |
| LATEST_TAG=$(echo "$RELEASES_JSON" | jq -r '.[] | select(.prerelease == true and (.name // "" | test("Codebuff CLI v"))) | .tag_name' | sort -V | tail -n 1) | |
| if [ "$LATEST_TAG" = "null" ] || [ -z "$LATEST_TAG" ]; then | |
| echo "No existing CLI prerelease found via releases, falling back to tags..." | |
| LATEST_TAG=$(git ls-remote --tags origin "v${BASE_VERSION}-beta.*" | awk '{print $2}' | sed 's@refs/tags/@@' | sort -V | tail -n 1) | |
| fi | |
| if [ -n "$LATEST_TAG" ] && [[ "$LATEST_TAG" != v* ]]; then | |
| LATEST_TAG="v${LATEST_TAG}" | |
| fi | |
| if [ -n "$LATEST_TAG" ]; then | |
| echo "Latest CLI beta tag: $LATEST_TAG" | |
| LATEST_VERSION=${LATEST_TAG#v} | |
| LATEST_BASE=$(echo "$LATEST_VERSION" | sed 's/-beta\..*$//') | |
| LATEST_BETA_NUM=$(echo "$LATEST_VERSION" | sed 's/.*-beta\.//') | |
| if [ "$LATEST_BASE" = "$BASE_VERSION" ]; then | |
| NEXT=$((LATEST_BETA_NUM + 1)) | |
| NEW_VERSION="${BASE_VERSION}-beta.${NEXT}" | |
| else | |
| echo "Base version changed since last prerelease, resetting counter" | |
| NEW_VERSION="${BASE_VERSION}-beta.1" | |
| fi | |
| else | |
| echo "No existing CLI beta tags found, starting with beta.1" | |
| NEW_VERSION="${BASE_VERSION}-beta.1" | |
| fi | |
| echo "New staging version: $NEW_VERSION" | |
| echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT | |
| node -e " | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const version = '$NEW_VERSION'; | |
| const stagingPath = path.join(process.cwd(), 'package.json'); | |
| const stagingPkg = JSON.parse(fs.readFileSync(stagingPath, 'utf8')); | |
| stagingPkg.version = version; | |
| fs.writeFileSync(stagingPath, JSON.stringify(stagingPkg, null, 2) + '\n'); | |
| const rootPkgPath = path.join(process.cwd(), '..', 'package.json'); | |
| const rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf8')); | |
| rootPkg.version = version; | |
| fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n'); | |
| " | |
| - name: Configure git | |
| run: | | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Commit staging release snapshot | |
| run: | | |
| git add -A | |
| git commit -m "Staging CLI Release v${{ steps.bump_version.outputs.new_version }} [codecane] | |
| Captures the staged state for the CLI prerelease, including the version bump. | |
| 🤖 Generated with Codebuff | |
| Co-Authored-By: Codebuff <noreply@codebuff.com>" | |
| - name: Create and push staging tag | |
| run: | | |
| set -euo pipefail | |
| TAG="v${{ steps.bump_version.outputs.new_version }}" | |
| if git ls-remote --tags origin "$TAG" | grep -q "$TAG"; then | |
| echo "Tag $TAG already exists on origin; skipping push." | |
| exit 0 | |
| fi | |
| if git rev-parse "$TAG" >/dev/null 2>&1; then | |
| echo "Tag $TAG already exists locally; removing before re-tagging." | |
| git tag -d "$TAG" | |
| fi | |
| git tag "$TAG" | |
| set +e | |
| git push origin "$TAG" | |
| PUSH_STATUS=$? | |
| set -e | |
| if [ "$PUSH_STATUS" -ne 0 ]; then | |
| echo "Push failed, re-checking remote for $TAG..." | |
| if git ls-remote --tags origin "$TAG" | grep -q "$TAG"; then | |
| echo "Tag $TAG is present on origin after push attempt; continuing." | |
| exit 0 | |
| fi | |
| echo "Failed to push tag $TAG and it is not present on origin." | |
| exit 1 | |
| fi | |
| echo "Pushed tag $TAG to origin." | |
| - name: Upload staging metadata | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: cli-staging-metadata | |
| path: cli/release-staging/ | |
| build-staging-binaries: | |
| needs: prepare-and-commit-staging | |
| uses: ./.github/workflows/cli-release-build.yml | |
| with: | |
| binary-name: codecane | |
| new-version: ${{ needs.prepare-and-commit-staging.outputs.new_version }} | |
| artifact-name: cli-staging-metadata | |
| checkout-ref: ${{ github.event.pull_request.head.sha }} | |
| env-overrides: '{}' | |
| secrets: inherit | |
| create-staging-release: | |
| needs: [prepare-and-commit-staging, build-staging-binaries] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| - name: Clean up old CLI prereleases | |
| run: | | |
| ONE_WEEK_AGO=$(date -d '7 days ago' -u +%Y-%m-%dT%H:%M:%SZ) | |
| echo "Removing CLI prereleases older than: $ONE_WEEK_AGO" | |
| RELEASES=$(curl -s -H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \ | |
| "https://api.github.com/repos/CodebuffAI/codebuff/releases?per_page=100") | |
| if echo "$RELEASES" | jq -e . >/dev/null 2>&1; then | |
| 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)"') | |
| if [ -n "$OLD" ]; then | |
| echo "Deleting old prereleases:" | |
| echo "$OLD" | |
| echo "$OLD" | while IFS=: read -r RELEASE_ID TAG_NAME; do | |
| curl -s -X DELETE \ | |
| -H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \ | |
| "https://api.github.com/repos/CodebuffAI/codebuff/releases/$RELEASE_ID" | |
| done | |
| else | |
| echo "No stale prereleases found." | |
| fi | |
| else | |
| echo "Failed to parse releases response:" | |
| echo "$RELEASES" | head -20 | |
| fi | |
| - name: Download all binary artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: binaries/ | |
| - name: Download staging metadata | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: cli-staging-metadata | |
| path: cli/release-staging/ | |
| - name: Create GitHub prerelease | |
| env: | |
| VERSION: ${{ needs.prepare-and-commit-staging.outputs.new_version }} | |
| run: | | |
| EXISTING_RELEASE=$(curl -s -H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \ | |
| "https://api.github.com/repos/CodebuffAI/codebuff/releases/tags/v${VERSION}") | |
| if echo "$EXISTING_RELEASE" | jq -e '.id' >/dev/null 2>&1; then | |
| echo "Release for v${VERSION} already exists; skipping creation." | |
| exit 0 | |
| fi | |
| CURRENT_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") | |
| RELEASE_BODY=$(cat <<'EOF' | |
| ## Codecane v${VERSION} (Staging) | |
| **⚠️ This is a staging build intended for internal testing.** | |
| ### Included Binaries | |
| - `codecane-linux-x64.tar.gz` | |
| - `codecane-linux-arm64.tar.gz` | |
| - `codecane-darwin-x64.tar.gz` | |
| - `codecane-darwin-arm64.tar.gz` | |
| - `codecane-win32-x64.tar.gz` | |
| After downloading, extract the tarball, add the binary to your PATH, and run `codecane --help` for usage. | |
| EOF | |
| ) | |
| curl -s -X POST \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| -H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \ | |
| -H "Content-Type: application/json" \ | |
| https://api.github.com/repos/CodebuffAI/codebuff/releases \ | |
| -d "{ | |
| \"tag_name\": \"v${VERSION}\", | |
| \"name\": \"Codecane v${VERSION} (Staging)\", | |
| \"body\": \"${RELEASE_BODY//$'\n'/\\n}\", | |
| \"prerelease\": true, | |
| \"published_at\": \"$CURRENT_TIME\" | |
| }" | |
| - name: Upload release assets | |
| env: | |
| VERSION: ${{ needs.prepare-and-commit-staging.outputs.new_version }} | |
| run: | | |
| RELEASE_ID=$(curl -s -H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \ | |
| "https://api.github.com/repos/CodebuffAI/codebuff/releases/tags/v${VERSION}" | jq -r '.id') | |
| if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then | |
| echo "Failed to resolve release ID for v${VERSION}" | |
| exit 1 | |
| fi | |
| for file in binaries/*/codecane-*; do | |
| if [ -f "$file" ]; then | |
| FILENAME=$(basename "$file") | |
| echo "Uploading $FILENAME" | |
| curl -s -X POST \ | |
| -H "Authorization: token ${{ secrets.CODEBUFF_GITHUB_TOKEN }}" \ | |
| -H "Content-Type: application/octet-stream" \ | |
| --data-binary @"$file" \ | |
| "https://uploads.github.com/repos/CodebuffAI/codebuff/releases/$RELEASE_ID/assets?name=$FILENAME" | |
| fi | |
| done | |
| publish-staging-npm: | |
| needs: [prepare-and-commit-staging, build-staging-binaries, create-staging-release] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| - name: Download CLI staging package | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: cli-staging-metadata | |
| path: cli/release-staging/ | |
| - name: Set up Node.js for npm publishing | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| registry-url: https://registry.npmjs.org/ | |
| - name: Check if version already published | |
| id: check_npm | |
| env: | |
| VERSION: ${{ needs.prepare-and-commit-staging.outputs.new_version }} | |
| run: | | |
| if npm view codecane@"$VERSION" version >/dev/null 2>&1; then | |
| echo "codecane@$VERSION already published to npm; skipping publish." | |
| echo "published=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "published=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Publish codecane staging package to npm | |
| if: steps.check_npm.outputs.published != 'true' | |
| run: | | |
| cd cli/release-staging | |
| npm publish --access public | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} |