Skip to content

Release

Release #97

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., 2.6.0) - leave empty to use existing tag'
required: false
type: string
dry_run:
description: 'Dry run (skip actual publish)'
required: false
type: boolean
default: false
concurrency:
group: release-${{ github.ref_name }}
cancel-in-progress: false
permissions:
contents: read
jobs:
validate:
name: Validate Release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
npm_tag: ${{ steps.version.outputs.npm_tag }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" = "push" ]; then
# Tag push - extract version from tag
TAG="${GITHUB_REF#refs/tags/}"
VERSION="${TAG#v}"
elif [ -n "${{ inputs.version }}" ]; then
# Manual dispatch with version input
VERSION="${{ inputs.version }}"
TAG="v${VERSION}"
else
# Manual dispatch without version - use package.json
VERSION=$(node -p "require('./package.json').version")
TAG="v${VERSION}"
fi
if [[ "$VERSION" == *"-"* ]]; then
PRE_ID="${VERSION#*-}"
PRE_ID="${PRE_ID%%.*}"
case "$PRE_ID" in
beta) NPM_TAG="beta";;
rc) NPM_TAG="rc";;
*) NPM_TAG="next";;
esac
else
NPM_TAG="latest"
fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "npm_tag=${NPM_TAG}" >> $GITHUB_OUTPUT
echo "[PKG] Release version: ${VERSION} (tag: ${TAG})"
echo "[PKG] npm tag: ${NPM_TAG}"
- name: Validate version format
run: |
VERSION="${{ steps.version.outputs.version }}"
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
echo "[ERROR] Invalid version format: $VERSION"
echo "Expected: X.Y.Z or X.Y.Z-prerelease"
exit 1
fi
echo "[OK] Version format valid: $VERSION"
- name: Enforce release source ref
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${GITHUB_REF}" != "refs/heads/main" ]; then
echo "[ERROR] workflow_dispatch releases must run from refs/heads/main (got ${GITHUB_REF})"
exit 1
fi
if [ "${{ github.event_name }}" = "push" ]; then
git fetch origin main
if ! git merge-base --is-ancestor "${GITHUB_SHA}" "origin/main"; then
echo "[ERROR] Release tag commit is not reachable from origin/main"
exit 1
fi
fi
echo "[OK] Release source ref is valid"
- name: Verify version consistency
run: |
VERSION="${{ steps.version.outputs.version }}"
echo "Checking version consistency across all files..."
# Check package.json
PKG_VERSION=$(node -p "require('./package.json').version")
if [ "$PKG_VERSION" != "$VERSION" ]; then
echo "[ERROR] package.json version ($PKG_VERSION) doesn't match release version ($VERSION)"
exit 1
fi
# Check root plugin.json (plugin repos have independent versions)
PLUGIN_VERSION=$(node -p "require('./.claude-plugin/plugin.json').version")
if [ "$PLUGIN_VERSION" != "$VERSION" ]; then
echo "[ERROR] .claude-plugin/plugin.json version ($PLUGIN_VERSION) doesn't match release version ($VERSION)"
exit 1
fi
echo "[OK] All version numbers match: $VERSION"
test:
name: Run Tests
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run tests
run: npm test
- name: Validate plugin structure
run: node scripts/validate-plugins.js
- name: Validate repo consistency
run: npm run validate
- name: Validate agent templates
run: node scripts/expand-templates.js --check
- name: Validate adapter freshness
run: node scripts/gen-adapters.js --check
publish-npm:
name: Publish to npm
runs-on: ubuntu-latest
needs: [validate, test]
environment: npm
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: '20'
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- name: Publish to npm
if: ${{ !inputs.dry_run }}
run: npm publish --provenance --access public --tag "${{ needs.validate.outputs.npm_tag }}"
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Dry run publish
if: ${{ inputs.dry_run }}
run: |
echo "[DRY-RUN] Skipping actual publish"
npm publish --dry-run --tag "${{ needs.validate.outputs.npm_tag }}"
create-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [validate, publish-npm]
if: ${{ !inputs.dry_run }}
permissions:
contents: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Create tag if needed
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
TAG="${{ needs.validate.outputs.tag }}"
if ! git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Creating tag: $TAG"
git tag "$TAG"
git push origin "$TAG"
else
echo "Tag $TAG already exists"
fi
- name: Extract changelog for version
id: changelog
run: |
VERSION="${{ needs.validate.outputs.version }}"
# Extract changelog section for this version
CHANGELOG=$(awk "/^## \\[${VERSION}\\]/{flag=1; next} /^## \\[/{flag=0} flag" CHANGELOG.md)
if [ -z "$CHANGELOG" ]; then
CHANGELOG="See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details."
fi
# Write to file to preserve formatting
echo "$CHANGELOG" > release_notes.md
echo "Generated release notes:"
cat release_notes.md
- name: Create GitHub Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
with:
tag_name: ${{ needs.validate.outputs.tag }}
name: ${{ needs.validate.outputs.tag }}
body_path: release_notes.md
draft: false
prerelease: ${{ contains(needs.validate.outputs.version, '-') }}
generate_release_notes: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
notify:
name: Release Summary
runs-on: ubuntu-latest
needs: [validate, publish-npm, create-release]
if: always()
steps:
- name: Summary
run: |
echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ needs.validate.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**Tag:** ${{ needs.validate.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.publish-npm.result }}" = "success" ]; then
echo "[OK] **npm:** Published successfully" >> $GITHUB_STEP_SUMMARY
echo " - https://www.npmjs.com/package/agentsys" >> $GITHUB_STEP_SUMMARY
else
echo "[ERROR] **npm:** ${{ needs.publish-npm.result }}" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.create-release.result }}" = "success" ]; then
echo "[OK] **GitHub Release:** Created successfully" >> $GITHUB_STEP_SUMMARY
echo " - https://github.com/${{ github.repository }}/releases/tag/${{ needs.validate.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.create-release.result }}" = "skipped" ]; then
echo "[SKIP] **GitHub Release:** Skipped (dry run)" >> $GITHUB_STEP_SUMMARY
else
echo "[ERROR] **GitHub Release:** ${{ needs.create-release.result }}" >> $GITHUB_STEP_SUMMARY
fi