Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Library/Homebrew/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
require "api_hashable"
require "utils/output"
require "pypi_packages"
require "time"
Comment thread
MikeMcQuaid marked this conversation as resolved.

# A formula provides instructions and metadata for Homebrew to install a piece
# of software. Every Homebrew formula is a {Formula}.
Expand Down Expand Up @@ -2132,6 +2133,9 @@ def std_npm_args(prefix: libexec, ignore_scripts: true)
}
def std_pip_args(prefix: self.prefix, build_isolation: false)
args = ["--verbose", "--no-deps", "--no-binary=:all:", "--ignore-installed", "--no-compile"]
# Delay packages published in the last day so builds are less likely to
# install a freshly compromised PyPI release.
args << "--uploaded-prior-to=#{(time - (24 * 60 * 60)).iso8601(0)}"
Comment thread
MikeMcQuaid marked this conversation as resolved.
Comment on lines +2136 to +2138
Copy link
Copy Markdown
Member

@cho-m cho-m Apr 4, 2026

Choose a reason for hiding this comment

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

This will only impact build-time packages when run in build-isolation. And nothing when no-build-isolation as we use --no-deps.

To cooldown a Python formula's dependencies, need to update the resource resolver:

def self.pip_report(packages, python_name: "python", print_stderr: false)
return [] if packages.blank?
command = [
Formula[python_name].opt_libexec/"bin/python", "-m", "pip", "install", "-q", "--disable-pip-version-check",
"--dry-run", "--ignore-installed", "--report=/dev/stdout", *packages.map(&:to_s)


EDIT: Though this may want to wait for bump support, i.e. #21888, so that the cooldown is applied to both main package and dependencies

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, let's add there too.

Though this may want to wait for bump support, i.e. #21888, so that the cooldown is applied to both main package and dependencies

Let's not. Let's not perfect be the enemy of good here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@cho-m thanks: #21920

args << "--prefix=#{prefix}" if prefix
args << "--no-build-isolation" unless build_isolation
args
Expand Down
6 changes: 6 additions & 0 deletions Library/Homebrew/language/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,13 @@ def self.std_npm_install_args(libexec, ignore_scripts: true)
(libexec/"lib").mkpath

# npm install args for global style module format installed into libexec
# Delay packages published in the last day so builds are less likely to
# install a freshly compromised npm release or dependency.
args = %W[
--loglevel=silly
--global
--build-from-source
--min-release-age=1
--#{npm_cache_config}
--prefix=#{libexec}
#{Dir.pwd}/#{pack}
Expand All @@ -85,9 +88,12 @@ def self.std_npm_install_args(libexec, ignore_scripts: true)
def self.local_npm_install_args(ignore_scripts: true)
setup_npm_environment
# npm install args for local style module format
# Delay packages published in the last day so builds are less likely to
# install a freshly compromised npm release or dependency.
args = %W[
--loglevel=silly
--build-from-source
--min-release-age=1
--#{npm_cache_config}
]

Expand Down
14 changes: 14 additions & 0 deletions Library/Homebrew/test/formula_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2502,5 +2502,19 @@ def install
expect(described_class.all(eval_all: true)).to eq([])
end
end

describe "#std_pip_args" do
let(:f) do
formula do
url "foo-1.0"
end
end

it "filters packages uploaded within the last day" do
allow(f).to receive(:time).and_return(Time.utc(2026, 4, 4, 12, 0, 0))

expect(f.std_pip_args).to include("--uploaded-prior-to=2026-04-03T12:00:00Z")
end
end
end
# rubocop:enable Lint/DuplicateMethods
25 changes: 20 additions & 5 deletions Library/Homebrew/test/language/node_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
let(:npm_pack_cmd) { ["npm", "pack", "--ignore-scripts"] }

describe "#setup_npm_environment" do
before do
described_class.instance_variable_set(:@env_set, false)
end

it "calls prepend_path when node formula exists only during the first call" do
node = formula "node" do
url "node-test-v1.0"
Expand All @@ -15,7 +19,6 @@
without_partial_double_verification do
expect(ENV).to receive(:prepend_path)
end
described_class.instance_variable_set(:@env_set, false)
described_class.setup_npm_environment

expect(described_class.instance_variable_get(:@env_set)).to be(true)
Expand All @@ -26,6 +29,7 @@
end

it "does not call prepend_path when node formula does not exist" do
allow(Formula).to receive(:[]).with("node").and_raise(FormulaUnavailableError.new("node"))
without_partial_double_verification do
expect(ENV).not_to receive(:prepend_path)
end
Expand All @@ -50,6 +54,10 @@
describe "#std_npm_install_args" do
let(:npm_install_arg) { Pathname("libexec") }

before do
allow(described_class).to receive(:setup_npm_environment)
end

it "raises error with non zero exitstatus" do
allow(Utils).to receive(:popen_read).with(*npm_pack_cmd).and_return(`false`)
expect { described_class.std_npm_install_args(npm_install_arg) }.to raise_error("npm failed to pack #{Dir.pwd}")
Expand All @@ -63,12 +71,19 @@
it "does not raise error with a zero exitstatus" do
allow(Utils).to receive(:popen_read).with(*npm_pack_cmd).and_return(`echo pack.tgz`)
resp = described_class.std_npm_install_args(npm_install_arg)
expect(resp).to include("--prefix=#{npm_install_arg}", "#{Dir.pwd}/pack.tgz")
expect(resp).to include("--min-release-age=1", "--prefix=#{npm_install_arg}", "#{Dir.pwd}/pack.tgz")
end
end

specify "#local_npm_install_args" do
resp = described_class.local_npm_install_args
expect(resp).to include("--loglevel=silly", "--build-from-source", "--cache=#{HOMEBREW_CACHE}/npm_cache")
describe "#local_npm_install_args" do
before do
allow(described_class).to receive(:setup_npm_environment)
end

it "includes the default npm install arguments" do
resp = described_class.local_npm_install_args
expect(resp).to include("--loglevel=silly", "--build-from-source", "--cache=#{HOMEBREW_CACHE}/npm_cache",
"--min-release-age=1")
end
end
end
Loading