Merge pull request #19563 from botantony/python-shebang

shebangs: fix broken shebangs like `#!python`
This commit is contained in:
Mike McQuaid 2025-03-21 09:22:54 +00:00 committed by GitHub
commit 6f04ee4cff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 74 additions and 6 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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.")

View File

@ -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.")

View File

@ -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(