mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
Sign bundled macOS helper binaries (#35264)
## Why The macOS release workflow fetched `rg` and zsh while assembling package archives, after the signing stage. This left the bundled helper executables outside the workflow's signing and notarization checks. ## What changed - Fetch, sign, notarize, and upload the pinned macOS `rg` and zsh binaries with the other release artifacts. - Build package archives from those signed helpers via `--rg-bin` and the new `--zsh-bin` override. - Verify the helpers' architecture, signatures, and absence of entitlements in the final package. ## Testing - Cover the prebuilt zsh override and verify that package assembly preserves the supplied helper binaries. GitOrigin-RevId: a3865c04fa2f0f4df32e627ee7202bc87bdc3241
This commit is contained in:
committed by
copyberry
parent
0d2a0aa76b
commit
a453588416
10
.github/scripts/build-codex-package-archive.sh
vendored
10
.github/scripts/build-codex-package-archive.sh
vendored
@@ -10,6 +10,8 @@ Usage: build-codex-package-archive.sh \
|
|||||||
--archive-dir <dir> \
|
--archive-dir <dir> \
|
||||||
[--bwrap-bin <path>] \
|
[--bwrap-bin <path>] \
|
||||||
[--code-mode-host-bin <path>] \
|
[--code-mode-host-bin <path>] \
|
||||||
|
[--rg-bin <path>] \
|
||||||
|
[--zsh-bin <path>] \
|
||||||
[--zsh-manifest <path>] \
|
[--zsh-manifest <path>] \
|
||||||
[--codex-command-runner-bin <path>] \
|
[--codex-command-runner-bin <path>] \
|
||||||
[--codex-windows-sandbox-setup-bin <path>] \
|
[--codex-windows-sandbox-setup-bin <path>] \
|
||||||
@@ -56,6 +58,14 @@ while [[ $# -gt 0 ]]; do
|
|||||||
code_mode_host_bin_provided="true"
|
code_mode_host_bin_provided="true"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
--rg-bin)
|
||||||
|
resource_args+=(--rg-bin "${2:?--rg-bin requires a value}")
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--zsh-bin)
|
||||||
|
resource_args+=(--zsh-bin "${2:?--zsh-bin requires a value}")
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
--zsh-manifest)
|
--zsh-manifest)
|
||||||
resource_args+=(--zsh-manifest "${2:?--zsh-manifest requires a value}")
|
resource_args+=(--zsh-manifest "${2:?--zsh-manifest requires a value}")
|
||||||
shift 2
|
shift 2
|
||||||
|
|||||||
105
.github/workflows/rust-release.yml
vendored
105
.github/workflows/rust-release.yml
vendored
@@ -597,6 +597,63 @@ jobs:
|
|||||||
--report-dir "${report_dir}/${binary}"
|
--report-dir "${report_dir}/${binary}"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
- name: Fetch, sign, and notarize pinned macOS helpers
|
||||||
|
if: ${{ matrix.bundle == 'primary' }}
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
TARGET: ${{ matrix.target }}
|
||||||
|
APPLE_NOTARIZATION_KEY_P8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }}
|
||||||
|
APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
|
||||||
|
APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
signed_root="${GITHUB_WORKSPACE}/signed-resources/${TARGET}"
|
||||||
|
zsh_manifest="${RUNNER_TEMP}/codex-zsh-${TARGET}"
|
||||||
|
mkdir -p "$signed_root"
|
||||||
|
curl -fsSL \
|
||||||
|
"https://github.com/${GITHUB_REPOSITORY}/releases/download/${CODEX_ZSH_RELEASE_TAG}/codex-zsh" \
|
||||||
|
-o "$zsh_manifest"
|
||||||
|
|
||||||
|
PYTHONPATH="${GITHUB_WORKSPACE}/scripts" python3 - "$TARGET" "$signed_root" "$zsh_manifest" <<'PY'
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from codex_package.ripgrep import fetch_rg
|
||||||
|
from codex_package.targets import TARGET_SPECS
|
||||||
|
from codex_package.zsh import resolve_zsh_bin
|
||||||
|
|
||||||
|
spec = TARGET_SPECS[sys.argv[1]]
|
||||||
|
signed_root = Path(sys.argv[2])
|
||||||
|
zsh_bin = resolve_zsh_bin(spec, Path(sys.argv[3]))
|
||||||
|
if zsh_bin is None:
|
||||||
|
raise RuntimeError(f"Pinned zsh release is missing {spec.target}")
|
||||||
|
shutil.copy2(fetch_rg(spec), signed_root / "rg")
|
||||||
|
shutil.copy2(zsh_bin, signed_root / "zsh")
|
||||||
|
PY
|
||||||
|
|
||||||
|
for resource in rg zsh; do
|
||||||
|
binary="${signed_root}/${resource}"
|
||||||
|
report_dir="${GITHUB_WORKSPACE}/macos-binary-signing-verification/${TARGET}/${resource}"
|
||||||
|
mkdir -p "$report_dir"
|
||||||
|
chmod 0755 "$binary"
|
||||||
|
.github/scripts/macos-signing/sign_macos_code.sh \
|
||||||
|
--target "$binary" \
|
||||||
|
--identity unused \
|
||||||
|
--deep false \
|
||||||
|
--identifier "com.openai.codex.${resource}" \
|
||||||
|
--options runtime \
|
||||||
|
--timestamp true
|
||||||
|
|
||||||
|
rcodesign print-signature-info "$binary" \
|
||||||
|
>"${report_dir}/signature-info.yaml"
|
||||||
|
|
||||||
|
.github/scripts/macos-signing/notarize_macos_binary_with_rcodesign.sh \
|
||||||
|
--binary "$binary" \
|
||||||
|
--report-dir "$report_dir"
|
||||||
|
done
|
||||||
|
|
||||||
- name: Upload signed macOS binaries
|
- name: Upload signed macOS binaries
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
@@ -604,6 +661,14 @@ jobs:
|
|||||||
path: signed-macos/${{ matrix.target }}/*
|
path: signed-macos/${{ matrix.target }}/*
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Upload signed macOS helpers
|
||||||
|
if: ${{ matrix.bundle == 'primary' }}
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.target }}-signed-resources
|
||||||
|
path: signed-resources/${{ matrix.target }}/*
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload binary signing verification
|
- name: Upload binary signing verification
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
@@ -659,6 +724,12 @@ jobs:
|
|||||||
name: ${{ matrix.artifact_name }}-signed-binaries
|
name: ${{ matrix.artifact_name }}-signed-binaries
|
||||||
path: codex-rs/target/${{ matrix.target }}/release
|
path: codex-rs/target/${{ matrix.target }}/release
|
||||||
|
|
||||||
|
- name: Download signed macOS helpers
|
||||||
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.target }}-signed-resources
|
||||||
|
path: codex-rs/signed-resources/${{ matrix.target }}
|
||||||
|
|
||||||
- name: Verify signed macOS binaries
|
- name: Verify signed macOS binaries
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -669,6 +740,12 @@ jobs:
|
|||||||
codesign --verify --strict --verbose=2 "$binary_path"
|
codesign --verify --strict --verbose=2 "$binary_path"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
for resource in rg zsh; do
|
||||||
|
resource_path="signed-resources/${{ matrix.target }}/${resource}"
|
||||||
|
chmod 0755 "$resource_path"
|
||||||
|
codesign --verify --strict --verbose=2 "$resource_path"
|
||||||
|
done
|
||||||
|
|
||||||
- name: Build unsigned macOS DMG
|
- name: Build unsigned macOS DMG
|
||||||
if: ${{ matrix.build_dmg == 'true' }}
|
if: ${{ matrix.build_dmg == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -730,14 +807,6 @@ jobs:
|
|||||||
cp "target/${{ matrix.target }}/release/${binary}" "$dest/${binary}-${{ matrix.target }}"
|
cp "target/${{ matrix.target }}/release/${binary}" "$dest/${binary}-${{ matrix.target }}"
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Download packaged zsh manifest
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
curl -fsSL \
|
|
||||||
"https://github.com/${GITHUB_REPOSITORY}/releases/download/${CODEX_ZSH_RELEASE_TAG}/codex-zsh" \
|
|
||||||
-o "${RUNNER_TEMP}/codex-zsh"
|
|
||||||
|
|
||||||
- name: Build Codex package archive
|
- name: Build Codex package archive
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
@@ -750,7 +819,8 @@ jobs:
|
|||||||
--bundle "$BUNDLE" \
|
--bundle "$BUNDLE" \
|
||||||
--entrypoint-dir "target/${TARGET}/release" \
|
--entrypoint-dir "target/${TARGET}/release" \
|
||||||
--archive-dir "dist/${TARGET}" \
|
--archive-dir "dist/${TARGET}" \
|
||||||
--zsh-manifest "${RUNNER_TEMP}/codex-zsh"
|
--rg-bin "signed-resources/${TARGET}/rg" \
|
||||||
|
--zsh-bin "signed-resources/${TARGET}/zsh"
|
||||||
|
|
||||||
- name: Build Python runtime wheel
|
- name: Build Python runtime wheel
|
||||||
if: ${{ matrix.bundle == 'primary' }}
|
if: ${{ matrix.bundle == 'primary' }}
|
||||||
@@ -1056,6 +1126,23 @@ jobs:
|
|||||||
verify_signed_binary "${package_dir}/bin/${package_entrypoint}" "$package_entrypoint"
|
verify_signed_binary "${package_dir}/bin/${package_entrypoint}" "$package_entrypoint"
|
||||||
verify_signed_binary "${package_dir}/bin/codex-code-mode-host" "codex-code-mode-host"
|
verify_signed_binary "${package_dir}/bin/codex-code-mode-host" "codex-code-mode-host"
|
||||||
|
|
||||||
|
for resource in \
|
||||||
|
"${package_dir}/codex-path/rg" \
|
||||||
|
"${package_dir}/codex-resources/zsh/bin/zsh"
|
||||||
|
do
|
||||||
|
chmod 0755 "$resource"
|
||||||
|
lipo "$resource" -verify_arch "$expected_arch"
|
||||||
|
codesign --verify --strict --verbose=2 "$resource"
|
||||||
|
entitlements="$(mktemp)"
|
||||||
|
codesign -d --entitlements :- "$resource" >"$entitlements"
|
||||||
|
if [[ -s "$entitlements" ]]; then
|
||||||
|
echo "Bundled helper $resource must not have code-signing entitlements." >&2
|
||||||
|
plutil -p "$entitlements" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -f "$entitlements"
|
||||||
|
done
|
||||||
|
|
||||||
if [[ "${{ matrix.verify_dmg }}" != "true" ]]; then
|
if [[ "${{ matrix.verify_dmg }}" != "true" ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -78,6 +78,6 @@ The patched zsh fork used by `shell_zsh_fork` is fetched from the DotSlash
|
|||||||
manifest at `scripts/codex_package/codex-zsh` when the selected target has a
|
manifest at `scripts/codex_package/codex-zsh` when the selected target has a
|
||||||
matching prebuilt artifact. Downloaded archives are cached under
|
matching prebuilt artifact. Downloaded archives are cached under
|
||||||
`$TMPDIR/codex-package/<target>-zsh` and installed at
|
`$TMPDIR/codex-package/<target>-zsh` and installed at
|
||||||
`codex-resources/zsh/bin/zsh`. Pass `--zsh-manifest` to use a different
|
`codex-resources/zsh/bin/zsh`. Pass `--zsh-bin` to package a prebuilt, signed
|
||||||
DotSlash manifest, such as the manifest published with a standalone zsh
|
executable, or `--zsh-manifest` to use a different DotSlash manifest, such as
|
||||||
artifact release.
|
the manifest published with a standalone zsh artifact release.
|
||||||
|
|||||||
@@ -98,7 +98,8 @@ def parse_args() -> argparse.Namespace:
|
|||||||
"targets, bwrap is built with Cargo."
|
"targets, bwrap is built with Cargo."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
zsh_source = parser.add_mutually_exclusive_group()
|
||||||
|
zsh_source.add_argument(
|
||||||
"--zsh-manifest",
|
"--zsh-manifest",
|
||||||
type=Path,
|
type=Path,
|
||||||
help=(
|
help=(
|
||||||
@@ -106,6 +107,11 @@ def parse_args() -> argparse.Namespace:
|
|||||||
"scripts/codex_package/codex-zsh."
|
"scripts/codex_package/codex-zsh."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
zsh_source.add_argument(
|
||||||
|
"--zsh-bin",
|
||||||
|
type=Path,
|
||||||
|
help="Optional prebuilt zsh executable instead of fetching from a manifest.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--codex-command-runner-bin",
|
"--codex-command-runner-bin",
|
||||||
type=Path,
|
type=Path,
|
||||||
@@ -182,7 +188,7 @@ def main() -> int:
|
|||||||
entrypoint_bin=source_outputs.entrypoint_bin,
|
entrypoint_bin=source_outputs.entrypoint_bin,
|
||||||
code_mode_host_bin=source_outputs.code_mode_host_bin,
|
code_mode_host_bin=source_outputs.code_mode_host_bin,
|
||||||
rg_bin=resolve_rg_bin(spec, args.rg_bin),
|
rg_bin=resolve_rg_bin(spec, args.rg_bin),
|
||||||
zsh_bin=resolve_zsh_bin(spec, args.zsh_manifest),
|
zsh_bin=resolve_zsh_bin(spec, args.zsh_manifest, zsh_bin=args.zsh_bin),
|
||||||
bwrap_bin=source_outputs.bwrap_bin,
|
bwrap_bin=source_outputs.bwrap_bin,
|
||||||
codex_command_runner_bin=source_outputs.codex_command_runner_bin,
|
codex_command_runner_bin=source_outputs.codex_command_runner_bin,
|
||||||
codex_windows_sandbox_setup_bin=source_outputs.codex_windows_sandbox_setup_bin,
|
codex_windows_sandbox_setup_bin=source_outputs.codex_windows_sandbox_setup_bin,
|
||||||
|
|||||||
@@ -15,6 +15,56 @@ from codex_package.targets import TARGET_SPECS
|
|||||||
|
|
||||||
|
|
||||||
class PackageLayoutTest(unittest.TestCase):
|
class PackageLayoutTest(unittest.TestCase):
|
||||||
|
def test_macos_package_preserves_prebuilt_resource_binaries(self) -> None:
|
||||||
|
for variant_name in ("codex", "codex-app-server"):
|
||||||
|
for target in ("aarch64-apple-darwin", "x86_64-apple-darwin"):
|
||||||
|
with self.subTest(variant=variant_name, target=target):
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
root = Path(temp_dir)
|
||||||
|
package_dir = root / "package"
|
||||||
|
package_dir.mkdir()
|
||||||
|
rg_bin = touch_executable(root / "signed-rg")
|
||||||
|
zsh_bin = touch_executable(root / "signed-zsh")
|
||||||
|
rg_bin.write_bytes(b"signed ripgrep binary")
|
||||||
|
zsh_bin.write_bytes(b"signed zsh binary")
|
||||||
|
variant = PACKAGE_VARIANTS[variant_name]
|
||||||
|
spec = TARGET_SPECS[target]
|
||||||
|
inputs = PackageInputs(
|
||||||
|
entrypoint_bin=touch_executable(
|
||||||
|
root / variant.executable_stem
|
||||||
|
),
|
||||||
|
code_mode_host_bin=touch_executable(
|
||||||
|
root / "codex-code-mode-host"
|
||||||
|
),
|
||||||
|
rg_bin=rg_bin,
|
||||||
|
zsh_bin=zsh_bin,
|
||||||
|
bwrap_bin=None,
|
||||||
|
codex_command_runner_bin=None,
|
||||||
|
codex_windows_sandbox_setup_bin=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
build_package_dir(package_dir, "1.2.3", variant, spec, inputs)
|
||||||
|
validate_package_dir(
|
||||||
|
package_dir, variant, spec, include_zsh=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
{
|
||||||
|
"rg": (package_dir / "codex-path" / "rg").read_bytes(),
|
||||||
|
"zsh": (
|
||||||
|
package_dir
|
||||||
|
/ "codex-resources"
|
||||||
|
/ "zsh"
|
||||||
|
/ "bin"
|
||||||
|
/ "zsh"
|
||||||
|
).read_bytes(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rg": b"signed ripgrep binary",
|
||||||
|
"zsh": b"signed zsh binary",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_app_server_package_places_code_mode_host_beside_entrypoint(self) -> None:
|
def test_app_server_package_places_code_mode_host_beside_entrypoint(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
root = Path(temp_dir)
|
root = Path(temp_dir)
|
||||||
|
|||||||
@@ -16,6 +16,20 @@ from codex_package.zsh import resolve_zsh_bin
|
|||||||
|
|
||||||
|
|
||||||
class ResolveZshBinTest(unittest.TestCase):
|
class ResolveZshBinTest(unittest.TestCase):
|
||||||
|
def test_uses_prebuilt_executable_override(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
signed_zsh = Path(temp_dir) / "signed-zsh"
|
||||||
|
signed_zsh.write_bytes(b"signed zsh binary")
|
||||||
|
signed_zsh.chmod(0o755)
|
||||||
|
|
||||||
|
with patch("codex_package.zsh.fetch_dotslash_executable") as fetch:
|
||||||
|
zsh_bin = resolve_zsh_bin(
|
||||||
|
TARGET_SPECS["aarch64-apple-darwin"], zsh_bin=signed_zsh
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(zsh_bin, signed_zsh.resolve())
|
||||||
|
fetch.assert_not_called()
|
||||||
|
|
||||||
def test_uses_manifest_override(self) -> None:
|
def test_uses_manifest_override(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
root = Path(temp_dir)
|
root = Path(temp_dir)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from pathlib import Path
|
|||||||
from .dotslash import fetch_dotslash_executable
|
from .dotslash import fetch_dotslash_executable
|
||||||
from .targets import REPO_ROOT
|
from .targets import REPO_ROOT
|
||||||
from .targets import TargetSpec
|
from .targets import TargetSpec
|
||||||
|
from .targets import resolve_input_path
|
||||||
|
|
||||||
|
|
||||||
ZSH_MANIFEST = REPO_ROOT / "scripts" / "codex_package" / "codex-zsh"
|
ZSH_MANIFEST = REPO_ROOT / "scripts" / "codex_package" / "codex-zsh"
|
||||||
@@ -14,7 +15,12 @@ ZSH_RESOURCE_PATH = Path("zsh") / "bin" / "zsh"
|
|||||||
def resolve_zsh_bin(
|
def resolve_zsh_bin(
|
||||||
spec: TargetSpec,
|
spec: TargetSpec,
|
||||||
manifest_path: Path | None = None,
|
manifest_path: Path | None = None,
|
||||||
|
*,
|
||||||
|
zsh_bin: Path | None = None,
|
||||||
) -> Path | None:
|
) -> Path | None:
|
||||||
|
if zsh_bin is not None:
|
||||||
|
return resolve_input_path(zsh_bin, "zsh executable", "--zsh-bin")
|
||||||
|
|
||||||
return fetch_dotslash_executable(
|
return fetch_dotslash_executable(
|
||||||
spec,
|
spec,
|
||||||
manifest_path=manifest_path or ZSH_MANIFEST,
|
manifest_path=manifest_path or ZSH_MANIFEST,
|
||||||
|
|||||||
Reference in New Issue
Block a user