Skip to content

GitHubReleases: group bottle uploads by release - #24416

Merged
MikeMcQuaid merged 1 commit into
Homebrew:mainfrom
hezhizhen:release-batch-uploads
Nov 1, 2026
Merged

MikeMcQuaid merged 1 commit into
Homebrew:mainfrom
hezhizhen:release-batch-uploads

Conversation

@hezhizhen

@hezhizhen hezhizhen commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

When several formulae share a GitHub Release, upload_bottles publishes it after the first formula. Immutable releases then reject the remaining assets.

Group bottles by parsed owner, repository and tag so every formula's assets reach the release before it is published. Owner and repository names are normalised to lowercase, following GitHub's API contract. Tag case is preserved. An upload failure leaves that release as a draft. Retrying replaces only matching bottle assets in that draft, including empty assets left by failed uploads. Replacement files are checked for readability before removing existing draft assets. Other draft assets are retained. Different repositories and tags retain separate releases.

for each owner, repository and tag
  find or create the release
  upload all formulae and bottle architectures
  publish if it is a draft

Follow-up to #24403 for batches where multiple formulae share a release.

Evidence

Run this from the repository root to reproduce without publishing a real release. The GitHub API methods model the immutable-release restriction:

./bin/brew ruby -- - <<'RUBY'
require "github_releases"

release = nil
events = []
GitHub.define_singleton_method(:get_release) do |*|
  if release.nil? || release.fetch("draft")
    raise GitHub::API::HTTPNotFoundError.new("not found")
  end

  release.dup
end
GitHub.define_singleton_method(:create_or_update_release) do |*, **options|
  release = { "id" => 123, "draft" => options.fetch(:draft) }
  events << (release.fetch("draft") ? "create draft" : "publish release")
  release.dup
end
GitHub.define_singleton_method(:upload_release_asset) do |*, remote_file:, **|
  raise "immutable release rejects #{remote_file}" unless release.fetch("draft")

  events << "upload #{remote_file}"
end
bottles = %w[alpha beta].to_h do |name|
  [name, {
    "bottle" => {
      "root_url" => "https://github.com/audit-example/homebrew-fixture/releases/download/batch-test",
      "tags" => {
        "arm64" => {
          "filename" => "#{name}.bottle.tar.gz",
          "local_filename" => "#{name}.bottle.tar.gz",
        },
      },
    },
  }]
end
GitHubReleases.new.upload_bottles(bottles)
puts events
RUBY
  • Before: the second formula fails with immutable release rejects beta.bottle.tar.gz.
  • After: create draft → upload alpha.bottle.tar.gz → upload beta.bottle.tar.gz → publish release.

Focused regression examples and full checks:

./bin/brew tests --only=github_releases:80 --no-parallel
./bin/brew tests --only=github_releases:153 --no-parallel
./bin/brew tests --only=github_releases:178 --no-parallel
./bin/brew tests --only=github_releases:228 --no-parallel
./bin/brew tests --only=github_releases:303 --no-parallel
./bin/brew tests --only=github_releases:330 --no-parallel
./bin/brew lgtm --online

The shared-release regression was verified to fail before the fix and pass afterwards. The full file passes all nine examples, including existing published-release and leftover-draft behaviour. Global typechecking and style checks pass. The shared-release fixture mixes owner/repository case; separate-release coverage also distinguishes test from Test. Failure-then-retry coverage includes an unrelated draft asset and an empty starter asset, with separate guards for published assets and missing local replacements.

Merge Danger

Door: Two-way

The implementation can be reverted. Uploads that fail before completion leave the shared release unpublished.

Blast Radius: Publishing

Affects GitHub Release bottle uploads through brew pr-upload and brew pr-pull.


  • Have you followed our Contributing guidelines?
  • Have you checked for other open Pull Requests for the same change?
  • Have you explained what your changes do? Performance claims (e.g. "this is faster") must include brew benchmark results.
  • Have you explained why you'd like these changes included, not just what they do?
  • For bug fixes, have you given step-by-step brew commands to reproduce the bug?
  • Have you written new tests (excluding integration tests)? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) locally?

  • I did not use AI/LLM to create this PR, or I disclosed the tool/model below and reviewed its output; I did not attribute commits to AI and will answer maintainer questions and review comments myself without AI/LLM.

AI disclosure: OpenAI Codex (GPT-6) helped draft the implementation, tests and PR description. I reviewed the changes and verified them with the regression checks above.


Copilot AI lite review requested due to automatic review settings November 1, 2026 03:39
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 204a7990-58f5-43e6-b014-79c3015d6155
Commits

Reviewing files that changed from the base of the PR and between bdf27f7 and 54d58b7.

Files selected for processing: 2
  • Library/Homebrew/github_releases.rb
  • Library/Homebrew/test/github_releases_spec.rb

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

Summary

Summary by CodeRabbit

  • Improvements
    • Bottle uploads for the same repository and release tag are grouped together, including when repository URL casing differs. The release is published only after all bottles in the group upload successfully; if an upload fails, the error is reported and the release remains a draft.
    • Bottles for different repositories or differently cased release tags use separate releases.
    • Upload progress is updated after each bottle’s assets are uploaded, and is not shown for batches of fewer than three bottles.
    • When uploading to a draft release, an existing asset with the same filename is replaced, provided the local file can be read.

upload_bottles groups bottles by lower-cased owner and repository plus release tag. It creates or reuses one release per group, uploads each bottle’s assets, and publishes draft releases after the group uploads. For draft releases, it replaces matching assets after checking that the local file is readable.

Changes: Release uploads

Layer / File(s) Summary
Group uploads and validate release sequencing
Library/Homebrew/github_releases.rb, Library/Homebrew/test/github_releases_spec.rb
upload_bottles groups bottles by lower-cased owner and repository plus tag. It uploads each group’s bottles to one release and publishes the release after its uploads. Specs cover shared releases, upload error propagation, and separation by repository and tag.
Draft asset replacement
Library/Homebrew/github_releases.rb, Library/Homebrew/test/github_releases_spec.rb
For draft releases, matching existing assets are deleted before replacement, after the local file readability check. Specs cover retrying partial uploads, retaining unrelated assets, published-release conflicts, and missing replacement files.
Merge Risk: Minimal · up to 54d58

The previously identified grouping and retry failures are addressed. No actionable merge-blocking risk remains after normal checks.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title names the main change: bottle uploads are grouped by release, which matches the raw summary (grouping by owner, repository and tag) and the objectives (PR #24416). The title is specific and …
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.
Description check Passed The description directly explains the grouped GitHub release upload changes, retry behaviour, failure handling, tests, and affected commands.
Full details: Title check

Explanation

The title names the main change: bottle uploads are grouped by release, which matches the raw summary (grouping by owner, repository and tag) and the objectives (PR #24416). The title is specific and concise and avoids vague terms. The 'GitHubReleases' prefix matches the changed class.

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

@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


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: de08e9aa-98f3-4e77-be66-609303450b22
Commits

Reviewing files that changed from the base of the PR and between 1f55ebf and d1a0eec.

Files selected for processing: 2
  • Library/Homebrew/github_releases.rb
  • Library/Homebrew/test/github_releases_spec.rb

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread Library/Homebrew/github_releases.rb Resolved

Copilot AI 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.

🟢 Approval recommended

No active review issues are identified, and the change includes regression coverage for shared-release ordering, upload failures, and release separation.

No open issues 🎉

What changed in this PR

Updates GitHub Release bottle uploads to group formulae sharing a release, ensuring all assets are uploaded before the release is published.

Changes:

  • Groups bottles by root_url and processes each release independently.
  • Uploads all formula assets before publishing the release.
  • Preserves separate handling for repositories and release tags.
  • Adds regression coverage for shared releases, upload failures, and release separation.
File Description
Library/​Homebrew/​test/​github_releases_spec.rb Tests shared-release ordering, draft preservation after failures, and release separation.
Library/​Homebrew/​github_releases.rb Groups bottle uploads by release URL and publishes each draft after all associated uploads succeed.

Review effort: Lite

@hezhizhen
hezhizhen force-pushed the release-batch-uploads branch from d1a0eec to bdf27f7 Compare November 1, 2026 03:59
Copilot AI lite review requested due to automatic review settings November 1, 2026 03:59

@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


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3f8ac35a-c5ca-4db8-85dc-dc99c71be4fc
Commits

Reviewing files that changed from the base of the PR and between d1a0eec and bdf27f7.

Files selected for processing: 2
  • Library/Homebrew/github_releases.rb
  • Library/Homebrew/test/github_releases_spec.rb

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

Comment thread Library/Homebrew/github_releases.rb Resolved

Copilot AI 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.

🔵 Needs a closer look

Maintainer validation is still needed for the production GitHub Release grouping and draft-to-published sequencing, especially live API behavior and immutable-release edge cases.

No open issues 🎉

Review effort: Lite

- Upload all formulae sharing a release before publishing it, so
  immutable releases do not reject the remaining bottle assets.
- Leave incomplete uploads as drafts and cover separate repositories
  and tags in regression tests.
- Normalise owner and repository names when grouping release URLs,
  while preserving case-sensitive tags.
- Resume draft uploads by replacing only matching bottle assets, after
  checking that the local replacements are readable.
@hezhizhen
hezhizhen force-pushed the release-batch-uploads branch from bdf27f7 to 54d58b7 Compare November 1, 2026 04:42
Copilot AI lite review requested due to automatic review settings November 1, 2026 04:42

Copilot AI 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.

🔵 Needs a closer look

Because it changes production GitHub Release publishing across shared and separate repositories, maintainer validation of live drafts, asset uploads, and immutable-release behavior is still needed beyond the focused mocks.

No open issues 🎉

Review effort: Lite

@MikeMcQuaid MikeMcQuaid left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

@MikeMcQuaid
MikeMcQuaid added this pull request to the merge queue Nov 1, 2026
Merged via the queue into Homebrew:main with commit c6c584d Nov 1, 2026
54 checks passed
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.

3 participants

⌂ 换网址