Merge pull request #20402 from Homebrew/head-url-must-always-specify-branch
Ensure that `head` Git URLs always specify a branch name
This commit is contained in:
commit
d7b59fdfe8
@ -3807,12 +3807,12 @@ class Formula
|
||||
# If called as a method this provides just the {url} for the {SoftwareSpec}.
|
||||
# If a block is provided you can also add {.depends_on} and {Patch}es just to the {.head} {SoftwareSpec}.
|
||||
# The download strategies (e.g. `:using =>`) are the same as for {url}.
|
||||
# `master` is the default branch for Git and doesn't need stating with a `branch:` parameter.
|
||||
# Git repositories must always specify `branch:`.
|
||||
#
|
||||
# ### Example
|
||||
#
|
||||
# ```ruby
|
||||
# head "https://we.prefer.https.over.git.example.com/.git"
|
||||
# head "https://we.prefer.https.over.git.example.com/.git", branch: "main"
|
||||
# ```
|
||||
#
|
||||
# ```ruby
|
||||
|
@ -179,17 +179,21 @@ module Homebrew
|
||||
|
||||
def audit_head_branch
|
||||
return unless @online
|
||||
return unless @strict
|
||||
return if spec_name != :head
|
||||
return unless Utils::Git.remote_exists?(url)
|
||||
return if specs[:tag].present?
|
||||
return if specs[:revision].present?
|
||||
# Skip `resource` URLs as they use SHAs instead of branch specifiers.
|
||||
return if name != owner.name
|
||||
return unless url.end_with?(".git")
|
||||
return unless Utils::Git.remote_exists?(url)
|
||||
|
||||
branch = Utils.popen_read("git", "ls-remote", "--symref", url, "HEAD")
|
||||
detected_branch = Utils.popen_read("git", "ls-remote", "--symref", url, "HEAD")
|
||||
.match(%r{ref: refs/heads/(.*?)\s+HEAD})&.to_a&.second
|
||||
return if branch.blank? || branch == specs[:branch]
|
||||
|
||||
problem "Specify the default branch as `branch: \"#{branch}\"`"
|
||||
message = "Git `head` URL must specify a branch name"
|
||||
message += " - try `branch: \"#{detected_branch}\"`" if detected_branch.present?
|
||||
|
||||
problem message if specs[:branch].blank? || detected_branch != specs[:branch]
|
||||
end
|
||||
|
||||
def problem(text)
|
||||
|
@ -13,7 +13,7 @@ module RuboCop
|
||||
# - `bottle :unneeded`/`:disable` and `bottle do` should not be simultaneously present
|
||||
# - `stable do` should not be present without a `head` spec
|
||||
# - `stable do` should not be present with only `url|checksum|mirror|version`
|
||||
# - `head do` should not be present with only `url`
|
||||
# - `head do` should not be present with only `url|branch`
|
||||
class ComponentsRedundancy < FormulaCop
|
||||
HEAD_MSG = "`head` and `head do` should not be simultaneously present"
|
||||
BOTTLE_MSG = "`bottle :modifier` and `bottle do` should not be simultaneously present"
|
||||
@ -54,8 +54,9 @@ module RuboCop
|
||||
head_block = find_block(body_node, :head)
|
||||
if head_block && !head_block.body.nil?
|
||||
child_nodes = head_block.body.begin_type? ? head_block.body.child_nodes : [head_block.body]
|
||||
if child_nodes.all? { |n| n.send_type? && n.method_name == :url }
|
||||
problem "`head do` should not be present with only `url`"
|
||||
shorthand_head_methods = [:url, :branch]
|
||||
if child_nodes.all? { |n| n.send_type? && shorthand_head_methods.include?(n.method_name) }
|
||||
problem "`head do` should not be present with only #{shorthand_head_methods.join("/")}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -11,7 +11,7 @@ RSpec.describe Homebrew::DevCmd::Bump do
|
||||
content = <<~RUBY
|
||||
desc "HEAD-only test formula"
|
||||
homepage "https://brew.sh"
|
||||
head "https://github.com/Homebrew/brew.git"
|
||||
head "https://github.com/Homebrew/brew.git", branch: "main"
|
||||
RUBY
|
||||
setup_test_formula("headonly", content)
|
||||
|
||||
|
@ -285,7 +285,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license "GPL-3.0-or-later"
|
||||
end
|
||||
RUBY
|
||||
@ -301,7 +301,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license all_of: ["GPL-3.0-or-later", "MIT"]
|
||||
end
|
||||
RUBY
|
||||
@ -317,7 +317,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license "GPL-3.0-or-later" => { with: "LLVM-exception" }
|
||||
end
|
||||
RUBY
|
||||
@ -332,7 +332,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license "GPL-3.0-or-later" => { with: "zzz" }
|
||||
end
|
||||
RUBY
|
||||
@ -351,7 +351,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license "GPL-3.0-or-later" => { with: "#{deprecated_spdx_exception}" }
|
||||
end
|
||||
RUBY
|
||||
@ -371,7 +371,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
fa = formula_auditor "cask", <<~RUBY, spdx_license_data:, online: true, new_formula: true
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license "GPL-3.0-or-later"
|
||||
end
|
||||
RUBY
|
||||
@ -385,7 +385,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
fa = formula_auditor "cask", <<~RUBY, spdx_license_data:, online: true, new_formula: true
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license any_of: ["GPL-3.0-or-later", "MIT"]
|
||||
end
|
||||
RUBY
|
||||
@ -399,7 +399,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license "0BSD"
|
||||
end
|
||||
RUBY
|
||||
@ -416,7 +416,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license "0BSD"
|
||||
end
|
||||
RUBY
|
||||
@ -433,7 +433,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license #{license_any_mismatch}
|
||||
end
|
||||
RUBY
|
||||
@ -450,7 +450,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
head "https://github.com/cask/cask.git", branch: "main"
|
||||
license #{license_any}
|
||||
end
|
||||
RUBY
|
||||
@ -714,6 +714,78 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "requires `branch:` to be specified for Git head URLs" do
|
||||
fa = formula_auditor "foo", <<~RUBY, online: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
head "https://github.com/Homebrew/homebrew-test-bot.git"
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_specs
|
||||
# This is `.last` because the first problem is the unreachable stable URL.
|
||||
expect(fa.problems.last[:message]).to match("Git `head` URL must specify a branch name")
|
||||
end
|
||||
|
||||
it "suggests a detected default branch for Git head URLs" do
|
||||
fa = formula_auditor "foo", <<~RUBY, online: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
head "https://github.com/Homebrew/homebrew-test-bot.git", branch: "master"
|
||||
end
|
||||
RUBY
|
||||
|
||||
message = "Git `head` URL must specify a branch name - try `branch: \"main\"`"
|
||||
fa.audit_specs
|
||||
# This is `.last` because the first problem is the unreachable stable URL.
|
||||
expect(fa.problems.last[:message]).to match(message)
|
||||
end
|
||||
|
||||
it "ignores a pre-existing correct HEAD branch name" do
|
||||
fa = formula_auditor "foo", <<~RUBY, online: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
head "https://github.com/Homebrew/homebrew-test-bot.git", branch: "main"
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_specs
|
||||
expect(fa.problems).not_to match("Git `head` URL must specify a branch name")
|
||||
end
|
||||
|
||||
it "ignores `branch:` for non-Git head URLs" do
|
||||
fa = formula_auditor "foo", <<~RUBY, online: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
head "https://brew.sh/foo.tgz", branch: "develop"
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_specs
|
||||
expect(fa.problems).not_to match("Git `head` URL must specify a branch name")
|
||||
end
|
||||
|
||||
it "ignores `branch:` for `resource` URLs" do
|
||||
fa = formula_auditor "foo", <<~RUBY, online: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
|
||||
resource "bar" do
|
||||
url "https://raw.githubusercontent.com/Homebrew/homebrew-core/HEAD/Formula/bar.rb"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_specs
|
||||
expect(fa.problems).not_to match("Git `head` URL must specify a branch name")
|
||||
end
|
||||
|
||||
it "allows versions with no throttle rate" do
|
||||
fa = formula_auditor "bar", <<~RUBY, core_tap: true
|
||||
class Bar < Formula
|
||||
@ -770,7 +842,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
class Bar < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
head "https://brew.sh/foo.git"
|
||||
head "https://brew.sh/foo.git", branch: "develop"
|
||||
end
|
||||
RUBY
|
||||
|
||||
@ -783,7 +855,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
class BarAT1 < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
head "https://brew.sh/foo.git"
|
||||
head "https://brew.sh/foo.git", branch: "develop"
|
||||
end
|
||||
RUBY
|
||||
|
||||
@ -796,7 +868,7 @@ RSpec.describe Homebrew::FormulaAuditor do
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
head "https://brew.sh/foo.git"
|
||||
head "https://brew.sh/foo.git", branch: "develop"
|
||||
end
|
||||
RUBY
|
||||
|
||||
|
@ -17,7 +17,7 @@ RSpec.describe Homebrew::Livecheck do
|
||||
desc "Test formula"
|
||||
homepage "https://brew.sh"
|
||||
url "https://brew.sh/test-0.0.1.tgz"
|
||||
head "https://github.com/Homebrew/brew.git"
|
||||
head "https://github.com/Homebrew/brew.git", branch: "main"
|
||||
|
||||
livecheck do
|
||||
url "https://formulae.brew.sh/api/formula/ruby.json"
|
||||
@ -252,7 +252,7 @@ RSpec.describe Homebrew::Livecheck do
|
||||
desc "Test formula with a duplicate URL"
|
||||
homepage "https://github.com/Homebrew/brew.git"
|
||||
url "https://brew.sh/test-0.0.1.tgz"
|
||||
head "https://github.com/Homebrew/brew.git"
|
||||
head "https://github.com/Homebrew/brew.git", branch: "main"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -12,7 +12,7 @@ RSpec.describe Homebrew::Livecheck::SkipConditions do
|
||||
desc "Test formula"
|
||||
homepage "https://brew.sh"
|
||||
url "https://brew.sh/test-0.0.1.tgz"
|
||||
head "https://github.com/Homebrew/brew.git"
|
||||
head "https://github.com/Homebrew/brew.git", branch: "main"
|
||||
|
||||
livecheck do
|
||||
url "https://formulae.brew.sh/api/formula/ruby.json"
|
||||
@ -34,7 +34,7 @@ RSpec.describe Homebrew::Livecheck::SkipConditions do
|
||||
head_only: formula("test_head_only") do
|
||||
desc "HEAD-only test formula"
|
||||
homepage "https://brew.sh"
|
||||
head "https://github.com/Homebrew/brew.git"
|
||||
head "https://github.com/Homebrew/brew.git", branch: "main"
|
||||
end,
|
||||
gist: formula("test_gist") do
|
||||
desc "Gist test formula"
|
||||
|
@ -8,7 +8,7 @@ RSpec.describe Livecheck do
|
||||
formula do
|
||||
homepage "https://brew.sh"
|
||||
url "https://brew.sh/test-0.0.1.tgz"
|
||||
head "https://github.com/Homebrew/brew.git"
|
||||
head "https://github.com/Homebrew/brew.git", branch: "main"
|
||||
end
|
||||
end
|
||||
let(:livecheck_f) { described_class.new(f.class) }
|
||||
|
@ -25,7 +25,7 @@ RSpec.describe RuboCop::Cop::FormulaAudit::ComponentsRedundancy do
|
||||
it "reports an offense if both `head` and `head do` are present" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
head "https://brew.sh/foo.git"
|
||||
head "https://brew.sh/foo.git", branch: "develop"
|
||||
head do
|
||||
^^^^^^^ FormulaAudit/ComponentsRedundancy: `head` and `head do` should not be simultaneously present
|
||||
# stuff
|
||||
@ -50,7 +50,7 @@ RSpec.describe RuboCop::Cop::FormulaAudit::ComponentsRedundancy do
|
||||
it "reports no offenses if `stable do` is present with a `head` method" do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
class Foo < Formula
|
||||
head "https://brew.sh/foo.git"
|
||||
head "https://brew.sh/foo.git", branch: "develop"
|
||||
|
||||
stable do
|
||||
# stuff
|
||||
@ -82,17 +82,30 @@ RSpec.describe RuboCop::Cop::FormulaAudit::ComponentsRedundancy do
|
||||
end
|
||||
|
||||
head do
|
||||
^^^^^^^ FormulaAudit/ComponentsRedundancy: `head do` should not be present with only `url`
|
||||
^^^^^^^ FormulaAudit/ComponentsRedundancy: `head do` should not be present with only url/branch
|
||||
url "https://brew.sh/foo.git"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "reports an offense if `head do` is present with only `url` and `branch`" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
|
||||
head do
|
||||
^^^^^^^ FormulaAudit/ComponentsRedundancy: `head do` should not be present with only url/branch
|
||||
url "https://brew.sh/foo.git", branch: "develop"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "reports no offenses if `stable do` is present with `url` and `depends_on`" do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
class Foo < Formula
|
||||
head "https://brew.sh/foo.git"
|
||||
head "https://brew.sh/foo.git", branch: "trunk"
|
||||
|
||||
stable do
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
@ -109,6 +122,7 @@ RSpec.describe RuboCop::Cop::FormulaAudit::ComponentsRedundancy do
|
||||
|
||||
head do
|
||||
url "https://brew.sh/foo.git"
|
||||
branch "develop"
|
||||
depends_on "bar"
|
||||
end
|
||||
end
|
||||
|
@ -193,6 +193,7 @@ RSpec.describe Utils::AST::FormulaAST do
|
||||
|
||||
head do
|
||||
url "https://brew.sh/foo.git"
|
||||
branch "develop"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
@ -205,6 +206,7 @@ RSpec.describe Utils::AST::FormulaAST do
|
||||
|
||||
head do
|
||||
url "https://brew.sh/foo.git"
|
||||
branch "develop"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
@ -355,7 +357,7 @@ RSpec.describe Utils::AST::FormulaAST do
|
||||
described_class.new <<~RUBY.chomp
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tar.gz"
|
||||
head "https://brew.sh/foo.git"
|
||||
head "https://brew.sh/foo.git", branch: "develop"
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
@ -364,7 +366,7 @@ RSpec.describe Utils::AST::FormulaAST do
|
||||
<<~RUBY.chomp
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tar.gz"
|
||||
head "https://brew.sh/foo.git"
|
||||
head "https://brew.sh/foo.git", branch: "develop"
|
||||
|
||||
bottle do
|
||||
sha256 "f7b1fc772c79c20fddf621ccc791090bc1085fcef4da6cca03399424c66e06ca" => :sierra
|
||||
@ -387,6 +389,7 @@ RSpec.describe Utils::AST::FormulaAST do
|
||||
|
||||
head do
|
||||
url "https://brew.sh/foo.git"
|
||||
branch "develop"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
@ -403,6 +406,7 @@ RSpec.describe Utils::AST::FormulaAST do
|
||||
|
||||
head do
|
||||
url "https://brew.sh/foo.git"
|
||||
branch "develop"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
@ -841,7 +841,7 @@ end
|
||||
|
||||
Formulae can specify an alternate download for the upstream project's development/cutting-edge source (e.g. `master`/`main`/`trunk`) using [`head`](https://rubydoc.brew.sh/Formula#head-class_method), which can be activated by passing `--HEAD` when installing. Specifying it is done in the same manner as [`url`](https://rubydoc.brew.sh/Formula#url-class_method), with added conventions for fetching from version control repositories:
|
||||
|
||||
* Git repositories need `branch:` specified to fetch a branch other than "master". If the repository is very large, specify `only_path` to [limit the checkout to one path](Cask-Cookbook.md#git-urls).
|
||||
* Git repositories **must always** specify `branch:`. If the repository is very large, specify `only_path` to [limit the checkout to one path](Cask-Cookbook.md#git-urls).
|
||||
|
||||
```sh
|
||||
head "https://github.com/some/package.git", branch: "main"
|
||||
|
Loading…
x
Reference in New Issue
Block a user