Skip to content

Add the PwrAgent Codex release pipeline - #2

Merged
Harold Hunt (huntharo) merged 2 commits into
pwragentfrom
agent/pwragent-release-workflows
Aug 19, 2026
Merged

Add the PwrAgent Codex release pipeline#2
Harold Hunt (huntharo) merged 2 commits into
pwragentfrom
agent/pwragent-release-workflows

Conversation

@huntharo

Copy link
Copy Markdown

Builds and signs downstream Codex binaries once, for PwrAgent, PwrSnap, and
PwrGit to share, so none of them signs Codex itself. Modeled on the equivalent
pipeline in pwrdrvr/grok-build.

Upstream's rust-release.yml cannot be reused here: it targets self-hosted
runner groups (codex-runners, macos-15-xlarge) that do not exist on this
fork, and signs through OpenAI's codesigning environment. So this is a
separate workflow on GitHub-hosted runners, signing with PwrDrvr credentials
from the apple-signing and windows-signing environments.

Ships five assets: macOS arm64/x64, Linux arm64/x64, Windows x64. Each carries
codex, codex-app-server, and codex-code-mode-host, plus the Windows
sandbox helpers on Windows, alongside LICENSE, NOTICE, and a provenance stamp.

Signing is fail-closed by construction, and check-release-signing.py pins the
properties that make it so:

  • The jobs that build and stage enter no environment and read no secrets. They
    hand off a tarball plus its SHA-256; the signing jobs verify that digest
    before touching a credential.
  • release-candidate requires both signing jobs, so there is no path from a
    build to a published asset that skips a signer.
  • The Windows TrustedSigning client is downloaded, catalog-verified, and
    checksummed in the unprivileged prepare job and shipped to the signing job as
    pinned bytes. The signing job may not call Save-Module/Install-Module.
  • Every shipped executable is signed and then individually verified, rather
    than just the CLI entrypoint.

The contract also pins the workflow to GitHub-hosted runners, since reaching
for a self-hosted group is exactly how this would silently stop working on the
fork.

ci:release-signing on a pull request runs the whole pipeline, signing
included, and produces a signed-release-candidate without publishing. A
pwragent-v* tag publishes an immutable prerelease.

Deliberate deviations from upstream, recorded in docs/pwragent-distribution.md:
glibc rather than MUSL, no bundled bwrap, no DMG or dSYM archives, and no
Windows arm64. The bwrap omission is the one most likely to need revisiting
if a PwrDrvr product ships Codex on Linux to end users.

macOS binaries are signed but not notarized. They are meant to nest inside an
already-notarized PwrDrvr bundle, which works because they carry the same Team
ID and hardened runtime; shipping one standalone would need a notarization step
that does not exist yet.


Before this can produce a signed build

Set up the two environments on pwrdrvr/codex:

Environment Secrets Variables
apple-signing CSC_LINK, CSC_KEY_PASSWORD
windows-signing AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET WIN_AZURE_SIGN_ACCOUNT, WIN_AZURE_SIGN_ENDPOINT, WIN_AZURE_SIGN_PUBLISHER_NAME, WIN_AZURE_SIGN_PROFILE

Values are the same ones pwrdrvr/grok-build uses; they are tabulated in docs/pwragent-distribution.md.

Then apply the ci:release-signing label to this PR to exercise the whole pipeline, signing included. Without the label only pwragent-release-check.yml runs.

Unverified

The contract check and the YAML job graph were run locally; nothing else was. These are the parts most likely to need a second pass, in rough order of likelihood:

  1. Build feasibility on hosted runners. Upstream builds this workspace on self-hosted XL machines. A hosted ubuntu-24.04 / macos-15 / windows-2022 runner may run out of disk or exceed the 120-minute timeout. If so the fix is probably swap-space/disk reclamation plus sccache, or falling back to a self-hosted runner for the heavy targets.
  2. macos-15-intel availability for this org.
  3. --bin codex without bwrap. Upstream builds bwrap first and embeds its digest. Skipping it should be fine, but nothing here proves the Linux binary behaves sanely without a bundled bwrap.
  4. refs/pull/<n>/merge and environment protection. If either environment gets a branch rule, labeled PR runs will block on a reviewer instead of signing.

🤖 Generated with Claude Code

Builds and signs downstream Codex binaries once, for PwrAgent, PwrSnap, and
PwrGit to share, so none of them signs Codex itself. Modeled on the equivalent
pipeline in pwrdrvr/grok-build.

Upstream's `rust-release.yml` cannot be reused here: it targets self-hosted
runner groups (`codex-runners`, `macos-15-xlarge`) that do not exist on this
fork, and signs through OpenAI's `codesigning` environment. So this is a
separate workflow on GitHub-hosted runners, signing with PwrDrvr credentials
from the `apple-signing` and `windows-signing` environments.

Ships five assets: macOS arm64/x64, Linux arm64/x64, Windows x64. Each carries
`codex`, `codex-app-server`, and `codex-code-mode-host`, plus the Windows
sandbox helpers on Windows, alongside LICENSE, NOTICE, and a provenance stamp.

Signing is fail-closed by construction, and `check-release-signing.py` pins the
properties that make it so:

- The jobs that build and stage enter no environment and read no secrets. They
  hand off a tarball plus its SHA-256; the signing jobs verify that digest
  before touching a credential.
- `release-candidate` requires both signing jobs, so there is no path from a
  build to a published asset that skips a signer.
- The Windows TrustedSigning client is downloaded, catalog-verified, and
  checksummed in the unprivileged prepare job and shipped to the signing job as
  pinned bytes. The signing job may not call `Save-Module`/`Install-Module`.
- Every shipped executable is signed and then individually verified, rather
  than just the CLI entrypoint.

The contract also pins the workflow to GitHub-hosted runners, since reaching
for a self-hosted group is exactly how this would silently stop working on the
fork.

`ci:release-signing` on a pull request runs the whole pipeline, signing
included, and produces a `signed-release-candidate` without publishing. A
`pwragent-v*` tag publishes an immutable prerelease.

Deliberate deviations from upstream, recorded in docs/pwragent-distribution.md:
glibc rather than MUSL, no bundled `bwrap`, no DMG or dSYM archives, and no
Windows arm64. The `bwrap` omission is the one most likely to need revisiting
if a PwrDrvr product ships Codex on Linux to end users.

macOS binaries are signed but not notarized. They are meant to nest inside an
already-notarized PwrDrvr bundle, which works because they carry the same Team
ID and hardened runtime; shipping one standalone would need a notarization step
that does not exist yet.
- `workflow_dispatch` built macOS and Windows but produced nothing usable for
  them, because every artifact-producing job downstream is gated on a tag or
  the `ci:release-signing` label. Manual runs now emit `unsigned-*` archives,
  as grok-build does, so a smoke test is worth the runner time it costs.
- Dropped the separate `signing-input-digest-*` artifact. It duplicated the
  checksum already shipped inside the payload artifact, written by the same
  job into the same store, so it added no integrity property. The comment
  claiming it approximated grok-build's job-output digest was wrong and is
  replaced with an accurate note about why this side cannot have one.
- `windows-prepare` now prepares the TrustedSigning client before the release
  build. It takes about a minute and depends on PSGallery, so failing it after
  a two-hour compile threw the compile away.
- Added a Cargo registry cache to all five build jobs, keyed on Cargo.lock.
  The target directory is deliberately not cached: five platforms would churn
  through the repository cache budget.
@huntharo
Harold Hunt (huntharo) merged commit 26dae44 into pwragent Aug 19, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant