Skip to content

Publish Package

Publish Package #53

Workflow file for this run

name: Publish Package
on:
workflow_dispatch:
inputs:
package:
description: "Package to publish"
required: true
type: choice
options:
- all
- main
- sdk
default: "all"
version:
description: "Version bump type"
required: true
type: choice
options:
- patch
- minor
- major
- prepatch
- preminor
- premajor
- prerelease
custom_version:
description: "Custom version (optional, overrides version type)"
required: false
type: string
dry_run:
description: "Dry run (do not actually publish)"
required: false
type: boolean
default: false
tag:
description: "NPM dist-tag"
required: false
type: choice
options:
- latest
- next
- beta
- alpha
default: "latest"
# Prevent concurrent publishes
concurrency:
group: publish-package
cancel-in-progress: false
permissions:
contents: write
id-token: write
env:
NPM_CONFIG_FUND: false
jobs:
# Build Rust binaries for all platforms (in parallel)
build-binaries:
name: Build relay-pty (${{ matrix.target }})
runs-on: ${{ matrix.os }}
if: github.event.inputs.package == 'all' || github.event.inputs.package == 'main'
strategy:
matrix:
include:
- os: macos-latest
target: aarch64-apple-darwin
binary_name: relay-pty-darwin-arm64
- os: macos-latest
target: x86_64-apple-darwin
binary_name: relay-pty-darwin-x64
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
binary_name: relay-pty-linux-x64
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
binary_name: relay-pty-linux-arm64
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools (Linux ARM64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
workspaces: relay-pty
key: ${{ matrix.target }}
- name: Build relay-pty
working-directory: relay-pty
run: cargo build --release --target ${{ matrix.target }}
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
- name: Copy binary with platform name
run: cp relay-pty/target/${{ matrix.target }}/release/relay-pty bin/${{ matrix.binary_name }}
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.binary_name }}
path: bin/${{ matrix.binary_name }}
retention-days: 1
# Build all packages once, version them, and upload
build:
name: Build & Version
runs-on: ubuntu-latest
outputs:
new_version: ${{ steps.bump.outputs.new_version }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: package-lock.json
registry-url: "https://registry.npmjs.org"
- name: Install dependencies
run: npm ci
- name: Ensure rollup optional dependencies are installed
run: npm install --no-save rollup || true
- name: Version all packages
id: bump
run: |
CUSTOM_VERSION="${{ github.event.inputs.custom_version }}"
VERSION_TYPE="${{ github.event.inputs.version }}"
CURRENT_VERSION=$(node -p "require('./package.json').version")
echo "Current version: $CURRENT_VERSION"
if [ -n "$CUSTOM_VERSION" ]; then
echo "Setting version to custom value: $CUSTOM_VERSION"
npm version "$CUSTOM_VERSION" --no-git-tag-version --allow-same-version
else
echo "Bumping version: $VERSION_TYPE"
npm version "$VERSION_TYPE" --no-git-tag-version --preid=beta
fi
NEW_VERSION=$(node -p "require('./package.json').version")
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "New version: $NEW_VERSION"
# Sync all package versions and internal dependencies using node script
# (avoids npm version which validates dependencies against registry)
node -e "
const fs = require('fs');
const path = require('path');
const version = '$NEW_VERSION';
// Update root package internal dependencies
const rootPkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
for (const dep of Object.keys(rootPkg.dependencies || {})) {
if (dep.startsWith('@agent-relay/')) {
rootPkg.dependencies[dep] = version;
}
}
fs.writeFileSync('package.json', JSON.stringify(rootPkg, null, 2) + '\n');
// Update all sub-packages: version + internal dependencies
const packagesDir = 'packages';
for (const dir of fs.readdirSync(packagesDir)) {
const pkgPath = path.join(packagesDir, dir, 'package.json');
if (fs.existsSync(pkgPath)) {
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
// Update package version
pkg.version = version;
console.log('@agent-relay/' + dir);
console.log('v' + version);
// Update internal dependencies
for (const dep of Object.keys(pkg.dependencies || {})) {
if (dep.startsWith('@agent-relay/')) {
pkg.dependencies[dep] = version;
}
}
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
}
}
"
- name: Clean reinstall after version bump
run: |
# Clear npm cache and node_modules to ensure fresh platform-specific deps
npm cache clean --force
rm -rf node_modules packages/*/node_modules package-lock.json
npm install
- name: Ensure rollup optional dependencies are installed
run: npm install --no-save rollup || true
- name: Build all packages
run: npm run build
- name: Run tests
run: npm test
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-output
path: |
package.json
package-lock.json
packages/*/package.json
packages/*/dist/
dist/
retention-days: 1
# Publish all packages in parallel (npm publish doesn't need deps published first)
publish-packages:
name: Publish ${{ matrix.package }}
needs: build
runs-on: ubuntu-latest
if: github.event.inputs.package == 'all'
strategy:
fail-fast: false
max-parallel: 10
matrix:
package:
# All packages - published in parallel
- protocol
- storage
- state
- policy
- memory
- utils
- continuity
- trajectory
- hooks
- resiliency
- user-directory
- api-types
- spawner
- mcp
- config
- bridge
- wrapper
- sdk
- daemon
- telemetry
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-output
path: .
- name: Update npm for OIDC support
run: npm install -g npm@latest
- name: Dry run check
if: github.event.inputs.dry_run == 'true'
working-directory: packages/${{ matrix.package }}
run: |
echo "Dry run - would publish @agent-relay/${{ matrix.package }}"
npm publish --dry-run --access public --tag ${{ github.event.inputs.tag }} --ignore-scripts
- name: Publish to NPM
if: github.event.inputs.dry_run != 'true'
working-directory: packages/${{ matrix.package }}
run: npm publish --access public --provenance --tag ${{ github.event.inputs.tag }} --ignore-scripts
# Publish SDK only (when selected)
publish-sdk-only:
name: Publish SDK to NPM
needs: build
runs-on: ubuntu-latest
if: github.event.inputs.package == 'sdk'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-output
path: .
- name: Update npm for OIDC support
run: npm install -g npm@latest
- name: Dry run check
if: github.event.inputs.dry_run == 'true'
working-directory: packages/sdk
run: npm publish --dry-run --access public --tag ${{ github.event.inputs.tag }} --ignore-scripts
- name: Publish SDK to NPM
if: github.event.inputs.dry_run != 'true'
working-directory: packages/sdk
run: npm publish --access public --provenance --tag ${{ github.event.inputs.tag }} --ignore-scripts
# Pre-publish verification: ensure all binaries are valid before publishing
verify-binaries-linux:
name: Verify Binaries (Linux)
needs: [build, build-binaries]
runs-on: ubuntu-latest
if: github.event.inputs.package == 'all' || github.event.inputs.package == 'main'
steps:
- name: Download all binaries
uses: actions/download-artifact@v4
with:
pattern: relay-pty-*
path: bin/
merge-multiple: true
- name: List downloaded binaries
run: |
echo "Downloaded binaries:"
ls -la bin/
- name: Verify all platform binaries exist
run: |
MISSING=0
for BINARY in relay-pty-darwin-arm64 relay-pty-darwin-x64 relay-pty-linux-x64 relay-pty-linux-arm64; do
if [ -f "bin/$BINARY" ]; then
echo "✓ $BINARY exists ($(stat -c%s "bin/$BINARY" 2>/dev/null || stat -f%z "bin/$BINARY") bytes)"
else
echo "✗ $BINARY MISSING"
MISSING=1
fi
done
if [ $MISSING -eq 1 ]; then
echo ""
echo "ERROR: Some binaries are missing! Cannot publish."
exit 1
fi
echo ""
echo "All platform binaries present."
- name: Make binaries executable
run: chmod +x bin/relay-pty-*
- name: Verify Linux binary works
run: |
echo "Testing relay-pty-linux-x64 --help..."
OUTPUT=$(./bin/relay-pty-linux-x64 --help 2>&1) || true
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "PTY wrapper"; then
echo "✓ Linux x64 binary works"
else
echo "✗ Linux x64 binary failed --help test"
exit 1
fi
- name: Verify binary sizes are reasonable
run: |
# Binaries should be at least 1MB (to catch empty/corrupt files)
MIN_SIZE=1000000
for BINARY in bin/relay-pty-*; do
SIZE=$(stat -c%s "$BINARY" 2>/dev/null || stat -f%z "$BINARY")
if [ "$SIZE" -lt "$MIN_SIZE" ]; then
echo "✗ $BINARY is suspiciously small: $SIZE bytes"
exit 1
fi
echo "✓ $BINARY size OK: $SIZE bytes"
done
- name: Summary
run: |
echo "## Pre-Publish Binary Verification (Linux)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Binary | Status | Size |" >> $GITHUB_STEP_SUMMARY
echo "|--------|--------|------|" >> $GITHUB_STEP_SUMMARY
for BINARY in bin/relay-pty-*; do
NAME=$(basename "$BINARY")
SIZE=$(stat -c%s "$BINARY" 2>/dev/null || stat -f%z "$BINARY")
SIZE_MB=$(echo "scale=2; $SIZE / 1048576" | bc)
echo "| $NAME | ✅ | ${SIZE_MB}MB |" >> $GITHUB_STEP_SUMMARY
done
# Verify macOS binaries actually work on macOS (critical - catches issues before publish)
verify-binaries-macos:
name: Verify Binaries (macOS ${{ matrix.arch }})
needs: [build-binaries]
if: github.event.inputs.package == 'all' || github.event.inputs.package == 'main'
strategy:
fail-fast: true
matrix:
include:
- os: macos-latest
arch: arm64
binary: relay-pty-darwin-arm64
runs-on: ${{ matrix.os }}
steps:
- name: Download binary
uses: actions/download-artifact@v4
with:
name: ${{ matrix.binary }}
path: bin/
- name: Make binary executable
run: chmod +x bin/${{ matrix.binary }}
- name: Verify binary exists and has reasonable size
run: |
if [ ! -f "bin/${{ matrix.binary }}" ]; then
echo "ERROR: Binary not found!"
exit 1
fi
SIZE=$(stat -f%z "bin/${{ matrix.binary }}")
echo "Binary size: $SIZE bytes"
# Should be at least 1MB
if [ "$SIZE" -lt 1000000 ]; then
echo "ERROR: Binary is suspiciously small!"
exit 1
fi
echo "✓ Binary exists with valid size"
- name: Test binary --help
run: |
echo "Testing ${{ matrix.binary }} --help..."
OUTPUT=$(./bin/${{ matrix.binary }} --help 2>&1) || true
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "PTY wrapper"; then
echo "✓ ${{ matrix.binary }} --help works"
else
echo "✗ ${{ matrix.binary }} --help failed!"
exit 1
fi
- name: Summary
run: |
echo "## macOS ${{ matrix.arch }} Binary Verification" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "✅ **${{ matrix.binary }}** verified on macOS ${{ matrix.arch }}" >> $GITHUB_STEP_SUMMARY
# Gate job that requires both Linux and macOS verification to pass
verify-binaries:
name: All Binaries Verified
needs: [verify-binaries-linux, verify-binaries-macos]
runs-on: ubuntu-latest
if: github.event.inputs.package == 'all' || github.event.inputs.package == 'main'
steps:
- name: All binary checks passed
run: |
echo "✅ All binary verification checks passed!"
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Binary Verification Gate" >> $GITHUB_STEP_SUMMARY
echo "✅ All platform binaries verified and ready for publish" >> $GITHUB_STEP_SUMMARY
# Publish main package
publish-main:
name: Publish Main Package
needs: [build, build-binaries, verify-binaries, publish-packages]
runs-on: ubuntu-latest
if: |
always() &&
(github.event.inputs.package == 'all' || github.event.inputs.package == 'main') &&
needs.build.result == 'success' &&
needs.verify-binaries.result == 'success' &&
(needs.publish-packages.result == 'success' || needs.publish-packages.result == 'skipped')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-output
path: .
- name: Download relay-pty binaries
uses: actions/download-artifact@v4
with:
pattern: relay-pty-*
path: bin/
merge-multiple: true
- name: Make binaries executable
run: chmod +x bin/relay-pty-*
- name: Update npm for OIDC support
run: npm install -g npm@latest
- name: Dry run check
if: github.event.inputs.dry_run == 'true'
run: |
echo "Dry run - would publish agent-relay@${{ needs.build.outputs.new_version }}"
npm publish --dry-run --access public --tag ${{ github.event.inputs.tag }} --ignore-scripts
- name: Publish to NPM
if: github.event.inputs.dry_run != 'true'
run: npm publish --access public --provenance --tag ${{ github.event.inputs.tag }} --ignore-scripts
# Create git tag and release
create-release:
name: Create Release
needs: [build, build-binaries, verify-binaries, publish-main]
runs-on: ubuntu-latest
if: |
always() &&
github.event.inputs.dry_run != 'true' &&
needs.publish-main.result == 'success'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-output
path: .
- name: Download relay-pty binaries
uses: actions/download-artifact@v4
with:
pattern: relay-pty-*
path: release-binaries/
merge-multiple: true
- name: Make binaries executable
run: chmod +x release-binaries/relay-pty-*
- name: Commit and tag
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
NEW_VERSION="${{ needs.build.outputs.new_version }}"
git add package.json package-lock.json packages/*/package.json
if ! git diff --staged --quiet; then
git commit -m "chore(release): v${NEW_VERSION}"
git push origin HEAD:main
fi
git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}"
git push origin "v${NEW_VERSION}"
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ needs.build.outputs.new_version }}
name: v${{ needs.build.outputs.new_version }}
body: |
## agent-relay v${{ needs.build.outputs.new_version }}
### Installation
```bash
npm install -g agent-relay@${{ needs.build.outputs.new_version }}
npm install @agent-relay/sdk@${{ needs.build.outputs.new_version }}
```
### relay-pty binaries
Pre-built binaries for PTY support:
- `relay-pty-linux-x64` - Linux x86_64
- `relay-pty-linux-arm64` - Linux ARM64
- `relay-pty-darwin-x64` - macOS Intel
- `relay-pty-darwin-arm64` - macOS Apple Silicon
files: |
release-binaries/relay-pty-*
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Trigger post-publish verification
verify-publish:
name: Verify Published Package
needs: [build, publish-main]
if: |
always() &&
github.event.inputs.dry_run != 'true' &&
needs.publish-main.result == 'success'
uses: ./.github/workflows/verify-publish.yml
with:
version: ${{ needs.build.outputs.new_version }}
summary:
name: Summary
needs: [build, build-binaries, verify-binaries, publish-packages, publish-main, verify-publish]
runs-on: ubuntu-latest
if: always()
steps:
- name: Summary
run: |
echo "## NPM Publish Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version**: \`${{ needs.build.outputs.new_version }}\`" >> $GITHUB_STEP_SUMMARY
echo "**NPM Tag**: \`${{ github.event.inputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
echo "**Dry Run**: \`${{ github.event.inputs.dry_run }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Results" >> $GITHUB_STEP_SUMMARY
echo "| Stage | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Build | ${{ needs.build.result == 'success' && '✅' || '❌' }} ${{ needs.build.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Build Binaries | ${{ needs.build-binaries.result == 'success' && '✅' || (needs.build-binaries.result == 'skipped' && '⏭️' || '❌') }} ${{ needs.build-binaries.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Verify Binaries** | ${{ needs.verify-binaries.result == 'success' && '✅' || (needs.verify-binaries.result == 'skipped' && '⏭️' || '❌') }} ${{ needs.verify-binaries.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Publish Packages | ${{ needs.publish-packages.result == 'success' && '✅' || (needs.publish-packages.result == 'skipped' && '⏭️' || '❌') }} ${{ needs.publish-packages.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Publish Main | ${{ needs.publish-main.result == 'success' && '✅' || (needs.publish-main.result == 'skipped' && '⏭️' || '❌') }} ${{ needs.publish-main.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Post-Publish Verify | ${{ needs.verify-publish.result == 'success' && '✅' || (needs.verify-publish.result == 'skipped' && '⏭️' || '❌') }} ${{ needs.verify-publish.result }} |" >> $GITHUB_STEP_SUMMARY