- postinstall.mjs: fix alias package detection - migrate-legacy-plugin-entry: dedupe + regression tests - task_system: default consistency across runtime paths - task() contract: consistent tool behavior - runtime model selection, tool cap, stale-task cancellation - recovery sanitization, context-limit gating - Ralph semantic DONE hardening, Atlas fallback persistence - native-skill description/content, skill path traversal guard - publish workflow: platform awaited via reusable workflow job - release: version edits reapplied before commit/tag - JSONC plugin migration: top-level plugin key safety - cold-cache: user fallback models skip disconnected providers - docs/version/release framing updates Verified: bun test (4599 pass), tsc --noEmit clean, bun run build clean
374 lines
14 KiB
YAML
374 lines
14 KiB
YAML
name: publish-platform
|
|
run-name: "platform packages ${{ inputs.version }}"
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
version:
|
|
required: true
|
|
type: string
|
|
dist_tag:
|
|
required: false
|
|
type: string
|
|
default: ""
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: "Version to publish (e.g., 3.0.0-beta.12)"
|
|
required: true
|
|
type: string
|
|
dist_tag:
|
|
description: "npm dist tag (e.g., beta, latest)"
|
|
required: false
|
|
type: string
|
|
default: ""
|
|
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
|
|
jobs:
|
|
# =============================================================================
|
|
# Job 1: Build binaries for all platforms
|
|
# - Windows builds on windows-latest (avoid bun cross-compile segfault)
|
|
# - All other platforms build on ubuntu-latest
|
|
# - Uploads compressed artifacts for the publish job
|
|
# =============================================================================
|
|
build:
|
|
runs-on: ${{ startsWith(matrix.platform, 'windows-') && 'windows-latest' || 'ubuntu-latest' }}
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
strategy:
|
|
fail-fast: false
|
|
max-parallel: 11
|
|
matrix:
|
|
platform: [darwin-arm64, darwin-x64, darwin-x64-baseline, linux-x64, linux-x64-baseline, linux-arm64, linux-x64-musl, linux-x64-musl-baseline, linux-arm64-musl, windows-x64, windows-x64-baseline]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: latest
|
|
|
|
- name: Install dependencies
|
|
run: bun install
|
|
env:
|
|
BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi"
|
|
|
|
- name: Validate release inputs
|
|
id: validate
|
|
env:
|
|
INPUT_VERSION: ${{ inputs.version }}
|
|
INPUT_DIST_TAG: ${{ inputs.dist_tag }}
|
|
run: |
|
|
VERSION="$INPUT_VERSION"
|
|
DIST_TAG="$INPUT_DIST_TAG"
|
|
|
|
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$ ]]; then
|
|
echo "::error::Invalid version: $VERSION"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "$DIST_TAG" ] && ! [[ "$DIST_TAG" =~ ^[a-z][a-z0-9-]*$ ]]; then
|
|
echo "::error::Invalid dist_tag: $DIST_TAG"
|
|
exit 1
|
|
fi
|
|
|
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
echo "dist_tag=$DIST_TAG" >> $GITHUB_OUTPUT
|
|
|
|
- name: Check if already published
|
|
id: check
|
|
env:
|
|
VERSION: ${{ steps.validate.outputs.version }}
|
|
run: |
|
|
PLATFORM_KEY="${{ matrix.platform }}"
|
|
PLATFORM_KEY="${PLATFORM_KEY//-/_}"
|
|
|
|
# Check oh-my-opencode
|
|
OC_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/oh-my-opencode-${{ matrix.platform }}/${VERSION}")
|
|
# Check oh-my-openagent
|
|
OA_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/oh-my-openagent-${{ matrix.platform }}/${VERSION}")
|
|
|
|
echo "oh-my-opencode-${{ matrix.platform }}@${VERSION}: ${OC_STATUS}"
|
|
echo "oh-my-openagent-${{ matrix.platform }}@${VERSION}: ${OA_STATUS}"
|
|
|
|
if [ "$OC_STATUS" = "200" ]; then
|
|
echo "skip_opencode=true" >> $GITHUB_OUTPUT
|
|
echo "✓ oh-my-opencode-${{ matrix.platform }}@${VERSION} already published"
|
|
else
|
|
echo "skip_opencode=false" >> $GITHUB_OUTPUT
|
|
echo "→ oh-my-opencode-${{ matrix.platform }}@${VERSION} needs publishing"
|
|
fi
|
|
|
|
if [ "$OA_STATUS" = "200" ]; then
|
|
echo "skip_openagent=true" >> $GITHUB_OUTPUT
|
|
echo "✓ oh-my-openagent-${{ matrix.platform }}@${VERSION} already published"
|
|
else
|
|
echo "skip_openagent=false" >> $GITHUB_OUTPUT
|
|
echo "→ oh-my-openagent-${{ matrix.platform }}@${VERSION} needs publishing"
|
|
fi
|
|
|
|
# Skip build only if BOTH are already published
|
|
if [ "$OC_STATUS" = "200" ] && [ "$OA_STATUS" = "200" ]; then
|
|
echo "skip=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "skip=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Update version in package.json
|
|
if: steps.check.outputs.skip != 'true'
|
|
env:
|
|
VERSION: ${{ steps.validate.outputs.version }}
|
|
run: |
|
|
cd packages/${{ matrix.platform }}
|
|
jq --arg v "$VERSION" '.version = $v' package.json > tmp.json && mv tmp.json package.json
|
|
|
|
- name: Set root package version
|
|
if: steps.check.outputs.skip != 'true'
|
|
env:
|
|
VERSION: ${{ steps.validate.outputs.version }}
|
|
run: |
|
|
jq --arg v "$VERSION" '.version = $v' package.json > tmp.json && mv tmp.json package.json
|
|
|
|
- name: Pre-download baseline compile target
|
|
if: steps.check.outputs.skip != 'true' && endsWith(matrix.platform, '-baseline')
|
|
shell: bash
|
|
run: |
|
|
BUN_VERSION=$(bun --version)
|
|
PLATFORM="${{ matrix.platform }}"
|
|
PKG_NAME="bun-${PLATFORM}"
|
|
CACHE_DIR=$(bun pm cache)
|
|
CACHE_DEST="${CACHE_DIR}/${PKG_NAME}-v${BUN_VERSION}"
|
|
|
|
if [[ -f "$CACHE_DEST" ]]; then
|
|
echo "✓ Compile target already cached at ${CACHE_DEST}"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Pre-downloading ${PKG_NAME} v${BUN_VERSION} to ${CACHE_DEST}"
|
|
TARBALL_URL="https://registry.npmjs.org/@oven/bun-${PLATFORM}/-/bun-${PLATFORM}-${BUN_VERSION}.tgz"
|
|
echo "URL: ${TARBALL_URL}"
|
|
|
|
mkdir -p "$(dirname "$CACHE_DEST")"
|
|
TMP_DIR=$(mktemp -d)
|
|
|
|
# Download and extract the bun binary from npm tarball
|
|
curl -fsSL --retry 5 --retry-delay 5 "${TARBALL_URL}" | tar -xzf - -C "${TMP_DIR}"
|
|
|
|
if [[ "$PLATFORM" == windows-* ]]; then
|
|
BIN_NAME="bun.exe"
|
|
else
|
|
BIN_NAME="bun"
|
|
fi
|
|
|
|
# npm tarball has package/bin/bun structure
|
|
if [[ -f "${TMP_DIR}/package/bin/${BIN_NAME}" ]]; then
|
|
cp "${TMP_DIR}/package/bin/${BIN_NAME}" "${CACHE_DEST}"
|
|
elif [[ -f "${TMP_DIR}/package/${BIN_NAME}" ]]; then
|
|
cp "${TMP_DIR}/package/${BIN_NAME}" "${CACHE_DEST}"
|
|
else
|
|
echo "Could not find ${BIN_NAME} in tarball, listing contents:"
|
|
find "${TMP_DIR}" -type f
|
|
exit 1
|
|
fi
|
|
|
|
chmod +x "${CACHE_DEST}" 2>/dev/null || true
|
|
echo "✓ Pre-downloaded to ${CACHE_DEST}"
|
|
ls -lh "${CACHE_DEST}"
|
|
|
|
- name: Build binary
|
|
if: steps.check.outputs.skip != 'true'
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 5
|
|
retry_wait_seconds: 10
|
|
shell: bash
|
|
command: |
|
|
PLATFORM="${{ matrix.platform }}"
|
|
case "$PLATFORM" in
|
|
darwin-arm64) TARGET="bun-darwin-arm64" ;;
|
|
darwin-x64) TARGET="bun-darwin-x64" ;;
|
|
darwin-x64-baseline) TARGET="bun-darwin-x64-baseline" ;;
|
|
linux-x64) TARGET="bun-linux-x64" ;;
|
|
linux-x64-baseline) TARGET="bun-linux-x64-baseline" ;;
|
|
linux-arm64) TARGET="bun-linux-arm64" ;;
|
|
linux-x64-musl) TARGET="bun-linux-x64-musl" ;;
|
|
linux-x64-musl-baseline) TARGET="bun-linux-x64-musl-baseline" ;;
|
|
linux-arm64-musl) TARGET="bun-linux-arm64-musl" ;;
|
|
windows-x64) TARGET="bun-windows-x64" ;;
|
|
windows-x64-baseline) TARGET="bun-windows-x64-baseline" ;;
|
|
esac
|
|
|
|
if [[ "$PLATFORM" == windows-* ]]; then
|
|
OUTPUT="packages/${PLATFORM}/bin/oh-my-opencode.exe"
|
|
else
|
|
OUTPUT="packages/${PLATFORM}/bin/oh-my-opencode"
|
|
fi
|
|
|
|
bun build src/cli/index.ts --compile --minify --target=$TARGET --outfile=$OUTPUT
|
|
|
|
echo "Built binary:"
|
|
ls -lh "$OUTPUT"
|
|
|
|
- name: Compress binary
|
|
if: steps.check.outputs.skip != 'true'
|
|
run: |
|
|
PLATFORM="${{ matrix.platform }}"
|
|
cd packages/${PLATFORM}
|
|
|
|
if [[ "$PLATFORM" == windows-* ]]; then
|
|
# Windows: use 7z (pre-installed on windows-latest)
|
|
7z a -tzip ../../binary-${PLATFORM}.zip bin/ package.json
|
|
else
|
|
# Unix: use tar.gz
|
|
tar -czvf ../../binary-${PLATFORM}.tar.gz bin/ package.json
|
|
fi
|
|
|
|
cd ../..
|
|
echo "Compressed artifact:"
|
|
ls -lh binary-${PLATFORM}.*
|
|
|
|
- name: Upload artifact
|
|
if: steps.check.outputs.skip != 'true'
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: binary-${{ matrix.platform }}
|
|
path: |
|
|
binary-${{ matrix.platform }}.tar.gz
|
|
binary-${{ matrix.platform }}.zip
|
|
retention-days: 1
|
|
if-no-files-found: error
|
|
|
|
publish:
|
|
needs: build
|
|
if: always() && !cancelled()
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
max-parallel: 2
|
|
matrix:
|
|
platform: [darwin-arm64, darwin-x64, darwin-x64-baseline, linux-x64, linux-x64-baseline, linux-arm64, linux-x64-musl, linux-x64-musl-baseline, linux-arm64-musl, windows-x64, windows-x64-baseline]
|
|
steps:
|
|
- name: Validate release inputs
|
|
id: validate
|
|
env:
|
|
INPUT_VERSION: ${{ inputs.version }}
|
|
INPUT_DIST_TAG: ${{ inputs.dist_tag }}
|
|
run: |
|
|
VERSION="$INPUT_VERSION"
|
|
DIST_TAG="$INPUT_DIST_TAG"
|
|
|
|
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$ ]]; then
|
|
echo "::error::Invalid version: $VERSION"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "$DIST_TAG" ] && ! [[ "$DIST_TAG" =~ ^[a-z][a-z0-9-]*$ ]]; then
|
|
echo "::error::Invalid dist_tag: $DIST_TAG"
|
|
exit 1
|
|
fi
|
|
|
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
echo "dist_tag=$DIST_TAG" >> $GITHUB_OUTPUT
|
|
|
|
- name: Check if already published
|
|
id: check
|
|
env:
|
|
VERSION: ${{ steps.validate.outputs.version }}
|
|
run: |
|
|
OC_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/oh-my-opencode-${{ matrix.platform }}/${VERSION}")
|
|
OA_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/oh-my-openagent-${{ matrix.platform }}/${VERSION}")
|
|
|
|
if [ "$OC_STATUS" = "200" ]; then
|
|
echo "skip_opencode=true" >> $GITHUB_OUTPUT
|
|
echo "✓ oh-my-opencode-${{ matrix.platform }}@${VERSION} already published"
|
|
else
|
|
echo "skip_opencode=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
if [ "$OA_STATUS" = "200" ]; then
|
|
echo "skip_openagent=true" >> $GITHUB_OUTPUT
|
|
echo "✓ oh-my-openagent-${{ matrix.platform }}@${VERSION} already published"
|
|
else
|
|
echo "skip_openagent=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
# Need artifact if either package needs publishing
|
|
if [ "$OC_STATUS" = "200" ] && [ "$OA_STATUS" = "200" ]; then
|
|
echo "skip_all=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "skip_all=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Download artifact
|
|
id: download
|
|
if: steps.check.outputs.skip_all != 'true'
|
|
continue-on-error: true
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: binary-${{ matrix.platform }}
|
|
path: .
|
|
|
|
- name: Extract artifact
|
|
if: steps.check.outputs.skip_all != 'true' && steps.download.outcome == 'success'
|
|
run: |
|
|
PLATFORM="${{ matrix.platform }}"
|
|
mkdir -p packages/${PLATFORM}
|
|
|
|
if [[ "$PLATFORM" == windows-* ]]; then
|
|
unzip binary-${PLATFORM}.zip -d packages/${PLATFORM}/
|
|
else
|
|
tar -xzvf binary-${PLATFORM}.tar.gz -C packages/${PLATFORM}/
|
|
fi
|
|
|
|
echo "Extracted contents:"
|
|
ls -la packages/${PLATFORM}/
|
|
ls -la packages/${PLATFORM}/bin/
|
|
|
|
- uses: actions/setup-node@v4
|
|
if: steps.check.outputs.skip_all != 'true' && steps.download.outcome == 'success'
|
|
with:
|
|
node-version: "24"
|
|
registry-url: "https://registry.npmjs.org"
|
|
|
|
- name: Publish oh-my-opencode-${{ matrix.platform }}
|
|
if: steps.check.outputs.skip_opencode != 'true' && steps.download.outcome == 'success'
|
|
env:
|
|
DIST_TAG: ${{ steps.validate.outputs.dist_tag }}
|
|
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
|
NPM_CONFIG_PROVENANCE: true
|
|
run: |
|
|
cd packages/${{ matrix.platform }}
|
|
|
|
if [ -n "$DIST_TAG" ]; then
|
|
npm publish --access public --provenance --tag "$DIST_TAG"
|
|
else
|
|
npm publish --access public --provenance
|
|
fi
|
|
timeout-minutes: 15
|
|
|
|
- name: Publish oh-my-openagent-${{ matrix.platform }}
|
|
if: steps.check.outputs.skip_openagent != 'true' && steps.download.outcome == 'success'
|
|
env:
|
|
DIST_TAG: ${{ steps.validate.outputs.dist_tag }}
|
|
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
|
NPM_CONFIG_PROVENANCE: true
|
|
run: |
|
|
cd packages/${{ matrix.platform }}
|
|
|
|
# Rename package for oh-my-openagent
|
|
jq --arg name "oh-my-openagent-${{ matrix.platform }}" \
|
|
--arg desc "Platform-specific binary for oh-my-openagent (${{ matrix.platform }})" \
|
|
'.name = $name | .description = $desc | .bin = {"oh-my-openagent": (.bin | to_entries | .[0].value)}' \
|
|
package.json > tmp.json && mv tmp.json package.json
|
|
|
|
if [ -n "$DIST_TAG" ]; then
|
|
npm publish --access public --provenance --tag "$DIST_TAG"
|
|
else
|
|
npm publish --access public --provenance
|
|
fi
|
|
timeout-minutes: 15
|