Skip to content

feat: Enhance OpenNext.js CLI with project root detection and testing… #1

feat: Enhance OpenNext.js CLI with project root detection and testing…

feat: Enhance OpenNext.js CLI with project root detection and testing… #1

Workflow file for this run

# Package Release Workflow
#
# 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
#
# **Trigger:**
# Push a version tag:
# ```bash
# git tag v1.0.0
# git push origin main --tags
# ```
#
# **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:
inputs:
version:
description: 'Version to release (e.g., 0.1.0). Leave empty to use package.json version.'
required: false
type: string
jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: write # Required to create GitHub releases
id-token: write # Required for npm publish (OIDC)
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0 # Full history for changelog generation
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- 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 input version or package.json version
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
if [ -n "${{ github.event.inputs.version }}" ]; then
VERSION="${{ github.event.inputs.version }}"
else
# Read from CLI package.json
VERSION=$(node -p "require('./packages/opennextjs-cli/package.json').version")
fi
else
# Extract version from tag (e.g., v1.0.0 → 1.0.0)
TAG_NAME="${{ github.ref_name }}"
VERSION="${TAG_NAME#v}"
fi
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
- 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: Check if git-cliff is installed
id: git-cliff-check
run: |
if command -v git-cliff &> /dev/null; then
echo "installed=true" >> $GITHUB_OUTPUT
else
echo "installed=false" >> $GITHUB_OUTPUT
fi
- name: Install git-cliff (if needed)
if: steps.git-cliff-check.outputs.installed == 'false'
run: |
# Install git-cliff on Ubuntu
sudo apt-get update && sudo apt-get install -y git-cliff || \
echo "⚠️ git-cliff installation failed, will skip changelog generation" >&2
- name: Generate package-specific changelog sections
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -e
TAG_VERSION="${{ steps.version.outputs.VERSION }}"
TAG="v$TAG_VERSION"
# Generate CLI changelog section (only commits affecting packages/opennextjs-cli/**)
echo "📝 Generating CLI changelog section..."
git-cliff \
--config cliff.toml \
--include-path "packages/opennextjs-cli/**" \
--tag "$TAG" \
--latest \
--unreleased \
--output /tmp/cli-changelog.md \
--verbose || \
echo "⚠️ CLI changelog generation failed" >&2
# Generate MCP changelog section (only commits affecting packages/opennextjs-mcp/**)
echo "📝 Generating MCP changelog section..."
git-cliff \
--config cliff.toml \
--include-path "packages/opennextjs-mcp/**" \
--tag "$TAG" \
--latest \
--unreleased \
--output /tmp/mcp-changelog.md \
--verbose || \
echo "⚠️ MCP changelog generation failed" >&2
# Combine changelog sections
echo "📝 Combining changelog sections..."
# Start with version header
echo "## [$TAG_VERSION] - $(date +%Y-%m-%d)" > /tmp/changelog-section.md
echo "" >> /tmp/changelog-section.md
# Extract CLI section (skip header, get body)
if [ -f /tmp/cli-changelog.md ] && [ -s /tmp/cli-changelog.md ]; then
echo "### @jsonbored/opennextjs-cli" >> /tmp/changelog-section.md
echo "" >> /tmp/changelog-section.md
# Skip the header and version line, get the body
tail -n +2 /tmp/cli-changelog.md | sed '/^## \[/,$d' >> /tmp/changelog-section.md || true
echo "" >> /tmp/changelog-section.md
fi
# Extract MCP section (skip header, get body)
if [ -f /tmp/mcp-changelog.md ] && [ -s /tmp/mcp-changelog.md ]; then
echo "### @jsonbored/opennextjs-mcp" >> /tmp/changelog-section.md
echo "" >> /tmp/changelog-section.md
# Skip the header and version line, get the body
tail -n +2 /tmp/mcp-changelog.md | sed '/^## \[/,$d' >> /tmp/changelog-section.md || true
echo "" >> /tmp/changelog-section.md
fi
# If no package-specific sections, create a generic one
if [ ! -s /tmp/cli-changelog.md ] && [ ! -s /tmp/mcp-changelog.md ]; then
echo "Initial release of OpenNext.js CLI and MCP server packages." >> /tmp/changelog-section.md
echo "" >> /tmp/changelog-section.md
fi
# Update main CHANGELOG.md (prepend new section)
if [ -f CHANGELOG.md ]; then
# Prepend new section to existing changelog
cat /tmp/changelog-section.md CHANGELOG.md > /tmp/combined-changelog.md
mv /tmp/combined-changelog.md CHANGELOG.md
else
# Create new changelog with header
cat > CHANGELOG.md << 'EOF'
# Changelog
All notable changes to this project will be documented in this file.

Check failure on line 223 in .github/workflows/release.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/release.yml

Invalid workflow file

You have an error in your yaml syntax on line 223
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
EOF
cat /tmp/changelog-section.md >> CHANGELOG.md
fi
# Set changelog section for GitHub release
echo "CHANGELOG_SECTION<<EOF" >> $GITHUB_ENV
cat /tmp/changelog-section.md >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "✅ Changelog generated successfully"
- 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@v2
with:
tag_name: ${{ github.ref_name }}
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 }}