Merge pull request #10370 from Rylan12/brew-release
Add brew release command
This commit is contained in:
commit
2c83ea7339
@ -172,7 +172,7 @@ update-preinstall() {
|
|||||||
|
|
||||||
if [[ "$HOMEBREW_COMMAND" = "install" || "$HOMEBREW_COMMAND" = "upgrade" ||
|
if [[ "$HOMEBREW_COMMAND" = "install" || "$HOMEBREW_COMMAND" = "upgrade" ||
|
||||||
"$HOMEBREW_COMMAND" = "bump-formula-pr" || "$HOMEBREW_COMMAND" = "bump-cask-pr" ||
|
"$HOMEBREW_COMMAND" = "bump-formula-pr" || "$HOMEBREW_COMMAND" = "bump-cask-pr" ||
|
||||||
"$HOMEBREW_COMMAND" = "bundle" ||
|
"$HOMEBREW_COMMAND" = "bundle" || "$HOMEBREW_COMMAND" = "release" ||
|
||||||
"$HOMEBREW_COMMAND" = "tap" && $HOMEBREW_ARG_COUNT -gt 1 ||
|
"$HOMEBREW_COMMAND" = "tap" && $HOMEBREW_ARG_COUNT -gt 1 ||
|
||||||
"$HOMEBREW_CASK_COMMAND" = "install" || "$HOMEBREW_CASK_COMMAND" = "upgrade" ]]
|
"$HOMEBREW_CASK_COMMAND" = "install" || "$HOMEBREW_CASK_COMMAND" = "upgrade" ]]
|
||||||
then
|
then
|
||||||
|
|||||||
@ -138,6 +138,12 @@ module Homebrew
|
|||||||
sig { returns(T.nilable(T::Boolean)) }
|
sig { returns(T.nilable(T::Boolean)) }
|
||||||
def reset_cache?; end
|
def reset_cache?; end
|
||||||
|
|
||||||
|
sig { returns(T.nilable(T::Boolean)) }
|
||||||
|
def major?; end
|
||||||
|
|
||||||
|
sig { returns(T.nilable(T::Boolean)) }
|
||||||
|
def minor?; end
|
||||||
|
|
||||||
sig { returns(T.nilable(String)) }
|
sig { returns(T.nilable(String)) }
|
||||||
def tag; end
|
def tag; end
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cli/parser"
|
require "cli/parser"
|
||||||
|
require "release_notes"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
extend T::Sig
|
extend T::Sig
|
||||||
@ -31,6 +32,9 @@ module Homebrew
|
|||||||
def release_notes
|
def release_notes
|
||||||
args = release_notes_args.parse
|
args = release_notes_args.parse
|
||||||
|
|
||||||
|
# TODO: (2.8) Deprecate this command now that the `brew release` command exists.
|
||||||
|
# odeprecated "`brew release-notes`"
|
||||||
|
|
||||||
previous_tag = args.named.first
|
previous_tag = args.named.first
|
||||||
|
|
||||||
if previous_tag.present?
|
if previous_tag.present?
|
||||||
@ -55,25 +59,9 @@ module Homebrew
|
|||||||
odie "Ref #{ref} does not exist!"
|
odie "Ref #{ref} does not exist!"
|
||||||
end
|
end
|
||||||
|
|
||||||
output = Utils.popen_read(
|
release_notes = ReleaseNotes.generate_release_notes previous_tag, end_ref, markdown: args.markdown?
|
||||||
"git", "-C", HOMEBREW_REPOSITORY, "log", "--pretty=format:'%s >> - %b%n'", "#{previous_tag}..#{end_ref}"
|
|
||||||
).lines.grep(/Merge pull request/)
|
|
||||||
|
|
||||||
output.map! do |s|
|
|
||||||
s.gsub(%r{.*Merge pull request #(\d+) from ([^/]+)/[^>]*(>>)*},
|
|
||||||
"https://github.com/Homebrew/brew/pull/\\1 (@\\2)")
|
|
||||||
end
|
|
||||||
if args.markdown?
|
|
||||||
output.map! do |s|
|
|
||||||
/(.*\d)+ \(@(.+)\) - (.*)/ =~ s
|
|
||||||
"- [#{Regexp.last_match(3)}](#{Regexp.last_match(1)}) (@#{Regexp.last_match(2)})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
$stderr.puts "Release notes between #{previous_tag} and #{end_ref}:"
|
$stderr.puts "Release notes between #{previous_tag} and #{end_ref}:"
|
||||||
if args.markdown? && args.named.first
|
puts release_notes
|
||||||
puts "Release notes for major and minor releases can be found in the [Homebrew blog](https://brew.sh/blog/)."
|
|
||||||
end
|
|
||||||
puts output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
88
Library/Homebrew/dev-cmd/release.rb
Executable file
88
Library/Homebrew/dev-cmd/release.rb
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
# typed: true
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cli/parser"
|
||||||
|
require "release_notes"
|
||||||
|
|
||||||
|
module Homebrew
|
||||||
|
extend T::Sig
|
||||||
|
|
||||||
|
module_function
|
||||||
|
|
||||||
|
sig { returns(CLI::Parser) }
|
||||||
|
def release_args
|
||||||
|
Homebrew::CLI::Parser.new do
|
||||||
|
description <<~EOS
|
||||||
|
Create a new draft Homebrew/brew release with the appropriate version number and release notes.
|
||||||
|
|
||||||
|
By default, `brew release` will bump the patch version number. Pass
|
||||||
|
`--major` or `--minor` to bump the major or minor version numbers, respectively.
|
||||||
|
The command will fail if the previous major or minor release was made less than
|
||||||
|
one month ago.
|
||||||
|
|
||||||
|
Requires write access to the Homebrew/brew repository.
|
||||||
|
EOS
|
||||||
|
switch "--major",
|
||||||
|
description: "Create a major release."
|
||||||
|
switch "--minor",
|
||||||
|
description: "Create a minor release."
|
||||||
|
conflicts "--major", "--minor"
|
||||||
|
|
||||||
|
named_args :none
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def release
|
||||||
|
args = release_args.parse
|
||||||
|
|
||||||
|
safe_system "git", "-C", HOMEBREW_REPOSITORY, "fetch", "origin" if Homebrew::EnvConfig.no_auto_update?
|
||||||
|
|
||||||
|
begin
|
||||||
|
latest_release = GitHub.get_latest_release "Homebrew", "brew"
|
||||||
|
rescue GitHub::HTTPNotFoundError
|
||||||
|
odie "No existing releases found!"
|
||||||
|
end
|
||||||
|
latest_version = Version.new latest_release["tag_name"]
|
||||||
|
|
||||||
|
if args.major? || args.minor?
|
||||||
|
one_month_ago = Date.today << 1
|
||||||
|
latest_major_minor_release = begin
|
||||||
|
GitHub.get_release "Homebrew", "brew", "#{latest_version.major_minor}.0"
|
||||||
|
rescue GitHub::HTTPNotFoundError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if latest_major_minor_release.blank?
|
||||||
|
opoo "Unable to determine the release date of the latest major/minor release."
|
||||||
|
elsif Date.parse(latest_major_minor_release["published_at"]) > one_month_ago
|
||||||
|
odie "The latest major/minor release was less than one month ago."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
new_version = if args.major?
|
||||||
|
Version.new "#{latest_version.major.to_i + 1}.0.0"
|
||||||
|
elsif args.minor?
|
||||||
|
Version.new "#{latest_version.major}.#{latest_version.minor.to_i + 1}.0"
|
||||||
|
else
|
||||||
|
Version.new "#{latest_version.major}.#{latest_version.minor}.#{latest_version.patch.to_i + 1}"
|
||||||
|
end.to_s
|
||||||
|
|
||||||
|
ohai "Creating draft release for version #{new_version}"
|
||||||
|
|
||||||
|
release_notes = if args.major? || args.minor?
|
||||||
|
"Release notes for this release can be found on the [Homebrew blog](https://brew.sh/blog/#{new_version}).\n"
|
||||||
|
else
|
||||||
|
""
|
||||||
|
end
|
||||||
|
release_notes += ReleaseNotes.generate_release_notes latest_version, "origin/HEAD", markdown: true
|
||||||
|
|
||||||
|
begin
|
||||||
|
release = GitHub.create_or_update_release "Homebrew", "brew", new_version, body: release_notes, draft: true
|
||||||
|
rescue *GitHub::API_ERRORS => e
|
||||||
|
odie "Unable to create release: #{e.message}!"
|
||||||
|
end
|
||||||
|
|
||||||
|
puts release["html_url"]
|
||||||
|
exec_browser release["html_url"]
|
||||||
|
end
|
||||||
|
end
|
||||||
35
Library/Homebrew/release_notes.rb
Normal file
35
Library/Homebrew/release_notes.rb
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# typed: true
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Helper functions for generating release notes.
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
module ReleaseNotes
|
||||||
|
extend T::Sig
|
||||||
|
|
||||||
|
module_function
|
||||||
|
|
||||||
|
sig {
|
||||||
|
params(start_ref: T.any(String, Version), end_ref: T.any(String, Version), markdown: T.nilable(T::Boolean))
|
||||||
|
.returns(String)
|
||||||
|
}
|
||||||
|
def generate_release_notes(start_ref, end_ref, markdown: false)
|
||||||
|
log_output = Utils.popen_read(
|
||||||
|
"git", "-C", HOMEBREW_REPOSITORY, "log", "--pretty=format:'%s >> - %b%n'", "#{start_ref}..#{end_ref}"
|
||||||
|
).lines.grep(/Merge pull request/)
|
||||||
|
|
||||||
|
log_output.map! do |s|
|
||||||
|
s.gsub(%r{.*Merge pull request #(\d+) from ([^/]+)/[^>]*(>>)*},
|
||||||
|
"https://github.com/Homebrew/brew/pull/\\1 (@\\2)")
|
||||||
|
end
|
||||||
|
|
||||||
|
if markdown
|
||||||
|
log_output.map! do |s|
|
||||||
|
/(.*\d)+ \(@(.+)\) - (.*)/ =~ s
|
||||||
|
"- [#{Regexp.last_match(3)}](#{Regexp.last_match(1)}) (@#{Regexp.last_match(2)})\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
log_output.join
|
||||||
|
end
|
||||||
|
end
|
||||||
8
Library/Homebrew/test/dev-cmd/release_spec.rb
Normal file
8
Library/Homebrew/test/dev-cmd/release_spec.rb
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# typed: false
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
|
describe "Homebrew.release_args" do
|
||||||
|
it_behaves_like "parseable arguments"
|
||||||
|
end
|
||||||
33
Library/Homebrew/test/release_notes_spec.rb
Normal file
33
Library/Homebrew/test/release_notes_spec.rb
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# typed: false
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "release_notes"
|
||||||
|
|
||||||
|
describe ReleaseNotes do
|
||||||
|
before do
|
||||||
|
HOMEBREW_REPOSITORY.cd do
|
||||||
|
system "git", "init"
|
||||||
|
system "git", "commit", "--allow-empty", "-m", "Initial commit"
|
||||||
|
system "git", "tag", "release-notes-testing"
|
||||||
|
system "git", "commit", "--allow-empty", "-m", "Merge pull request #1 from Homebrew/fix", "-m", "Do something"
|
||||||
|
system "git", "commit", "--allow-empty", "-m", "make a change"
|
||||||
|
system "git", "commit", "--allow-empty", "-m", "Merge pull request #2 from User/fix", "-m", "Do something else"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe ".generate_release_notes" do
|
||||||
|
it "generates release notes" do
|
||||||
|
expect(described_class.generate_release_notes("release-notes-testing", "HEAD")).to eq <<~NOTES
|
||||||
|
https://github.com/Homebrew/brew/pull/2 (@User) - Do something else
|
||||||
|
https://github.com/Homebrew/brew/pull/1 (@Homebrew) - Do something
|
||||||
|
NOTES
|
||||||
|
end
|
||||||
|
|
||||||
|
it "generates markdown release notes" do
|
||||||
|
expect(described_class.generate_release_notes("release-notes-testing", "HEAD", markdown: true)).to eq <<~NOTES
|
||||||
|
- [Do something else](https://github.com/Homebrew/brew/pull/2) (@User)
|
||||||
|
- [Do something](https://github.com/Homebrew/brew/pull/1) (@Homebrew)
|
||||||
|
NOTES
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -482,7 +482,12 @@ module GitHub
|
|||||||
open_api(url, request_method: :GET)
|
open_api(url, request_method: :GET)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_or_update_release(user, repo, tag, id: nil, name: nil, draft: false)
|
def get_latest_release(user, repo)
|
||||||
|
url = "#{API_URL}/repos/#{user}/#{repo}/releases/latest"
|
||||||
|
open_api(url, request_method: :GET)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_or_update_release(user, repo, tag, id: nil, name: nil, body: nil, draft: false)
|
||||||
url = "#{API_URL}/repos/#{user}/#{repo}/releases"
|
url = "#{API_URL}/repos/#{user}/#{repo}/releases"
|
||||||
method = if id
|
method = if id
|
||||||
url += "/#{id}"
|
url += "/#{id}"
|
||||||
@ -495,6 +500,7 @@ module GitHub
|
|||||||
name: name || tag,
|
name: name || tag,
|
||||||
draft: draft,
|
draft: draft,
|
||||||
}
|
}
|
||||||
|
data[:body] = body if body.present?
|
||||||
open_api(url, data: data, request_method: method, scopes: CREATE_ISSUE_FORK_OR_PR_SCOPES)
|
open_api(url, data: data, request_method: method, scopes: CREATE_ISSUE_FORK_OR_PR_SCOPES)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1663,6 +1663,23 @@ _brew_reinstall() {
|
|||||||
__brew_complete_casks
|
__brew_complete_casks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_brew_release() {
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
__brewcomp "
|
||||||
|
--debug
|
||||||
|
--help
|
||||||
|
--major
|
||||||
|
--minor
|
||||||
|
--quiet
|
||||||
|
--verbose
|
||||||
|
"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
_brew_release_notes() {
|
_brew_release_notes() {
|
||||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
@ -2418,6 +2435,7 @@ _brew() {
|
|||||||
prof) _brew_prof ;;
|
prof) _brew_prof ;;
|
||||||
readall) _brew_readall ;;
|
readall) _brew_readall ;;
|
||||||
reinstall) _brew_reinstall ;;
|
reinstall) _brew_reinstall ;;
|
||||||
|
release) _brew_release ;;
|
||||||
release-notes) _brew_release_notes ;;
|
release-notes) _brew_release_notes ;;
|
||||||
remove) _brew_remove ;;
|
remove) _brew_remove ;;
|
||||||
rm) _brew_rm ;;
|
rm) _brew_rm ;;
|
||||||
|
|||||||
@ -72,6 +72,7 @@ pr-upload
|
|||||||
prof
|
prof
|
||||||
readall
|
readall
|
||||||
reinstall
|
reinstall
|
||||||
|
release
|
||||||
release-notes
|
release-notes
|
||||||
remove
|
remove
|
||||||
rm
|
rm
|
||||||
|
|||||||
@ -1208,6 +1208,22 @@ Run Homebrew with a Ruby profiler, e.g. `brew prof readall`.
|
|||||||
* `--stackprof`:
|
* `--stackprof`:
|
||||||
Use `stackprof` instead of `ruby-prof` (the default).
|
Use `stackprof` instead of `ruby-prof` (the default).
|
||||||
|
|
||||||
|
### `release` [*`--major`*] [*`--minor`*]
|
||||||
|
|
||||||
|
Create a new draft Homebrew/brew release with the appropriate version number and release notes.
|
||||||
|
|
||||||
|
By default, `brew release` will bump the patch version number. Pass
|
||||||
|
`--major` or `--minor` to bump the major or minor version numbers, respectively.
|
||||||
|
The command will fail if the previous major or minor release was made less than
|
||||||
|
one month ago.
|
||||||
|
|
||||||
|
Requires write access to the Homebrew/brew repository.
|
||||||
|
|
||||||
|
* `--major`:
|
||||||
|
Create a major release.
|
||||||
|
* `--minor`:
|
||||||
|
Create a minor release.
|
||||||
|
|
||||||
### `release-notes` [*`options`*] [*`previous_tag`*] [*`end_ref`*]
|
### `release-notes` [*`options`*] [*`previous_tag`*] [*`end_ref`*]
|
||||||
|
|
||||||
Print the merged pull requests on Homebrew/brew between two Git refs.
|
Print the merged pull requests on Homebrew/brew between two Git refs.
|
||||||
|
|||||||
@ -11,16 +11,12 @@ Homebrew release:
|
|||||||
[Homebrew/discussions (forum)](https://github.com/homebrew/discussions/discussions) to see if there is
|
[Homebrew/discussions (forum)](https://github.com/homebrew/discussions/discussions) to see if there is
|
||||||
anything pressing that needs to be fixed or merged before the next release.
|
anything pressing that needs to be fixed or merged before the next release.
|
||||||
If so, fix and merge these changes.
|
If so, fix and merge these changes.
|
||||||
2. After no code changes have happened for at least a couple of hours (ideally 24 hours)
|
2. Ensure that no code changes have happened for at least a couple of hours (ideally 24 hours)
|
||||||
and you are confident there's no major regressions on the current `master`
|
and that you are confident there are no major regressions on the current `master`
|
||||||
branch you can create a new Git tag. Ideally this should be signed with your
|
branch.
|
||||||
GPG key. This can then be pushed to GitHub.
|
3. Run `brew release` to create a new draft release. For major or minor version bumps,
|
||||||
3. Use `brew release-notes --markdown $PREVIOUS_TAG` to generate the release
|
pass `--major` or `--minor`, respectively.
|
||||||
notes for the release.
|
4. Publish the draft release on [GitHub](https://github.com/Homebrew/brew/releases).
|
||||||
4. [Create a new release on GitHub](https://github.com/Homebrew/brew/releases/new)
|
|
||||||
based on the new tag.
|
|
||||||
|
|
||||||
You can watch a video of the above process [on YouTube](https://youtu.be/dQCpLaXOf6k)
|
|
||||||
|
|
||||||
If this is a major or minor release (e.g. X.0.0 or X.Y.0) then there are a few more steps:
|
If this is a major or minor release (e.g. X.0.0 or X.Y.0) then there are a few more steps:
|
||||||
|
|
||||||
|
|||||||
@ -1686,6 +1686,23 @@ Run Homebrew with a Ruby profiler, e\.g\. \fBbrew prof readall\fR\.
|
|||||||
\fB\-\-stackprof\fR
|
\fB\-\-stackprof\fR
|
||||||
Use \fBstackprof\fR instead of \fBruby\-prof\fR (the default)\.
|
Use \fBstackprof\fR instead of \fBruby\-prof\fR (the default)\.
|
||||||
.
|
.
|
||||||
|
.SS "\fBrelease\fR [\fI\-\-major\fR] [\fI\-\-minor\fR]"
|
||||||
|
Create a new draft Homebrew/brew release with the appropriate version number and release notes\.
|
||||||
|
.
|
||||||
|
.P
|
||||||
|
By default, \fBbrew release\fR will bump the patch version number\. Pass \fB\-\-major\fR or \fB\-\-minor\fR to bump the major or minor version numbers, respectively\. The command will fail if the previous major or minor release was made less than one month ago\.
|
||||||
|
.
|
||||||
|
.P
|
||||||
|
Requires write access to the Homebrew/brew repository\.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
\fB\-\-major\fR
|
||||||
|
Create a major release\.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
\fB\-\-minor\fR
|
||||||
|
Create a minor release\.
|
||||||
|
.
|
||||||
.SS "\fBrelease\-notes\fR [\fIoptions\fR] [\fIprevious_tag\fR] [\fIend_ref\fR]"
|
.SS "\fBrelease\-notes\fR [\fIoptions\fR] [\fIprevious_tag\fR] [\fIend_ref\fR]"
|
||||||
Print the merged pull requests on Homebrew/brew between two Git refs\. If no \fIprevious_tag\fR is provided it defaults to the latest tag\. If no \fIend_ref\fR is provided it defaults to \fBorigin/master\fR\.
|
Print the merged pull requests on Homebrew/brew between two Git refs\. If no \fIprevious_tag\fR is provided it defaults to the latest tag\. If no \fIend_ref\fR is provided it defaults to \fBorigin/master\fR\.
|
||||||
.
|
.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user