Files
codex/.github/scripts/macos-signing/sign_macos_cli_package.py
T
Dmitriy Kovalenko b91f2515f5 Ship the standalone executor in release packages (#57837)
## What changed

- Include `codex-exec-server` in canonical CLI and app-server packages and advertise its path through `execServer` in `codex-package.json`.
- Build and sign the executor in release workflows, preserve signed prebuilt inputs, and verify packaged signatures. Use empty entitlements for the macOS executor.
- Have installers check for the executor when package metadata advertises it, while continuing to accept older packages.
- Omit the standalone executor and its manifest entry from npm packages to stay within npm's package size limit. Keep `codex exec-server` and its runtime helpers available.

## Testing

Add package smoke tests for executor initialization, version reporting, filesystem and process sandbox enforcement, and invalid-argument/configuration parity with `codex exec-server`. Extend archive, npm staging, and macOS signing tests to cover executor contents, executable permissions, preserved bytes, and entitlement verification.

GitOrigin-RevId: bc741fe7faa6fe631d076de18d448c55247c23c7
2026-10-28 21:09:05 +00:00

192 lines
7.4 KiB
Python

"""Sign and verify macOS CLI packages using the shared AKV and native tools."""
import argparse
import os
import plistlib
import subprocess
import sys
from pathlib import Path
import provisioned_macos_cli_package as bundle
def main(configuration: bundle.ProfileConfiguration | None = None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("operation", choices=("sign", "verify"))
parser.add_argument("package", type=Path)
args, configuration = bundle.parse_profile_args(parser, configuration)
package = args.package.resolve(strict=True)
if not package.is_dir():
parser.error("package must be an extracted CLI package directory")
repo_root = os.environ.get("CODEX_REPO_ROOT")
if not repo_root:
parser.error("CODEX_REPO_ROOT must be set")
reports = Path(repo_root) / "signing-verification"
reports.mkdir(parents=True, exist_ok=True)
provisioned = os.environ.get("PROVISIONED_MACOS", "false") == "true"
if provisioned:
if args.operation == "sign":
configuration.validate_signing_certificate(
Path(os.environ["OAI_AKV_SIGNING_CERTIFICATE_PEM"])
)
bundle.prepare(package, reports, configuration)
else:
bundle.verify(package, reports, os.environ["TARGET"], configuration)
for relative in ("bin/codex", *bundle.HELPERS):
binary = package / relative
name = binary.name
target = binary
identifier = name
entitlements = bundle.SIGNING / f"{name}.entitlements.plist"
if provisioned and relative == "bin/codex":
binary = package / bundle.EXECUTABLE
target = package / bundle.APP
identifier = bundle.CODE_SIGNING_ID
entitlements = reports / "codex-provisioned-entitlements.plist"
if args.operation == "sign":
command = [
"bash",
str(bundle.SIGNING / "sign_macos_code.sh"),
"--target",
str(target),
"--identity",
"unused",
"--deep",
"false",
"--options",
"runtime",
"--timestamp",
"true",
]
if relative.startswith("bin/"):
command.extend(
["--identifier", identifier, "--entitlements", str(entitlements)]
)
else:
command.extend(["--identifier", f"com.openai.codex.{name}"])
subprocess.run(command, check=True)
with (reports / f"{name}-signature.yaml").open("wb") as output:
subprocess.run(
["rcodesign", "print-signature-info", str(binary)],
stdout=output,
check=True,
)
if not provisioned:
subprocess.run(
[
"bash",
str(bundle.SIGNING / "notarize_macos_binary_with_akv.sh"),
"--binary",
str(binary),
"--report-dir",
str(reports / name),
],
check=True,
)
else:
target_triple = os.environ["TARGET"]
architectures = {
"aarch64-apple-darwin": "arm64",
"x86_64-apple-darwin": "x86_64",
}
if target_triple not in architectures:
raise ValueError(f"Unexpected macOS target: {target_triple}")
subprocess.run(
["lipo", str(binary), "-verify_arch", architectures[target_triple]],
check=True,
)
# Preserve the CLI's existing login-keychain identity while checking
# its bundle/App ID and provisioning independently in bundle.verify.
requirement = (
"=anchor apple generic"
" and certificate 1[field.1.2.840.113635.100.6.2.6] exists"
" and certificate leaf[field.1.2.840.113635.100.6.1.13] exists"
)
if provisioned and relative == "bin/codex":
requirement += (
f' and identifier "{bundle.CODE_SIGNING_ID}"'
f' and certificate leaf[subject.OU] = "{configuration.team_id}"'
)
subprocess.run(
[
"codesign",
"--verify",
"--strict",
"--verbose=2",
"--test-requirement",
requirement,
str(target),
],
check=True,
)
signature = reports / f"{name}-signature.txt"
with signature.open("wb") as output:
subprocess.run(
["codesign", "-d", "--verbose=4", str(target)],
stderr=output,
check=True,
)
actual = reports / f"{name}-entitlements.plist"
with actual.open("wb") as output:
subprocess.run(
["codesign", "-d", "--entitlements", ":-", str(target)],
stdout=output,
check=True,
)
if relative.startswith("bin/"):
# Signing tools may omit the blob when no entitlements are requested.
if (
actual.stat().st_size == 0
and plistlib.loads(entitlements.read_bytes()) == {}
):
actual.write_bytes(entitlements.read_bytes())
expected = reports / f"{name}-expected.plist"
subprocess.run(
[
"plutil",
"-convert",
"xml1",
"-o",
str(expected),
str(entitlements),
],
check=True,
)
subprocess.run(["plutil", "-convert", "xml1", str(actual)], check=True)
subprocess.run(["diff", "-u", str(expected), str(actual)], check=True)
elif actual.stat().st_size:
raise ValueError(
f"Bundled helper {name} must not have signing entitlements"
)
if provisioned and args.operation == "sign":
archive = Path(os.environ["RUNNER_TEMP"]) / "provisioned-cli.zip"
subprocess.run(["zip", "-q", "-r", str(archive), "."], cwd=package, check=True)
command = [
sys.executable,
str(bundle.SIGNING / "notarize_with_akv.py"),
"--file",
str(archive),
"--report-log",
str(reports / "notarization-developer-log.json"),
"--max-wait-seconds",
"1200",
]
with (
(reports / "notarization.log").open("w") as output,
subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
) as process,
):
assert process.stdout is not None
for line in process.stdout:
print(line, end="", flush=True)
output.write(line)
if returncode := process.wait():
raise subprocess.CalledProcessError(returncode, command)
if __name__ == "__main__":
main()