Skip to content

ci: release-please failed to tag v0.3.0 after PR #80 merge — investigate root cause + harden recovery path #82

@StefanSteiner

Description

@StefanSteiner

What happened

After merging the v0.3.0 release PR (#80), release-please was supposed to:

  1. Detect the merged release PR
  2. Promote its label from `autorelease: pending` → `autorelease: tagged`
  3. Create the `v0.3.0` git tag and GitHub Release at the merge SHA

Steps 2 and 3 silently did not happen. The next `release-please.yml` run on main aborted with the message:

```
⚠ Found pull request #80: 'chore: release main'
⚠ There are untagged, merged release PRs outstanding - aborting
```

(See run 26610521512.)

This is the same failure mode tracked upstream as googleapis/release-please#1946 — release-please refuses to do anything further while an untagged-merged release PR exists.

Manual recovery (worked, documenting for the next time)

The recovery is two `gh` commands:

```bash

1. Create the tag + GitHub Release at the merged release-PR SHA.

--target accepts a 40-char SHA; positional arg is just the tag name.

--notes-file points at the relevant CHANGELOG.md section (extract via

awk between the H2 anchors).

gh release create v0.3.0 \
-R tableau/hyper-api-rust \
--target \
--title "v0.3.0" \
--notes-file /tmp/v0.3.0-release-notes.md \
--latest

2. Promote the release PR's label so future release-please runs don't

abort. (The tag-creation in step 1 fires release.yml automatically;

it does NOT re-promote the label, that's a separate housekeeping step.)

gh pr edit -R tableau/hyper-api-rust \
--remove-label "autorelease: pending" \
--add-label "autorelease: tagged"
```

After step 1, `release.yml` fires on the `release: published` event and crates.io publish runs cleanly. After step 2, future pushes don't trip the abort.

Why release-please didn't tag automatically

Unclear — needs investigation against the action's behavior on this specific run. Hypotheses:

  1. Token / permission mismatch. The release PR was edited (manual CHANGELOG edits by the maintainer + the action's own `chore: sync Cargo.lock` commit). When the bot processes a release PR with multi-author commits, label-promotion + tag-creation may behave differently than the simple "single bot author" path.
  2. Race / re-entrancy. The release-please workflow run that handled the merge was triggered by the merge commit itself (not by a synthetic event after merge). If the action's tag-creation step requires a separate run, the merge-time run wouldn't perform it. The next run then sees the un-tagged merged PR and aborts.
  3. Action version regression. `googleapis/release-please-action@v5` — confirm the SHA we ran matches a known-good release. The log shows `SHA:45996ed1f6d02564a971a2fa1b5860e934307cf7`.

Hardening options

  1. Add a follow-up step in `release-please.yml` that detects merged release PRs missing a tag and creates the tag + Release explicitly. Pattern: query the GraphQL API for closed PRs with `autorelease: pending` and no matching tag at the merge SHA, then `gh release create` from the workflow itself. ~30 lines of bash; defends against #1946 silently.
  2. Document the recovery procedure in `docs/GITHUB_OPERATIONS.md` — "Cutting a release" section already exists; add a "What to do if release-please doesn't tag" subsection with the exact `gh` commands above.
  3. Pin `release-please-action` to a SHA known to handle this — but if no fixed version exists upstream (#1946 is open), pinning doesn't help.
  4. Switch to a custom release-tagging step — heavier lift; involves replacing the v5 action with a lower-level invocation.

Recommend option 1 + option 2 together. Option 1 is the structural fix; option 2 is the on-call runbook for when option 1 also fails.

Acceptance criteria

  • Document the manual recovery in `docs/GITHUB_OPERATIONS.md`
  • Add a workflow step that detects an untagged merged release PR and tags it (with conservative guards — must verify the merge SHA, must verify the manifest matches the tag, must require `autorelease: pending` label as input)
  • Test against a synthetic dry-run scenario (e.g. release-as in a fork) before relying on it for v0.4.0

Priority

Medium. v0.3.0 shipped successfully via manual recovery; the same recovery will work for v0.4.0 if needed. But the manual step is a foot-gun for the next maintainer who hits it cold and doesn't know the gh-release-create+label-edit sequence.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions