Skip to content

ci(release): gate mark-latest and web deploy on PyPI publish - #66

Merged
lukeocodes merged 1 commit into
mainfrom
chore/release-flow-post-publish
May 8, 2026
Merged

lukeocodes merged 1 commit into
mainfrom
chore/release-flow-post-publish

Conversation

@lukeocodes

Copy link
Copy Markdown
Contributor

Summary

Move `mark-latest` and the cli.deepgram.com production deploy downstream of `publish` so they run in parallel with `bump-brew-formula` instead of in parallel with `build` / `test` / `publish`.

Why

PyPI is the gating artifact for everything that follows a release. Today:

  • `mark-latest` could flip the "latest" pointer on a tag whose PyPI artifact hadn't been published yet — a user clicking "latest" would land on a 404.
  • `deploy-web` could ship a new cli.deepgram.com banner advertising `pip install deepctl@X.Y.Z` before that version existed on PyPI.
  • `bump-brew-formula` was already correctly downstream of `publish` (it needs the published SHA).

Now all three downstream-of-publish jobs share the same correctness invariant: nothing promotes a release until PyPI says it exists.

Job graph

Before:

```
release-please ─┬─> build ─> test ─> publish ─> bump-brew-formula
├─> mark-latest
└─> deploy-web
```

After:

```
release-please ─> build ─> test ─> publish ─┬─> mark-latest
├─> deploy-web
└─> bump-brew-formula
```

Diff

Two job definitions changed — `needs: release-please` ➜ `needs: [release-please, publish]`. Comments updated to capture the installability invariant. Total: +11 / -7.

Both jobs were running in parallel with build/test/publish — only release-please
was a 'needs' dependency. That meant 'latest' could flip and cli.deepgram.com
could ship a new version banner before the PyPI artifact was actually
installable.

Move both downstream of publish so they run in parallel with bump-brew-formula.
Final shape:

  release-please -> build -> test -> publish ─┬─> mark-latest
                                              ├─> deploy-web (production)
                                              └─> bump-brew-formula

PyPI is the gating artifact for all three: brew formula needs the SHA, web
landing page advertises the install command, mark-latest promotes the tag.
@lukeocodes
lukeocodes merged commit 66e60d1 into main May 8, 2026
39 checks passed
@lukeocodes
lukeocodes deleted the chore/release-flow-post-publish branch May 8, 2026 19:12
dg-coreylweathers added a commit that referenced this pull request Nov 12, 2026
## Summary

After the release workflow opens the "Bump deepctl to X" PR on
deepgram/homebrew-tap, it now merges that PR in the same step, using the
same `CLI_TAP_SYNC_PAT` that pushed the branch. The step is also made
safe to re-run and safe against two releases racing on the tap.

## Why

The tap has no branch protection, no checks and no reviewers, so an open
bump PR only delays Homebrew users. The 0.3.1, 0.3.2 and 0.3.3 bumps
(homebrew-tap #9, #10, #11) all sat unmerged while the tap's formula
stayed at 0.2.26. #11 was merged by hand; this keeps that from happening
again.

The formula is generated from PyPI metadata by
`scripts/bump_brew_formula.py`, so there is nothing for a reviewer to
judge in the tap PR itself. The merged PR stays as the audit trail, and
a broken formula is fixed by editing the template, as the generated PR
body already says.

## What changed (`bump-brew-formula` job only)

- **Serialized.** The job gets `concurrency: {group: homebrew-tap-bump,
cancel-in-progress: false}`, so two root releases in quick succession
bump the tap one after the other instead of both reading the same
starting formula.
- **Version guard.** Before touching git, the step reads the version the
tap currently serves from `git show HEAD:Formula/deepgram.rb` (the
regenerate step has already overwritten the working tree, so HEAD is the
tap's state) and the regenerated version from the working tree.
- It fails the job if the tag, the current formula or the regenerated
formula is not a plain `X.Y.Z` version, or if the regenerated formula
does not point at the tagged version.
- It exits 0 with a warning unless the new version is strictly greater
than what the tap serves. Re-running an older release can no longer
downgrade the tap, and re-running a release that already landed is a
no-op.
- **Formula syntax check.** `ruby -c Formula/deepgram.rb` runs before
the commit. This is a Ruby syntax check only: it catches a broken
template or a mangled resource block, not a wrong sha256 or a failing
`brew install`.
- **Re-runnable push and PR.** The branch is pushed with `--force`, so a
re-run replaces a branch left behind by an earlier failed attempt. If an
open PR for that branch already exists, it is reused instead of `gh pr
create` failing on the duplicate.
- The existing-PR lookup only accepts same-repo PRs (`isCrossRepository
== false`) authored by the PAT's own login. The tap is public, so anyone
can open a fork PR whose head branch is named `bump-deepctl-X`; an
unfiltered lookup could have merged it with the PAT.
- The resulting PR URL must match
`https://github.com/deepgram/homebrew-tap/pull/N` or the job fails.
- **Merge with retry.** GitHub computes mergeability after a PR is
created and gh does not wait for it, so `gh pr merge <url> --merge
--delete-branch` is tried up to five times with a short backoff. If a
merge attempt fails but the PR's state is `MERGED` (merged by hand
mid-loop, or merged and then the branch delete failed), that counts as
success. After five failed attempts the job fails with an error
annotation.
- **Stale bump cleanup.** After a successful merge, open
`bump-deepctl-*` PRs that are same-repo, authored by the PAT owner, and
for a strictly older plain `X.Y.Z` version are closed with their branch
deleted and a comment linking the superseding PR. This is best effort:
listing or closing failures emit a warning and never fail the job.

## Checked

- The workflow YAML parses, and `bash -n` passes on the extracted step
script.
- actionlint 1.7.12 with shellcheck 0.11.0 reports 0 findings on
`.github/workflows/release.yml`. The one intentional single-quoted jq
`$login` carries a `shellcheck disable=SC2016` with a comment.
- No job other than `bump-brew-formula` is modified.
- Typed `ci:` so release-please adds no changelog line and bumps no
version for CI plumbing (precedent: #66, #70).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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