This repository was archived by the owner on Sep 26, 2026. It is now read-only.
Repository navigation
Release #3
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
| # Automatically releases both @jsonbored/opennextjs-cli and @jsonbored/opennextjs-mcp | |
| # packages to npm when a version tag is pushed. Supports both NPM_TOKEN (for first release) | |
| # and OIDC (for subsequent releases). Both packages are released with synchronized versions. | |
| # | |
| # **What it does:** | |
| # 1. Builds both packages (CLI and MCP) | |
| # 2. Extracts version from tag (e.g., v1.0.0 → 1.0.0) | |
| # 3. Verifies both package.json versions match tag version | |
| # 4. Generates changelog automatically using git-cliff | |
| # 5. Publishes both packages to npm (tries OIDC first, falls back to NPM_TOKEN) | |
| # 6. Creates GitHub Release with changelog notes | |
| # | |
| # **Triggers:** | |
| # 1. Push a version tag (automatic): | |
| # ```bash | |
| # git tag v1.0.0 | |
| # git push origin v1.0.0 | |
| # ``` | |
| # 2. Manual workflow_dispatch (button in GitHub Actions dashboard): | |
| # - Go to Actions → Release → Run workflow | |
| # - Enter version (e.g., "0.1.0") or leave empty to use package.json version | |
| # - Click "Run workflow" | |
| # - Workflow will: create tag → generate changelogs → commit → push tag → publish to npm | |
| # | |
| # **Prerequisites:** | |
| # - package.json version must match tag version (e.g., 1.0.0) | |
| # | |
| # **First Release (NPM_TOKEN):** | |
| # - Requires NPM_TOKEN secret in GitHub repository | |
| # - Create token: npmjs.com → Account Settings → Access Tokens → Generate New Token (Automation) | |
| # - Add secret: GitHub repo → Settings → Secrets and variables → Actions → New repository secret | |
| # - Name: NPM_TOKEN | |
| # - Value: Your npm automation token | |
| # | |
| # **Subsequent Releases (OIDC - Recommended):** | |
| # - After first release, set up OIDC trusted publishing: | |
| # 1. Go to npmjs.com → Account Settings → Access Tokens → Automation | |
| # 2. Click "Add GitHub Actions" or "Configure" next to "Trusted Publishers" | |
| # 3. Select repository: JSONbored/opennextjs-cli | |
| # 4. Select workflow: .github/workflows/release.yml | |
| # 5. Save | |
| # - Once OIDC is configured, NPM_TOKEN is no longer needed | |
| # - More secure than token-based authentication | |
| # - Automatic token rotation | |
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - v*.*.* # Matches v1.2.3 | |
| workflow_dispatch: {} | |
| permissions: read-all | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| permissions: | |
| actions: read | |
| checks: read | |
| contents: write # Required to create GitHub releases and push tags/commits | |
| deployments: none | |
| id-token: write # Required for npm publish (OIDC) | |
| issues: none | |
| packages: none | |
| pull-requests: none | |
| repository-projects: none | |
| security-events: none | |
| statuses: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 # Full history for changelog generation | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: '22' | |
| registry-url: https://registry.npmjs.org | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Lint | |
| run: pnpm lint | |
| - name: Type check | |
| run: pnpm type-check | |
| - name: Test | |
| run: pnpm test | |
| - name: Build CLI package | |
| working-directory: packages/opennextjs-cli | |
| run: pnpm build | |
| - name: Build MCP package | |
| working-directory: packages/opennextjs-mcp | |
| run: pnpm build | |
| - name: Extract version from tag or input | |
| id: version | |
| run: | | |
| # For workflow_dispatch, use package.json version | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then | |
| # Read from CLI package.json | |
| VERSION=$(node -p "require('./packages/opennextjs-cli/package.json').version") | |
| # Remove 'v' prefix if present | |
| VERSION="${VERSION#v}" | |
| TAG="v$VERSION" | |
| echo "TAG=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "IS_MANUAL=true" >> "$GITHUB_OUTPUT" | |
| else | |
| # Extract version from tag (e.g., v1.0.0 → 1.0.0) | |
| TAG_NAME="${{ github.ref_name }}" | |
| VERSION="${TAG_NAME#v}" | |
| TAG="$TAG_NAME" | |
| echo "IS_MANUAL=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Version: $VERSION" | |
| echo "Tag: $TAG" | |
| - name: Verify CLI package.json version matches tag | |
| working-directory: packages/opennextjs-cli | |
| run: | | |
| PACKAGE_VERSION=$(node -p "require('./package.json').version") | |
| TAG_VERSION="${{ steps.version.outputs.VERSION }}" | |
| if [ "$PACKAGE_VERSION" != "$TAG_VERSION" ]; then | |
| echo "❌ CLI version mismatch: package.json ($PACKAGE_VERSION) != tag ($TAG_VERSION)" >&2 | |
| exit 1 | |
| fi | |
| echo "✅ CLI version match: $PACKAGE_VERSION" | |
| - name: Verify MCP package.json version matches tag | |
| working-directory: packages/opennextjs-mcp | |
| run: | | |
| PACKAGE_VERSION=$(node -p "require('./package.json').version") | |
| TAG_VERSION="${{ steps.version.outputs.VERSION }}" | |
| if [ "$PACKAGE_VERSION" != "$TAG_VERSION" ]; then | |
| echo "❌ MCP version mismatch: package.json ($PACKAGE_VERSION) != tag ($TAG_VERSION)" >&2 | |
| exit 1 | |
| fi | |
| echo "✅ MCP version match: $PACKAGE_VERSION" | |
| - name: Setup git-cliff | |
| uses: kenji-miyake/setup-git-cliff@e4913b34dd9c321f11f2ef3c3866f800714a1d2e # v1 | |
| with: | |
| version: latest | |
| - name: Check if tag exists (for manual trigger) | |
| if: steps.version.outputs.IS_MANUAL == 'true' | |
| id: tag_check | |
| run: | | |
| TAG="${{ steps.version.outputs.TAG }}" | |
| if git rev-parse "$TAG" >/dev/null 2>&1; then | |
| echo "EXISTS=true" >> "$GITHUB_OUTPUT" | |
| echo "⚠️ Tag $TAG already exists - will use existing tag" | |
| else | |
| echo "EXISTS=false" >> "$GITHUB_OUTPUT" | |
| echo "✅ Tag $TAG does not exist - will create it and generate changelogs first" | |
| fi | |
| - name: Generate changelogs and create tag (manual trigger, tag doesn't exist) | |
| if: steps.version.outputs.IS_MANUAL == 'true' && steps.tag_check.outputs.EXISTS == 'false' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -e | |
| TAG="${{ steps.version.outputs.TAG }}" | |
| echo "📝 Generating CLI changelog..." | |
| pnpm changelog:package opennextjs-cli --tag "$TAG" || { | |
| echo "⚠️ CLI changelog generation failed" >&2 | |
| exit 1 | |
| } | |
| echo "📝 Generating MCP changelog..." | |
| pnpm changelog:package opennextjs-mcp --tag "$TAG" || { | |
| echo "⚠️ MCP changelog generation failed" >&2 | |
| exit 1 | |
| } | |
| echo "✅ Changelogs generated successfully" | |
| # Commit changelogs | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add packages/opennextjs-cli/CHANGELOG.md packages/opennextjs-mcp/CHANGELOG.md | |
| if git diff --staged --quiet; then | |
| echo "No changelog changes to commit" | |
| else | |
| git commit -m "chore: update changelogs for $TAG release" | |
| git push origin main | |
| echo "✅ Changelogs committed and pushed" | |
| fi | |
| # Create and push tag (this will trigger this workflow again with tag push event) | |
| git tag -a "$TAG" -m "Release $TAG" | |
| git push origin "$TAG" | |
| echo "✅ Tag $TAG created and pushed" | |
| echo "ℹ️ This workflow will run again automatically when the tag is pushed" | |
| - name: Generate package-specific changelogs (when tag already exists) | |
| if: | | |
| (steps.version.outputs.IS_MANUAL == 'true' && steps.tag_check.outputs.EXISTS == 'true') || | |
| steps.version.outputs.IS_MANUAL == 'false' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -e | |
| TAG="${{ steps.version.outputs.TAG }}" | |
| # Generate CLI changelog (writes directly to packages/opennextjs-cli/CHANGELOG.md) | |
| echo "📝 Generating CLI changelog..." | |
| pnpm changelog:package opennextjs-cli --tag "$TAG" || \ | |
| echo "⚠️ CLI changelog generation failed" >&2 | |
| # Generate MCP changelog (writes directly to packages/opennextjs-mcp/CHANGELOG.md) | |
| echo "📝 Generating MCP changelog..." | |
| pnpm changelog:package opennextjs-mcp --tag "$TAG" || \ | |
| echo "⚠️ MCP changelog generation failed" >&2 | |
| # Extract changelog sections for GitHub Release notes | |
| # Read from package-specific CHANGELOG.md files | |
| echo "📝 Extracting changelog sections for release notes..." | |
| VERSION="${{ steps.version.outputs.VERSION }}" | |
| # Start with version header | |
| echo "## [$VERSION] - $(date +%Y-%m-%d)" > /tmp/changelog-section.md | |
| echo "" >> /tmp/changelog-section.md | |
| # Extract CLI section from packages/opennextjs-cli/CHANGELOG.md | |
| if [ -f "packages/opennextjs-cli/CHANGELOG.md" ]; then | |
| # Find the version section in CLI changelog | |
| CLI_SECTION=$(awk "/^## \[$VERSION\]/,/^## \[|^<!-- generated/" "packages/opennextjs-cli/CHANGELOG.md" | head -n -1 || true) | |
| if [ -n "$CLI_SECTION" ]; then | |
| echo "### @jsonbored/opennextjs-cli" >> /tmp/changelog-section.md | |
| echo "" >> /tmp/changelog-section.md | |
| # Remove the version header line, keep the rest | |
| echo "$CLI_SECTION" | sed '/^## \[/d' >> /tmp/changelog-section.md || true | |
| echo "" >> /tmp/changelog-section.md | |
| fi | |
| fi | |
| # Extract MCP section from packages/opennextjs-mcp/CHANGELOG.md | |
| if [ -f "packages/opennextjs-mcp/CHANGELOG.md" ]; then | |
| # Find the version section in MCP changelog | |
| MCP_SECTION=$(awk "/^## \[$VERSION\]/,/^## \[|^<!-- generated/" "packages/opennextjs-mcp/CHANGELOG.md" | head -n -1 || true) | |
| if [ -n "$MCP_SECTION" ]; then | |
| echo "### @jsonbored/opennextjs-mcp" >> /tmp/changelog-section.md | |
| echo "" >> /tmp/changelog-section.md | |
| # Remove the version header line, keep the rest | |
| echo "$MCP_SECTION" | sed '/^## \[/d' >> /tmp/changelog-section.md || true | |
| echo "" >> /tmp/changelog-section.md | |
| fi | |
| fi | |
| # If no package-specific sections, create a generic one | |
| if [ ! -s /tmp/changelog-section.md ] || [ "$(wc -l < /tmp/changelog-section.md)" -lt 5 ]; then | |
| echo "Initial release of OpenNext.js CLI and MCP server packages." >> /tmp/changelog-section.md | |
| echo "" >> /tmp/changelog-section.md | |
| fi | |
| # Set changelog section for GitHub release | |
| echo "CHANGELOG_SECTION<<EOF" >> "$GITHUB_ENV" # Trunk: Add missing newline for echo | |
| cat /tmp/changelog-section.md >> "$GITHUB_ENV" | |
| echo "EOF" >> "$GITHUB_ENV" # Trunk: Add missing newline | |
| echo "✅ Changelogs generated successfully" | |
| echo " - packages/opennextjs-cli/CHANGELOG.md" | |
| echo " - packages/opennextjs-mcp/CHANGELOG.md" | |
| - name: Publish CLI to npm (try OIDC first) | |
| id: publish-cli-oidc | |
| working-directory: packages/opennextjs-cli | |
| run: | | |
| # Try publishing with OIDC (if configured) | |
| npm publish --access public && \ | |
| echo "✅ Published @jsonbored/opennextjs-cli@${{ steps.version.outputs.VERSION }} to npm (via OIDC)" || \ | |
| echo "⚠️ OIDC publish failed, will try NPM_TOKEN fallback" | |
| continue-on-error: true | |
| - name: Publish CLI to npm (fallback to NPM_TOKEN) | |
| if: steps.publish-cli-oidc.outcome == 'failure' | |
| working-directory: packages/opennextjs-cli | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| if [ -z "$NODE_AUTH_TOKEN" ]; then | |
| echo "❌ NPM_TOKEN secret not found. For first release, you need to:" >&2 | |
| echo " 1. Create npm automation token: https://www.npmjs.com/settings/JSONbored/tokens" >&2 | |
| echo " 2. Add NPM_TOKEN secret to GitHub: Settings → Secrets and variables → Actions" >&2 | |
| echo " 3. Name the secret: NPM_TOKEN" >&2 | |
| exit 1 | |
| fi | |
| # Configure npm to use token | |
| echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" > ~/.npmrc | |
| npm publish --access public | |
| echo "✅ Published @jsonbored/opennextjs-cli@${{ steps.version.outputs.VERSION }} to npm (via NPM_TOKEN)" | |
| - name: Publish MCP to npm (try OIDC first) | |
| id: publish-mcp-oidc | |
| working-directory: packages/opennextjs-mcp | |
| run: | | |
| # Try publishing with OIDC (if configured) | |
| npm publish --access public && \ | |
| echo "✅ Published @jsonbored/opennextjs-mcp@${{ steps.version.outputs.VERSION }} to npm (via OIDC)" || \ | |
| echo "⚠️ OIDC publish failed, will try NPM_TOKEN fallback" | |
| continue-on-error: true | |
| - name: Publish MCP to npm (fallback to NPM_TOKEN) | |
| if: steps.publish-mcp-oidc.outcome == 'failure' | |
| working-directory: packages/opennextjs-mcp | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| if [ -z "$NODE_AUTH_TOKEN" ]; then | |
| echo "❌ NPM_TOKEN secret not found" >&2 | |
| exit 1 | |
| fi | |
| # Configure npm to use token (if not already configured) | |
| if [ ! -f ~/.npmrc ]; then | |
| echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" > ~/.npmrc | |
| fi | |
| npm publish --access public | |
| echo "✅ Published @jsonbored/opennextjs-mcp@${{ steps.version.outputs.VERSION }} to npm (via NPM_TOKEN)" | |
| echo "" >&2 | |
| echo "💡 After first release, set up OIDC trusted publishing for both packages:" >&2 | |
| echo " 1. Go to: https://www.npmjs.com/settings/JSONbored/automation" >&2 | |
| echo " 2. Add trusted publisher for @jsonbored/opennextjs-cli" >&2 | |
| echo " 3. Add trusted publisher for @jsonbored/opennextjs-mcp" >&2 | |
| echo " 4. Both use: repository JSONbored/opennextjs-cli, workflow .github/workflows/release.yml" >&2 | |
| echo " Then you can remove the NPM_TOKEN secret." >&2 | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2 | |
| with: | |
| tag_name: ${{ steps.version.outputs.TAG }} | |
| name: Release ${{ steps.version.outputs.VERSION }} | |
| body: | | |
| ## Packages Released | |
| - `@jsonbored/opennextjs-cli@${{ steps.version.outputs.VERSION }}` | |
| - `@jsonbored/opennextjs-mcp@${{ steps.version.outputs.VERSION }}` | |
| ${{ env.CHANGELOG_SECTION }} | |
| draft: false | |
| prerelease: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |