Skip to content

Harden the release permissions / inputs - #809

Merged
CleanCut merged 4 commits into
mainfrom
cleancut/windows-release-deptree-disk
Jul 30, 2026
Merged

Harden the release permissions / inputs#809
CleanCut merged 4 commits into
mainfrom
cleancut/windows-release-deptree-disk

Conversation

@CleanCut

Copy link
Copy Markdown
Contributor

Each job in the release now carries only the token permissions it uses, rather than every job inheriting broad permissions.

Workflow inputs no longer reach a PowerShell body as expanded source. ARCHITECTURE and PYTHON_VERSIONS arrive as environment variables, so their contents cannot be parsed as script.

The wheel enumeration stops setting pyo3's interpreter variables. They were added to keep that build a no-op, but maturin resolves no runnable interpreter for an abi3 wheel and sets no such variables itself, so matching them reasoned about a mechanism that was never in play. The v0.52.6 release measured the enumeration at 0.76s with nothing recompiled, and the comment now records that number instead of an argument for it.

bin/README.md still pointed patch_dev_version at CI workflows that no longer exist. It is used only by publish_test_pypi for local builds.

…shell code

Each job in the release now carries only the token permissions it uses, rather than every job
inheriting id-token: write, contents: write and pull-requests: write from the workflow. Only the job
that creates the draft release can write repository contents; the platform jobs get an OIDC token and
read access; the job that just computes outputs gets nothing. Because a called workflow can only
reduce what its calling job grants, those per-job grants are also the ceiling for the reusable
platform workflows, and the Windows workflow scopes its own three jobs explicitly on top of that. The
workflow-level default is now empty, so a job added later without a permissions block fails loudly
instead of quietly inheriting write access. Nothing in the release path ever used
pull-requests: write, so that grant is gone.

Workflow inputs no longer reach a PowerShell body as expanded source. ARCHITECTURE and
PYTHON_VERSIONS arrive as environment variables, so their contents cannot be parsed as script.

The wheel enumeration stops setting pyo3's interpreter variables. They were added to keep that build
a no-op, but maturin resolves no runnable interpreter for an abi3 wheel and sets no such variables
itself, so matching them reasoned about a mechanism that was never in play. The v0.52.6 release
measured the enumeration at 0.76s with nothing recompiled, and the comment now records that number
instead of an argument for it.

bin/README.md still pointed patch_dev_version at CI workflows that no longer exist; it is used only
by publish_test_pypi for local builds.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved release workflow security by switching to least-privilege permissions across build and publishing steps.
    • Enhanced Windows release packaging by improving multi-Python wheel build orchestration and applying consistent architecture-specific artifact naming.
  • Documentation
    • Clarified that development version stamping is intended for local Python package publishing/testing, not CI workflows.

Walkthrough

Release workflows now apply explicit least-privilege permissions. The Windows workflow updates runner permissions, PowerShell environment handling, multi-version wheel builds, architecture-based archive naming, and dependency-cache refresh behavior. Development-version and Windows release-profile documentation are also clarified.

Changes

Release workflow permissions

Layer / File(s) Summary
Least-privilege release permissions
.github/workflows/release.yml, .github/workflows/release_windows.yml
Workflows and jobs now declare restrictive defaults and explicit permissions for OIDC, repository reads, runner lifecycle operations, and draft release creation.

Windows release build and packaging

Layer / File(s) Summary
Windows release runner and packaging flow
.github/workflows/release_windows.yml, Cargo.toml
The Windows workflow uses PowerShell environment handling, builds wheels for each requested Python version, uses env.ARCHITECTURE for archive filenames, refreshes exact-hit dependency cache objects, and documents the Windows thin-LTO override.

Documentation clarification

Layer / File(s) Summary
Dev-version documentation clarification
bin/README.md
Documentation identifies publish_test_pypi as the use case for local development version stamping.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant release_windows
  participant uv
  participant maturin
  participant S3Cache
  participant Archives
  release_windows->>uv: Install requested Python versions
  release_windows->>maturin: Build wheels per interpreter
  release_windows->>S3Cache: Refresh exact-hit dependency object
  release_windows->>Archives: Create architecture-specific zip files
Loading

Possibly related PRs

  • Oxen-AI/Oxen#699: Overlaps with Windows release build and S3-backed caching changes.
  • Oxen-AI/Oxen#726: Overlaps with Windows dependency-tree cache handling.
  • Oxen-AI/Oxen#808: Overlaps with Windows wheel-build orchestration and dependency-cache logic.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: tightening release permissions and hardening workflow inputs.
Description check ✅ Passed The description is clearly related to the PR and accurately covers the permissions and input handling updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cleancut/windows-release-deptree-disk

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release_windows.yml:
- Around line 196-203: Update the release workflow’s Python version handling
before the uv python install step: split PYTHON_VERSIONS into individual
versions and install/pass each version separately. In the foreach loop around
maturin build, resolve the matching cpython-$version directory to a concrete
python.exe path instead of passing a literal wildcard, then use that resolved
path for --interpreter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b61b4539-1f23-438c-81b5-fc0a8a114722

📥 Commits

Reviewing files that changed from the base of the PR and between 33b584d and b18a59b.

📒 Files selected for processing (3)
  • .github/workflows/release.yml
  • .github/workflows/release_windows.yml
  • bin/README.md

Comment thread .github/workflows/release_windows.yml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release_windows.yml:
- Around line 203-205: Update the foreach version build loop to resolve the
cpython-$version.*\python.exe wildcard to exactly one matching interpreter path
before invoking maturin. Pass that resolved path to maturin’s --interpreter
argument, and preserve the existing nonzero LASTEXITCODE handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1785bfca-728b-47ef-885b-2d108277b886

📥 Commits

Reviewing files that changed from the base of the PR and between b18a59b and 7b8bbd5.

📒 Files selected for processing (1)
  • .github/workflows/release_windows.yml

Comment on lines 203 to 205
foreach ($version in $versions) {
uvx maturin build --release --interpreter ($uvPython -f $version)
uvx maturin build --release --interpreter "C:\Users\Administrator\AppData\Roaming\uv\python\cpython-$version.*\python.exe"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major

Resolve the interpreter glob before invoking maturin.

The quoted cpython-$version.*\python.exe argument is passed literally to the native command; PowerShell does not expand this wildcard, and maturin does not resolve it. Resolve exactly one matching python.exe path before calling maturin. This is the same unresolved issue raised in the previous review.

🛠️ Proposed fix
 foreach ($version in $versions) {
-    uvx maturin build --release --interpreter "C:\Users\Administrator\AppData\Roaming\uv\python\cpython-$version.*\python.exe"
+    $interpreters = @(Get-ChildItem -Path "C:\Users\Administrator\AppData\Roaming\uv\python\cpython-$version.*\python.exe" -File -ErrorAction SilentlyContinue)
+    if ($interpreters.Count -ne 1) {
+        throw "Expected exactly one Python interpreter for $version, found $($interpreters.Count)"
+    }
+    uvx maturin build --release --interpreter $interpreters[0].FullName
     if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release_windows.yml around lines 203 - 205, Update the
foreach version build loop to resolve the cpython-$version.*\python.exe wildcard
to exactly one matching interpreter path before invoking maturin. Pass that
resolved path to maturin’s --interpreter argument, and preserve the existing
nonzero LASTEXITCODE handling.

@Eric-Laurence Eric-Laurence left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security 👍 👍 👍

CleanCut added 2 commits July 29, 2026 17:36
An exact cache hit re-archives nothing, so the stored tree's age kept climbing while it was still
serving every release, and an age-based expiry would eventually delete a tree the next release
depends on. The save step now copies the object onto itself on a hit, restarting its clock and
leaving the archive, its size and its storage class untouched. That took 30 seconds server-side for
the current 7.6 GiB tree, and a failure is reported as a warning rather than failing the release,
since a stale timestamp costs one slow build and nothing more.

No expiry is configured on the bucket today, so this changes nothing by itself; it is what makes
adding one safe.

The maturin interpreter argument also picks up a comment. It is a glob that reaches maturin
unexpanded and matches nothing, which reads like an oversight and has been raised as one several
times. The v0.52.4 and v0.52.6 logs show maturin building the abi3 wheel from bundled sysconfig with
no interpreter resolved, and dropping the flag is not equivalent: with none given, maturin falls
back to PYO3_PYTHON or whatever python happens to be on PATH.
Fat LTO costs 17m23s to build the CLI on a Windows release runner where thin costs 10m38s, and
Windows binary size is not a constraint. Six profile variants were measured back to back on one
runner with every build cold, and fat accounted for 405 of the 590 seconds available across all of
them, so thin is where nearly all of the win is and the remaining levers grow the binary far faster
than they save time.

The Linux and macOS releases keep fat LTO and stay as small as they can be. Cargo cannot set a
profile per target, so the Windows job overrides the shared profile through
CARGO_PROFILE_RELEASE_LTO, which also reaches the cdylib that maturin builds.

codegen-units stays at 1 everywhere. Raising it to 16 saves roughly two more minutes but grows the
binary by 14 MiB and its effect on runtime is unmeasured; ENG-1459 tracks measuring that.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/release_windows.yml (1)

199-200: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize whitespace before splatting versions. -split ' ' leaves empty items for repeated/leading/trailing spaces and misses tabs/newlines, so uv python install can receive bad version args. Split on \s+, filter empties, and reject an empty list before invoking uv.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release_windows.yml around lines 199 - 200, Update the
PowerShell version parsing around $env:PYTHON_VERSIONS to split on \s+, remove
empty entries, and reject an empty resulting list before calling uv python
install. Preserve the existing splatting behavior for valid version lists and
ensure uv is not invoked when no versions are configured.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/release_windows.yml:
- Around line 199-200: Update the PowerShell version parsing around
$env:PYTHON_VERSIONS to split on \s+, remove empty entries, and reject an empty
resulting list before calling uv python install. Preserve the existing splatting
behavior for valid version lists and ensure uv is not invoked when no versions
are configured.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5b646be9-004f-4c7f-80e0-6cbd938257f6

📥 Commits

Reviewing files that changed from the base of the PR and between 7b8bbd5 and 2a04b74.

📒 Files selected for processing (2)
  • .github/workflows/release_windows.yml
  • Cargo.toml

@CleanCut
CleanCut merged commit f5ef872 into main Jul 30, 2026
9 checks passed
@CleanCut
CleanCut deleted the cleancut/windows-release-deptree-disk branch July 30, 2026 15:15
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.

2 participants