Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

# stage_npm_packages.py requires DotSlash when staging releases.
- uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2

- name: Stage npm package
id: stage_npm_package
env:
Expand All @@ -55,17 +52,13 @@ jobs:
# cross-platform native payload required by the npm package layout.
# Passing the workflow URL directly avoids relying on old rust-v*
# branches remaining discoverable via `gh run list --branch ...`.
CODEX_VERSION=0.125.0
WORKFLOW_URL="https://github.com/openai/codex/actions/runs/26131514935"
CODEX_VERSION=0.133.0-alpha.4
WORKFLOW_URL="https://github.com/openai/codex/actions/runs/26201494185"
OUTPUT_DIR="${RUNNER_TEMP}"
# This reused workflow predates codex-package archive artifacts, so
# CI synthesizes the package layout from the older per-binary
# artifacts. Release staging must use real package archives.
python3 ./scripts/stage_npm_packages.py \
--release-version "$CODEX_VERSION" \
--workflow-url "$WORKFLOW_URL" \
--package codex \
--allow-legacy-codex-package \
--output-dir "$OUTPUT_DIR"
PACK_OUTPUT="${OUTPUT_DIR}/codex-npm-${CODEX_VERSION}.tgz"
echo "pack_output=$PACK_OUTPUT" >> "$GITHUB_OUTPUT"
Expand Down
10 changes: 5 additions & 5 deletions codex-cli/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ example, to stage the CLI, responses proxy, and SDK packages for version `0.6.0`
--package codex-sdk
```

This downloads the native package archive artifacts once, hydrates `vendor/` for each
package, and writes tarballs to `dist/npm/`.
This downloads the required native package archive artifacts, hydrates `vendor/` for
each package, and writes tarballs to `dist/npm/`.

When `--package codex` is provided, the staging helper builds the lightweight
`@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 --component codex-package` first and pass
`--vendor-src` pointing to the directory that contains the populated `vendor/` tree.
Direct `build_npm_package.py` invocations are still useful for package-specific
debugging, but native packages expect `--vendor-src` to point at a prehydrated
`vendor/` tree. Release packaging should use `scripts/stage_npm_packages.py`.
93 changes: 15 additions & 78 deletions codex-cli/scripts/build_npm_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import argparse
import json
import os
import shutil
import subprocess
import sys
Expand All @@ -16,7 +17,6 @@
CODEX_SDK_ROOT = REPO_ROOT / "sdk" / "typescript"
CODEX_NPM_NAME = "@openai/codex"
CODEX_PACKAGE_COMPONENT = "codex-package"
CODEX_PACKAGE_ENTRIES = ("codex-package.json", "bin", "codex-resources", "codex-path")

# `npm_name` is the local optional-dependency alias consumed by `bin/codex.js`.
# The underlying package published to npm is always `@openai/codex`.
Expand Down Expand Up @@ -88,16 +88,6 @@

PACKAGE_CHOICES = tuple(PACKAGE_NATIVE_COMPONENTS)

COMPONENT_DEST_DIR: dict[str, str] = {
"bwrap": "codex-resources",
"codex": "codex",
"codex-responses-api-proxy": "codex-responses-api-proxy",
"codex-windows-sandbox-setup": "codex",
"codex-command-runner": "codex",
"rg": "path",
}


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Build or stage the Codex CLI npm package.")
parser.add_argument(
Expand Down Expand Up @@ -140,16 +130,6 @@ def parse_args() -> argparse.Namespace:
type=Path,
help="Directory containing pre-installed native binaries to bundle (vendor root).",
)
parser.add_argument(
"--allow-missing-native-component",
dest="allow_missing_native_components",
action="append",
default=[],
help=(
"Native component that may be absent from --vendor-src. Intended for CI "
"compatibility with older artifact workflows; releases should not use this."
),
)
return parser.parse_args()


Expand Down Expand Up @@ -190,7 +170,6 @@ def main() -> int:
staging_dir,
native_components,
target_filter={target_filter} if target_filter else None,
allow_missing_components=set(args.allow_missing_native_components),
)

if release_version:
Expand Down Expand Up @@ -346,7 +325,7 @@ def compute_platform_package_version(version: str, platform_tag: str) -> str:


def run_command(cmd: list[str], cwd: Path | None = None) -> None:
print("+", " ".join(cmd))
print("+", " ".join(cmd), flush=True)
subprocess.run(cmd, cwd=cwd, check=True)


Expand Down Expand Up @@ -376,18 +355,12 @@ def copy_native_binaries(
staging_dir: Path,
components: list[str],
target_filter: set[str] | None = None,
allow_missing_components: set[str] | None = None,
) -> None:
vendor_src = vendor_src.resolve()
if not vendor_src.exists():
raise RuntimeError(f"Vendor source directory not found: {vendor_src}")

components_set = {
component
for component in components
if component == CODEX_PACKAGE_COMPONENT or component in COMPONENT_DEST_DIR
}
allow_missing_components = allow_missing_components or set()
components_set = set(components)
if not components_set:
return

Expand All @@ -410,34 +383,20 @@ def copy_native_binaries(
dest_target_dir = vendor_dest / target_dir.name

if CODEX_PACKAGE_COMPONENT in components_set:
validate_codex_package_dir(target_dir)
if dest_target_dir.exists():
shutil.rmtree(dest_target_dir)
dest_target_dir.mkdir(parents=True, exist_ok=True)
for entry in CODEX_PACKAGE_ENTRIES:
src = target_dir / entry
dest = dest_target_dir / entry
if src.is_dir():
shutil.copytree(src, dest)
else:
shutil.copy2(src, dest)
shutil.copytree(target_dir, dest_target_dir)
else:
dest_target_dir.mkdir(parents=True, exist_ok=True)

for component in components_set - {CODEX_PACKAGE_COMPONENT}:
dest_dir_name = COMPONENT_DEST_DIR.get(component)
if dest_dir_name is None:
continue

src_component_dir = target_dir / dest_dir_name
for component in sorted(components_set - {CODEX_PACKAGE_COMPONENT}):
src_component_dir = target_dir / component
if not src_component_dir.exists():
if component in allow_missing_components:
continue
raise RuntimeError(
f"Missing native component '{component}' in vendor source: {src_component_dir}"
)

dest_component_dir = dest_target_dir / dest_dir_name
dest_component_dir = dest_target_dir / component
if dest_component_dir.exists():
shutil.rmtree(dest_component_dir)
shutil.copytree(src_component_dir, dest_component_dir)
Expand All @@ -448,45 +407,23 @@ def copy_native_binaries(
missing_list = ", ".join(missing_targets)
raise RuntimeError(f"Missing target directories in vendor source: {missing_list}")


def validate_codex_package_dir(package_dir: Path) -> None:
is_windows = "windows" in package_dir.name
required_files = [
Path("codex-package.json"),
Path("bin") / ("codex.exe" if is_windows else "codex"),
Path("codex-path") / ("rg.exe" if is_windows else "rg"),
]

if "linux" in package_dir.name:
required_files.append(Path("codex-resources") / "bwrap")

if is_windows:
required_files.extend(
[
Path("codex-resources") / "codex-command-runner.exe",
Path("codex-resources") / "codex-windows-sandbox-setup.exe",
]
)

missing_files = [
str(relative_path)
for relative_path in required_files
if not (package_dir / relative_path).is_file()
]
if missing_files:
missing = ", ".join(missing_files)
raise RuntimeError(f"Missing files in Codex package directory {package_dir}: {missing}")


def run_npm_pack(staging_dir: Path, output_path: Path) -> Path:
output_path = output_path.resolve()
output_path.parent.mkdir(parents=True, exist_ok=True)

with tempfile.TemporaryDirectory(prefix="codex-npm-pack-") as pack_dir_str:
pack_dir = Path(pack_dir_str)
npm_cache_dir = pack_dir / "npm-cache"
npm_logs_dir = pack_dir / "npm-logs"
npm_cache_dir.mkdir()
npm_logs_dir.mkdir()
env = os.environ.copy()
env["NPM_CONFIG_CACHE"] = str(npm_cache_dir)
env["NPM_CONFIG_LOGS_DIR"] = str(npm_logs_dir)
stdout = subprocess.check_output(
["npm", "pack", "--json", "--pack-destination", str(pack_dir)],
cwd=staging_dir,
env=env,
text=True,
)
try:
Expand Down
Loading
Loading