mirror of
https://github.com/openprose/prose.git
synced 2026-09-19 05:55:05 +08:00
356 lines
10 KiB
YAML
356 lines
10 KiB
YAML
name: CLI Publish
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: "Version to publish. Must match cli/package.json, for example 1.2.3."
|
|
required: true
|
|
type: string
|
|
npm_tag:
|
|
description: "npm dist-tag to publish."
|
|
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: cli-publish-${{ inputs.version }}
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
PACKAGE_NAME: "@openprose/prose-cli"
|
|
RELEASE_ACTORS: "irl-dan josemontesdeoca"
|
|
|
|
jobs:
|
|
authorize:
|
|
name: Authorize 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 }}
|
|
INPUT_DRY_RUN: ${{ inputs.dry_run }}
|
|
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
|
|
|
|
[ "$GITHUB_REF" = "refs/heads/main" ] || fail "CLI releases must be run from main, not $GITHUB_REF"
|
|
|
|
version="${INPUT_VERSION#v}"
|
|
case "$version" in
|
|
""|*[!A-Za-z0-9._-]*) fail "version may only contain letters, numbers, dots, underscores, and dashes" ;;
|
|
esac
|
|
|
|
package_version="$(node -p 'require("./cli/package.json").version')"
|
|
[ "$version" = "$package_version" ] || {
|
|
fail "Input version ($version) must match cli/package.json ($package_version)"
|
|
}
|
|
|
|
installer_version="$(sed -n 's/^DEFAULT_VERSION="\([^"]*\)"/\1/p' cli/install.sh)"
|
|
[ "$version" = "$installer_version" ] || {
|
|
fail "Input version ($version) must match cli/install.sh DEFAULT_VERSION ($installer_version)"
|
|
}
|
|
|
|
release_tag="v$version"
|
|
if [ "$INPUT_DRY_RUN" = "true" ]; then
|
|
printf '%s\n' "Dry run requested: skipping tag, release, and npm-version uniqueness checks."
|
|
else
|
|
if git ls-remote --exit-code --tags origin "refs/tags/$release_tag" >/dev/null 2>&1; then
|
|
fail "Git tag already exists: $release_tag"
|
|
fi
|
|
|
|
if gh release view "$release_tag" >/dev/null 2>&1; then
|
|
fail "GitHub release already exists: $release_tag"
|
|
fi
|
|
|
|
if npm view "$PACKAGE_NAME@$version" version >/tmp/prose-npm-version 2>/dev/null; then
|
|
published_version="$(cat /tmp/prose-npm-version)"
|
|
fail "npm package version already exists: $PACKAGE_NAME@$published_version"
|
|
fi
|
|
fi
|
|
|
|
{
|
|
printf 'npm_tag=%s\n' "$INPUT_NPM_TAG"
|
|
printf 'release_tag=%s\n' "$release_tag"
|
|
printf 'version=%s\n' "$version"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
verify:
|
|
name: Verify package
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
needs: authorize
|
|
defaults:
|
|
run:
|
|
working-directory: cli
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
cache-dependency-path: cli/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Typecheck
|
|
run: npm run typecheck
|
|
|
|
- name: Test
|
|
run: npm test
|
|
|
|
- name: Build
|
|
run: npm run build
|
|
|
|
- name: Audit production dependencies
|
|
run: npm audit --omit=dev
|
|
|
|
- name: Dry-run npm publish
|
|
run: npm publish --dry-run
|
|
|
|
release-assets:
|
|
name: Build ${{ matrix.target_os }}-${{ matrix.target_arch }} tarball
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 25
|
|
needs:
|
|
- authorize
|
|
- verify
|
|
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: 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 Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
cache-dependency-path: cli/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- 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.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 npm package and GitHub release
|
|
if: ${{ !inputs.dry_run }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 25
|
|
needs:
|
|
- authorize
|
|
- release-assets
|
|
environment: release
|
|
permissions:
|
|
contents: write
|
|
id-token: write
|
|
defaults:
|
|
run:
|
|
working-directory: 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 Node for trusted publishing
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
registry-url: https://registry.npmjs.org
|
|
cache: npm
|
|
cache-dependency-path: cli/package-lock.json
|
|
|
|
- name: Install npm with trusted publishing support
|
|
run: npm install --global npm@latest
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- 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"
|
|
|
|
cat > "$notes_path" <<EOF
|
|
Prose CLI $VERSION.
|
|
|
|
Install with npm:
|
|
|
|
npm install --global $PACKAGE_NAME@$VERSION
|
|
|
|
Install with the release tarball:
|
|
|
|
curl -fsSL https://raw.githubusercontent.com/openprose/prose/main/cli/install.sh | sh
|
|
EOF
|
|
|
|
gh release create "$RELEASE_TAG" "$asset_dir"/* \
|
|
--target "$GITHUB_SHA" \
|
|
--title "Prose CLI $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
|