Compare commits

...

5 Commits

Author SHA1 Message Date
shijie-openai
6802842406 Update to use the correct path 2025-12-06 13:11:58 -08:00
shijie-openai
ebacaaa698 linx signing only 2025-12-06 12:26:19 -08:00
shijie-openai
74fa7d920a only keeping build and codesigning for local testing 2025-12-06 12:26:19 -08:00
shijie-openai
9a73414efe WIP: updating windows code signing process with OIDC 2025-12-06 12:26:19 -08:00
shijie-openai
20624d5c88 WIP 2025-12-06 12:26:19 -08:00
2 changed files with 414 additions and 334 deletions

View File

@@ -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

View File

@@ -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: ${{ 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 [[ -z "${APPLE_CERTIFICATE:-}" ]]; then
echo "APPLE_CERTIFICATE is required for macOS signing"
exit 1
fi
# - 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 [[ -z "${APPLE_CERTIFICATE_PASSWORD:-}" ]]; then
echo "APPLE_CERTIFICATE_PASSWORD is required for macOS signing"
exit 1
fi
# - 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
cert_path="${RUNNER_TEMP}/apple_signing_certificate.p12"
echo "$APPLE_CERTIFICATE" | base64 -d > "$cert_path"
# if [[ -z "${APPLE_CERTIFICATE:-}" ]]; then
# echo "APPLE_CERTIFICATE is required for macOS signing"
# exit 1
# fi
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"
# if [[ -z "${APPLE_CERTIFICATE_PASSWORD:-}" ]]; then
# echo "APPLE_CERTIFICATE_PASSWORD is required for macOS signing"
# exit 1
# fi
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
}
# cert_path="${RUNNER_TEMP}/apple_signing_certificate.p12"
# echo "$APPLE_CERTIFICATE" | base64 -d > "$cert_path"
while IFS= read -r keychain; do
[[ -n "$keychain" ]] && keychain_args+=("$keychain")
done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g')
# 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"
if ((${#keychain_args[@]} > 0)); then
security list-keychains -s "$keychain_path" "${keychain_args[@]}"
else
security list-keychains -s "$keychain_path"
fi
# 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
# }
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
# while IFS= read -r keychain; do
# [[ -n "$keychain" ]] && keychain_args+=("$keychain")
# done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g')
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 ((${#keychain_args[@]} > 0)); then
# security list-keychains -s "$keychain_path" "${keychain_args[@]}"
# else
# security list-keychains -s "$keychain_path"
# fi
if ((${#codesign_hashes[@]} == 0)); then
echo "No signing identities found in $keychain_path"
cleanup_keychain
rm -f "$cert_path"
exit 1
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
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
# 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)
APPLE_CODESIGN_IDENTITY="${codesign_hashes[0]}"
# if ((${#codesign_hashes[@]} == 0)); then
# echo "No signing identities found in $keychain_path"
# cleanup_keychain
# rm -f "$cert_path"
# exit 1
# fi
rm -f "$cert_path"
# 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
echo "APPLE_CODESIGN_IDENTITY=$APPLE_CODESIGN_IDENTITY" >> "$GITHUB_ENV"
echo "APPLE_CODESIGN_KEYCHAIN=$keychain_path" >> "$GITHUB_ENV"
echo "::add-mask::$APPLE_CODESIGN_IDENTITY"
# APPLE_CODESIGN_IDENTITY="${codesign_hashes[0]}"
- if: ${{ matrix.runner == 'macos-15-xlarge' }}
name: Sign macOS binaries
shell: bash
run: |
set -euo pipefail
# rm -f "$cert_path"
if [[ -z "${APPLE_CODESIGN_IDENTITY:-}" ]]; then
echo "APPLE_CODESIGN_IDENTITY is required for macOS signing"
exit 1
fi
# echo "APPLE_CODESIGN_IDENTITY=$APPLE_CODESIGN_IDENTITY" >> "$GITHUB_ENV"
# echo "APPLE_CODESIGN_KEYCHAIN=$keychain_path" >> "$GITHUB_ENV"
# echo "::add-mask::$APPLE_CODESIGN_IDENTITY"
keychain_args=()
if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" && -f "${APPLE_CODESIGN_KEYCHAIN}" ]]; then
keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}")
fi
# - if: ${{ matrix.runner == 'macos-15-xlarge' }}
# name: Sign macOS binaries
# shell: bash
# run: |
# set -euo pipefail
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 [[ -z "${APPLE_CODESIGN_IDENTITY:-}" ]]; then
# echo "APPLE_CODESIGN_IDENTITY is required for macOS signing"
# exit 1
# fi
- 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
# keychain_args=()
# if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" && -f "${APPLE_CODESIGN_KEYCHAIN}" ]]; then
# keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}")
# fi
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
# 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
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
# - 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
notarize_binary() {
local binary="$1"
local source_path="target/${{ matrix.target }}/release/${binary}"
local archive_path="${RUNNER_TEMP}/${binary}.zip"
# 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
if [[ ! -f "$source_path" ]]; then
echo "Binary $source_path not found"
exit 1
fi
# 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
rm -f "$archive_path"
ditto -c -k --keepParent "$source_path" "$archive_path"
# notarize_binary() {
# local binary="$1"
# local source_path="target/${{ matrix.target }}/release/${binary}"
# local archive_path="${RUNNER_TEMP}/${binary}.zip"
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)
# if [[ ! -f "$source_path" ]]; then
# echo "Binary $source_path not found"
# exit 1
# fi
status=$(printf '%s\n' "$submission_json" | jq -r '.status // "Unknown"')
submission_id=$(printf '%s\n' "$submission_json" | jq -r '.id // ""')
# rm -f "$archive_path"
# ditto -c -k --keepParent "$source_path" "$archive_path"
if [[ -z "$submission_id" ]]; then
echo "Failed to retrieve submission ID for $binary"
exit 1
fi
# 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)
echo "::notice title=Notarization::$binary submission ${submission_id} completed with status ${status}"
# status=$(printf '%s\n' "$submission_json" | jq -r '.status // "Unknown"')
# submission_id=$(printf '%s\n' "$submission_json" | jq -r '.id // ""')
if [[ "$status" != "Accepted" ]]; then
echo "Notarization failed for ${binary} (submission ${submission_id}, status ${status})"
exit 1
fi
}
# if [[ -z "$submission_id" ]]; then
# echo "Failed to retrieve submission ID for $binary"
# exit 1
# fi
notarize_binary "codex"
notarize_binary "codex-responses-api-proxy"
# 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
# - 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
# 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
# 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 }}
# 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
# steps:
# - name: Checkout repository
# uses: actions/checkout@v6
- uses: actions/download-artifact@v4
with:
path: dist
# - uses: actions/download-artifact@v4
# with:
# path: dist
- name: List
run: ls -R 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*
# # 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/
# 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: 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}"
# - 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
# 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 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: Setup Node.js for npm packaging
# uses: actions/setup-node@v5
# with:
# node-version: 22
- name: Install dependencies
run: pnpm install --frozen-lockfile
# - 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
# # 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, '-') }}
# - 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
# - 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
# # 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"
# 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
# # 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
# - 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
# # 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"
)
# 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
# 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
# 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
# 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