diff --git a/.github/actions/linux-code-sign/action.yml b/.github/actions/linux-code-sign/action.yml new file mode 100644 index 00000000000..5a117b0805f --- /dev/null +++ b/.github/actions/linux-code-sign/action.yml @@ -0,0 +1,44 @@ +name: linux-code-sign +description: Sign Linux artifacts with cosign. +inputs: + target: + description: Target triple for the artifacts to sign. + required: true + artifacts-dir: + description: Absolute path to the directory containing built binaries to sign. + required: true + +runs: + using: composite + steps: + - name: Install cosign + uses: sigstore/cosign-installer@v3.7.0 + + - name: Cosign Linux artifacts + shell: bash + env: + COSIGN_EXPERIMENTAL: "1" + COSIGN_YES: "true" + COSIGN_OIDC_CLIENT_ID: "sigstore" + COSIGN_OIDC_ISSUER: "https://oauth2.sigstore.dev/auth" + run: | + set -euo pipefail + + dest="${{ inputs.artifacts-dir }}" + if [[ ! -d "$dest" ]]; then + echo "Destination $dest does not exist" + exit 1 + fi + + for binary in codex codex-responses-api-proxy; do + artifact="${dest}/${binary}" + if [[ ! -f "$artifact" ]]; then + echo "Binary $artifact not found" + exit 1 + fi + + cosign sign-blob \ + --yes \ + --bundle "${artifact}.sigstore" \ + "$artifact" + done diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 14f8aa03279..47b561d08d8 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -10,6 +10,8 @@ on: push: tags: - "rust-v*.*.*" + # DO NOT SUBMIT + pull_request: {} concurrency: group: ${{ github.workflow }} @@ -17,6 +19,7 @@ concurrency: jobs: tag-check: + if: github.event_name != 'pull_request' runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -46,10 +49,14 @@ jobs: echo "::endgroup::" build: - needs: tag-check + # needs: tag-check + if: ${{ github.event_name == 'pull_request' || needs.tag-check.result == 'success' || needs.tag-check.result == 'skipped' }} name: Build - ${{ matrix.runner }} - ${{ matrix.target }} runs-on: ${{ matrix.runner }} timeout-minutes: 30 + permissions: + contents: read + id-token: write defaults: run: working-directory: codex-rs @@ -100,174 +107,193 @@ jobs: - name: Cargo build run: cargo build --target ${{ matrix.target }} --release --bin codex --bin codex-responses-api-proxy - - if: ${{ matrix.runner == 'macos-15-xlarge' }} - name: Configure Apple code signing - shell: bash - env: - KEYCHAIN_PASSWORD: actions - APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE_P12 }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - run: | - set -euo pipefail - - if [[ -z "${APPLE_CERTIFICATE:-}" ]]; then - echo "APPLE_CERTIFICATE is required for macOS signing" - exit 1 - fi - - if [[ -z "${APPLE_CERTIFICATE_PASSWORD:-}" ]]; then - echo "APPLE_CERTIFICATE_PASSWORD is required for macOS signing" - exit 1 - fi - - cert_path="${RUNNER_TEMP}/apple_signing_certificate.p12" - echo "$APPLE_CERTIFICATE" | base64 -d > "$cert_path" - - keychain_path="${RUNNER_TEMP}/codex-signing.keychain-db" - security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path" - security set-keychain-settings -lut 21600 "$keychain_path" - security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path" - - keychain_args=() - cleanup_keychain() { - if ((${#keychain_args[@]} > 0)); then - security list-keychains -s "${keychain_args[@]}" || true - security default-keychain -s "${keychain_args[0]}" || true - else - security list-keychains -s || true - fi - if [[ -f "$keychain_path" ]]; then - security delete-keychain "$keychain_path" || true - fi - } - - while IFS= read -r keychain; do - [[ -n "$keychain" ]] && keychain_args+=("$keychain") - done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g') - - if ((${#keychain_args[@]} > 0)); then - security list-keychains -s "$keychain_path" "${keychain_args[@]}" - else - security list-keychains -s "$keychain_path" - fi - - security default-keychain -s "$keychain_path" - security import "$cert_path" -k "$keychain_path" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security - security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$keychain_path" > /dev/null - - codesign_hashes=() - while IFS= read -r hash; do - [[ -n "$hash" ]] && codesign_hashes+=("$hash") - done < <(security find-identity -v -p codesigning "$keychain_path" \ - | sed -n 's/.*\([0-9A-F]\{40\}\).*/\1/p' \ - | sort -u) - - if ((${#codesign_hashes[@]} == 0)); then - echo "No signing identities found in $keychain_path" - cleanup_keychain - rm -f "$cert_path" - exit 1 - fi - - if ((${#codesign_hashes[@]} > 1)); then - echo "Multiple signing identities found in $keychain_path:" - printf ' %s\n' "${codesign_hashes[@]}" - cleanup_keychain - rm -f "$cert_path" - exit 1 - fi - - APPLE_CODESIGN_IDENTITY="${codesign_hashes[0]}" - - rm -f "$cert_path" - - echo "APPLE_CODESIGN_IDENTITY=$APPLE_CODESIGN_IDENTITY" >> "$GITHUB_ENV" - echo "APPLE_CODESIGN_KEYCHAIN=$keychain_path" >> "$GITHUB_ENV" - echo "::add-mask::$APPLE_CODESIGN_IDENTITY" - - - if: ${{ matrix.runner == 'macos-15-xlarge' }} - name: Sign macOS binaries - shell: bash - run: | - set -euo pipefail - - if [[ -z "${APPLE_CODESIGN_IDENTITY:-}" ]]; then - echo "APPLE_CODESIGN_IDENTITY is required for macOS signing" - exit 1 - fi - - keychain_args=() - if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" && -f "${APPLE_CODESIGN_KEYCHAIN}" ]]; then - keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}") - fi - - for binary in codex codex-responses-api-proxy; do - path="target/${{ matrix.target }}/release/${binary}" - codesign --force --options runtime --timestamp --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$path" - done - - - if: ${{ matrix.runner == 'macos-15-xlarge' }} - name: Notarize macOS binaries - shell: bash - env: - APPLE_NOTARIZATION_KEY_P8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }} - APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }} - APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }} - run: | - set -euo pipefail - - for var in APPLE_NOTARIZATION_KEY_P8 APPLE_NOTARIZATION_KEY_ID APPLE_NOTARIZATION_ISSUER_ID; do - if [[ -z "${!var:-}" ]]; then - echo "$var is required for notarization" - exit 1 - fi - done - - notary_key_path="${RUNNER_TEMP}/notarytool.key.p8" - echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$notary_key_path" - cleanup_notary() { - rm -f "$notary_key_path" - } - trap cleanup_notary EXIT - - notarize_binary() { - local binary="$1" - local source_path="target/${{ matrix.target }}/release/${binary}" - local archive_path="${RUNNER_TEMP}/${binary}.zip" - - if [[ ! -f "$source_path" ]]; then - echo "Binary $source_path not found" - exit 1 - fi - - rm -f "$archive_path" - ditto -c -k --keepParent "$source_path" "$archive_path" - - submission_json=$(xcrun notarytool submit "$archive_path" \ - --key "$notary_key_path" \ - --key-id "$APPLE_NOTARIZATION_KEY_ID" \ - --issuer "$APPLE_NOTARIZATION_ISSUER_ID" \ - --output-format json \ - --wait) - - status=$(printf '%s\n' "$submission_json" | jq -r '.status // "Unknown"') - submission_id=$(printf '%s\n' "$submission_json" | jq -r '.id // ""') - - if [[ -z "$submission_id" ]]; then - echo "Failed to retrieve submission ID for $binary" - exit 1 - fi - - echo "::notice title=Notarization::$binary submission ${submission_id} completed with status ${status}" - - if [[ "$status" != "Accepted" ]]; then - echo "Notarization failed for ${binary} (submission ${submission_id}, status ${status})" - exit 1 - fi - } - - notarize_binary "codex" - notarize_binary "codex-responses-api-proxy" + - if: ${{ contains(matrix.target, 'linux') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) }} + name: Cosign Linux artifacts + uses: ./.github/actions/linux-code-sign + with: + target: ${{ matrix.target }} + artifacts-dir: ${{ github.workspace }}/codex-rs/target/${{ matrix.target }}/release + + # - if: ${{ contains(matrix.target, 'windows') }} + # name: Sign Windows binaries with Azure Trusted Signing + # uses: ./.github/actions/windows-code-sign + # with: + # target: ${{ matrix.target }} + # client-id: ${{ secrets.AZURE_TRUSTED_SIGNING_CLIENT_ID }} + # tenant-id: ${{ secrets.AZURE_TRUSTED_SIGNING_TENANT_ID }} + # subscription-id: ${{ secrets.AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID }} + # endpoint: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} + # account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} + # certificate-profile: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }} + + # - if: ${{ matrix.runner == 'macos-15-xlarge' }} + # name: Configure Apple code signing + # shell: bash + # env: + # KEYCHAIN_PASSWORD: actions + # APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE_P12 }} + # APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + # run: | + # set -euo pipefail + + # if [[ -z "${APPLE_CERTIFICATE:-}" ]]; then + # echo "APPLE_CERTIFICATE is required for macOS signing" + # exit 1 + # fi + + # if [[ -z "${APPLE_CERTIFICATE_PASSWORD:-}" ]]; then + # echo "APPLE_CERTIFICATE_PASSWORD is required for macOS signing" + # exit 1 + # fi + + # cert_path="${RUNNER_TEMP}/apple_signing_certificate.p12" + # echo "$APPLE_CERTIFICATE" | base64 -d > "$cert_path" + + # keychain_path="${RUNNER_TEMP}/codex-signing.keychain-db" + # security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path" + # security set-keychain-settings -lut 21600 "$keychain_path" + # security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path" + + # keychain_args=() + # cleanup_keychain() { + # if ((${#keychain_args[@]} > 0)); then + # security list-keychains -s "${keychain_args[@]}" || true + # security default-keychain -s "${keychain_args[0]}" || true + # else + # security list-keychains -s || true + # fi + # if [[ -f "$keychain_path" ]]; then + # security delete-keychain "$keychain_path" || true + # fi + # } + + # while IFS= read -r keychain; do + # [[ -n "$keychain" ]] && keychain_args+=("$keychain") + # done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g') + + # if ((${#keychain_args[@]} > 0)); then + # security list-keychains -s "$keychain_path" "${keychain_args[@]}" + # else + # security list-keychains -s "$keychain_path" + # fi + + # security default-keychain -s "$keychain_path" + # security import "$cert_path" -k "$keychain_path" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security + # security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$keychain_path" > /dev/null + + # codesign_hashes=() + # while IFS= read -r hash; do + # [[ -n "$hash" ]] && codesign_hashes+=("$hash") + # done < <(security find-identity -v -p codesigning "$keychain_path" \ + # | sed -n 's/.*\([0-9A-F]\{40\}\).*/\1/p' \ + # | sort -u) + + # if ((${#codesign_hashes[@]} == 0)); then + # echo "No signing identities found in $keychain_path" + # cleanup_keychain + # rm -f "$cert_path" + # exit 1 + # fi + + # if ((${#codesign_hashes[@]} > 1)); then + # echo "Multiple signing identities found in $keychain_path:" + # printf ' %s\n' "${codesign_hashes[@]}" + # cleanup_keychain + # rm -f "$cert_path" + # exit 1 + # fi + + # APPLE_CODESIGN_IDENTITY="${codesign_hashes[0]}" + + # rm -f "$cert_path" + + # echo "APPLE_CODESIGN_IDENTITY=$APPLE_CODESIGN_IDENTITY" >> "$GITHUB_ENV" + # echo "APPLE_CODESIGN_KEYCHAIN=$keychain_path" >> "$GITHUB_ENV" + # echo "::add-mask::$APPLE_CODESIGN_IDENTITY" + + # - if: ${{ matrix.runner == 'macos-15-xlarge' }} + # name: Sign macOS binaries + # shell: bash + # run: | + # set -euo pipefail + + # if [[ -z "${APPLE_CODESIGN_IDENTITY:-}" ]]; then + # echo "APPLE_CODESIGN_IDENTITY is required for macOS signing" + # exit 1 + # fi + + # keychain_args=() + # if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" && -f "${APPLE_CODESIGN_KEYCHAIN}" ]]; then + # keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}") + # fi + + # for binary in codex codex-responses-api-proxy; do + # path="target/${{ matrix.target }}/release/${binary}" + # codesign --force --options runtime --timestamp --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$path" + # done + + # - if: ${{ matrix.runner == 'macos-15-xlarge' }} + # name: Notarize macOS binaries + # shell: bash + # env: + # APPLE_NOTARIZATION_KEY_P8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }} + # APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }} + # APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }} + # run: | + # set -euo pipefail + + # for var in APPLE_NOTARIZATION_KEY_P8 APPLE_NOTARIZATION_KEY_ID APPLE_NOTARIZATION_ISSUER_ID; do + # if [[ -z "${!var:-}" ]]; then + # echo "$var is required for notarization" + # exit 1 + # fi + # done + + # notary_key_path="${RUNNER_TEMP}/notarytool.key.p8" + # echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$notary_key_path" + # cleanup_notary() { + # rm -f "$notary_key_path" + # } + # trap cleanup_notary EXIT + + # notarize_binary() { + # local binary="$1" + # local source_path="target/${{ matrix.target }}/release/${binary}" + # local archive_path="${RUNNER_TEMP}/${binary}.zip" + + # if [[ ! -f "$source_path" ]]; then + # echo "Binary $source_path not found" + # exit 1 + # fi + + # rm -f "$archive_path" + # ditto -c -k --keepParent "$source_path" "$archive_path" + + # submission_json=$(xcrun notarytool submit "$archive_path" \ + # --key "$notary_key_path" \ + # --key-id "$APPLE_NOTARIZATION_KEY_ID" \ + # --issuer "$APPLE_NOTARIZATION_ISSUER_ID" \ + # --output-format json \ + # --wait) + + # status=$(printf '%s\n' "$submission_json" | jq -r '.status // "Unknown"') + # submission_id=$(printf '%s\n' "$submission_json" | jq -r '.id // ""') + + # if [[ -z "$submission_id" ]]; then + # echo "Failed to retrieve submission ID for $binary" + # exit 1 + # fi + + # echo "::notice title=Notarization::$binary submission ${submission_id} completed with status ${status}" + + # if [[ "$status" != "Accepted" ]]; then + # echo "Notarization failed for ${binary} (submission ${submission_id}, status ${status})" + # exit 1 + # fi + # } + + # notarize_binary "codex" + # notarize_binary "codex-responses-api-proxy" - name: Stage artifacts shell: bash @@ -283,6 +309,11 @@ jobs: cp target/${{ matrix.target }}/release/codex-responses-api-proxy "$dest/codex-responses-api-proxy-${{ matrix.target }}" fi + if [[ "${{ matrix.target }}" == *linux* ]]; then + cp target/${{ matrix.target }}/release/codex.sigstore "$dest/codex-${{ matrix.target }}.sigstore" + cp target/${{ matrix.target }}/release/codex-responses-api-proxy.sigstore "$dest/codex-responses-api-proxy-${{ matrix.target }}.sigstore" + fi + - if: ${{ matrix.runner == 'windows-11-arm' }} name: Install zstd shell: powershell @@ -321,6 +352,11 @@ jobs: continue fi + # Don't try to compress signature bundles. + if [[ "$base" == *.sigstore ]]; then + continue + fi + # Create per-binary tar.gz tar -C "$dest" -czf "$dest/${base}.tar.gz" "$base" @@ -340,28 +376,28 @@ jobs: zstd "${zstd_args[@]}" "$dest/$base" done - - name: Remove signing keychain - if: ${{ always() && matrix.runner == 'macos-15-xlarge' }} - shell: bash - env: - APPLE_CODESIGN_KEYCHAIN: ${{ env.APPLE_CODESIGN_KEYCHAIN }} - run: | - set -euo pipefail - if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" ]]; then - keychain_args=() - while IFS= read -r keychain; do - [[ "$keychain" == "$APPLE_CODESIGN_KEYCHAIN" ]] && continue - [[ -n "$keychain" ]] && keychain_args+=("$keychain") - done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g') - if ((${#keychain_args[@]} > 0)); then - security list-keychains -s "${keychain_args[@]}" - security default-keychain -s "${keychain_args[0]}" - fi - - if [[ -f "$APPLE_CODESIGN_KEYCHAIN" ]]; then - security delete-keychain "$APPLE_CODESIGN_KEYCHAIN" - fi - fi + # - name: Remove signing keychain + # if: ${{ always() && matrix.runner == 'macos-15-xlarge' }} + # shell: bash + # env: + # APPLE_CODESIGN_KEYCHAIN: ${{ env.APPLE_CODESIGN_KEYCHAIN }} + # run: | + # set -euo pipefail + # if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" ]]; then + # keychain_args=() + # while IFS= read -r keychain; do + # [[ "$keychain" == "$APPLE_CODESIGN_KEYCHAIN" ]] && continue + # [[ -n "$keychain" ]] && keychain_args+=("$keychain") + # done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g') + # if ((${#keychain_args[@]} > 0)); then + # security list-keychains -s "${keychain_args[@]}" + # security default-keychain -s "${keychain_args[0]}" + # fi + + # if [[ -f "$APPLE_CODESIGN_KEYCHAIN" ]]; then + # security delete-keychain "$APPLE_CODESIGN_KEYCHAIN" + # fi + # fi - uses: actions/upload-artifact@v5 with: @@ -371,201 +407,201 @@ jobs: path: | codex-rs/dist/${{ matrix.target }}/* - shell-tool-mcp: - name: shell-tool-mcp - needs: tag-check - uses: ./.github/workflows/shell-tool-mcp.yml - with: - release-tag: ${{ github.ref_name }} - publish: true - secrets: inherit - - release: - needs: - - build - - shell-tool-mcp - name: release - runs-on: ubuntu-latest - permissions: - contents: write - actions: read - outputs: - version: ${{ steps.release_name.outputs.name }} - tag: ${{ github.ref_name }} - should_publish_npm: ${{ steps.npm_publish_settings.outputs.should_publish }} - npm_tag: ${{ steps.npm_publish_settings.outputs.npm_tag }} - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - uses: actions/download-artifact@v4 - with: - path: dist - - - name: List - run: ls -R dist/ - - # This is a temporary fix: we should modify shell-tool-mcp.yml so these - # files do not end up in dist/ in the first place. - - name: Delete entries from dist/ that should not go in the release - run: | - rm -rf dist/shell-tool-mcp* - - ls -R dist/ - - - name: Define release name - id: release_name - run: | - # Extract the version from the tag name, which is in the format - # "rust-v0.1.0". - version="${GITHUB_REF_NAME#rust-v}" - echo "name=${version}" >> $GITHUB_OUTPUT - - - name: Determine npm publish settings - id: npm_publish_settings - env: - VERSION: ${{ steps.release_name.outputs.name }} - run: | - set -euo pipefail - version="${VERSION}" - - if [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "should_publish=true" >> "$GITHUB_OUTPUT" - echo "npm_tag=" >> "$GITHUB_OUTPUT" - elif [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+-alpha\.[0-9]+$ ]]; then - echo "should_publish=true" >> "$GITHUB_OUTPUT" - echo "npm_tag=alpha" >> "$GITHUB_OUTPUT" - else - echo "should_publish=false" >> "$GITHUB_OUTPUT" - echo "npm_tag=" >> "$GITHUB_OUTPUT" - fi - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - run_install: false - - - name: Setup Node.js for npm packaging - uses: actions/setup-node@v5 - with: - node-version: 22 - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - # stage_npm_packages.py requires DotSlash when staging releases. - - uses: facebook/install-dotslash@v2 - - name: Stage npm packages - env: - GH_TOKEN: ${{ github.token }} - run: | - ./scripts/stage_npm_packages.py \ - --release-version "${{ steps.release_name.outputs.name }}" \ - --package codex \ - --package codex-responses-api-proxy \ - --package codex-sdk - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - name: ${{ steps.release_name.outputs.name }} - tag_name: ${{ github.ref_name }} - files: dist/** - # Mark as prerelease only when the version has a suffix after x.y.z - # (e.g. -alpha, -beta). Otherwise publish a normal release. - prerelease: ${{ contains(steps.release_name.outputs.name, '-') }} - - - uses: facebook/dotslash-publish-release@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag: ${{ github.ref_name }} - config: .github/dotslash-config.json - - # Publish to npm using OIDC authentication. - # July 31, 2025: https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/ - # npm docs: https://docs.npmjs.com/trusted-publishers - publish-npm: - # Publish to npm for stable releases and alpha pre-releases with numeric suffixes. - if: ${{ needs.release.outputs.should_publish_npm == 'true' }} - name: publish-npm - needs: release - runs-on: ubuntu-latest - permissions: - id-token: write # Required for OIDC - contents: read - - steps: - - name: Setup Node.js - uses: actions/setup-node@v5 - with: - node-version: 22 - registry-url: "https://registry.npmjs.org" - scope: "@openai" - - # Trusted publishing requires npm CLI version 11.5.1 or later. - - name: Update npm - run: npm install -g npm@latest - - - name: Download npm tarballs from release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -euo pipefail - version="${{ needs.release.outputs.version }}" - tag="${{ needs.release.outputs.tag }}" - mkdir -p dist/npm - gh release download "$tag" \ - --repo "${GITHUB_REPOSITORY}" \ - --pattern "codex-npm-${version}.tgz" \ - --dir dist/npm - gh release download "$tag" \ - --repo "${GITHUB_REPOSITORY}" \ - --pattern "codex-responses-api-proxy-npm-${version}.tgz" \ - --dir dist/npm - gh release download "$tag" \ - --repo "${GITHUB_REPOSITORY}" \ - --pattern "codex-sdk-npm-${version}.tgz" \ - --dir dist/npm - - # No NODE_AUTH_TOKEN needed because we use OIDC. - - name: Publish to npm - env: - VERSION: ${{ needs.release.outputs.version }} - NPM_TAG: ${{ needs.release.outputs.npm_tag }} - run: | - set -euo pipefail - tag_args=() - if [[ -n "${NPM_TAG}" ]]; then - tag_args+=(--tag "${NPM_TAG}") - fi - - tarballs=( - "codex-npm-${VERSION}.tgz" - "codex-responses-api-proxy-npm-${VERSION}.tgz" - "codex-sdk-npm-${VERSION}.tgz" - ) - - for tarball in "${tarballs[@]}"; do - npm publish "${GITHUB_WORKSPACE}/dist/npm/${tarball}" "${tag_args[@]}" - done - - update-branch: - name: Update latest-alpha-cli branch - permissions: - contents: write - needs: release - runs-on: ubuntu-latest - - steps: - - name: Update latest-alpha-cli branch - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -euo pipefail - gh api \ - repos/${GITHUB_REPOSITORY}/git/refs/heads/latest-alpha-cli \ - -X PATCH \ - -f sha="${GITHUB_SHA}" \ - -F force=true + # shell-tool-mcp: + # name: shell-tool-mcp + # needs: tag-check + # uses: ./.github/workflows/shell-tool-mcp.yml + # with: + # release-tag: ${{ github.ref_name }} + # publish: true + # secrets: inherit + + # release: + # needs: + # - build + # - shell-tool-mcp + # name: release + # runs-on: ubuntu-latest + # permissions: + # contents: write + # actions: read + # outputs: + # version: ${{ steps.release_name.outputs.name }} + # tag: ${{ github.ref_name }} + # should_publish_npm: ${{ steps.npm_publish_settings.outputs.should_publish }} + # npm_tag: ${{ steps.npm_publish_settings.outputs.npm_tag }} + + # steps: + # - name: Checkout repository + # uses: actions/checkout@v6 + + # - uses: actions/download-artifact@v4 + # with: + # path: dist + + # - name: List + # run: ls -R dist/ + + # # This is a temporary fix: we should modify shell-tool-mcp.yml so these + # # files do not end up in dist/ in the first place. + # - name: Delete entries from dist/ that should not go in the release + # run: | + # rm -rf dist/shell-tool-mcp* + + # ls -R dist/ + + # - name: Define release name + # id: release_name + # run: | + # # Extract the version from the tag name, which is in the format + # # "rust-v0.1.0". + # version="${GITHUB_REF_NAME#rust-v}" + # echo "name=${version}" >> $GITHUB_OUTPUT + + # - name: Determine npm publish settings + # id: npm_publish_settings + # env: + # VERSION: ${{ steps.release_name.outputs.name }} + # run: | + # set -euo pipefail + # version="${VERSION}" + + # if [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # echo "should_publish=true" >> "$GITHUB_OUTPUT" + # echo "npm_tag=" >> "$GITHUB_OUTPUT" + # elif [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+-alpha\.[0-9]+$ ]]; then + # echo "should_publish=true" >> "$GITHUB_OUTPUT" + # echo "npm_tag=alpha" >> "$GITHUB_OUTPUT" + # else + # echo "should_publish=false" >> "$GITHUB_OUTPUT" + # echo "npm_tag=" >> "$GITHUB_OUTPUT" + # fi + + # - name: Setup pnpm + # uses: pnpm/action-setup@v4 + # with: + # run_install: false + + # - name: Setup Node.js for npm packaging + # uses: actions/setup-node@v5 + # with: + # node-version: 22 + + # - name: Install dependencies + # run: pnpm install --frozen-lockfile + + # # stage_npm_packages.py requires DotSlash when staging releases. + # - uses: facebook/install-dotslash@v2 + # - name: Stage npm packages + # env: + # GH_TOKEN: ${{ github.token }} + # run: | + # ./scripts/stage_npm_packages.py \ + # --release-version "${{ steps.release_name.outputs.name }}" \ + # --package codex \ + # --package codex-responses-api-proxy \ + # --package codex-sdk + + # - name: Create GitHub Release + # uses: softprops/action-gh-release@v2 + # with: + # name: ${{ steps.release_name.outputs.name }} + # tag_name: ${{ github.ref_name }} + # files: dist/** + # # Mark as prerelease only when the version has a suffix after x.y.z + # # (e.g. -alpha, -beta). Otherwise publish a normal release. + # prerelease: ${{ contains(steps.release_name.outputs.name, '-') }} + + # - uses: facebook/dotslash-publish-release@v2 + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # with: + # tag: ${{ github.ref_name }} + # config: .github/dotslash-config.json + + # # Publish to npm using OIDC authentication. + # # July 31, 2025: https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/ + # # npm docs: https://docs.npmjs.com/trusted-publishers + # publish-npm: + # # Publish to npm for stable releases and alpha pre-releases with numeric suffixes. + # if: ${{ needs.release.outputs.should_publish_npm == 'true' }} + # name: publish-npm + # needs: release + # runs-on: ubuntu-latest + # permissions: + # id-token: write # Required for OIDC + # contents: read + + # steps: + # - name: Setup Node.js + # uses: actions/setup-node@v5 + # with: + # node-version: 22 + # registry-url: "https://registry.npmjs.org" + # scope: "@openai" + + # # Trusted publishing requires npm CLI version 11.5.1 or later. + # - name: Update npm + # run: npm install -g npm@latest + + # - name: Download npm tarballs from release + # env: + # GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # run: | + # set -euo pipefail + # version="${{ needs.release.outputs.version }}" + # tag="${{ needs.release.outputs.tag }}" + # mkdir -p dist/npm + # gh release download "$tag" \ + # --repo "${GITHUB_REPOSITORY}" \ + # --pattern "codex-npm-${version}.tgz" \ + # --dir dist/npm + # gh release download "$tag" \ + # --repo "${GITHUB_REPOSITORY}" \ + # --pattern "codex-responses-api-proxy-npm-${version}.tgz" \ + # --dir dist/npm + # gh release download "$tag" \ + # --repo "${GITHUB_REPOSITORY}" \ + # --pattern "codex-sdk-npm-${version}.tgz" \ + # --dir dist/npm + + # # No NODE_AUTH_TOKEN needed because we use OIDC. + # - name: Publish to npm + # env: + # VERSION: ${{ needs.release.outputs.version }} + # NPM_TAG: ${{ needs.release.outputs.npm_tag }} + # run: | + # set -euo pipefail + # tag_args=() + # if [[ -n "${NPM_TAG}" ]]; then + # tag_args+=(--tag "${NPM_TAG}") + # fi + + # tarballs=( + # "codex-npm-${VERSION}.tgz" + # "codex-responses-api-proxy-npm-${VERSION}.tgz" + # "codex-sdk-npm-${VERSION}.tgz" + # ) + + # for tarball in "${tarballs[@]}"; do + # npm publish "${GITHUB_WORKSPACE}/dist/npm/${tarball}" "${tag_args[@]}" + # done + + # update-branch: + # name: Update latest-alpha-cli branch + # permissions: + # contents: write + # needs: release + # runs-on: ubuntu-latest + + # steps: + # - name: Update latest-alpha-cli branch + # env: + # GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # run: | + # set -euo pipefail + # gh api \ + # repos/${GITHUB_REPOSITORY}/git/refs/heads/latest-alpha-cli \ + # -X PATCH \ + # -f sha="${GITHUB_SHA}" \ + # -F force=true