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
22 changes: 22 additions & 0 deletions Library/Homebrew/formula_auditor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,28 @@ def audit_deps
EOS
end

sig { void }
def audit_node_modules
return unless @core_tap

node_modules = formula.libexec/"lib/node_modules"
return unless node_modules.directory?

incompatible_license_packages = %w[
@anthropic-ai/claude-agent-sdk
]

incompatible_license_packages.each do |package|
# Search for package in all nested node_modules. Also including dot match for .pnpm hoisted packages
next if node_modules.glob("{**/node_modules/,}#{package}/", File::FNM_DOTMATCH).empty?

problem <<~EOS
Formula #{formula.name} uses #{package} which has an incompatible license.
All installed npm dependencies must satisfy #{Formatter.url("https://docs.brew.sh/License-Guidelines")}
EOS
end
end

def audit_conflicts
tap = formula.tap
formula.conflicts.each do |conflict|
Expand Down
54 changes: 54 additions & 0 deletions Library/Homebrew/test/formula_auditor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,60 @@ class Cask < Formula
end
end

describe "#audit_node_modules" do
let(:fa) do
formula_auditor("foo", <<~RUBY, core_tap:)
class Foo < Formula
url "https://brew.sh/foo-1.0.tgz"
homepage "https://brew.sh"
end
RUBY
end
let(:node_modules) { fa.formula.libexec/"lib/node_modules" }
let(:reject_package) { "@anthropic-ai/claude-agent-sdk" }
let(:audit_message) { "uses #{reject_package} which has an incompatible license" }

context "when core tap" do
let(:core_tap) { true }

it "detects unacceptable npm packages" do
(node_modules/reject_package).mkpath
fa.audit_node_modules
expect(fa.problems.first[:message]).to match audit_message
end

it "detects unacceptable npm packages in nested node_modules" do
(node_modules/"foo/node_modules/bar/node_modules"/reject_package).mkpath
fa.audit_node_modules
expect(fa.problems.first[:message]).to match audit_message
end

it "detects unacceptable npm packages in .pnpm hoisted directory" do
(node_modules/".pnpm/node_modules"/reject_package).mkpath
fa.audit_node_modules
expect(fa.problems.first[:message]).to match audit_message
end

it "skips audit when no node_modules" do
fa.formula.libexec.mkpath
fa.audit_node_modules
expect(fa.problems).to be_empty
end
end

context "when non-core tap" do
let(:core_tap) { false }

it "skips audit" do
(node_modules/reject_package).mkpath
(node_modules/"foo/node_modules/bar/node_modules"/reject_package).mkpath
(node_modules/".pnpm/node_modules"/reject_package).mkpath
fa.audit_node_modules
expect(fa.problems).to be_empty
end
end
end

describe "#audit_file" do
specify "no issue" do
fa = formula_auditor "foo", <<~RUBY
Expand Down
Loading