Skip to content

[codex] ci(release): target PyPI artifact downloads - #93

Merged
Navi Bot (project-navi-bot) merged 6 commits into
mainfrom
codex/release-artifact-hygiene
May 28, 2026
Merged

[codex] ci(release): target PyPI artifact downloads#93
Navi Bot (project-navi-bot) merged 6 commits into
mainfrom
codex/release-artifact-hygiene

Conversation

@Fieldnote-Echo

Copy link
Copy Markdown
Member

Summary

  • download only wheels-* artifacts and the sdist artifact into the PyPI upload dist/ directory
  • remove the broad dist cleanup step because SBOM/provenance/sigstore artifacts are no longer downloaded there
  • simplify the release publish invariant script to pin targeted wheel/sdist downloads while keeping the SBOM gitignore guard

Closes #53.

Issue #29 is already obsolete on main: the unified release.yml has no macOS x86_64 wheel leg, and Intel macOS users install from the sdist.

Validation

  • bash tests/release_publish_invariants.sh
  • bash tests/release_signed_release_invariants.sh
  • pinned actionlint v1.7.12
  • pinned zizmor --offline --persona=regular .github/workflows/

Signed-off-by: Nelson Spence <nelson@projectnavi.ai>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates the release-publish SBOM invariants script to enforce targeted artifact downloads (for wheels and source distributions) instead of downloading all artifacts and then deleting the SBOM. It also introduces an awk-based check to guard against broad artifact downloads into the dist/ directory. The review feedback suggests making the regular expressions and the awk parser more robust by anchoring patterns to the start of the line to avoid matching commented-out lines, and by supporting optional single or double quotes around YAML values.

Comment thread tests/release_publish_invariants.sh Outdated
Comment thread tests/release_publish_invariants.sh Outdated
@codecov

codecov Bot commented May 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Target PyPI artifact downloads to exclude SBOM and provenance

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Replace broad artifact downloads with targeted wheels and sdist downloads
• Eliminate need for post-download cleanup by using download-artifact selectors
• Simplify release publish invariant script to verify targeted downloads
• Remove SBOM/provenance/Sigstore artifacts from PyPI upload directory
Diagram
flowchart LR
  A["Broad artifact download<br/>+ cleanup step"] -->|Refactor| B["Targeted downloads<br/>wheels-* + sdist"]
  B -->|Result| C["Clean dist/<br/>PyPI ready"]
  D["Complex invariant<br/>script logic"] -->|Simplify| E["Targeted download<br/>verification"]

Loading

Grey Divider

File Changes

1. tests/release_publish_invariants.sh ✨ Enhancement +37/-67

Simplify invariant script to verify targeted downloads

• Removed complex step-ordering validation logic for SBOM cleanup
• Replaced with simpler targeted artifact download verification
• Added checks for wheels-* pattern and sdist name selectors
• Added guard against reintroducing broad downloads without selectors

tests/release_publish_invariants.sh


2. .github/workflows/release.yml ✨ Enhancement +7/-5

Replace broad download with targeted artifact selectors

• Split single download-artifact step into two targeted steps
• First step downloads wheels-* artifacts using pattern selector
• Second step downloads sdist artifact using name selector
• Removed post-download cleanup step that deleted non-wheel/tar.gz files

.github/workflows/release.yml


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented May 28, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Invariant misses SBOM download ✓ Resolved 🐞 Bug ≡ Correctness
Description
tests/release_publish_invariants.sh only verifies that wheels-* and sdist targeted downloads exist
and only blocks selector-less downloads into dist/, so it would still pass if an additional
download-artifact step pulls sbom-python (or other non-distributables) into dist/ and breaks the
PyPI publish again.
Code

tests/release_publish_invariants.sh[R34-62]

Evidence
The invariant script’s awk check only flags download steps targeting dist/ when they lack *any*
selector, so a step like name: sbom-python + path: dist would not be considered “broad” and
would slip through. The workflow explicitly uploads a sbom-python artifact, making this regression
plausible and impactful (twine rejects the stray SBOM).

tests/release_publish_invariants.sh[34-62]
.github/workflows/release.yml[331-349]
.github/workflows/release.yml[576-607]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The invariant script currently:
- asserts that *some* `pattern: wheels-*` and `name: sdist` occur in the `publish-pypi` job, and
- only fails on a download step into `dist` that has **no** selector.

That means a future change could add `actions/download-artifact` with `name: sbom-python` (or provenance/sigstore artifacts) **into `dist/`**, and the invariant would still pass even though PyPI upload would fail.

### Issue Context
The workflow produces a `sbom-python` artifact, and the publish job uploads everything under `dist/`.

### Fix Focus Areas
- tests/release_publish_invariants.sh[34-62]

### What to change
Update the awk guard (or add a new one) to:
1. Identify every `actions/download-artifact@...` step in the `publish-pypi` job that targets `path: dist`.
2. Fail unless that step is **exactly one of**:
  - `pattern: wheels-*` (optionally with `merge-multiple: true`)
  - `name: sdist`
3. Fail if any other `name:`/`pattern:` is used with `path: dist` (e.g., `name: sbom-python`).

This pins the intended invariant: `dist/` contains only wheels + sdist at publish time.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. release_publish_invariants.sh still parses YAML ✓ Resolved 📎 Requirement gap ⚙ Maintainability
Description
tests/release_publish_invariants.sh still includes non-gitignore workflow parsing/guards (grep/awk
over release.yml) instead of retaining only the gitignore assertion. This violates the checklist
requirement to remove the publish-job parsing logic to reduce fragility and maintenance burden.
Code

tests/release_publish_invariants.sh[R22-62]

Evidence
PR Compliance ID 3 requires removing publish-job YAML/step-order parsing and keeping only the
gitignore assertion. The updated script adds/keeps additional guards that parse release.yml (via
grep/awk) to enforce artifact download shapes, which is explicitly beyond the allowed scope.

Simplify release publish invariants: keep only gitignore check, remove publish-step ordering guard
tests/release_publish_invariants.sh[22-62]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Compliance requires `tests/release_publish_invariants.sh` to retain only the robust `git check-ignore` assertion for `*.cdx.json` SBOMs, and to remove the remaining workflow parsing/guards.

## Issue Context
The current script still parses `.github/workflows/release.yml` with `grep`/`awk` to enforce targeted artifact download patterns. This reintroduces YAML-coupled invariants that the checklist explicitly asked to remove.

## Fix Focus Areas
- tests/release_publish_invariants.sh[22-62]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Guard brittle path matching ✓ Resolved 🐞 Bug ☼ Reliability
Description
The broad-download detector only treats path: dist as the upload directory, so an equivalent broad
download using path: dist/ or path: ./dist would not be caught and could reintroduce
non-distributables into the PyPI upload dir without failing CI.
Code

tests/release_publish_invariants.sh[R56-60]

Evidence
The guard’s state machine only sets has_dist when it sees an exact path: dist line; if the
workflow uses a semantically equivalent path string, the guard won’t detect downloads into the
publish directory.

tests/release_publish_invariants.sh[56-60]
.github/workflows/release.yml[592-603]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The invariant’s awk matcher only recognizes `path: dist` with an exact regex. Equivalent paths like `dist/` or `./dist` are common and would cause the guard to miss a broad download into the upload directory.

### Issue Context
This script is intended to prevent regressions in `.github/workflows/release.yml` publish logic. If it misses a broad download into `dist/`, the publish job can again pick up SBOM/provenance assets and fail.

### Fix Focus Areas
- tests/release_publish_invariants.sh[56-60]

### What to change
Relax the `path:` match to accept common equivalent forms, e.g.:
- `path: dist`
- `path: dist/`
- `path: ./dist`

Implement by adjusting the awk regex for `path:` to something like:
- `^[[:space:]]*path:[[:space:]]*(\./)?dist/?[[:space:]]*$`

(And apply the same normalization anywhere else you key off `dist`.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Updates the unified release.yml publish flow so the PyPI upload directory (dist/) only receives Python distribution artifacts (wheels + sdist), structurally preventing SBOM/provenance assets from breaking twine uploads and allowing the prior cleanup step to be removed.

Changes:

  • Switch PyPI publish to targeted actions/download-artifact downloads: pattern: wheels-* and name: sdist into dist/.
  • Remove the “keep-only wheels/sdist” find ... -delete cleanup step from the PyPI publish job.
  • Simplify tests/release_publish_invariants.sh to assert the targeted-download shape and retain the SBOM .gitignore guard.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
tests/release_publish_invariants.sh Simplifies the release-publish invariant checks to focus on SBOM gitignore + targeted artifact downloads into dist/.
.github/workflows/release.yml Updates the publish-pypi job to download only wheel and sdist artifacts into dist/ before trusted publishing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/release_publish_invariants.sh Outdated
Comment thread tests/release_publish_invariants.sh Outdated
Comment thread tests/release_publish_invariants.sh Outdated
Comment thread tests/release_publish_invariants.sh Outdated
Signed-off-by: Nelson Spence <nelson@projectnavi.ai>
@project-navi-bot
Navi Bot (project-navi-bot) merged commit cf64cc7 into main May 28, 2026
25 checks passed
@project-navi-bot
Navi Bot (project-navi-bot) deleted the codex/release-artifact-hygiene branch May 28, 2026 17:19
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.

Restructure PyPI publish so the SBOM never enters dist/ (retire the cleanup step + most of the release guard)

3 participants