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: ${{ matrix.platform == 'windows-x64' && 'windows-latest' || 'ubuntu-latest' }} defaults: run: shell: bash strategy: fail-fast: false max-parallel: 7 matrix: platform: [darwin-arm64, darwin-x64, linux-x64, linux-arm64, linux-x64-musl, linux-arm64-musl, windows-x64] 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: Check if already published id: check run: | PKG_NAME="oh-my-opencode-${{ matrix.platform }}" VERSION="${{ inputs.version }}" STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/${PKG_NAME}/${VERSION}") # Convert platform name for output (replace - with _) PLATFORM_KEY="${{ matrix.platform }}" PLATFORM_KEY="${PLATFORM_KEY//-/_}" if [ "$STATUS" = "200" ]; then echo "skip=true" >> $GITHUB_OUTPUT echo "skip_${PLATFORM_KEY}=true" >> $GITHUB_OUTPUT echo "✓ ${PKG_NAME}@${VERSION} already published" else echo "skip=false" >> $GITHUB_OUTPUT echo "skip_${PLATFORM_KEY}=false" >> $GITHUB_OUTPUT echo "→ ${PKG_NAME}@${VERSION} needs publishing" fi - name: Update version in package.json if: steps.check.outputs.skip != 'true' run: | VERSION="${{ inputs.version }}" cd packages/${{ matrix.platform }} jq --arg v "$VERSION" '.version = $v' package.json > tmp.json && mv tmp.json package.json - name: Build binary if: steps.check.outputs.skip != 'true' run: | PLATFORM="${{ matrix.platform }}" case "$PLATFORM" in darwin-arm64) TARGET="bun-darwin-arm64" ;; darwin-x64) TARGET="bun-darwin-x64" ;; linux-x64) TARGET="bun-linux-x64" ;; linux-arm64) TARGET="bun-linux-arm64" ;; linux-x64-musl) TARGET="bun-linux-x64-musl" ;; linux-arm64-musl) TARGET="bun-linux-arm64-musl" ;; windows-x64) TARGET="bun-windows-x64" ;; esac if [ "$PLATFORM" = "windows-x64" ]; 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-x64" ]; 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 # ============================================================================= # Job 2: Publish all platforms using OIDC/Provenance # - Runs on ubuntu-latest for ALL platforms (just downloading artifacts) # - Uses npm Trusted Publishing (OIDC) - no NODE_AUTH_TOKEN needed # - Fresh OIDC token at publish time avoids timeout issues # ============================================================================= publish: needs: build runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 2 matrix: platform: [darwin-arm64, darwin-x64, linux-x64, linux-arm64, linux-x64-musl, linux-arm64-musl, windows-x64] steps: - name: Check if already published id: check run: | PKG_NAME="oh-my-opencode-${{ matrix.platform }}" VERSION="${{ inputs.version }}" STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/${PKG_NAME}/${VERSION}") if [ "$STATUS" = "200" ]; then echo "skip=true" >> $GITHUB_OUTPUT echo "✓ ${PKG_NAME}@${VERSION} already published, skipping" else echo "skip=false" >> $GITHUB_OUTPUT echo "→ ${PKG_NAME}@${VERSION} will be published" fi - name: Download artifact if: steps.check.outputs.skip != 'true' uses: actions/download-artifact@v4 with: name: binary-${{ matrix.platform }} path: . - name: Extract artifact if: steps.check.outputs.skip != 'true' run: | PLATFORM="${{ matrix.platform }}" mkdir -p packages/${PLATFORM} if [ "$PLATFORM" = "windows-x64" ]; 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/ # Use setup-node WITHOUT registry-url to avoid NODE_AUTH_TOKEN injection # OIDC requires npm 11.5.1+ and NO token to be set - uses: actions/setup-node@v4 if: steps.check.outputs.skip != 'true' with: node-version: "24" # DO NOT set registry-url - it injects NODE_AUTH_TOKEN which breaks OIDC - name: Check npm version and OIDC environment if: steps.check.outputs.skip != 'true' run: | echo "=== Environment Check ===" echo "npm version: $(npm --version)" echo "node version: $(node --version)" echo "" echo "=== OIDC Environment Variables ===" echo "ACTIONS_ID_TOKEN_REQUEST_URL: ${ACTIONS_ID_TOKEN_REQUEST_URL:-(not set)}" echo "ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${ACTIONS_ID_TOKEN_REQUEST_TOKEN:+[REDACTED]}" echo "" echo "=== Auth-related env vars ===" echo "NODE_AUTH_TOKEN: ${NODE_AUTH_TOKEN:-(not set)}" echo "NPM_CONFIG_USERCONFIG: ${NPM_CONFIG_USERCONFIG:-(not set)}" echo "" # Verify npm version >= 11.5.1 for OIDC support NPM_VERSION=$(npm --version) NPM_MAJOR=$(echo $NPM_VERSION | cut -d. -f1) NPM_MINOR=$(echo $NPM_VERSION | cut -d. -f2) NPM_PATCH=$(echo $NPM_VERSION | cut -d. -f3) if [ "$NPM_MAJOR" -lt 11 ] || ([ "$NPM_MAJOR" -eq 11 ] && [ "$NPM_MINOR" -lt 5 ]) || ([ "$NPM_MAJOR" -eq 11 ] && [ "$NPM_MINOR" -eq 5 ] && [ "$NPM_PATCH" -lt 1 ]); then echo "::warning::npm version $NPM_VERSION may not support OIDC. Upgrading to latest..." npm install -g npm@latest echo "Updated npm version: $(npm --version)" else echo "✓ npm version $NPM_VERSION supports OIDC" fi - name: Publish ${{ matrix.platform }} if: steps.check.outputs.skip != 'true' run: | cd packages/${{ matrix.platform }} # Ensure no .npmrc files interfere rm -f ~/.npmrc 2>/dev/null || true rm -f .npmrc 2>/dev/null || true TAG_ARG="" if [ -n "${{ inputs.dist_tag }}" ]; then TAG_ARG="--tag ${{ inputs.dist_tag }}" fi echo "Publishing oh-my-opencode-${{ matrix.platform }}..." echo "Registry: https://registry.npmjs.org" # Publish with provenance - npm will use OIDC automatically # when ACTIONS_ID_TOKEN_REQUEST_URL is set and no token is present npm publish --access public --provenance --registry https://registry.npmjs.org $TAG_ARG env: npm_config_fetch_timeout: "600000" npm_config_fetch_retry_maxtimeout: "120000" timeout-minutes: 15