From dac4b6fd56ef36818c5f21f7b26af5ac59a99c6c Mon Sep 17 00:00:00 2001 From: botantony Date: Thu, 20 Mar 2025 20:13:04 +0100 Subject: [PATCH] shebangs: fix broken shebangs like `#!python` Signed-off-by: botantony --- Library/Homebrew/language/node.rb | 2 +- Library/Homebrew/language/perl.rb | 2 +- Library/Homebrew/language/python.rb | 2 +- .../test/language/node/shebang_spec.rb | 22 ++++++++++++++- .../test/language/perl/shebang_spec.rb | 28 ++++++++++++++++++- .../test/language/python/shebang_spec.rb | 24 +++++++++++++++- 6 files changed, 74 insertions(+), 6 deletions(-) diff --git a/Library/Homebrew/language/node.rb b/Library/Homebrew/language/node.rb index de08fffd4a..ccd1467ada 100644 --- a/Library/Homebrew/language/node.rb +++ b/Library/Homebrew/language/node.rb @@ -96,7 +96,7 @@ module Language module_function # A regex to match potential shebang permutations. - NODE_SHEBANG_REGEX = %r{^#! ?/usr/bin/(?:env )?node( |$)} + NODE_SHEBANG_REGEX = %r{^#! ?(?:/usr/bin/(?:env )?)?node( |$)} # The length of the longest shebang matching `SHEBANG_REGEX`. NODE_SHEBANG_MAX_LENGTH = T.let("#! /usr/bin/env node ".length, Integer) diff --git a/Library/Homebrew/language/perl.rb b/Library/Homebrew/language/perl.rb index 4c08aef894..b163b1f85a 100644 --- a/Library/Homebrew/language/perl.rb +++ b/Library/Homebrew/language/perl.rb @@ -15,7 +15,7 @@ module Language module_function # A regex to match potential shebang permutations. - PERL_SHEBANG_REGEX = %r{^#! ?/usr/bin/(?:env )?perl( |$)} + PERL_SHEBANG_REGEX = %r{^#! ?(?:/usr/bin/(?:env )?)?perl( |$)} # The length of the longest shebang matching `SHEBANG_REGEX`. PERL_SHEBANG_MAX_LENGTH = T.let("#! /usr/bin/env perl ".length, Integer) diff --git a/Library/Homebrew/language/python.rb b/Library/Homebrew/language/python.rb index 6428bce228..e3c279e6f5 100644 --- a/Library/Homebrew/language/python.rb +++ b/Library/Homebrew/language/python.rb @@ -112,7 +112,7 @@ module Language module_function # A regex to match potential shebang permutations. - PYTHON_SHEBANG_REGEX = %r{^#! ?/usr/bin/(?:env )?python(?:[23](?:\.\d{1,2})?)?( |$)} + PYTHON_SHEBANG_REGEX = %r{^#! ?(?:/usr/bin/(?:env )?)?python(?:[23](?:\.\d{1,2})?)?( |$)} # The length of the longest shebang matching `SHEBANG_REGEX`. PYTHON_SHEBANG_MAX_LENGTH = T.let("#! /usr/bin/env pythonx.yyy ".length, Integer) diff --git a/Library/Homebrew/test/language/node/shebang_spec.rb b/Library/Homebrew/test/language/node/shebang_spec.rb index 6744434824..eefb1fe0b8 100644 --- a/Library/Homebrew/test/language/node/shebang_spec.rb +++ b/Library/Homebrew/test/language/node/shebang_spec.rb @@ -5,6 +5,7 @@ require "utils/shebang" RSpec.describe Language::Node::Shebang do let(:file) { Tempfile.new("node-shebang") } + let(:broken_file) { Tempfile.new("node-shebang") } let(:f) do f = {} @@ -40,9 +41,16 @@ RSpec.describe Language::Node::Shebang do c EOS file.flush + broken_file.write <<~EOS + #!node + a + b + c + EOS + broken_file.flush end - after { file.unlink } + after { [file, broken_file].each(&:unlink) } describe "#detected_node_shebang" do it "can be used to replace Node shebangs" do @@ -57,6 +65,18 @@ RSpec.describe Language::Node::Shebang do EOS end + it "can fix broken shebang like `#!node`" do + allow(Formulary).to receive(:factory).with(f[:node18].name).and_return(f[:node18]) + Utils::Shebang.rewrite_shebang described_class.detected_node_shebang(f[:versioned_node_dep]), broken_file.path + + expect(File.read(broken_file)).to eq <<~EOS + #!#{HOMEBREW_PREFIX/"opt/node@18/bin/node"} + a + b + c + EOS + end + it "errors if formula doesn't depend on node" do expect { Utils::Shebang.rewrite_shebang described_class.detected_node_shebang(f[:no_deps]), file.path } .to raise_error(ShebangDetectionError, "Cannot detect Node shebang: formula does not depend on Node.") diff --git a/Library/Homebrew/test/language/perl/shebang_spec.rb b/Library/Homebrew/test/language/perl/shebang_spec.rb index 6677ae74f6..cab3d2b2d0 100644 --- a/Library/Homebrew/test/language/perl/shebang_spec.rb +++ b/Library/Homebrew/test/language/perl/shebang_spec.rb @@ -5,6 +5,7 @@ require "utils/shebang" RSpec.describe Language::Perl::Shebang do let(:file) { Tempfile.new("perl-shebang") } + let(:broken_file) { Tempfile.new("perl-shebang") } let(:f) do f = {} @@ -39,9 +40,16 @@ RSpec.describe Language::Perl::Shebang do c EOS file.flush + broken_file.write <<~EOS + #!perl + a + b + c + EOS + broken_file.flush end - after { file.unlink } + after { [file, broken_file].each(&:unlink) } describe "#detected_perl_shebang" do it "can be used to replace Perl shebangs when depends_on \"perl\" is used" do @@ -74,6 +82,24 @@ RSpec.describe Language::Perl::Shebang do EOS end + it "can fix broken shebang like `#!perl`" do + allow(Formulary).to receive(:factory).with(f[:perl].name).and_return(f[:perl]) + Utils::Shebang.rewrite_shebang described_class.detected_perl_shebang(f[:uses_from_macos]), broken_file.path + + expected_shebang = if OS.mac? + "/usr/bin/perl#{MacOS.preferred_perl_version}" + else + HOMEBREW_PREFIX/"opt/perl/bin/perl" + end + + expect(File.read(broken_file)).to eq <<~EOS + #!#{expected_shebang} + a + b + c + EOS + end + it "errors if formula doesn't depend on perl" do expect { Utils::Shebang.rewrite_shebang described_class.detected_perl_shebang(f[:no_deps]), file.path } .to raise_error(ShebangDetectionError, "Cannot detect Perl shebang: formula does not depend on Perl.") diff --git a/Library/Homebrew/test/language/python/shebang_spec.rb b/Library/Homebrew/test/language/python/shebang_spec.rb index c823702cef..7ba87c06e0 100644 --- a/Library/Homebrew/test/language/python/shebang_spec.rb +++ b/Library/Homebrew/test/language/python/shebang_spec.rb @@ -5,6 +5,7 @@ require "utils/shebang" RSpec.describe Language::Python::Shebang do let(:file) { Tempfile.new("python-shebang") } + let(:broken_file) { Tempfile.new("python-shebang") } let(:f) do f = {} @@ -40,9 +41,16 @@ RSpec.describe Language::Python::Shebang do c EOS file.flush + broken_file.write <<~EOS + #!python + a + b + c + EOS + broken_file.flush end - after { file.unlink } + after { [file, broken_file].each(&:unlink) } describe "#detected_python_shebang" do it "can be used to replace Python shebangs" do @@ -72,6 +80,20 @@ RSpec.describe Language::Python::Shebang do EOS end + it "can fix broken shebang line `#!python`" do + Utils::Shebang.rewrite_shebang( + described_class.detected_python_shebang(f[:versioned_python_dep], + use_python_from_path: true), broken_file.path + ) + + expect(File.read(broken_file)).to eq <<~EOS + #!/usr/bin/env python3 + a + b + c + EOS + end + it "errors if formula doesn't depend on python" do expect do Utils::Shebang.rewrite_shebang(