name: publish run-name: "${{ format('release {0}', inputs.bump) }}" on: workflow_dispatch: inputs: bump: description: "Bump major, minor, or patch" required: true type: choice default: patch options: - patch - minor - major version: description: "Override version (e.g., 3.0.0-beta.6 for beta release). Takes precedence over bump." required: false type: string skip_platform: description: "Skip platform binary packages (use when already published)" required: false type: boolean default: false republish: description: "Re-publish mode: skip version check, only publish missing packages" required: false type: boolean default: false concurrency: ${{ github.workflow }}-${{ github.ref }} permissions: contents: write id-token: write jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Install dependencies run: bun install env: BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi" - name: Run tests run: bun test typecheck: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Install dependencies run: bun install env: BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi" - name: Type check run: bun run typecheck # Build everything and upload artifacts build: runs-on: ubuntu-latest needs: [test, typecheck] if: github.repository == 'code-yeongyu/oh-my-opencode' outputs: version: ${{ steps.version.outputs.version }} dist_tag: ${{ steps.version.outputs.dist_tag }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - run: git fetch --force --tags - uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Install dependencies run: bun install env: BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi" - name: Calculate version id: version run: | VERSION="${{ inputs.version }}" if [ -z "$VERSION" ]; then PREV=$(curl -s https://registry.npmjs.org/oh-my-opencode/latest | jq -r '.version // "0.0.0"') BASE="${PREV%%-*}" IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE" case "${{ inputs.bump }}" in major) VERSION="$((MAJOR+1)).0.0" ;; minor) VERSION="${MAJOR}.$((MINOR+1)).0" ;; *) VERSION="${MAJOR}.${MINOR}.$((PATCH+1))" ;; esac fi echo "version=$VERSION" >> $GITHUB_OUTPUT # Calculate dist tag if [[ "$VERSION" == *"-"* ]]; then DIST_TAG=$(echo "$VERSION" | cut -d'-' -f2 | cut -d'.' -f1) echo "dist_tag=${DIST_TAG:-next}" >> $GITHUB_OUTPUT else echo "dist_tag=" >> $GITHUB_OUTPUT fi echo "Version: $VERSION" - name: Update versions in package.json files run: bun run script/publish.ts --prepare-only env: VERSION: ${{ steps.version.outputs.version }} - name: Build main package run: | bun build src/index.ts --outdir dist --target bun --format esm --external @ast-grep/napi bun build src/cli/index.ts --outdir dist/cli --target bun --format esm --external @ast-grep/napi bunx tsc --emitDeclarationOnly bun run build:schema - name: Build platform binaries if: inputs.skip_platform != true run: bun run build:binaries - name: Upload main package artifact uses: actions/upload-artifact@v4 with: name: main-package path: | dist/ package.json assets/ README.md LICENSE.md retention-days: 1 - name: Upload platform artifacts if: inputs.skip_platform != true uses: actions/upload-artifact@v4 with: name: platform-packages path: packages/ retention-days: 1 - name: Git commit and tag run: | git config user.email "github-actions[bot]@users.noreply.github.com" git config user.name "github-actions[bot]" git add package.json assets/oh-my-opencode.schema.json packages/*/package.json || true git diff --cached --quiet || git commit -m "release: v${{ steps.version.outputs.version }}" git tag -f "v${{ steps.version.outputs.version }}" git push origin --tags --force git push origin HEAD || echo "Branch push failed (non-critical)" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Publish platform packages in parallel (each job gets fresh OIDC token) publish-platform: runs-on: ubuntu-latest needs: build if: inputs.skip_platform != true strategy: fail-fast: false matrix: platform: [darwin-arm64, darwin-x64, linux-x64, linux-arm64, linux-x64-musl, linux-arm64-musl, windows-x64] steps: - uses: actions/setup-node@v4 with: node-version: "24" registry-url: "https://registry.npmjs.org" - name: Download platform artifacts uses: actions/download-artifact@v4 with: name: platform-packages path: packages/ - name: Check if already published id: check run: | PKG_NAME="oh-my-opencode-${{ matrix.platform }}" VERSION="${{ needs.build.outputs.version }}" STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/${PKG_NAME}/${VERSION}") if [ "$STATUS" = "200" ]; then echo "skip=true" >> $GITHUB_OUTPUT echo "✓ ${PKG_NAME}@${VERSION} already published" else echo "skip=false" >> $GITHUB_OUTPUT echo "→ ${PKG_NAME}@${VERSION} needs publishing" fi - name: Publish ${{ matrix.platform }} if: steps.check.outputs.skip != 'true' run: | cd packages/${{ matrix.platform }} TAG_ARG="" if [ -n "${{ needs.build.outputs.dist_tag }}" ]; then TAG_ARG="--tag ${{ needs.build.outputs.dist_tag }}" fi npm publish --access public $TAG_ARG env: NPM_CONFIG_PROVENANCE: false # Publish main package after all platform packages publish-main: runs-on: ubuntu-latest needs: [build, publish-platform] if: always() && needs.build.result == 'success' && (inputs.skip_platform == true || needs.publish-platform.result == 'success' || needs.publish-platform.result == 'skipped') steps: - uses: actions/setup-node@v4 with: node-version: "24" registry-url: "https://registry.npmjs.org" - name: Download main package artifact uses: actions/download-artifact@v4 with: name: main-package path: . - name: Check if already published id: check run: | VERSION="${{ needs.build.outputs.version }}" STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/oh-my-opencode/${VERSION}") if [ "$STATUS" = "200" ]; then echo "skip=true" >> $GITHUB_OUTPUT echo "✓ oh-my-opencode@${VERSION} already published" else echo "skip=false" >> $GITHUB_OUTPUT fi - name: Publish main package if: steps.check.outputs.skip != 'true' run: | TAG_ARG="" if [ -n "${{ needs.build.outputs.dist_tag }}" ]; then TAG_ARG="--tag ${{ needs.build.outputs.dist_tag }}" fi npm publish --access public --provenance $TAG_ARG env: NPM_CONFIG_PROVENANCE: true # Create release and cleanup release: runs-on: ubuntu-latest needs: [build, publish-main] if: always() && needs.build.result == 'success' steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Generate changelog id: changelog run: | VERSION="${{ needs.build.outputs.version }}" # Find previous tag PREV_TAG="" if [[ "$VERSION" == *"-beta."* ]]; then BASE="${VERSION%-beta.*}" NUM="${VERSION##*-beta.}" PREV_NUM=$((NUM - 1)) if [ $PREV_NUM -ge 1 ]; then PREV_TAG="${BASE}-beta.${PREV_NUM}" git rev-parse "v${PREV_TAG}" >/dev/null 2>&1 || PREV_TAG="" fi fi if [ -z "$PREV_TAG" ]; then PREV_TAG=$(curl -s https://registry.npmjs.org/oh-my-opencode/latest | jq -r '.version // "0.0.0"') fi echo "Comparing v${PREV_TAG}..v${VERSION}" NOTES=$(git log "v${PREV_TAG}..v${VERSION}" --oneline --format="- %h %s" 2>/dev/null | grep -vE "^- \w+ (ignore:|test:|chore:|ci:|release:)" || echo "No notable changes") # Write to file for multiline support echo "$NOTES" > /tmp/changelog.md echo "notes_file=/tmp/changelog.md" >> $GITHUB_OUTPUT - name: Create GitHub release run: | VERSION="${{ needs.build.outputs.version }}" gh release view "v${VERSION}" >/dev/null 2>&1 || \ gh release create "v${VERSION}" --title "v${VERSION}" --notes-file /tmp/changelog.md env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Delete draft release run: gh release delete next --yes 2>/dev/null || true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Merge to master continue-on-error: true run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" VERSION="${{ needs.build.outputs.version }}" git stash --include-untracked || true git checkout master git reset --hard "v${VERSION}" git push -f origin master || echo "::warning::Failed to push to master" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}