Skip to content

Release CUA packages #11

Release CUA packages

Release CUA packages #11

Workflow file for this run

name: Release CUA CLI
on:
push:
tags:
- "cua-cli/v*"
workflow_dispatch:
inputs:
dist_tag:
description: "npm dist-tag to publish under (never 'latest'); also used as the prerelease id"
required: true
default: "ext"
type: string
permissions:
contents: read
id-token: write
concurrency:
group: release-cua-cli-${{ github.event_name }}-${{ github.ref_name }}
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
PTYWRIGHT_ZIG_VERSION: "0.15.2"
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Verify production release
if: github.event_name == 'push'
run: |
git fetch origin main:refs/remotes/origin/main
git merge-base --is-ancestor "$GITHUB_SHA" origin/main
node --input-type=module <<'EOF'
import { readFileSync } from "node:fs";
const tag = process.env.GITHUB_REF_NAME;
const prefix = "cua-cli/v";
if (!tag?.startsWith(prefix)) {
throw new Error(`Expected tag to start with ${prefix}, got ${tag}`);
}
const tagVersion = tag.slice(prefix.length);
const pkg = JSON.parse(readFileSync("packages/cli/package.json", "utf8"));
if (pkg.version !== tagVersion) {
throw new Error(`Tag version ${tagVersion} does not match ${pkg.name} package.json version ${pkg.version}`);
}
console.log(`${pkg.name}@${pkg.version}`);
EOF
- uses: actions/setup-node@v5
with:
node-version: 24
registry-url: https://registry.npmjs.org
- name: Ensure npm supports trusted publishing
run: npm install -g npm@^11.5.1
- run: npm ci
- name: Compute prerelease version
if: github.event_name == 'workflow_dispatch'
id: prerelease
env:
DIST_TAG: ${{ inputs.dist_tag }}
run: |
if [[ ! "$DIST_TAG" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
echo "dist_tag must be lowercase alphanumeric/hyphen, e.g. ext, next, pr-41 (got '$DIST_TAG')" >&2
exit 1
fi
if [[ "$DIST_TAG" == "latest" ]]; then
echo "refusing to publish a prerelease to the 'latest' dist-tag" >&2
exit 1
fi
BASE=$(node -p "require('./packages/cli/package.json').version")
VERSION="${BASE}-${DIST_TAG}.${GITHUB_RUN_NUMBER}"
npm pkg set version="$VERSION" --workspace @onkernel/cua-cli
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "cua-cli prerelease version: $VERSION"
- name: Cache Zig toolchain
if: github.event_name == 'push'
uses: actions/cache@v4
with:
path: .dev/tools/zig-x86_64-linux-${{ env.PTYWRIGHT_ZIG_VERSION }}
key: zig-${{ runner.os }}-${{ env.PTYWRIGHT_ZIG_VERSION }}
- name: Install Zig ${{ env.PTYWRIGHT_ZIG_VERSION }}
if: github.event_name == 'push'
run: |
set -euo pipefail
ZIG_DIR=".dev/tools/zig-x86_64-linux-${PTYWRIGHT_ZIG_VERSION}"
if [ ! -x "${ZIG_DIR}/zig" ]; then
mkdir -p .dev/tools
curl -fsSL "https://ziglang.org/download/${PTYWRIGHT_ZIG_VERSION}/zig-x86_64-linux-${PTYWRIGHT_ZIG_VERSION}.tar.xz" \
| tar -xJ -C .dev/tools
fi
"${ZIG_DIR}/zig" version
echo "${GITHUB_WORKSPACE}/${ZIG_DIR}" >> "${GITHUB_PATH}"
- name: Cache ptywright native artifacts
if: github.event_name == 'push'
uses: actions/cache@v4
with:
path: |
packages/ptywright/.cache
packages/ptywright/native/build
key: ptywright-${{ runner.os }}-${{ hashFiles('packages/ptywright/GHOSTTY_UPSTREAM', 'packages/ptywright/native/**', 'packages/ptywright/scripts/**') }}
- run: npm run build --workspace @onkernel/cua-ai
- run: npm run build --workspace @onkernel/cua-agent
- name: Verify published production dependencies
if: github.event_name == 'push'
run: |
node -p 'require("./packages/cli/package.json").dependencies["@onkernel/cua-ai"]' \
| xargs -I{} npm view @onkernel/cua-ai@{} version
node -p 'require("./packages/cli/package.json").dependencies["@onkernel/cua-agent"]' \
| xargs -I{} npm view @onkernel/cua-agent@{} version
- name: Build ptywright (native binding)
if: github.event_name == 'push'
run: npm run build --workspace @onkernel/ptywright
- name: Build cua-cli
run: npm run build --workspace @onkernel/cua-cli
- name: CLI unit tests
if: github.event_name == 'push'
env:
PTYWRIGHT_REQUIRED: "1"
run: npm test --workspace @onkernel/cua-cli
- name: Pack production tarball
if: github.event_name == 'push'
run: npm pack --workspace @onkernel/cua-cli --pack-destination "$RUNNER_TEMP"
- name: Pack branch workspace dependencies
if: github.event_name == 'workflow_dispatch'
run: |
mkdir -p "$RUNNER_TEMP/workspace-packages"
npm pack --workspace @onkernel/cua-ai --pack-destination "$RUNNER_TEMP/workspace-packages"
npm pack --workspace @onkernel/cua-agent --pack-destination "$RUNNER_TEMP/workspace-packages"
- name: Pack bundled CLI prerelease
if: github.event_name == 'workflow_dispatch'
run: |
export STAGE_DIR="$RUNNER_TEMP/cua-cli-prerelease"
mkdir -p "$STAGE_DIR/node_modules/@onkernel"
cp packages/cli/package.json packages/cli/README.md "$STAGE_DIR/"
cp -R packages/cli/dist "$STAGE_DIR/"
for package in cua-ai cua-agent; do
tarball=$(find "$RUNNER_TEMP/workspace-packages" -name "onkernel-${package}-*.tgz" -print -quit)
mkdir -p "$STAGE_DIR/node_modules/@onkernel/$package"
tar -xzf "$tarball" --strip-components=1 -C "$STAGE_DIR/node_modules/@onkernel/$package"
done
node --input-type=module <<'EOF'
import { readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
const packagePath = join(process.env.STAGE_DIR, "package.json");
const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
const bundled = ["@onkernel/cua-ai", "@onkernel/cua-agent"];
for (const name of bundled) {
const dependency = JSON.parse(
readFileSync(join(process.env.STAGE_DIR, "node_modules", name, "package.json"), "utf8"),
);
pkg.dependencies[name] = dependency.version;
for (const [dependencyName, version] of Object.entries(dependency.dependencies ?? {})) {
if (bundled.includes(dependencyName)) continue;
const current = pkg.dependencies[dependencyName];
if (current && current !== version) {
throw new Error(`Conflicting ${dependencyName} versions: ${current} and ${version}`);
}
pkg.dependencies[dependencyName] = version;
}
}
pkg.bundledDependencies = bundled;
writeFileSync(packagePath, `${JSON.stringify(pkg, null, 2)}\n`);
EOF
npm pack "$STAGE_DIR" --pack-destination "$RUNNER_TEMP"
- name: CLI bin smoke test
run: |
SMOKE_DIR=$(mktemp -d)
cd "$SMOKE_DIR"
npm init -y > /dev/null
npm install "$RUNNER_TEMP"/onkernel-cua-cli-*.tgz
if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
test -f node_modules/@onkernel/cua-cli/node_modules/@onkernel/cua-ai/dist/index.js
test -f node_modules/@onkernel/cua-cli/node_modules/@onkernel/cua-agent/dist/index.js
fi
OUTPUT=$(./node_modules/.bin/cua --help)
echo "$OUTPUT"
echo "$OUTPUT" | grep -q "Usage:"
echo "$OUTPUT" | grep -q "cua \[options\] \[prompt\.\.\.\]"
- name: Publish production release
if: github.event_name == 'push'
run: npm publish --workspace @onkernel/cua-cli --access public
- name: Publish prerelease
if: github.event_name == 'workflow_dispatch'
env:
DIST_TAG: ${{ inputs.dist_tag }}
run: npm publish "$RUNNER_TEMP"/onkernel-cua-cli-*.tgz --access public --tag "$DIST_TAG"
- name: Prerelease summary
if: github.event_name == 'workflow_dispatch'
env:
DIST_TAG: ${{ inputs.dist_tag }}
VERSION: ${{ steps.prerelease.outputs.version }}
run: |
{
echo "### Prerelease published"
echo ""
echo "- version: \`$VERSION\`"
echo "- dist-tag: \`$DIST_TAG\`"
echo "- branch: \`$GITHUB_REF_NAME\` (\`$GITHUB_SHA\`)"
echo ""
echo "Install:"
echo ""
echo '```'
echo "npm install -g @onkernel/cua-cli@$DIST_TAG"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"