From f2953eb587acacc77be68e7fd20b7bdc76316eba Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Wed, 10 Sep 2025 12:35:41 -0700 Subject: [PATCH] fix: have the GitHub Action publish to npm --- .github/workflows/rust-release.yml | 5 ++ docs/release_management.md | 9 +-- scripts/publish_to_npm.py | 118 ----------------------------- 3 files changed, 6 insertions(+), 126 deletions(-) delete mode 100755 scripts/publish_to_npm.py diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index a765ba6a33..34226b5fe0 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -219,3 +219,8 @@ jobs: with: tag: ${{ github.ref_name }} config: .github/dotslash-config.json + + - name: Publish to npm + run: npm publish --access public "${GITHUB_WORKSPACE}/dist/npm/codex-npm-${{ steps.release_name.outputs.name }}.tgz" + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/docs/release_management.md b/docs/release_management.md index ed12de6e4a..d2b3264554 100644 --- a/docs/release_management.md +++ b/docs/release_management.md @@ -30,14 +30,7 @@ When the workflow finishes, the GitHub Release is "done," but you still have to ## Publishing to npm -After the GitHub Release is done, you can publish to npm. Note the GitHub Release includes the appropriate artifact for npm (which is the output of `npm pack`), which should be named `codex-npm-VERSION.tgz`. To publish to npm, run: - -``` -VERSION=0.21.0 -./scripts/publish_to_npm.py "$VERSION" -``` - -Note that you must have permissions to publish to https://www.npmjs.com/package/@openai/codex for this to succeed. +The GitHub Action is responsible for publishing to npm. ## Publishing to Homebrew diff --git a/scripts/publish_to_npm.py b/scripts/publish_to_npm.py deleted file mode 100755 index f79843ffc8..0000000000 --- a/scripts/publish_to_npm.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 - -""" -Download a release artifact for the npm package and publish it. - -Given a release version like `0.20.0`, this script: - - Downloads the `codex-npm-.tgz` asset from the GitHub release - tagged `rust-v` in the `openai/codex` repository using `gh`. - - Runs `npm publish` on the downloaded tarball to publish `@openai/codex`. - -Flags: - - `--dry-run` delegates to `npm publish --dry-run`. The artifact is still - downloaded so npm can inspect the archive contents without publishing. - -Requirements: - - GitHub CLI (`gh`) must be installed and authenticated to access the repo. - - npm must be logged in with an account authorized to publish - `@openai/codex`. This may trigger a browser for 2FA. -""" - -import argparse -import os -import subprocess -import sys -import tempfile -from pathlib import Path - - -def run_checked(cmd: list[str], cwd: Path | None = None) -> None: - """Run a subprocess command and raise if it fails.""" - proc = subprocess.run(cmd, cwd=str(cwd) if cwd else None) - proc.check_returncode() - - -def main() -> int: - parser = argparse.ArgumentParser( - description=( - "Download the npm release artifact for a given version and publish it." - ) - ) - parser.add_argument( - "version", - help="Release version to publish, e.g. 0.20.0 (without the 'v' prefix)", - ) - parser.add_argument( - "--dir", - type=Path, - help=( - "Optional directory to download the artifact into. Defaults to a temporary directory." - ), - ) - parser.add_argument( - "-n", - "--dry-run", - action="store_true", - help="Delegate to `npm publish --dry-run` (still downloads the artifact).", - ) - args = parser.parse_args() - - version: str = args.version.lstrip("v") - tag = f"rust-v{version}" - asset_name = f"codex-npm-{version}.tgz" - - download_dir_context_manager = ( - tempfile.TemporaryDirectory() if args.dir is None else None - ) - # Use provided dir if set, else the temporary one created above - download_dir: Path = args.dir if args.dir else Path(download_dir_context_manager.name) # type: ignore[arg-type] - download_dir.mkdir(parents=True, exist_ok=True) - - # 1) Download the artifact using gh - repo = "openai/codex" - gh_cmd = [ - "gh", - "release", - "download", - tag, - "--repo", - repo, - "--pattern", - asset_name, - "--dir", - str(download_dir), - ] - print(f"Downloading {asset_name} from {repo}@{tag} into {download_dir}...") - # Even in --dry-run we download so npm can inspect the tarball. - run_checked(gh_cmd) - - artifact_path = download_dir / asset_name - if not args.dry_run and not artifact_path.is_file(): - print( - f"Error: expected artifact not found after download: {artifact_path}", - file=sys.stderr, - ) - return 1 - - # 2) Publish to npm - npm_cmd = ["npm", "publish"] - if args.dry_run: - npm_cmd.append("--dry-run") - npm_cmd.append(str(artifact_path)) - - # Ensure CI is unset so npm can open a browser for 2FA if needed. - env = os.environ.copy() - if env.get("CI"): - env.pop("CI") - - print("Running:", " ".join(npm_cmd)) - proc = subprocess.run(npm_cmd, env=env) - proc.check_returncode() - - print("Publish complete.") - # Keep the temporary directory alive until here; it is cleaned up on exit - return 0 - - -if __name__ == "__main__": - sys.exit(main())