Skip to content

Publish Packages

Publish Packages #68

Workflow file for this run

name: Publish Packages
on:
push:
branches: [master]
paths:
- "packages/*/package.json"
workflow_dispatch:
permissions:
contents: write
id-token: write
env:
NODE_VERSION: "22"
jobs:
# =========================================================================
# GATE — only publish from release-prepare workflow (release/* branch merges)
# =========================================================================
release-gate:
name: 🔒 Release Gate
runs-on: ubuntu-latest
outputs:
is-release: ${{ steps.check.outputs.is-release }}
steps:
- name: 📥 Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 5
- name: 🔒 Check if this is a release commit
id: check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# For workflow_dispatch, always allow
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "is-release=true" >> $GITHUB_OUTPUT
echo "✅ Manual trigger — allowed"
exit 0
fi
# Check if the merge commit came from a release/* branch
COMMIT_MSG=$(git log -1 --pretty=%s)
echo "Commit message: $COMMIT_MSG"
# Match: merge commits from release/*, squash merges (PR title "Release ..."), or release prepare commits
if echo "$COMMIT_MSG" | grep -qE "(^Merge pull request .* from .*/release/|^Release |^chore: prepare release)"; then
echo "is-release=true" >> $GITHUB_OUTPUT
echo "✅ Release commit detected — proceeding with publish"
else
echo "is-release=false" >> $GITHUB_OUTPUT
echo "⛔ Not a release commit — skipping publish"
echo "💡 All releases must go through the release-prepare workflow"
fi
detect-changes:
name: 🔍 Detect Version Changes
needs: [release-gate]
if: needs.release-gate.outputs.is-release == 'true'
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.detect.outputs.packages }}
has-changes: ${{ steps.detect.outputs.has-changes }}
steps:
- name: 📥 Checkout code
uses: actions/checkout@v6
- name: 🔧 Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- name: 📦 Install dependencies
run: npm ci
- name: 🔍 Detect changed package versions
id: detect
run: |
# Function to get package info
get_package_info() {
local pkg_dir=$1
local pkg_json="${pkg_dir}/package.json"
if [ ! -f "$pkg_json" ]; then
return
fi
local name=$(node -p "require('./${pkg_json}').name")
local version=$(node -p "require('./${pkg_json}').version")
local private=$(node -p "require('./${pkg_json}').private || false")
# Skip private packages
if [ "$private" = "true" ]; then
return
fi
echo "${pkg_dir}|${name}|${version}"
}
# Function to check if version exists on npm
version_exists_on_npm() {
local name=$1
local version=$2
# Try to get the specific version from npm
if npm view "${name}@${version}" version 2>/dev/null; then
return 0 # Version exists
else
return 1 # Version doesn't exist
fi
}
changed_packages=()
# Check packages in dependency order (core first, then platform)
for pkg_dir in packages/core packages/platform; do
if [ ! -d "$pkg_dir" ]; then
continue
fi
pkg_info=$(get_package_info "$pkg_dir")
if [ -z "$pkg_info" ]; then
continue
fi
IFS='|' read -r dir name version <<< "$pkg_info"
echo "📦 Checking ${name}@${version}..."
# Check if this version exists on npm
if version_exists_on_npm "$name" "$version"; then
echo "⏭️ Version ${version} already published for ${name}"
else
echo "✨ New version detected: ${name}@${version}"
changed_packages+=("{\"name\":\"${name}\",\"version\":\"${version}\",\"dir\":\"${dir}\"}")
fi
done
# Build JSON array
if [ ${#changed_packages[@]} -eq 0 ]; then
echo "has-changes=false" >> $GITHUB_OUTPUT
echo "packages=[]" >> $GITHUB_OUTPUT
echo "ℹ️ No version changes detected"
else
echo "has-changes=true" >> $GITHUB_OUTPUT
packages_json=$(printf '%s\n' "${changed_packages[@]}" | jq -s -c '.')
echo "packages=${packages_json}" >> $GITHUB_OUTPUT
echo "🎯 Packages to publish:"
echo "${packages_json}" | jq '.'
fi
verify:
name: ✅ Verify All Packages
needs: [detect-changes]
if: needs.detect-changes.outputs.has-changes == 'true'
runs-on: ubuntu-latest
steps:
- name: 📥 Checkout code
uses: actions/checkout@v6
- name: 🔧 Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- name: 📦 Install dependencies
run: npm ci
- name: 🔍 Type check
run: npm run type-check
- name: 🎨 Lint
run: npm run lint
- name: 🎨 Format check
run: npm run format:check
- name: 🧪 Test
run: npm run test
- name: 🏗️ Build
run: npm run build
- name: ✅ All checks passed
run: echo "✅ All packages verified successfully" >> $GITHUB_STEP_SUMMARY
publish:
name: 📤 Publish ${{ matrix.package.name }}@${{ matrix.package.version }}
needs: [detect-changes, verify]
if: needs.detect-changes.outputs.has-changes == 'true' && needs.verify.result == 'success'
runs-on: ubuntu-latest
strategy:
matrix:
package: ${{ fromJson(needs.detect-changes.outputs.packages) }}
max-parallel: 1
fail-fast: true
steps:
- name: 📥 Checkout code
uses: actions/checkout@v6
- name: 🔧 Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
registry-url: "https://registry.npmjs.org"
- name: 📦 Install dependencies
run: npm ci
- name: 🏗️ Build all workspace packages
run: npm run build
- name: 📤 Publish to npm
working-directory: ${{ matrix.package.dir }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
echo "📤 Publishing ${{ matrix.package.name }}@${{ matrix.package.version }}"
npm publish --access public
- name: 🏷️ Create git tag
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
# Extract package name without scope
PKG_SHORT_NAME=$(echo "${{ matrix.package.name }}" | sed 's/@agentage\///')
TAG_NAME="${PKG_SHORT_NAME}@${{ matrix.package.version }}"
git tag -a "${TAG_NAME}" -m "Release ${{ matrix.package.name }}@${{ matrix.package.version }}"
git push origin "${TAG_NAME}"
- name: ✅ Published successfully
run: |
echo "🎉 Successfully published ${{ matrix.package.name }}@${{ matrix.package.version }}" >> $GITHUB_STEP_SUMMARY
echo "📦 Package URL: https://www.npmjs.com/package/${{ matrix.package.name }}/v/${{ matrix.package.version }}" >> $GITHUB_STEP_SUMMARY
summary:
name: 📋 Publish Summary
needs: [release-gate, detect-changes, verify, publish]
if: always()
runs-on: ubuntu-latest
steps:
- name: 📊 Create summary
run: |
echo "## 📦 Package Publishing Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.release-gate.outputs.is-release }}" != "true" ]; then
echo "⛔ **Blocked — not a release commit**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "All releases must go through the \`release-prepare\` workflow." >> $GITHUB_STEP_SUMMARY
echo "Run: \`gh workflow run release-prepare.yml -f bump_type=patch -f packages=core,platform\`" >> $GITHUB_STEP_SUMMARY
exit 0
elif [ "${{ needs.verify.result }}" != "success" ]; then
echo "❌ **Verification failed - packages not published**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Please fix verification errors and try again." >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.publish.result }}" == "success" ]; then
echo "✅ **All packages published successfully!**" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.publish.result }}" == "skipped" ]; then
echo "⏭️ **Publishing skipped - verification failed**" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ **Some packages failed to publish**" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Packages" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
packages='${{ needs.detect-changes.outputs.packages }}'
if [ "${{ needs.verify.result }}" == "success" ] && [ "${{ needs.publish.result }}" == "success" ]; then
echo "$packages" | jq -r '.[] | "- ✅ **\(.name)** version \(.version) - Published"' >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.verify.result }}" != "success" ]; then
echo "$packages" | jq -r '.[] | "- ❌ **\(.name)** version \(.version) - Verification failed"' >> $GITHUB_STEP_SUMMARY
else
echo "$packages" | jq -r '.[] | "- ⏭️ **\(.name)** version \(.version) - Skipped"' >> $GITHUB_STEP_SUMMARY
fi