mirror of
https://github.com/openprose/prose.git
synced 2026-09-19 05:55:05 +08:00
f9c84b834e
* Fix CLI workflows for the pnpm-workspace consolidation The Reactor consolidation moved the CLI into the pnpm workspace, but the npm-to-pnpm workflow conversion was incomplete and broke CI on main. - Add the missing pnpm/action-setup step before every setup-node that sets cache: pnpm (cli-real-harness-smoke, cli-release-check, release). Without pnpm on PATH, setup-node fails: "Unable to locate executable file: pnpm". - Rework the npm-package-smoke job to pack with pnpm. npm pack does not rewrite the workspace: protocol dependency on @openprose/reactor; pnpm pack does. The install smoke now resolves that dependency from a locally packed reactor tarball so it does not depend on the registry. * Build the Reactor dependency before the CLI compiles in CI After the workspace consolidation the CLI typechecks and builds against @openprose/reactor, which must be built first. - Add a "Build workspace" step before every CLI typecheck/build step (node-checks, release-tarball-smoke, cli-real-harness-smoke, and the release workflow's verify-cli and release-assets jobs). - Drop the deleted tools/cli/package-lock.json entries from .version-bump.json so bump-version.sh --check no longer fails. * Drop Node 18 from the CLI check matrix The CLI now requires Node 20+ (engines.node >=20.0.0, enforced by install.sh). The Node 18 matrix leg fails install.sh's version gate.
403 lines
12 KiB
YAML
403 lines
12 KiB
YAML
name: OpenProse Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: "Version to publish. Must match every declared OpenProse version, for example 0.13.0."
|
|
required: true
|
|
type: string
|
|
npm_tag:
|
|
description: "npm dist-tag for @openprose/prose-cli."
|
|
required: true
|
|
default: latest
|
|
type: choice
|
|
options:
|
|
- latest
|
|
- next
|
|
dry_run:
|
|
description: "Build and verify without publishing."
|
|
required: true
|
|
default: true
|
|
type: boolean
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: openprose-release-${{ inputs.version }}
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
PACKAGE_NAME: "@openprose/prose-cli"
|
|
RELEASE_ACTORS: "irl-dan josemontesdeoca"
|
|
|
|
jobs:
|
|
authorize:
|
|
name: Authorize and preflight release
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
outputs:
|
|
npm_tag: ${{ steps.release.outputs.npm_tag }}
|
|
release_tag: ${{ steps.release.outputs.release_tag }}
|
|
version: ${{ steps.release.outputs.version }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
|
|
- name: Validate release request
|
|
id: release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
GITHUB_TRIGGERING_ACTOR: ${{ github.triggering_actor }}
|
|
INPUT_NPM_TAG: ${{ inputs.npm_tag }}
|
|
INPUT_VERSION: ${{ inputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
fail() {
|
|
printf '%s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
actor="${GITHUB_ACTOR}"
|
|
triggering_actor="${GITHUB_TRIGGERING_ACTOR:-$GITHUB_ACTOR}"
|
|
|
|
case " $RELEASE_ACTORS " in
|
|
*" $actor "*) ;;
|
|
*) fail "Unauthorized release actor: $actor" ;;
|
|
esac
|
|
|
|
case " $RELEASE_ACTORS " in
|
|
*" $triggering_actor "*) ;;
|
|
*) fail "Unauthorized triggering actor: $triggering_actor" ;;
|
|
esac
|
|
|
|
version="${INPUT_VERSION#v}"
|
|
./scripts/release-preflight.sh \
|
|
--version "$version" \
|
|
--npm-tag "$INPUT_NPM_TAG" \
|
|
--require-main \
|
|
--check-remote
|
|
|
|
{
|
|
printf 'npm_tag=%s\n' "$INPUT_NPM_TAG"
|
|
printf 'release_tag=v%s\n' "$version"
|
|
printf 'version=%s\n' "$version"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
verify-cli:
|
|
name: Verify CLI package
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
needs: authorize
|
|
defaults:
|
|
run:
|
|
working-directory: tools/cli
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v4
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: pnpm
|
|
cache-dependency-path: pnpm-lock.yaml
|
|
|
|
- name: Install dependencies
|
|
run: corepack enable && pnpm install --frozen-lockfile --dir ../..
|
|
|
|
- name: Build workspace
|
|
run: pnpm --dir ../.. run build
|
|
|
|
- name: Typecheck
|
|
run: npm run typecheck
|
|
|
|
- name: Test
|
|
run: npm test
|
|
|
|
- name: Build
|
|
run: npm run build
|
|
|
|
- name: Audit production dependencies
|
|
run: npm run audit:policy
|
|
|
|
- name: Dry-run npm publish
|
|
run: npm publish --dry-run
|
|
|
|
verify-plugin:
|
|
name: Verify SKILL and plugin metadata
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
needs: authorize
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Version surfaces in sync
|
|
run: ./scripts/bump-version.sh --check
|
|
|
|
- name: Plugin copy in sync
|
|
run: ./scripts/sync-copy.sh --check
|
|
|
|
- name: Changelog has release notes
|
|
run: ./scripts/extract-changelog.sh "${{ needs.authorize.outputs.version }}"
|
|
|
|
- name: Validate Claude plugin manifest
|
|
run: |
|
|
node -e '
|
|
const fs = require("fs");
|
|
const m = JSON.parse(fs.readFileSync(".claude-plugin/plugin.json","utf8"));
|
|
if (!m.name || !m.version) { console.error("missing name/version"); process.exit(1); }
|
|
const skillsField = String(m.skills || "./skills").replace(/^\.\//,"").replace(/\/$/,"");
|
|
const target = skillsField + "/open-prose/SKILL.md";
|
|
if (!fs.existsSync(target)) { console.error("skills path not found: " + target); process.exit(1); }
|
|
console.log("ok: claude plugin manifest");
|
|
'
|
|
|
|
- name: Validate Codex plugin manifest
|
|
run: |
|
|
node -e '
|
|
const fs = require("fs");
|
|
const m = JSON.parse(fs.readFileSync(".codex-plugin/plugin.json","utf8"));
|
|
if (!m.name || !m.version) { console.error("missing name/version"); process.exit(1); }
|
|
if (!m.interface || !m.interface.displayName) { console.error("missing interface.displayName"); process.exit(1); }
|
|
const skillsField = String(m.skills || "./skills").replace(/^\.\//,"").replace(/\/$/,"");
|
|
const target = skillsField + "/open-prose/SKILL.md";
|
|
if (!fs.existsSync(target)) { console.error("skills path not found: " + target); process.exit(1); }
|
|
const mp = JSON.parse(fs.readFileSync(".agents/plugins/marketplace.json","utf8"));
|
|
if (!Array.isArray(mp.plugins) || mp.plugins[0].name !== "open-prose") { console.error("agents/plugins/marketplace.json mismatch"); process.exit(1); }
|
|
console.log("ok: codex plugin manifest");
|
|
'
|
|
|
|
- name: Plugin assets exist
|
|
run: |
|
|
test -f assets/plugin/logo.png
|
|
test -f assets/plugin/composer-icon.png
|
|
file assets/plugin/logo.png | grep -q 'PNG image data, 512 x 512'
|
|
file assets/plugin/composer-icon.png | grep -q 'PNG image data, 64 x 64'
|
|
|
|
- name: Codex interface has visual assets
|
|
run: |
|
|
jq -e '.interface.logo and .interface.composerIcon and .interface.brandColor and .interface.websiteURL' .codex-plugin/plugin.json
|
|
jq -re '.interface.brandColor' .codex-plugin/plugin.json | grep -E '^#[0-9A-Fa-f]{6}$'
|
|
|
|
release-assets:
|
|
name: Build ${{ matrix.target_os }}-${{ matrix.target_arch }} tarball
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 25
|
|
needs:
|
|
- authorize
|
|
- verify-cli
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- target_os: linux
|
|
target_arch: x64
|
|
- target_os: linux
|
|
target_arch: arm64
|
|
- target_os: darwin
|
|
target_arch: x64
|
|
- target_os: darwin
|
|
target_arch: arm64
|
|
defaults:
|
|
run:
|
|
working-directory: tools/cli
|
|
env:
|
|
TARGET_ARCH: ${{ matrix.target_arch }}
|
|
TARGET_OS: ${{ matrix.target_os }}
|
|
VERSION: ${{ needs.authorize.outputs.version }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v4
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: pnpm
|
|
cache-dependency-path: pnpm-lock.yaml
|
|
|
|
- name: Install dependencies
|
|
run: corepack enable && pnpm install --frozen-lockfile --dir ../..
|
|
|
|
- name: Build workspace
|
|
run: pnpm --dir ../.. run build
|
|
|
|
- name: Build release tarball
|
|
run: |
|
|
set -euo pipefail
|
|
release_dir="$RUNNER_TEMP/prose-release"
|
|
|
|
npm run release:tarball -- \
|
|
--version "$VERSION" \
|
|
--os "$TARGET_OS" \
|
|
--arch "$TARGET_ARCH" \
|
|
--out-dir "$release_dir"
|
|
|
|
cd "$release_dir"
|
|
sha256sum --check "prose-$VERSION-$TARGET_OS-$TARGET_ARCH.tar.gz.sha256"
|
|
tar -tzf "prose-$VERSION-$TARGET_OS-$TARGET_ARCH.tar.gz" >/dev/null
|
|
|
|
- name: Smoke native Linux x64 tarball
|
|
if: ${{ matrix.target_os == 'linux' && matrix.target_arch == 'x64' }}
|
|
run: |
|
|
set -euo pipefail
|
|
release_dir="$RUNNER_TEMP/prose-release"
|
|
install_dir="$RUNNER_TEMP/prose-install"
|
|
bin_dir="$RUNNER_TEMP/prose-bin"
|
|
|
|
env \
|
|
PROSE_VERSION="$VERSION" \
|
|
PROSE_OS="$TARGET_OS" \
|
|
PROSE_ARCH="$TARGET_ARCH" \
|
|
PROSE_BASE_URL="file://$release_dir" \
|
|
PROSE_INSTALL_DIR="$install_dir" \
|
|
PROSE_BIN_DIR="$bin_dir" \
|
|
sh install.sh
|
|
|
|
"$bin_dir/prose" --version
|
|
"$bin_dir/prose" run hello.prose.md --harness mock
|
|
|
|
- name: Upload release tarball
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: cli-release-${{ matrix.target_os }}-${{ matrix.target_arch }}
|
|
path: ${{ runner.temp }}/prose-release/*
|
|
if-no-files-found: error
|
|
|
|
publish:
|
|
name: Publish OpenProse release
|
|
if: ${{ !inputs.dry_run }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 25
|
|
needs:
|
|
- authorize
|
|
- verify-cli
|
|
- verify-plugin
|
|
- release-assets
|
|
environment: release
|
|
permissions:
|
|
contents: write
|
|
id-token: write
|
|
defaults:
|
|
run:
|
|
working-directory: tools/cli
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
NPM_CONFIG_PROVENANCE: "true"
|
|
NPM_TAG: ${{ needs.authorize.outputs.npm_tag }}
|
|
RELEASE_TAG: ${{ needs.authorize.outputs.release_tag }}
|
|
VERSION: ${{ needs.authorize.outputs.version }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v4
|
|
|
|
- name: Setup Node for trusted publishing
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
registry-url: https://registry.npmjs.org
|
|
cache: pnpm
|
|
cache-dependency-path: pnpm-lock.yaml
|
|
|
|
- name: Install npm with trusted publishing support
|
|
run: npm install --global npm@latest
|
|
|
|
- name: Install dependencies
|
|
run: corepack enable && pnpm install --frozen-lockfile --dir ../..
|
|
|
|
- name: Download release assets
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
pattern: cli-release-*
|
|
path: ${{ runner.temp }}/prose-release-assets
|
|
merge-multiple: true
|
|
|
|
- name: Validate release assets
|
|
run: |
|
|
set -euo pipefail
|
|
asset_dir="$RUNNER_TEMP/prose-release-assets"
|
|
|
|
expected_assets="
|
|
prose-$VERSION-linux-x64.tar.gz
|
|
prose-$VERSION-linux-arm64.tar.gz
|
|
prose-$VERSION-darwin-x64.tar.gz
|
|
prose-$VERSION-darwin-arm64.tar.gz
|
|
"
|
|
|
|
for asset in $expected_assets; do
|
|
[ -f "$asset_dir/$asset" ] || {
|
|
printf 'Missing release asset: %s\n' "$asset" >&2
|
|
exit 1
|
|
}
|
|
[ -f "$asset_dir/$asset.sha256" ] || {
|
|
printf 'Missing release checksum: %s.sha256\n' "$asset" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
|
|
cd "$asset_dir"
|
|
for checksum in *.sha256; do
|
|
sha256sum --check "$checksum"
|
|
done
|
|
|
|
- name: Create draft GitHub release
|
|
id: create_draft_release
|
|
run: |
|
|
set -euo pipefail
|
|
asset_dir="$RUNNER_TEMP/prose-release-assets"
|
|
notes_path="$RUNNER_TEMP/prose-release-notes.md"
|
|
|
|
../../scripts/extract-changelog.sh "$VERSION" > "$notes_path"
|
|
cat >> "$notes_path" <<EOF
|
|
|
|
## Install
|
|
|
|
Install with npm:
|
|
|
|
npm install --global $PACKAGE_NAME@$VERSION
|
|
|
|
Install with the release tarball:
|
|
|
|
curl -fsSL https://raw.githubusercontent.com/openprose/prose/main/tools/cli/install.sh | sh
|
|
EOF
|
|
|
|
gh release create "$RELEASE_TAG" "$asset_dir"/* \
|
|
--target "$GITHUB_SHA" \
|
|
--title "OpenProse $VERSION" \
|
|
--notes-file "$notes_path" \
|
|
--draft
|
|
|
|
- name: Publish npm package
|
|
id: publish_npm
|
|
run: |
|
|
set -euo pipefail
|
|
node --version
|
|
npm --version
|
|
npm publish --access public --tag "$NPM_TAG"
|
|
|
|
- name: Publish GitHub release
|
|
id: publish_github_release
|
|
run: gh release edit "$RELEASE_TAG" --draft=false
|
|
|
|
- name: Delete draft release after npm publish failure
|
|
if: ${{ failure() && steps.create_draft_release.outcome == 'success' && steps.publish_npm.outcome == 'failure' }}
|
|
run: gh release delete "$RELEASE_TAG" --yes --cleanup-tag || true
|