fix: make GitHub Action publish to npm using trusted publishing (#3431)
This commit is contained in:
23
.github/workflows/rust-release.yml
vendored
23
.github/workflows/rust-release.yml
vendored
@@ -11,6 +11,9 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- "rust-v*.*.*"
|
- "rust-v*.*.*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write # Required for OIDC
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}
|
group: ${{ github.workflow }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
@@ -187,6 +190,20 @@ jobs:
|
|||||||
version="${GITHUB_REF_NAME#rust-v}"
|
version="${GITHUB_REF_NAME#rust-v}"
|
||||||
echo "name=${version}" >> $GITHUB_OUTPUT
|
echo "name=${version}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# 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
|
||||||
|
- 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: Stage npm package
|
- name: Stage npm package
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
@@ -220,6 +237,12 @@ jobs:
|
|||||||
tag: ${{ github.ref_name }}
|
tag: ${{ github.ref_name }}
|
||||||
config: .github/dotslash-config.json
|
config: .github/dotslash-config.json
|
||||||
|
|
||||||
|
# No NODE_AUTH_TOKEN needed because we use OIDC.
|
||||||
|
- name: Publish to npm
|
||||||
|
# Do not publish alphas to npm.
|
||||||
|
if: ${{ !contains(steps.release_name.outputs.name, '-') }}
|
||||||
|
run: npm publish "${GITHUB_WORKSPACE}/dist/npm/codex-npm-${{ steps.release_name.outputs.name }}.tgz"
|
||||||
|
|
||||||
update-branch:
|
update-branch:
|
||||||
name: Update latest-alpha-cli branch
|
name: Update latest-alpha-cli branch
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/openai/codex.git"
|
"url": "git+https://github.com/openai/codex.git",
|
||||||
|
"directory": "codex-cli"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vscode/ripgrep": "^1.15.14"
|
"@vscode/ripgrep": "^1.15.14"
|
||||||
|
|||||||
@@ -30,14 +30,7 @@ When the workflow finishes, the GitHub Release is "done," but you still have to
|
|||||||
|
|
||||||
## Publishing to npm
|
## 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:
|
The GitHub Action is responsible for publishing to npm.
|
||||||
|
|
||||||
```
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Publishing to Homebrew
|
## Publishing to Homebrew
|
||||||
|
|
||||||
|
|||||||
@@ -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-<version>.tgz` asset from the GitHub release
|
|
||||||
tagged `rust-v<version>` 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())
|
|
||||||
Reference in New Issue
Block a user