Skip to content

ci: migrate release publish to shipper#566

Merged
EffortlessSteven merged 1 commit into
mainfrom
ci/publish-via-shipper
May 11, 2026
Merged

ci: migrate release publish to shipper#566
EffortlessSteven merged 1 commit into
mainfrom
ci/publish-via-shipper

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Summary

Migrate `release.yml` and `publish-retry.yml` from `cargo xtask publish` to shipper. Both v0.7.0 publish attempts via the hand-maintained `PUBLISH_CRATES` list have failed — the user built shipper for exactly this class of bug.

Why

Attempt 1 (tag at #562 merge): `uselesskey-core-seed` packaged before `uselesskey-core` because PUBLISH_CRATES listed compat shims before owner crates. Fixed in #565.

Attempt 2 (tag at #565 merge): `uselesskey-core` packaged but failed because its dev-dep `uselesskey-test-support` (workspace entry has `version = "0.7.0"`) is not in PUBLISH_CRATES at all.

Both failures are the same class of bug: a hand-maintained linear list of crates that silently drifts out of sync with cargo metadata when deps move. Shipper computes the publish DAG directly from cargo metadata, reconciles against registry truth, retries transient errors, and persists state under `.shipper/` so interrupted runs can resume.

Changes

Both workflows now run:

```yaml

  • name: Install shipper
    run: cargo install shipper --locked --version "0.3.0-rc.2"
  • name: Plan
    run: |
    mkdir -p .shipper
    shipper plan --format json | tee .shipper/plan.txt
  • name: Upload plan state
    uses: actions/upload-artifact@v7
    with: { name: shipper-state-plan, path: .shipper/, include-hidden-files: true, retention-days: 30 }
  • name: Preflight
    run: shipper preflight --policy safe
  • name: Publish
    run: shipper publish --policy safe --readiness-method both --max-attempts 12 --max-delay 15m
  • name: Upload final state
    if: always()
    uses: actions/upload-artifact@v7
    with: { name: shipper-state-final, path: .shipper/, include-hidden-files: true, retention-days: 90 }
    ```

`publish-retry.yml` keeps its workflow_dispatch inputs (`tag`, `resume_from`, `dry_run`):

  • Empty `resume_from` → `shipper publish` (reconciles against registry)
  • Non-empty `resume_from` → `shipper resume --resume-from `
  • `dry_run: true` → `shipper plan --format text` preview, no publish

Token still comes from `secrets.CARGO_REGISTRY_TOKEN`. OIDC via `crates-io-auth-action` is a separate follow-up.

Recovery plan for in-flight v0.7.0

Already at 0.7.0 on crates.io: `uselesskey-core-hmac-spec`, `uselesskey-jwk`, `uselesskey-core-jwk` (3 of 53). All others still at 0.6.0.

After merge:

  1. `gh workflow run publish-retry.yml -f tag=v0.7.0 -f dry_run=true` — preview shipper's plan
  2. Verify the 3 already-published crates show as skipped/satisfied
  3. `gh workflow run publish-retry.yml -f tag=v0.7.0` — execute live
  4. Shipper publishes the remaining ~50 crates in DAG order

Follow-up work

  • Adopt `rust-lang/crates-io-auth-action` OIDC token flow.
  • Drop hand-maintained `PUBLISH_CRATES` from `xtask/src/main.rs` once shipper handles all paths.
  • Originally planned: topo-sort verification in publish-check (no longer needed if shipper owns the publish lane).

Test plan

  • CI green on this PR (YAML lint, etc.)
  • Dry-run shipper plan against v0.7.0 shows correct DAG and skips the 3 already-published crates
  • Live shipper run publishes the remaining 50 crates

Both v0.7.0 publish attempts via `cargo xtask publish` failed:

1. First attempt (tag at #562 merge): `uselesskey-core-seed` packaged
   before `uselesskey-core` because PUBLISH_CRATES listed compat shims
   before owner crates. Fixed in #565.
2. Second attempt (tag at #565 merge): `uselesskey-core` packaged but
   failed because its dev-dep `uselesskey-test-support` (workspace
   entry has `version = "0.7.0"`) is not in PUBLISH_CRATES at all.

Both failures are the same class of bug: a hand-maintained linear
PUBLISH_CRATES order that drifts out of sync with cargo metadata when
deps move. The user built `shipper` for exactly this — it computes a
deterministic DAG from cargo metadata, reconciles against registry
truth, retries transient errors, and persists state under `.shipper/`
so interrupted runs can resume.

Switch both `release.yml` and `publish-retry.yml` to:

- `cargo install shipper --locked --version "0.3.0-rc.2"`
- `shipper plan` (emit JSON plan into `.shipper/`)
- `shipper preflight --policy safe`
- `shipper publish --policy safe --readiness-method both --max-attempts 12 --max-delay 15m`
- Upload `.shipper/` state as artifacts (plan + final)

`publish-retry.yml` adds a `shipper resume --resume-from <crate>` path
when `inputs.resume_from` is set, and uses `shipper plan --format text`
as the dry-run preview.

Token continues to come from `secrets.CARGO_REGISTRY_TOKEN`. OIDC via
`crates-io-auth-action` is a separate follow-up.

## Recovery for the in-flight v0.7.0

3 crates are at 0.7.0 on crates.io (`uselesskey-core-hmac-spec`,
`uselesskey-jwk`, `uselesskey-core-jwk`); 50 remain at 0.6.0. After
this PR merges, dispatch `Publish Retry` on `v0.7.0` with `dry_run:
true` to preview, then dispatch again non-dry-run. Shipper reconciles
against the registry before any cargo activity, so the 3 already-
published crates will be skipped.

## Future work

- Adopt `rust-lang/crates-io-auth-action` OIDC token flow.
- Drop the hand-maintained `PUBLISH_CRATES` constant in `xtask/src/main.rs`
  once shipper handles all paths.
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@EffortlessSteven has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9e37a956-1387-4542-945f-8f7f1659bf43

📥 Commits

Reviewing files that changed from the base of the PR and between 6e220bb and f14fd76.

📒 Files selected for processing (2)
  • .github/workflows/publish-retry.yml
  • .github/workflows/release.yml
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/publish-via-shipper

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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