Skip to content

Fix: publish to crates.io from the release workflow, and verify the registry - #531

Merged
0xLeif merged 2 commits into
mainfrom
0xleif/fix/crates-io-publish-gap
Sep 19, 2026
Merged

0xLeif merged 2 commits into
mainfrom
0xleif/fix/crates-io-publish-gap

Conversation

@0xLeif

@0xLeif 0xLeif commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Summary

crates.io has fledge at 1.7.0, but v1.7.1 and v1.7.2 are both tagged. Two releases were cut and never shipped, and nobody noticed.

The cause is not a failure. release.yml ran for both tags and succeeded — both runs are green in the Actions history. It has three jobs (test, build, release) and none of them publishes. grep -rn "cargo publish" .github/workflows/ returns nothing, and publishing is documented nowhere: not AGENTS.md, not CONTRIBUTING.md, not docs/. It was a manual step living in someone's head, and the signal you would check said everything was fine.

The fix

A publish job on the same v* tag trigger, gated after release:

  • Fails loudly when CARGO_REGISTRY_TOKEN is absent, rather than skipping. A skipped step is indistinguishable from a passing one, which is exactly how 1.7.1 and 1.7.2 slipped by. A release that did not publish is not a finished release and should be red.
  • Reads the registry back. cargo publish exiting 0 does not prove the crate is live — the index lags, and some no-op paths also exit 0. The job polls https://crates.io/api/v1/crates/fledge and compares max_version against the tag, retrying while the index catches up. CorvidLabs/hi hit this exact failure at 0.2.4/0.2.5.

AGENTS.md documents both, including the manual read-back check for anyone publishing by hand.

Two open ends, recorded in the change's context.md

  1. The repository has no secrets at all today (gh secret list is empty), so CARGO_REGISTRY_TOKEN must be created before the next release or the tag will fail at this job. That failure is by design, but it will be the first thing you hit.
  2. 1.7.1 and 1.7.2 remain unpublished. This stops the bleeding; it does not backfill. Publishing them from their existing tags versus skipping to 1.8.0 is a separate decision.

Sequencing note for v1.8.0

This needs to merge before 1.8.0 is tagged, or 1.8.0 ships without a publish job and repeats the same failure. main currently has 22 commits since v1.7.2 and Cargo.toml still says 1.7.2.

Verification

  • release.yml parses; jobs are test, build, release, publish, with publish.needs: [release]
  • Trigger unchanged (on: push: tags: v*)
  • fledge spec check — 33 specs, 0 errors, 0 warnings

The job itself cannot be exercised until a tag is pushed with the secret present — an honest limitation, mitigated by the failure being loud and naming its own fix.

🤖 Generated with Claude Code

0xLeif and others added 2 commits September 18, 2026 19:26
…egistry

crates.io has fledge at 1.7.0, but v1.7.1 and v1.7.2 are both tagged. Two
releases were cut and never shipped, and nobody noticed.

The cause is not a failure. release.yml ran for both tags and succeeded — both
runs are green in the Actions history. It has three jobs (test, build, release)
and none of them publishes; `grep -rn "cargo publish" .github/workflows/`
returns nothing. Publishing was documented nowhere: not AGENTS.md, not
CONTRIBUTING.md, not docs/. It was a manual step living in someone's head, and
the signal you would check said everything was fine.

Adds a publish job on the same v* tag trigger, after release:

  - Fails loudly when CARGO_REGISTRY_TOKEN is absent rather than skipping. A
    skipped step is indistinguishable from a passing one, which is exactly how
    1.7.1 and 1.7.2 slipped by. A release that did not publish is not a
    finished release and should be red.
  - Reads the registry back. `cargo publish` exiting 0 does not prove the crate
    is live — the index lags and some no-op paths also exit 0. The job polls
    crates.io and compares max_version to the tag, retrying while the index
    catches up. CorvidLabs/hi hit this exact failure at 0.2.4/0.2.5.

AGENTS.md documents both, including the manual read-back check.

Two open ends, recorded in the change's context.md:
  - The repository has no secrets at all today, so CARGO_REGISTRY_TOKEN must be
    created before the next release or the tag will fail at this job.
  - 1.7.1 and 1.7.2 remain unpublished. This stops the bleeding; it does not
    backfill.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9GwmgSChrXYZhLjUYejwP
First change on the workflow-v2 lifecycle: closes through review + finalize
rather than the legacy accept + archive, and finalize runs before merge so the
verification commit is not orphaned.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9GwmgSChrXYZhLjUYejwP
@0xLeif
0xLeif requested a review from a team as a code owner September 19, 2026 01:28
@0xLeif
0xLeif requested review from 0xGaspar, Kyntrin and tofu-ux and removed request for a team September 19, 2026 01:28
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-19T01:35:07.168433Z b43acdd PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@0xLeif
0xLeif merged commit 7214df6 into main Sep 19, 2026
20 checks passed
@0xLeif
0xLeif deleted the 0xleif/fix/crates-io-publish-gap branch September 19, 2026 01:33

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b43acddd5b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

echo "::error::CARGO_REGISTRY_TOKEN is not set, so this tag was built and released but NOT published to crates.io. Add the secret in Settings > Secrets and variables > Actions, then re-run this job." >&2
exit 1
fi
cargo publish

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Validate the tag before publishing the manifest

For any manually pushed or mistyped v* tag whose suffix differs from Cargo.toml, this command publishes the manifest's version before the later step compares crates.io with GITHUB_REF_NAME. For example, a v1.8.1 tag on a manifest still at 1.8.0 would irreversibly publish 1.8.0 and only then report failure. Read the package version and require it to equal ${GITHUB_REF_NAME#v} before invoking cargo publish.

AGENTS.md reference: AGENTS.md:L363-L363

Useful? React with 👍 / 👎.

echo "::error::CARGO_REGISTRY_TOKEN is not set, so this tag was built and released but NOT published to crates.io. Add the secret in Settings > Secrets and variables > Actions, then re-run this job." >&2
exit 1
fi
cargo publish

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make the publish job safe to rerun

If the registry verification times out, the job is cancelled after upload, or a runner fails between these steps, rerunning the failed job starts here and tries to upload the same version again. Published crate versions cannot be overwritten, so cargo publish fails and the rerun never reaches the read-back step that could confirm the already-live crate (Cargo publishing documentation). Check the registry first and skip the upload only when this exact version already exists, while still running verification.

AGENTS.md reference: AGENTS.md:L363-L363

Useful? React with 👍 / 👎.

Comment on lines +139 to +141
actual=$(curl -sS -A "fledge-release-check" \
"https://crates.io/api/v1/crates/fledge" \
| python3 -c "import json,sys; print(json.load(sys.stdin)['crate']['max_version'])")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retry failed registry queries inside the loop

When crates.io transiently refuses a connection, returns a rate-limit/server response, or emits non-JSON, curl or Python exits nonzero and set -euo pipefail terminates the step from this assignment immediately. Consequently, the six-attempt loop retries only a successful API response containing the previous version, not the transient API failures it is intended to tolerate; handle a failed query with continue and sleep rather than exiting.

AGENTS.md reference: AGENTS.md:L363-L363

Useful? React with 👍 / 👎.

Comment on lines +140 to +141
"https://crates.io/api/v1/crates/fledge" \
| python3 -c "import json,sys; print(json.load(sys.stdin)['crate']['max_version'])")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Verify the published version instead of the maximum

When two valid releases overlap or an older release is backfilled after a newer one, crates.io can contain the expected version while max_version reports the newer version, causing this job to fail after a successful publication. Query the expected version directly (or inspect the returned versions for an exact match) so the check proves that this tag's crate exists without assuming releases complete strictly in version order.

AGENTS.md reference: AGENTS.md:L363-L363

Useful? React with 👍 / 👎.

@github-actions github-actions 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.

✅ Corvin says...

      _
    <(^\  .oO(Caw! ^v^)
     |/(\
      \(\\
      " "\\

"Looking sharp! Like a beak should be."

CI Summary

Check Status
Dependency Audit ✅ Passed
Integration (3 OS) ✅ Passed
Lint (fmt + clippy) ✅ Passed
Spec Validation ✅ Passed
Tests (3 OS) ✅ Passed

Powered by corvid-pet

@0xLeif 0xLeif mentioned this pull request Sep 19, 2026
5 tasks done
0xLeif added a commit that referenced this pull request Sep 26, 2026
Rebasing onto main brought in four commits the section did not list:
#531, #533, #534 and #535. Add them where fledge's generator places
them.

Also drop the stale "## Unreleased" block. Both of its bullets describe
#520 (c4abcb2), which ships in 1.8.0, so they move under that entry as
sub-bullets instead of being advertised as unreleased. `fledge release`
inserts the new section above an existing Unreleased block rather than
consuming it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3ZZAEiUP7xRJPozhZb6rL
0xLeif added a commit that referenced this pull request Sep 30, 2026
…s.io-publish change (#537)

* Update: record the Windows CI evidence in the change record

test (windows-latest) is green on this PR (ureq 3.3.0, run 36257383427)
and on a workflow_dispatch probe of main + this fix + the v1.8.0
release commit (ureq 3.4.2, run 36257571327).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3ZZAEiUP7xRJPozhZb6rL

* Update: record the v1.8.1 release PR's Windows run in the change record

#532 was closed and replaced by #536 (v1.8.1), because 1.8.0 was
already on crates.io. test (windows-latest) is green there with ureq
3.4.2 (run 36260828952), which closes the last open task. Also name
#535 explicitly where the artifacts said "this PR".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3ZZAEiUP7xRJPozhZb6rL

* chore(lifecycle): record definition approval for the isolation-test change

Approved as user:0xLeif on Leif's explicit go in the orc session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3ZZAEiUP7xRJPozhZb6rL

* chore(lifecycle): materialize accept-windows-refused-connection-wording-in-the-isolation-test

* chore(lifecycle): record accept-windows-refused-connection-wording-in-the-isolation-test verification

* chore(lifecycle): archive accept-windows-refused-connection-wording-in-the-isolation-test

* chore(lifecycle): materialize publish-to-crates-io-from-the-release-workflow-and-verify-the-registry

* chore(lifecycle): record publish-to-crates-io-from-the-release-workflow-and-verify-the-registry verification

* chore(lifecycle): archive publish-to-crates-io-from-the-release-workflow-and-verify-the-registry

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
0xLeif added a commit that referenced this pull request Sep 30, 2026
fledge 1.8.0 was published to crates.io on 2026-09-19 from 435b7c5, a
release-branch head that never reached main. 1.8.1 is that release cut
from main, with #531, #533, #534 and #535 on top.

Bumps Cargo.toml and flake.nix to 1.8.1. Cargo.lock differs from the
published 1.8.0 lockfile only in fledge's own version; no dependency
moves. The bump was done by hand, because `fledge release` runs
`cargo generate-lockfile`, which re-resolves every dependency.

The changelog section covers every commit since v1.7.2, with a note on
1.8.0. The stale "Unreleased" block, which described #520 (shipped
here), is folded into that entry.


Claude-Session: https://claude.ai/code/session_01V3ZZAEiUP7xRJPozhZb6rL

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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