diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index d3b3da0c6e..8d68cac312 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -2,8 +2,6 @@ name: actionlint on: push: - branches: - - master paths: - '.github/workflows/*.ya?ml' - '.github/actionlint.yaml' @@ -11,7 +9,6 @@ on: paths: - '.github/workflows/*.ya?ml' - '.github/actionlint.yaml' - merge_group: env: HOMEBREW_DEVELOPER: 1 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d1c41aef1d..ac3c56d8a1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -2,9 +2,6 @@ name: Docker on: pull_request: - push: - branches: - - master merge_group: release: types: @@ -75,7 +72,7 @@ jobs: "homebrew/brew:latest" ) fi - elif [[ "${GITHUB_EVENT_NAME}" == "push" && + elif [[ "${GITHUB_EVENT_NAME}" == "merge_group" && "${GITHUB_REF}" == "refs/heads/master" && "${{ matrix.version }}" == "22.04" ]]; then tags+=( diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d4464c97db..84a849a63a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,9 +1,6 @@ name: Documentation CI on: - push: - branches: - - master pull_request: merge_group: diff --git a/.github/workflows/pkg-installer.yml b/.github/workflows/pkg-installer.yml index a6a92eb4d0..a80f823652 100644 --- a/.github/workflows/pkg-installer.yml +++ b/.github/workflows/pkg-installer.yml @@ -133,7 +133,7 @@ jobs: fi - name: Generate build provenance - uses: actions/attest-build-provenance@c4fbc648846ca6f503a13a2281a5e7b98aa57202 # v2.0.1 + uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0 with: subject-path: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 27985bd4d7..cfeb09c78a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,9 +1,6 @@ name: CI on: - push: - branches: - - master pull_request: merge_group: diff --git a/Library/Homebrew/cask/artifact/abstract_uninstall.rb b/Library/Homebrew/cask/artifact/abstract_uninstall.rb index 0cfdcad7ba..2242b7961a 100644 --- a/Library/Homebrew/cask/artifact/abstract_uninstall.rb +++ b/Library/Homebrew/cask/artifact/abstract_uninstall.rb @@ -468,9 +468,7 @@ module Cask trashed = trashed.split(":") untrashable = untrashable.split(":") - return trashed, untrashable if untrashable.empty? - - untrashable.delete_if do |path| + trashed_with_permissions, untrashable = untrashable.partition do |path| Utils.gain_permissions(path, ["-R"], SystemCommand) do system_command! HOMEBREW_LIBRARY_PATH/"cask/utils/trash.swift", args: [path], @@ -482,6 +480,10 @@ module Cask false end + trashed += trashed_with_permissions + + return trashed, untrashable if untrashable.empty? + opoo "The following files could not be trashed, please do so manually:" $stderr.puts untrashable diff --git a/Library/Homebrew/dev-cmd/tap-new.rb b/Library/Homebrew/dev-cmd/tap-new.rb index 6e59498428..5f1423fb64 100644 --- a/Library/Homebrew/dev-cmd/tap-new.rb +++ b/Library/Homebrew/dev-cmd/tap-new.rb @@ -4,6 +4,7 @@ require "abstract_command" require "fileutils" require "tap" +require "utils/uid" module Homebrew module DevCmd @@ -172,16 +173,32 @@ module Homebrew write_path(tap, ".github/workflows/publish.yml", actions_publish) unless args.no_git? - cd tap.path do + cd tap.path do |path| Utils::Git.set_name_email! Utils::Git.setup_gpg! # Would be nice to use --initial-branch here but it's not available in # older versions of Git that we support. safe_system "git", "-c", "init.defaultBranch=#{branch}", "init" - safe_system "git", "add", "--all" - safe_system "git", "commit", "-m", "Create #{tap} tap" - safe_system "git", "branch", "-m", branch + + args = [] + git_owner = File.stat(File.join(path, ".git")).uid + if git_owner != Process.uid && git_owner == Process.euid + # Under Homebrew user model, EUID is permitted to execute commands under the UID. + # Root users are never allowed (see brew.sh). + args << "-c" << "safe.directory=#{path}" + end + + # Use the configuration of the original user, which will have author information and signing keys. + Utils::UID.drop_euid do + env = { HOME: Utils::UID.uid_home }.compact + env[:TMPDIR] = nil if (tmpdir = ENV.fetch("TMPDIR", nil)) && !File.writable?(tmpdir) + with_env(env) do + safe_system "git", *args, "add", "--all" + safe_system "git", *args, "commit", "-m", "Create #{tap} tap" + safe_system "git", *args, "branch", "-m", branch + end + end end end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 3ef26943da..bede10e378 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -2005,6 +2005,8 @@ class Formula # If called with no parameters, does this with all compatible # universal binaries in a {Formula}'s {Keg}. # + # Raises an error if no universal binaries are found to deuniversalize. + # # @api public sig { params(targets: T.nilable(T.any(Pathname, String))).void } def deuniversalize_machos(*targets) @@ -2014,6 +2016,8 @@ class Formula end end + raise "No universal binaries found to deuniversalize" if targets.blank? + targets&.each do |target| extract_macho_slice_from(Pathname(target), Hardware::CPU.arch) end diff --git a/Library/Homebrew/livecheck/strategy/json.rb b/Library/Homebrew/livecheck/strategy/json.rb index a4631c58e2..b8f05e29ae 100644 --- a/Library/Homebrew/livecheck/strategy/json.rb +++ b/Library/Homebrew/livecheck/strategy/json.rb @@ -58,8 +58,10 @@ module Homebrew end # Parses JSON text and identifies versions using a `strategy` block. - # If a regex is provided, it will be passed as the second argument to - # the `strategy` block (after the parsed JSON data). + # If the block has two parameters, the parsed JSON data will be used as + # the first argument and the regex (if any) will be the second. + # Otherwise, only the parsed JSON data will be passed to the block. + # # @param content [String] the JSON text to parse and check # @param regex [Regexp, nil] a regex used for matching versions in the # content @@ -77,10 +79,8 @@ module Homebrew json = parse_json(content) return [] if json.blank? - block_return_value = if regex.present? + block_return_value = if block.arity == 2 yield(json, regex) - elsif block.arity == 2 - raise "Two arguments found in `strategy` block but no regex provided." else yield(json) end diff --git a/Library/Homebrew/livecheck/strategy/pypi.rb b/Library/Homebrew/livecheck/strategy/pypi.rb index 66351f884c..8711a2264a 100644 --- a/Library/Homebrew/livecheck/strategy/pypi.rb +++ b/Library/Homebrew/livecheck/strategy/pypi.rb @@ -20,10 +20,14 @@ module Homebrew # The default `strategy` block used to extract version information when # a `strategy` block isn't provided. - DEFAULT_BLOCK = T.let(proc do |json| - json.dig("info", "version").presence + DEFAULT_BLOCK = T.let(proc do |json, regex| + version = json.dig("info", "version") + next if version.blank? + + regex ? version[regex, 1] : version end.freeze, T.proc.params( - arg0: T::Hash[String, T.untyped], + json: T::Hash[String, T.untyped], + regex: T.nilable(Regexp), ).returns(T.nilable(String))) # The `Regexp` used to extract the package name and suffix (e.g. file diff --git a/Library/Homebrew/test/livecheck/strategy/json_spec.rb b/Library/Homebrew/test/livecheck/strategy/json_spec.rb index dc18a8f074..5949d83e19 100644 --- a/Library/Homebrew/test/livecheck/strategy/json_spec.rb +++ b/Library/Homebrew/test/livecheck/strategy/json_spec.rb @@ -107,11 +107,6 @@ RSpec.describe Homebrew::Livecheck::Strategy::Json do expect(json.versions_from_content(content_simple, regex) { next }).to eq([]) end - it "errors if a block uses two arguments but a regex is not given" do - expect { json.versions_from_content(content_simple) { |json, regex| json["version"][regex, 1] } } - .to raise_error("Two arguments found in `strategy` block but no regex provided.") - end - it "errors on an invalid return type from a block" do expect { json.versions_from_content(content_simple, regex) { 123 } } .to raise_error(TypeError, Homebrew::Livecheck::Strategy::INVALID_BLOCK_RETURN_VALUE_MSG) diff --git a/Library/Homebrew/test/livecheck/strategy/pypi_spec.rb b/Library/Homebrew/test/livecheck/strategy/pypi_spec.rb index be93644477..d1f44376bb 100644 --- a/Library/Homebrew/test/livecheck/strategy/pypi_spec.rb +++ b/Library/Homebrew/test/livecheck/strategy/pypi_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Homebrew::Livecheck::Strategy::Pypi do let(:pypi_url) { "https://files.pythonhosted.org/packages/ab/cd/efg/example-package-1.2.3.tar.gz" } let(:non_pypi_url) { "https://brew.sh/test" } - let(:regex) { /^v?(\d+(?:\.\d+)+)$/i } + let(:regex) { /^v?(\d+(?:\.\d+)+)/i } let(:generated) do { @@ -17,25 +17,26 @@ RSpec.describe Homebrew::Livecheck::Strategy::Pypi do end # This is a limited subset of a PyPI JSON API response object, for the sake - # of testing. + # of testing. Typical versions use a `1.2.3` format but this adds a suffix, + # so we can test regex matching. let(:content) do <<~JSON { "info": { - "version": "1.2.3" + "version": "1.2.3-456" } } JSON end - let(:matches) { ["1.2.3"] } + let(:matches) { ["1.2.3-456"] } let(:find_versions_return_hash) do { matches: { - "1.2.3" => Version.new("1.2.3"), + "1.2.3-456" => Version.new("1.2.3-456"), }, - regex: nil, + regex:, url: generated[:url], } end @@ -76,10 +77,17 @@ RSpec.describe Homebrew::Livecheck::Strategy::Pypi do { cached:, cached_default: cached.merge({ matches: {} }), + cached_regex: cached.merge({ + matches: { "1.2.3" => Version.new("1.2.3") }, + regex:, + }), } end it "finds versions in provided content" do + expect(pypi.find_versions(url: pypi_url, regex:, provided_content: content)) + .to eq(match_data[:cached_regex]) + expect(pypi.find_versions(url: pypi_url, provided_content: content)) .to eq(match_data[:cached]) end @@ -92,7 +100,7 @@ RSpec.describe Homebrew::Livecheck::Strategy::Pypi do next if match.blank? match[1] - end).to eq(match_data[:cached].merge({ regex: })) + end).to eq(match_data[:cached_regex]) expect(pypi.find_versions(url: pypi_url, provided_content: content) do |json| json.dig("info", "version").presence @@ -100,10 +108,14 @@ RSpec.describe Homebrew::Livecheck::Strategy::Pypi do end it "returns default match_data when block doesn't return version information" do + no_match_regex = /will_not_match/i + expect(pypi.find_versions(url: pypi_url, provided_content: '{"info":{"version":""}}')) .to eq(match_data[:cached_default]) expect(pypi.find_versions(url: pypi_url, provided_content: '{"other":true}')) .to eq(match_data[:cached_default]) + expect(pypi.find_versions(url: pypi_url, regex: no_match_regex, provided_content: content)) + .to eq(match_data[:cached_default].merge({ regex: no_match_regex })) end it "returns default match_data when url is blank" do diff --git a/Library/Homebrew/utils/github/api.rb b/Library/Homebrew/utils/github/api.rb index bcc2bcbe3b..26b4e3d0aa 100644 --- a/Library/Homebrew/utils/github/api.rb +++ b/Library/Homebrew/utils/github/api.rb @@ -135,15 +135,6 @@ module GitHub JSON::ParserError, ].freeze - sig { returns(T.nilable(String)) } - private_class_method def self.uid_home - require "etc" - Etc.getpwuid(Process.uid)&.dir - rescue ArgumentError - # Cover for misconfigured NSS setups - nil - end - # Gets the token from the GitHub CLI for github.com. sig { returns(T.nilable(String)) } def self.github_cli_token @@ -152,7 +143,7 @@ module GitHub # Avoid `Formula["gh"].opt_bin` so this method works even with `HOMEBREW_DISABLE_LOAD_FORMULA`. env = { "PATH" => PATH.new(HOMEBREW_PREFIX/"opt/gh/bin", ENV.fetch("PATH")), - "HOME" => uid_home, + "HOME" => Utils::UID.uid_home, }.compact gh_out, _, result = system_command "gh", args: ["auth", "token", "--hostname", "github.com"], @@ -173,7 +164,7 @@ module GitHub git_credential_out, _, result = system_command "git", args: ["credential-osxkeychain", "get"], input: ["protocol=https\n", "host=github.com\n"], - env: { "HOME" => uid_home }.compact, + env: { "HOME" => Utils::UID.uid_home }.compact, print_stderr: false return unless result.success? diff --git a/Library/Homebrew/utils/uid.rb b/Library/Homebrew/utils/uid.rb index 4c102a2b8a..129c569bb8 100644 --- a/Library/Homebrew/utils/uid.rb +++ b/Library/Homebrew/utils/uid.rb @@ -15,5 +15,14 @@ module Utils Process::Sys.seteuid(original_euid) end end + + sig { returns(T.nilable(String)) } + def self.uid_home + require "etc" + Etc.getpwuid(Process.uid)&.dir + rescue ArgumentError + # Cover for misconfigured NSS setups + nil + end end end