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 common/lib/dependabot/git_commit_checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ def head_commit_for_current_branch
raise Dependabot::GitDependencyReferenceNotFound, dependency.name
end

def head_commit_for_local_branch(name)
local_repo_git_metadata_fetcher.head_commit_for_ref(name)
end

def local_tags_for_latest_version_commit_sha
tags = allowed_version_tags
max_tag = max_version_tag(tags)
Expand Down
23 changes: 23 additions & 0 deletions common/spec/dependabot/git_commit_checker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,29 @@
end
end

describe "#head_commit_for_local_branch" do
let(:tip_of_example) { "303b8a83c87d5c6d749926cf02620465a5dcd0f2" }

subject { checker.head_commit_for_local_branch("example") }

let(:repo_url) { "https://github.com/gocardless/business.git" }
let(:service_pack_url) { repo_url + "/info/refs?service=git-upload-pack" }
before do
stub_request(:get, service_pack_url).
to_return(
status: 200,
body: fixture("git", "upload_packs", upload_pack_fixture),
headers: {
"content-type" => "application/x-git-upload-pack-advertisement"
}
)
end

let(:upload_pack_fixture) { "monolog" }

it { is_expected.to eq(tip_of_example) }
end

describe "#local_tag_for_latest_version" do
subject { checker.local_tag_for_latest_version }
let(:repo_url) { "https://github.com/gocardless/business.git" }
Expand Down
3 changes: 3 additions & 0 deletions github_actions/lib/dependabot/github_actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
require "dependabot/dependency"
Dependabot::Dependency.
register_production_check("github_actions", ->(_) { true })

require "dependabot/utils"
Dependabot::Utils.register_always_clone("github_actions")
56 changes: 46 additions & 10 deletions github_actions/lib/dependabot/github_actions/update_checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def fetch_latest_version
end

def fetch_latest_version_for_git_dependency
return git_commit_checker.head_commit_for_current_branch unless git_commit_checker.pinned?
return current_commit unless git_commit_checker.pinned?

# If the dependency is pinned to a tag that looks like a version then
# we want to update that tag.
Expand All @@ -70,18 +70,27 @@ def fetch_latest_version_for_git_dependency
return latest_version
end

# If the dependency is pinned to a commit SHA, we return a *version* so
# that we get nice behaviour in PullRequestCreator::MessageBuilder
if git_commit_checker.pinned_ref_looks_like_commit_sha?
latest_tag = git_commit_checker.local_tag_for_latest_version
return latest_tag.fetch(:version)
if git_commit_checker.pinned_ref_looks_like_commit_sha? && latest_version_tag
latest_version = latest_version_tag.fetch(:version)
return latest_commit_for_pinned_ref unless git_commit_checker.branch_or_ref_in_release?(latest_version)

return latest_version
end

# If the dependency is pinned to a tag that doesn't look like a
# version or a commit SHA then there's nothing we can do.
nil
end

def latest_commit_for_pinned_ref
@latest_commit_for_pinned_ref ||=
SharedHelpers.in_a_temporary_repo_directory("/", repo_contents_path) do
ref_branch = find_container_branch(current_commit)

git_commit_checker.head_commit_for_local_branch(ref_branch)
end
end

def latest_version_tag
@latest_version_tag ||= begin
return git_commit_checker.local_tag_for_latest_version if dependency.version.nil?
Expand Down Expand Up @@ -119,18 +128,28 @@ def updated_source
return dependency_source_details.merge(ref: new_tag.fetch(:tag))
end

latest_tag = git_commit_checker.local_tag_for_latest_version

# Update the pinned git commit if one is available
if git_commit_checker.pinned_ref_looks_like_commit_sha? &&
latest_tag.fetch(:commit_sha) != current_commit
return dependency_source_details.merge(ref: latest_tag.fetch(:commit_sha))
(new_commit_sha = latest_commit_sha) &&
new_commit_sha != current_commit
return dependency_source_details.merge(ref: new_commit_sha)
end

# Otherwise return the original source
dependency_source_details
end

def latest_commit_sha
new_tag = latest_version_tag
return unless new_tag

if git_commit_checker.branch_or_ref_in_release?(new_tag.fetch(:version))
new_tag.fetch(:commit_sha)
else
latest_commit_for_pinned_ref
end
end

def dependency_source_details
sources =
dependency.requirements.map { |r| r.fetch(:source) }.uniq.compact
Expand Down Expand Up @@ -180,6 +199,23 @@ def shortened_semver_version_eq?(base_version, other_version)

shortened_semver_eq?(base, other) || shortened_semver_eq?(other, base)
end

def find_container_branch(sha)
SharedHelpers.run_shell_command("git fetch #{current_commit}")

branches_including_ref = SharedHelpers.run_shell_command("git branch --contains #{sha}").split("\n")

current_branch = branches_including_ref.find { |line| line.start_with?("* ") }

if current_branch
current_branch.delete_prefix("* ")
elsif branches_including_ref.size > 1
# If there are multiple non default branches including the pinned SHA, then it's unclear how we should proceed
raise "Multiple ambiguous branches (#{branches_including_ref.join(', ')}) include #{current_commit}!"
else
branches_including_ref.first
end
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@
"https://github.com/actions/setup-node.git/info/refs" \
"?service=git-upload-pack"
end
let(:git_commit_checker) do
Dependabot::GitCommitChecker.new(
dependency: dependency,
credentials: github_credentials,
ignored_versions: ignored_versions,
raise_on_ignored: raise_on_ignored
)
end
before do
stub_request(:get, service_pack_url).
to_return(
Expand Down Expand Up @@ -128,7 +136,7 @@
it { is_expected.to be_falsey }
end

context "that is a git commit SHA" do
context "that is a git commit SHA pointing to the tip of a branch" do
let(:upload_pack_fixture) { "setup-node" }
let(:reference) { "1c24df3" }

Expand All @@ -141,6 +149,9 @@
body: comparison_response,
headers: { "Content-Type" => "application/json" }
)

checker.instance_variable_set(:@git_commit_checker, git_commit_checker)
allow(git_commit_checker).to receive(:branch_or_ref_in_release?).and_return(true)
end

context "when the specified commit has diverged from the latest release" do
Expand Down Expand Up @@ -202,9 +213,11 @@
describe "#latest_version" do
subject { checker.latest_version }

let(:tip_of_master) { "d963e800e3592dd31d6c76252092562d0bc7a3ba" }

context "given a dependency with a branch reference" do
let(:reference) { "master" }
it { is_expected.to eq("d963e800e3592dd31d6c76252092562d0bc7a3ba") }
it { is_expected.to eq(tip_of_master) }
end

context "given a dependency with a tag reference" do
Expand Down Expand Up @@ -276,9 +289,8 @@
}
end

allow_any_instance_of(Dependabot::GitCommitChecker).
to receive(:local_tags_for_latest_version_commit_sha).
and_return(version_tags)
checker.instance_variable_set(:@git_commit_checker, git_commit_checker)
allow(git_commit_checker).to receive(:local_tags_for_latest_version_commit_sha).and_return(version_tags)
end

context "using the major version" do
Expand Down Expand Up @@ -325,7 +337,7 @@
it { is_expected.to eq(Dependabot::GithubActions::Version.new("3")) }
end

context "given a git commit SHA" do
context "given a git commit SHA pointing to the tip of a branch" do
let(:reference) { "1c24df3" }

let(:repo_url) { "https://api.github.com/repos/actions/setup-node" }
Expand All @@ -337,6 +349,9 @@
body: comparison_response,
headers: { "Content-Type" => "application/json" }
)

checker.instance_variable_set(:@git_commit_checker, git_commit_checker)
allow(git_commit_checker).to receive(:branch_or_ref_in_release?).and_return(true)
end

context "when the specified commit has diverged from the latest release" do
Expand Down Expand Up @@ -370,6 +385,70 @@
expect(subject).to eq(Gem::Version.new("2.2.0"))
end
end

context "that is a git commit SHA not pointing to the tip of a branch" do
let(:reference) { "1c24df3" }
let(:exit_status) { double(success?: true) }

before do
checker.instance_variable_set(:@git_commit_checker, git_commit_checker)
allow(git_commit_checker).to receive(:branch_or_ref_in_release?).and_return(false)
allow(git_commit_checker).to receive(:head_commit_for_current_branch).and_return(reference)

allow(Open3).to receive(:capture2e).with(anything, "git fetch #{reference}").and_return(["", exit_status])
end

context "and it's in the current (default) branch" do
before do
allow(Open3).to receive(:capture2e).
with(anything, "git branch --contains #{reference}").
and_return(["* master\n", exit_status])
end

it "can update to the latest version" do
expect(subject).to eq(tip_of_master)
end
end

context "and it's on a different branch" do
let(:tip_of_releases_v1) { "5273d0df9c603edc4284ac8402cf650b4f1f6686" }

before do
allow(Open3).to receive(:capture2e).
with(anything, "git branch --contains #{reference}").
and_return(["releases/v1\n", exit_status])
end

it "can update to the latest version" do
expect(subject).to eq(tip_of_releases_v1)
end
end

context "and multiple branches include it, the current (default) branch among them" do
before do
allow(Open3).to receive(:capture2e).
with(anything, "git branch --contains #{reference}").
and_return(["* master\nv1.1\n", exit_status])
end

it "can update to the latest version" do
expect(subject).to eq(tip_of_master)
end
end

context "and multiple branches include it, the current (default) branch NOT among them" do
before do
allow(Open3).to receive(:capture2e).
with(anything, "git branch --contains #{reference}").
and_return(["3.3-stable\nproduction\n", exit_status])
end

it "raises an error" do
expect { subject }.
to raise_error("Multiple ambiguous branches (3.3-stable, production) include #{reference}!")
end
end
end
end

describe "#latest_resolvable_version" do
Expand All @@ -387,7 +466,7 @@
it { is_expected.to eq(dependency.requirements) }
end

context "given a git commit SHA" do
context "given a git commit SHA pointing to the tip of a branch" do
let(:reference) { "1c24df3" }

let(:repo_url) { "https://api.github.com/repos/actions/setup-node" }
Expand All @@ -399,6 +478,9 @@
body: comparison_response,
headers: { "Content-Type" => "application/json" }
)

checker.instance_variable_set(:@git_commit_checker, git_commit_checker)
allow(git_commit_checker).to receive(:branch_or_ref_in_release?).and_return(true)
end

context "when the specified reference is not in the latest release" do
Expand Down