Skip to content

fix: release recovery, package inventory and update checks - #17

Merged
ryan-morris merged 2 commits into
mainfrom
fix/review-findings
Oct 19, 2026
Merged

ryan-morris merged 2 commits into
mainfrom
fix/review-findings

Conversation

@ryan-morris

Copy link
Copy Markdown
Member

Fixes from a review of the release and CI workflows.

Release (release.yml, scripts/compute-version.sh)

  • A dispatch from a branch other than main no longer publishes. The plan job's first step fails with an ::error:: when dry_run is off and github.ref is not refs/heads/main. Dry runs still work from any ref. Push runs only ever happen on main, so they are unaffected.
  • The order is now tag, then packages, then GitHub release. The tag is pushed (as devenvy-bot, with the app-token step moved up) after verification and before the first NuGet upload. Before this, a run that uploaded some packages and then failed left nothing to show it had started. A later run after main moved computed the same version, and --skip-duplicate filled in the rest of the set from a different commit. Now that later run finds the tag at another commit and the existing refusal stops it before any upload. The error says to re-run the original run, which uses the same commit. The GitHub release is still created last, with --verify-tag, so it marks the version as finished. The ordering is explained in a comment in the workflow.
  • A tag with no GitHub release now gets resumed instead of skipped. compute-version.sh still exits 3 for an upstream build that is already tagged, and now also prints that version on stdout. The planner then checks for a GitHub release (GET releases/tags/v<version>). A 404 means the version is unfinished, so it is planned again under the same version. Any other API error fails the plan rather than guessing. Every step is conditional on its own absence (tag already at this commit, --skip-duplicate, release already there), so a resume picks up where the failed run stopped. A resume never publishes from a commit other than the one the tag points at: the tag check runs before any upload.

Package verification (scripts/verify-packages.py)

  • Packages are now classified by id as helper, .All meta or runtime, for a known cell and RID. An unrecognised id fails.
  • Every runtime package must carry a native payload. Before, that assertion sat inside if payload and could never fire, so an empty runtime package passed. The lib/*.dll and notices checks now also run for every runtime package.
  • --expect-complete now checks the full expected inventory: the helper, plus every cell's .All and all ALL_RIDS runtime packages. Cells and RIDs both come from gen-nuspec.sh (the SPDX table and ALL_RIDS). Before, the check only covered metas that happened to be present, so a cell that failed to pack entirely went unnoticed.
  • New --cells: CI packs only LGPLv2 in full, so it now passes --expect-complete --cells LGPLv2. The release keeps checking all four cells.

I checked this with synthetic feeds built in memory (10 cases):

  • Pass: a good complete feed, the CI shape, --cells with a subset, and a subset feed run without --expect-complete.
  • Fail: an empty runtime package (RID and Apple), a missing cell, a missing RID, and missing metas.
  • Against main, five of these cases give the wrong result: the empty-package, missing-cell and missing-metas feeds all pass there.

Update check (check-updates.yml)

  • Upstream releases are now read with gh api --paginate repos/devenvy/ffmpeg/releases, with drafts and pre-releases filtered out. Before, only the newest 100 were read, so a quiet series could fall out of the window, match nothing and fail every run.

CI (ci.yml)

  • The pack matrix now comes from versions.json through a small resolve job, the same source smoke-test.yml uses. result now needs resolve as well and is still the single required check.

Local checks

  • shellcheck scripts/*.sh, actionlint and zizmor (offline) are all clean.
  • I ran the plan script locally against the real tags and releases in three cases:
    • All series released: nothing planned.
    • A tag with no release: resumed under the same version.
    • A new upstream: released as a new version.

🤖 Generated with Claude Code

ryan-morris and others added 2 commits October 19, 2026 10:54
…w findings

- release.yml: a real (non-dry-run) dispatch from any ref other than
  refs/heads/main fails in the plan job before anything is built.
- release.yml: the tag is now pushed after verification but before any
  NuGet upload, and the GitHub release is created last. The tag records
  the source commit before anything irreversible, so a later run after
  main moved hits the existing tag-at-another-commit refusal instead of
  filling a partial upload in from different source.
- release.yml / compute-version.sh: a version that is tagged but has no
  GitHub release is planned again (resumed) instead of skipped as
  released. compute-version.sh prints the tagged version on its exit 3.
- verify-packages.py: packages are classified by id; every runtime
  package must carry a native payload, and --expect-complete checks the
  full helper/meta/runtime inventory from gen-nuspec.sh (ALL_RIDS and
  the SPDX cells) rather than only the metas that were found. --cells
  narrows it for CI, which packs one cell in full.
- check-updates.yml: every page of upstream releases is read, not the
  newest 100.
- ci.yml: the pack matrix comes from versions.json through a resolve
  job, which result now also needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	.github/workflows/release.yml
@ryan-morris
ryan-morris merged commit 6a97c99 into main Oct 19, 2026
66 checks passed
@ryan-morris
ryan-morris deleted the fix/review-findings branch October 19, 2026 19:49
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