Skip to content

fix: rename agent ID from @wilson to @winston across entire codebase #267

fix: rename agent ID from @wilson to @winston across entire codebase

fix: rename agent ID from @wilson to @winston across entire codebase #267

name: Evolith SDK CLI - Release Pipeline
on:
push:
branches:
- main
paths:
- 'sdk/cli/**'
- '.release-please-manifest.json'
- 'release-please-config.json'
- '.github/workflows/sdk-cli-release.yml'
- '.harness/**'
workflow_dispatch:
env:
NODE_VERSION: '20'
CLI_DIR: 'sdk/cli'
jobs:
# ============================================
# GATE 0: Evolith Core Validation
# ============================================
core-validation:
name: Evolith Core Validation
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: package-lock.json
- name: Install Dependencies
run: npm ci
- name: Validate Documentation Standards
run: node .harness/scripts/ci/01-validate-docs.mjs
- name: Check Bilingual Parity
run: node .harness/scripts/ci/04-check-bilingual-parity.mjs
- name: Verify Root Cleanliness
run: node .harness/scripts/ci/03-validate-root-cleanliness.mjs
- name: Verify Version Alignment
run: |
node .harness/scripts/verify-version-log.mjs
node .harness/scripts/verify-git-tag.mjs
# ============================================
# GATE 1: Release Please
# ============================================
release-please:
needs: core-validation
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
outputs:
release_created: ${{ steps.release.outputs.cli_release_created }}
tag_name: ${{ steps.release.outputs.cli_tag_name }}
version: ${{ steps.release.outputs.cli_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run Release Please
id: release
uses: googleapis/release-please-action@v4
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
release-type: node
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
- name: Log Release Decision
if: ${{ steps.release.outputs.cli_release_created }}
run: |
echo "Release created: ${{ steps.release.outputs.cli_tag_name }}"
echo "Version: ${{ steps.release.outputs.cli_version }}"
- name: No Release Needed
if: ${{ !steps.release.outputs.cli_release_created }}
run: |
echo "ℹ️ No release needed (no changes detected)"
# ============================================
# GATE 2: Build and Test
# ============================================
build-and-test:
needs: release-please
if: ${{ needs.release-please.outputs.release_created }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
- name: Install Dependencies
run: npm ci
- name: Generate SBOM (CycloneDX)
run: npx @cyclonedx/cyclonedx-npm --output-file sbom.json
- name: Architecture Boundary Lint (eslint-plugin-boundaries)
working-directory: ${{ env.CLI_DIR }}
run: npm run lint
- name: Type Check and Build
working-directory: ${{ env.CLI_DIR }}
run: npm run build
- name: Security Audit
working-directory: ${{ env.CLI_DIR }}
run: npm audit --audit-level=high
- name: Unit Tests (MUST PASS)
working-directory: ${{ env.CLI_DIR }}
run: npm test
- name: Verify Package Integrity
run: |
echo "Verifying package..."
node -e "
const pkg = require('./${{ env.CLI_DIR }}/package.json');
const binTargets = typeof pkg.bin === 'string' ? [pkg.bin] : Object.values(pkg.bin);
console.log('Name:', pkg.name);
console.log('Version:', pkg.version);
console.log('Main:', pkg.main);
console.log('Bin:', binTargets);
if (!pkg.name || !pkg.version || !pkg.main || binTargets.length === 0) {
throw new Error('package.json is missing required identity fields');
}
"
# ============================================
# GATE 3: Publish to NPM
# ============================================
publish-npm:
needs: build-and-test
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
- name: Install Dependencies
run: npm ci
- name: Build
working-directory: ${{ env.CLI_DIR }}
run: npm run build
- name: Publish to NPM
working-directory: ${{ env.CLI_DIR }}
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Confirm Publication
run: |
echo "✅ Published @evolith/smart-cli@${{ needs.release-please.outputs.version }} to NPM"
# ============================================
# GATE 4: Package Binaries
# ============================================
package-binaries:
needs: publish-npm
runs-on: ubuntu-latest
strategy:
matrix:
include:
- os: ubuntu-latest
target: node20-linux-x64
ext: ''
- os: macos-latest
target: node20-macos-x64
ext: ''
- os: windows-latest
target: node20-win-x64
ext: '.exe'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Dependencies
run: npm ci
- name: Build
working-directory: ${{ env.CLI_DIR }}
run: npm run build
- name: Package Binary
id: pkg
working-directory: ${{ env.CLI_DIR }}
run: npx pkg@5.8.1 . --targets ${{ matrix.target }} --output-dir ./binaries
- name: Rename Binary
shell: bash
run: |
cd ${{ env.CLI_DIR }}/binaries
produced=$(ls | head -1)
if [ -z "$produced" ]; then
echo "❌ pkg produced no binary for target ${{ matrix.target }}"
exit 1
fi
mv "$produced" "smart-cli-${{ matrix.target }}${{ matrix.ext }}"
ls -la
- name: Upload Binary Artifact
uses: actions/upload-artifact@v4
with:
name: binaries-${{ matrix.target }}
path: ${{ env.CLI_DIR }}/binaries/smart-cli-${{ matrix.target }}${{ matrix.ext }}
if-no-files-found: error
retention-days: 7
# ============================================
# GATE 5: Binary Smoke Test
# ============================================
smoke-test:
needs: [package-binaries, release-please]
strategy:
fail-fast: true
matrix:
include:
- os: ubuntu-latest
target: node20-linux-x64
bin: smart-cli-node20-linux-x64
- os: macos-latest
target: node20-macos-x64
bin: smart-cli-node20-macos-x64
- os: windows-latest
target: node20-win-x64
bin: smart-cli-node20-win-x64.exe
runs-on: ${{ matrix.os }}
steps:
- name: Download Binary
uses: actions/download-artifact@v4
with:
name: binaries-${{ matrix.target }}
path: ./binaries
- name: Setup Binary (non-Windows)
if: runner.os != 'Windows'
shell: bash
run: chmod +x ./binaries/${{ matrix.bin }}
- name: Smoke Test - Help Command
shell: bash
run: |
./binaries/${{ matrix.bin }} --help || { echo "❌ Binary failed to execute on ${{ matrix.os }}"; exit 1; }
echo "✅ Binary executable works on ${{ matrix.os }}"
- name: Smoke Test - Version
shell: bash
run: |
VERSION=$(./binaries/${{ matrix.bin }} --version) || { echo "❌ Binary --version failed on ${{ matrix.os }}"; exit 1; }
echo "Binary version: $VERSION"
# ============================================
# GATE 5b: Functional + NPM Smoke (Linux)
# ============================================
smoke-test-functional:
needs: [package-binaries, release-please]
runs-on: ubuntu-latest
steps:
- name: Download Binary
uses: actions/download-artifact@v4
with:
name: binaries-node20-linux-x64
path: ./binaries
- name: Setup Binary
shell: bash
run: chmod +x ./binaries/smart-cli-node20-linux-x64
- name: Smoke Test - Init Command
shell: bash
run: |
mkdir -p /tmp/evolith-test
cd /tmp/evolith-test
echo "test-project" | "$GITHUB_WORKSPACE/binaries/smart-cli-node20-linux-x64" init --runtime nodejs --monorepo none --arch clean \
|| { echo "❌ Init command smoke test failed"; exit 1; }
ls -la
echo "✅ Init command smoke test completed"
- name: Smoke Test - NPM Package
shell: bash
run: |
npm install -g @evolith/smart-cli@${{ needs.release-please.outputs.version }}
smart-cli --help || { echo "❌ NPM binary failed to execute"; exit 1; }
echo "✅ NPM package smoke test completed"
# ============================================
# GATE 6: Upload Release Assets
# ============================================
upload-assets:
needs: [package-binaries, smoke-test, smoke-test-functional]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download All Binaries
uses: actions/download-artifact@v4
with:
pattern: binaries-*
path: ./release-assets
merge-multiple: true
- name: List Assets
run: |
ls -la ./release-assets/
count=$(find ./release-assets -type f | wc -l)
if [ "$count" -lt 3 ]; then
echo "❌ Expected 3 platform binaries, found $count"
exit 1
fi
echo "✅ All $count platform binaries present"
- name: Upload to Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.release-please.outputs.tag_name }}
name: Release ${{ needs.release-please.outputs.tag_name }}
body: |
## Release Summary
**Version:** `${{ needs.release-please.outputs.version }}`
### ✅ Quality Gates Passed
- [x] Core Validation (docs, bilingual, cleanliness)
- [x] Build and Type Check
- [x] Unit Tests
- [x] Security Audit
- [x] NPM Publication
- [x] Binary Packaging
- [x] Smoke Tests
### 📦 Assets
| Platform | Architecture | Download |
|----------|--------------|----------|
| Linux | x64 | `smart-cli-node20-linux-x64` |
| macOS | x64 | `smart-cli-node20-macos-x64` |
| Windows | x64 | `smart-cli-node20-win-x64.exe` |
### 🚀 Installation
```bash
# NPM
npm install -g @evolith/smart-cli
# Or download binary directly
```
files: release-assets/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Confirm Release
run: |
echo "✅ Release ${{ needs.release-please.outputs.tag_name }} published with all assets"
# ============================================
# Cleanup on Failure
# ============================================
failure-notification:
needs: [publish-npm, package-binaries, smoke-test, smoke-test-functional, upload-assets]
if: failure()
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Notify Failure
run: |
echo "❌ Release pipeline failed"
echo "Please check the logs above for details"
shell: bash
- name: Create Issue on Failure
if: ${{ github.event_name == 'push' }}
uses: actions/github-script@v7
with:
script: |
try {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '❌ Release Pipeline Failed',
body: `Release pipeline failed for tag ${{ needs.release-please.outputs.tag_name || 'unknown' }}
Please check the workflow logs and fix the issue.
@beyondnetcode/evolith-team`,
labels: ['bug', 'release']
});
core.info('Failure issue created');
} catch (error) {
// Degrade safely: a missing permission must not fail the notifier itself.
core.warning(`Could not create failure issue: ${error.message}`);
}