Skip to content

fix(release): make Homebrew tap a best-effort gate in promote-release - #60

Merged
elkaix merged 3 commits into
mainfrom
fix/promote-release-homebrew-soft-gate
Jun 2, 2026
Merged

fix(release): make Homebrew tap a best-effort gate in promote-release#60
elkaix merged 3 commits into
mainfrom
fix/promote-release-homebrew-soft-gate

Conversation

@elkaix

@elkaix elkaix commented Jun 2, 2026

Copy link
Copy Markdown
Member

Problem

promote-release.yml polled Homebrew as a hard gate alongside platform assets and PyPI. When the tap is broken (lost HOMEBREW_TAP_TOKEN, org migration, etc.), the loop runs for its full 40-minute budget and then fails — leaving the GitHub Release permanently as a prerelease and /releases/latest pointing at the last good version (was: 0.28.0, even after 0.29.0 and 0.30.0 shipped to PyPI).

Fix

Hard gates (still block promotion):

  • All required platform release assets (.exe, .deb, .rpm, tarballs)
  • PyPI serving the new version

Soft gate — moved to best-effort step after promotion (warn, never block):

  • Homebrew tap formula version

The new Check Homebrew tap (best-effort) step mirrors the existing Trigger pythinker-home sync (best-effort) pattern exactly: checks once, emits a ::warning annotation + step summary note + Slack alert if behind, then exit 0. The job succeeds; the gap is visible without holding the release hostage.

What doesn't change

  • The 40-minute polling budget for assets + PyPI is unchanged
  • workflow_dispatch re-promote still works
  • Slack alerting for a lagging tap is added (same pattern as home-sync)
  • notify-failure job only fires on hard-gate failures (assets/PyPI timeout)

Summary by CodeRabbit

  • Chores

    • Removed Homebrew as a blocking gating requirement for release promotion; releases now proceed when platform assets and PyPI are ready.
    • Added best-effort Homebrew formula checks that warn (and optionally notify) if the tap lags but do not fail promotion.
    • Made website sync resilient: token minting and dispatch are non-blocking with retry/backoff behavior (3 attempts).
    • Added a local pre-use hook and config to enforce an Unreleased changelog entry when shipped-code paths are modified.
    • Updated ignore rules to exclude the local hooks/config dir except for the settings and hooks.
  • Documentation

    • Noted the workflow change in the changelog under Unreleased.

A broken tap (lost HOMEBREW_TAP_TOKEN, org migration, etc.) was blocking
the promote-release polling loop indefinitely, preventing the GitHub
Release from ever reaching Latest and leaving install scripts pointing at
a stale prerelease.

Remove homebrew_ready from the hard polling condition. Hard gates are now
only required platform assets + PyPI — the channels that actually serve
the install scripts and in-app updater.

Add a best-effort 'Check Homebrew tap' step that runs after promotion:
it checks once and emits a warning annotation + step summary note + Slack
alert if the tap lags, then exits 0. The job still succeeds; the warning
is visible in the run summary without holding the release hostage.
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The promote-release workflow stops blocking on Homebrew during readiness polling; it now promotes when assets and PyPI are ready. After promotion, a non-blocking Homebrew tap check warns if the formula lags. Website sync dispatch is made best-effort with non-fatal token minting and up to three retries before graceful exit.

Changes

Release Promotion Workflow Refactor

Layer / File(s) Summary
Remove Homebrew from readiness polling
.github/workflows/promote-release.yml
Updates top comments and modifies the install-channel readiness loop so all_ready depends only on required assets and PyPI; removes Homebrew-lag branch from polling.
Post-promotion Homebrew check and dispatch redesign
.github/workflows/promote-release.yml
Adds a non-blocking Homebrew tap check after promotion that warns (optionally sends Slack) if the formula is behind. Makes GitHub App token minting non-fatal (continue-on-error), conditionally skips dispatch with warnings when prerequisites fail, and retries dispatch up to 3 times with backoff before degrading to a successful exit with warnings.
Changelog note
CHANGELOG.md
Adds an Unreleased bullet documenting the Homebrew gating behavior change and the new non-blocking warning behavior.

Pre-tool changelog gate and repo metadata

Layer / File(s) Summary
Pre-tool changelog gate script
.claude/hooks/check-changelog.sh
Adds a hook that intercepts gh pr create, skips release branches or [skip changelog], detects shipped-code changes vs origin/main, and denies the tool invocation with a JSON payload if ## Unreleased is empty.
.claude settings and gitignore
.claude/settings.json, .gitignore, CHANGELOG.md
Registers the pre-tool hook in .claude/settings.json (PreToolUse), updates .gitignore to ignore .claude/* while keeping settings.json and hooks/**, and adds a CHANGELOG Unreleased bullet.

Sequence Diagram

sequenceDiagram
  participant PromoteJob as promote-release job
  participant AssetCheck as Asset readiness checks
  participant PyPI as PyPI readiness
  participant Homebrew as Homebrew tap check (post-promotion)
  participant GitHubApp as GitHub App token mint
  participant WebsiteDispatch as Website sync dispatch
  PromoteJob->>AssetCheck: poll required assets
  PromoteJob->>PyPI: poll PyPI serving status
  PromoteJob->>Homebrew: post-promotion best-effort check (warn if lag)
  PromoteJob->>GitHubApp: mint app token (continue-on-error)
  GitHubApp->>WebsiteDispatch: provide token (if minted)
  WebsiteDispatch->>WebsiteDispatch: retry up to 3 times with backoff
  WebsiteDispatch-->>PromoteJob: warn & exit 0 if dispatch fails
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

bug

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commits format (fix(release): ...) and accurately describes the main change of converting Homebrew tap to a best-effort gate.
Description check ✅ Passed The description clearly explains the problem, fix, and unchanged behavior, but is missing the template structure including Related Issue link and checklist items.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/promote-release-homebrew-soft-gate

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

elkaix added 2 commits June 2, 2026 13:13
Blocks gh pr create when shipped-code paths are changed but
## Unreleased in CHANGELOG.md is empty — catches the same gate
that changelog-entry-required CI enforces, before the push.

Escape hatches (mirroring CI): release/* branch, chore(release)
title, or [skip changelog] in the PR body.

Also unblocks .claude/settings.json and .claude/hooks/ from
.gitignore (changed .claude to .claude/* so negation patterns work)
so project-scoped hook config is tracked by the team.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/settings.json:
- Around line 3-14: The PreToolUse hook uses matcher "Bash" but runtime wiring
expects target "Shell", so update the hook matcher from "Bash" to "Shell" (or
add a mapping/alias) and ensure the config loader supports the nested hooks
shape hooks.PreToolUse[].hooks[] (matching how tests exercise TOML [[hooks]]);
specifically, change the matcher string in the PreToolUse entry to "Shell" or
add normalization logic that maps "Bash" → "Shell", and verify the settings
deserialization loads the nested hooks.PreToolUse[].hooks[] structure so the
command hook is actually registered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 51dc45c7-5ea9-4ba6-a494-60db80feab07

📥 Commits

Reviewing files that changed from the base of the PR and between c4ca024 and 060cf9c.

📒 Files selected for processing (3)
  • .claude/hooks/check-changelog.sh
  • .claude/settings.json
  • .gitignore

Comment thread .claude/settings.json
@elkaix

elkaix commented Jun 2, 2026

Copy link
Copy Markdown
Member Author

Re: CodeRabbit inline comment on .claude/settings.json — false positive, no change needed.

CodeRabbit suggests changing matcher: "Bash" to matcher: "Shell". This is incorrect for this context.

The .claude/settings.json file configures Claude Code's hook system, not pythinker-code's own tool runner. In Claude Code's settings schema, Bash is the exact tool name for the Bash tool — confirmed by the official Claude Code plugin SDK references (matcher: "Bash" appears throughout claude-plugins-official/plugins/plugin-dev/) and by the hook firing correctly during this session.

There is no Shell matcher in Claude Code's PreToolUse schema. Changing it would break the hook.

@elkaix
elkaix merged commit 139adc5 into main Jun 2, 2026
24 checks passed
@elkaix
elkaix deleted the fix/promote-release-homebrew-soft-gate branch July 17, 2026 20:04
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