platform packages 3.17.1 #84
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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' || startsWith(matrix.platform, 'darwin-') && 'macos-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: Ad-hoc sign darwin binary | |
| if: steps.check.outputs.skip != 'true' && startsWith(matrix.platform, 'darwin-') | |
| run: | | |
| BINARY="packages/${{ matrix.platform }}/bin/oh-my-opencode" | |
| codesign --sign - --force "$BINARY" | |
| echo "Signature info:" | |
| codesign -dvvv "$BINARY" 2>&1 | |
| codesign -dvvv "$BINARY" 2>&1 | grep -q "Signature=adhoc" || { echo "ERROR: binary is not ad-hoc signed"; exit 1; } | |
| - 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 |