diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 193bcd859..5a6da68b7 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -595,9 +595,9 @@ jobs: mkdir -p dist/npm patterns=( "codex-npm-${version}.tgz" - "codex-linux-*-npm-${version}.tgz" - "codex-darwin-*-npm-${version}.tgz" - "codex-win32-*-npm-${version}.tgz" + "codex-npm-linux-*-${version}.tgz" + "codex-npm-darwin-*-${version}.tgz" + "codex-npm-win32-*-${version}.tgz" "codex-responses-api-proxy-npm-${version}.tgz" "codex-sdk-npm-${version}.tgz" ) @@ -615,20 +615,44 @@ jobs: NPM_TAG: ${{ needs.release.outputs.npm_tag }} run: | set -euo pipefail - tag_args=() + prefix="" if [[ -n "${NPM_TAG}" ]]; then - tag_args+=(--tag "${NPM_TAG}") + prefix="${NPM_TAG}-" fi shopt -s nullglob - tarballs=(dist/npm/*-npm-"${VERSION}".tgz) + tarballs=(dist/npm/*-"${VERSION}".tgz) if [[ ${#tarballs[@]} -eq 0 ]]; then echo "No npm tarballs found in dist/npm for version ${VERSION}" exit 1 fi for tarball in "${tarballs[@]}"; do - npm publish "${GITHUB_WORKSPACE}/${tarball}" "${tag_args[@]}" + filename="$(basename "${tarball}")" + tag="" + + case "${filename}" in + codex-npm-linux-*-"${VERSION}".tgz|codex-npm-darwin-*-"${VERSION}".tgz|codex-npm-win32-*-"${VERSION}".tgz) + platform="${filename#codex-npm-}" + platform="${platform%-${VERSION}.tgz}" + tag="${prefix}${platform}" + ;; + codex-npm-"${VERSION}".tgz|codex-responses-api-proxy-npm-"${VERSION}".tgz|codex-sdk-npm-"${VERSION}".tgz) + tag="${NPM_TAG}" + ;; + *) + echo "Unexpected npm tarball: ${filename}" + exit 1 + ;; + esac + + publish_cmd=(npm publish "${GITHUB_WORKSPACE}/${tarball}") + if [[ -n "${tag}" ]]; then + publish_cmd+=(--tag "${tag}") + fi + + echo "+ ${publish_cmd[*]}" + "${publish_cmd[@]}" done update-branch: diff --git a/codex-cli/scripts/README.md b/codex-cli/scripts/README.md index b9d55bad2..ca0d54b54 100644 --- a/codex-cli/scripts/README.md +++ b/codex-cli/scripts/README.md @@ -15,7 +15,8 @@ This downloads the native artifacts once, hydrates `vendor/` for each package, a tarballs to `dist/npm/`. When `--package codex` is provided, the staging helper builds the lightweight -`@openai/codex` meta package plus all `@openai/codex-` native packages. +`@openai/codex` meta package plus all platform-native `@openai/codex` variants +that are later published under platform-specific dist-tags. If you need to invoke `build_npm_package.py` directly, run `codex-cli/scripts/install_native_deps.py` first and pass `--vendor-src` pointing to the diff --git a/codex-cli/scripts/build_npm_package.py b/codex-cli/scripts/build_npm_package.py index 066f09e7e..6e1c677b3 100755 --- a/codex-cli/scripts/build_npm_package.py +++ b/codex-cli/scripts/build_npm_package.py @@ -14,40 +14,49 @@ CODEX_CLI_ROOT = SCRIPT_DIR.parent REPO_ROOT = CODEX_CLI_ROOT.parent RESPONSES_API_PROXY_NPM_ROOT = REPO_ROOT / "codex-rs" / "responses-api-proxy" / "npm" CODEX_SDK_ROOT = REPO_ROOT / "sdk" / "typescript" +CODEX_NPM_NAME = "@openai/codex" +# `npm_name` is the local optional-dependency alias consumed by `bin/codex.js`. +# The underlying package published to npm is always `@openai/codex`. CODEX_PLATFORM_PACKAGES: dict[str, dict[str, str]] = { "codex-linux-x64": { "npm_name": "@openai/codex-linux-x64", + "npm_tag": "linux-x64", "target_triple": "x86_64-unknown-linux-musl", "os": "linux", "cpu": "x64", }, "codex-linux-arm64": { "npm_name": "@openai/codex-linux-arm64", + "npm_tag": "linux-arm64", "target_triple": "aarch64-unknown-linux-musl", "os": "linux", "cpu": "arm64", }, "codex-darwin-x64": { "npm_name": "@openai/codex-darwin-x64", + "npm_tag": "darwin-x64", "target_triple": "x86_64-apple-darwin", "os": "darwin", "cpu": "x64", }, "codex-darwin-arm64": { "npm_name": "@openai/codex-darwin-arm64", + "npm_tag": "darwin-arm64", "target_triple": "aarch64-apple-darwin", "os": "darwin", "cpu": "arm64", }, "codex-win32-x64": { "npm_name": "@openai/codex-win32-x64", + "npm_tag": "win32-x64", "target_triple": "x86_64-pc-windows-msvc", "os": "win32", "cpu": "x64", }, "codex-win32-arm64": { "npm_name": "@openai/codex-win32-arm64", + "npm_tag": "win32-arm64", "target_triple": "aarch64-pc-windows-msvc", "os": "win32", "cpu": "arm64", @@ -244,6 +253,8 @@ def stage_sources(staging_dir: Path, version: str, package: str) -> None: package_json_path = CODEX_CLI_ROOT / "package.json" elif package in CODEX_PLATFORM_PACKAGES: platform_package = CODEX_PLATFORM_PACKAGES[package] + platform_npm_tag = platform_package["npm_tag"] + platform_version = compute_platform_package_version(version, platform_npm_tag) readme_src = REPO_ROOT / "README.md" if readme_src.exists(): @@ -253,8 +264,8 @@ def stage_sources(staging_dir: Path, version: str, package: str) -> None: codex_package_json = json.load(fh) package_json = { - "name": platform_package["npm_name"], - "version": version, + "name": CODEX_NPM_NAME, + "version": platform_version, "license": codex_package_json.get("license", "Apache-2.0"), "os": [platform_package["os"]], "cpu": [platform_package["cpu"]], @@ -294,7 +305,10 @@ def stage_sources(staging_dir: Path, version: str, package: str) -> None: if package == "codex": package_json["files"] = ["bin"] package_json["optionalDependencies"] = { - CODEX_PLATFORM_PACKAGES[platform_package]["npm_name"]: version + CODEX_PLATFORM_PACKAGES[platform_package]["npm_name"]: ( + f"npm:{CODEX_NPM_NAME}@" + f"{compute_platform_package_version(version, CODEX_PLATFORM_PACKAGES[platform_package]['npm_tag'])}" + ) for platform_package in PACKAGE_EXPANSIONS["codex"] if platform_package != "codex" } @@ -316,6 +330,12 @@ def stage_sources(staging_dir: Path, version: str, package: str) -> None: out.write("\n") +def compute_platform_package_version(version: str, platform_tag: str) -> str: + # npm forbids republishing the same package name/version, so each + # platform-specific tarball needs a unique version string. + return f"{version}-{platform_tag}" + + def run_command(cmd: list[str], cwd: Path | None = None) -> None: print("+", " ".join(cmd)) subprocess.run(cmd, cwd=cwd, check=True) diff --git a/scripts/stage_npm_packages.py b/scripts/stage_npm_packages.py index ff08111b9..5bbee755e 100755 --- a/scripts/stage_npm_packages.py +++ b/scripts/stage_npm_packages.py @@ -26,6 +26,7 @@ _BUILD_MODULE = importlib.util.module_from_spec(_SPEC) _SPEC.loader.exec_module(_BUILD_MODULE) PACKAGE_NATIVE_COMPONENTS = getattr(_BUILD_MODULE, "PACKAGE_NATIVE_COMPONENTS", {}) PACKAGE_EXPANSIONS = getattr(_BUILD_MODULE, "PACKAGE_EXPANSIONS", {}) +CODEX_PLATFORM_PACKAGES = getattr(_BUILD_MODULE, "CODEX_PLATFORM_PACKAGES", {}) def parse_args() -> argparse.Namespace: @@ -129,6 +130,13 @@ def run_command(cmd: list[str]) -> None: subprocess.run(cmd, cwd=REPO_ROOT, check=True) +def tarball_name_for_package(package: str, version: str) -> str: + if package in CODEX_PLATFORM_PACKAGES: + platform = package.removeprefix("codex-") + return f"codex-npm-{platform}-{version}.tgz" + return f"{package}-npm-{version}.tgz" + + def main() -> int: args = parse_args() @@ -160,7 +168,7 @@ def main() -> int: for package in packages: staging_dir = Path(tempfile.mkdtemp(prefix=f"npm-stage-{package}-", dir=runner_temp)) - pack_output = output_dir / f"{package}-npm-{args.release_version}.tgz" + pack_output = output_dir / tarball_name_for_package(package, args.release_version) cmd = [ str(BUILD_SCRIPT),