dependabot-core 0.89.5 → 0.90.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,169 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "excon"
4
- require "toml-rb"
5
-
6
- require "dependabot/source"
7
- require "dependabot/update_checkers/go/dep"
8
- require "dependabot/git_commit_checker"
9
- require "dependabot/utils/go/path_converter"
10
-
11
- module Dependabot
12
- module UpdateCheckers
13
- module Go
14
- class Dep
15
- class LatestVersionFinder
16
- def initialize(dependency:, dependency_files:, credentials:,
17
- ignored_versions:)
18
- @dependency = dependency
19
- @dependency_files = dependency_files
20
- @credentials = credentials
21
- @ignored_versions = ignored_versions
22
- end
23
-
24
- def latest_version
25
- @latest_version ||=
26
- if git_dependency? then latest_version_for_git_dependency
27
- else latest_release_tag_version
28
- end
29
- end
30
-
31
- private
32
-
33
- attr_reader :dependency, :dependency_files, :credentials,
34
- :ignored_versions
35
-
36
- def latest_release_tag_version
37
- if @latest_release_tag_lookup_attempted
38
- return @latest_release_tag_version
39
- end
40
-
41
- @latest_release_tag_lookup_attempted = true
42
-
43
- latest_release_str = fetch_latest_release_tag&.sub(/^v?/, "")
44
- return unless latest_release_str
45
- return unless version_class.correct?(latest_release_str)
46
-
47
- @latest_release_tag_version =
48
- version_class.new(latest_release_str)
49
- end
50
-
51
- def fetch_latest_release_tag
52
- # If this is a git dependency then getting the latest tag is trivial
53
- if git_dependency?
54
- return git_commit_checker.
55
- local_tag_for_latest_version&.fetch(:tag)
56
- end
57
-
58
- # If not, we need to find the URL for the source code.
59
- path = dependency.requirements.
60
- map { |r| r.dig(:source, :source) }.compact.first
61
- path ||= dependency.name
62
-
63
- source_url = git_source(path)
64
- return unless source_url
65
-
66
- # Given a source, we want to find the latest tag. Piggy-back off the
67
- # logic in GitCommitChecker to do so.
68
- git_dep = Dependency.new(
69
- name: dependency.name,
70
- version: dependency.version,
71
- requirements: [{
72
- file: "Gopkg.toml",
73
- groups: [],
74
- requirement: nil,
75
- source: { type: "git", url: source_url }
76
- }],
77
- package_manager: dependency.package_manager
78
- )
79
-
80
- GitCommitChecker.
81
- new(dependency: git_dep, credentials: credentials).
82
- local_tag_for_latest_version&.fetch(:tag)
83
- end
84
-
85
- def latest_version_for_git_dependency
86
- latest_release = latest_release_tag_version
87
-
88
- # If there's been a release that includes the current pinned ref or
89
- # that the current branch is behind, we switch to that release.
90
- return latest_release if branch_or_ref_in_release?(latest_release)
91
-
92
- # Otherwise, if the gem isn't pinned, the latest version is just the
93
- # latest commit for the specified branch.
94
- unless git_commit_checker.pinned?
95
- return git_commit_checker.head_commit_for_current_branch
96
- end
97
-
98
- # If the dependency is pinned to a tag that looks like a version
99
- # then we want to update that tag.
100
- if git_commit_checker.pinned_ref_looks_like_version?
101
- latest_tag = git_commit_checker.local_tag_for_latest_version
102
- return version_from_tag(latest_tag)
103
- end
104
-
105
- # If the dependency is pinned to a tag that doesn't look like a
106
- # version then there's nothing we can do.
107
- nil
108
- end
109
-
110
- def git_source(path)
111
- Dependabot::Utils::Go::PathConverter.git_url_for_path(path)
112
- end
113
-
114
- def version_from_tag(tag)
115
- # To compare with the current version we either use the commit SHA
116
- # (if that's what the parser picked up) of the tag name.
117
- if dependency.version&.match?(/^[0-9a-f]{40}$/)
118
- return tag&.fetch(:commit_sha)
119
- end
120
-
121
- tag&.fetch(:tag)
122
- end
123
-
124
- def branch_or_ref_in_release?(release)
125
- return false unless release
126
-
127
- git_commit_checker.branch_or_ref_in_release?(release)
128
- end
129
-
130
- def git_dependency?
131
- git_commit_checker.git_dependency?
132
- end
133
-
134
- def git_commit_checker
135
- @git_commit_checker ||=
136
- GitCommitChecker.new(
137
- dependency: dependency,
138
- credentials: credentials,
139
- ignored_versions: ignored_versions
140
- )
141
- end
142
-
143
- def parsed_file(file)
144
- @parsed_file ||= {}
145
- @parsed_file[file.name] ||= TomlRB.parse(file.content)
146
- end
147
-
148
- def version_class
149
- Utils.version_class_for_package_manager(dependency.package_manager)
150
- end
151
-
152
- def manifest
153
- @manifest ||= dependency_files.find { |f| f.name == "Gopkg.toml" }
154
- raise "No Gopkg.lock!" unless @manifest
155
-
156
- @manifest
157
- end
158
-
159
- def lockfile
160
- @lockfile = dependency_files.find { |f| f.name == "Gopkg.lock" }
161
- raise "No Gopkg.lock!" unless @lockfile
162
-
163
- @lockfile
164
- end
165
- end
166
- end
167
- end
168
- end
169
- end
@@ -1,223 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dependabot/update_checkers/go/dep"
4
- require "dependabot/utils/go/requirement"
5
- require "dependabot/utils/go/version"
6
-
7
- module Dependabot
8
- module UpdateCheckers
9
- module Go
10
- class Dep
11
- class RequirementsUpdater
12
- class UnfixableRequirement < StandardError; end
13
-
14
- VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-*]+)*/.freeze
15
- ALLOWED_UPDATE_STRATEGIES = %i(widen_ranges bump_versions).freeze
16
-
17
- def initialize(requirements:, updated_source:, update_strategy:,
18
- latest_version:, latest_resolvable_version:)
19
- @requirements = requirements
20
- @updated_source = updated_source
21
- @update_strategy = update_strategy
22
-
23
- check_update_strategy
24
-
25
- if latest_version && version_class.correct?(latest_version)
26
- @latest_version = version_class.new(latest_version)
27
- end
28
-
29
- return unless latest_resolvable_version
30
- return unless version_class.correct?(latest_resolvable_version)
31
-
32
- @latest_resolvable_version =
33
- version_class.new(latest_resolvable_version)
34
- end
35
-
36
- def updated_requirements
37
- requirements.map do |req|
38
- req = req.merge(source: updated_source)
39
- next req unless latest_resolvable_version
40
- next initial_req_after_source_change(req) unless req[:requirement]
41
-
42
- case update_strategy
43
- when :widen_ranges then widen_requirement(req)
44
- when :bump_versions then update_version(req)
45
- else raise "Unexpected update strategy: #{update_strategy}"
46
- end
47
- end
48
- end
49
-
50
- private
51
-
52
- attr_reader :requirements, :updated_source, :update_strategy,
53
- :latest_version, :latest_resolvable_version
54
-
55
- def check_update_strategy
56
- return if ALLOWED_UPDATE_STRATEGIES.include?(update_strategy)
57
-
58
- raise "Unknown update strategy: #{update_strategy}"
59
- end
60
-
61
- def updating_from_git_to_version?
62
- return false unless updated_source&.fetch(:type) == "default"
63
-
64
- original_source = requirements.map { |r| r[:source] }.compact.first
65
- original_source&.fetch(:type) == "git"
66
- end
67
-
68
- def initial_req_after_source_change(req)
69
- return req unless updating_from_git_to_version?
70
- return req unless req.fetch(:requirement).nil?
71
-
72
- new_req =
73
- if req.fetch(:file) == "go.mod"
74
- "v#{latest_resolvable_version.to_s.gsub(/^v/, '')}"
75
- else
76
- "^#{latest_resolvable_version}"
77
- end
78
- req.merge(requirement: new_req)
79
- end
80
-
81
- def widen_requirement(req)
82
- current_requirement = req[:requirement]
83
- version = latest_resolvable_version
84
-
85
- ruby_reqs = ruby_requirements(current_requirement)
86
- return req if ruby_reqs.any? { |r| r.satisfied_by?(version) }
87
-
88
- reqs = current_requirement.strip.split(",").map(&:strip)
89
-
90
- updated_requirement =
91
- if current_requirement.include?("||")
92
- # Further widen the range by adding another OR condition
93
- current_requirement + " || ^#{version}"
94
- elsif reqs.any? { |r| r.match?(/(<|-\s)/) }
95
- # Further widen the range by updating the upper bound
96
- update_range_requirement(current_requirement)
97
- else
98
- # Convert existing requirement to a range
99
- create_new_range_requirement(reqs)
100
- end
101
-
102
- req.merge(requirement: updated_requirement)
103
- end
104
-
105
- def update_version(req)
106
- current_requirement = req[:requirement]
107
- version = latest_resolvable_version
108
-
109
- ruby_reqs = ruby_requirements(current_requirement)
110
- reqs = current_requirement.strip.split(",").map(&:strip)
111
-
112
- if ruby_reqs.any? { |r| r.satisfied_by?(version) } &&
113
- current_requirement.match?(/(<|-\s|\|\|)/)
114
- return req
115
- end
116
-
117
- updated_requirement =
118
- if current_requirement.include?("||")
119
- # Further widen the range by adding another OR condition
120
- current_requirement + " || ^#{version}"
121
- elsif reqs.any? { |r| r.match?(/(<|-\s)/) }
122
- # Further widen the range by updating the upper bound
123
- update_range_requirement(current_requirement)
124
- else
125
- update_version_requirement(reqs)
126
- end
127
-
128
- req.merge(requirement: updated_requirement)
129
- end
130
-
131
- def ruby_requirements(requirement_string)
132
- requirement_class.requirements_array(requirement_string)
133
- end
134
-
135
- def update_range_requirement(req_string)
136
- range_requirement = req_string.split(",").
137
- find { |r| r.match?(/<|(\s+-\s+)/) }
138
-
139
- versions = range_requirement.scan(VERSION_REGEX)
140
- upper_bound = versions.map { |v| version_class.new(v) }.max
141
- new_upper_bound = update_greatest_version(
142
- upper_bound,
143
- latest_resolvable_version
144
- )
145
-
146
- req_string.sub(
147
- upper_bound.to_s,
148
- new_upper_bound.to_s
149
- )
150
- end
151
-
152
- def create_new_range_requirement(string_reqs)
153
- version = latest_resolvable_version
154
-
155
- lower_bound =
156
- string_reqs.
157
- map { |req| requirement_class.new(req) }.
158
- flat_map { |req| req.requirements.map(&:last) }.
159
- min.to_s
160
-
161
- upper_bound =
162
- if string_reqs.first.start_with?("~") &&
163
- version.to_s.split(".").count > 1
164
- create_upper_bound_for_tilda_req(string_reqs.first)
165
- else
166
- upper_bound_parts = [version.to_s.split(".").first.to_i + 1]
167
- upper_bound_parts.
168
- fill("0", 1..(lower_bound.split(".").count - 1)).
169
- join(".")
170
- end
171
-
172
- ">= #{lower_bound}, < #{upper_bound}"
173
- end
174
-
175
- def update_version_requirement(string_reqs)
176
- version = latest_resolvable_version.to_s.gsub(/^v/, "")
177
- current_req = string_reqs.first
178
-
179
- current_req.gsub(VERSION_REGEX, version)
180
- end
181
-
182
- def create_upper_bound_for_tilda_req(string_req)
183
- tilda_version = requirement_class.new(string_req).
184
- requirements.map(&:last).
185
- min.to_s
186
-
187
- upper_bound_parts = latest_resolvable_version.to_s.split(".")
188
- upper_bound_parts.slice(0, tilda_version.to_s.split(".").count)
189
- upper_bound_parts[-1] = "0"
190
- upper_bound_parts[-2] = (upper_bound_parts[-2].to_i + 1).to_s
191
-
192
- upper_bound_parts.join(".")
193
- end
194
-
195
- def update_greatest_version(old_version, version_to_be_permitted)
196
- version = version_class.new(old_version)
197
- version = version.release if version.prerelease?
198
-
199
- index_to_update =
200
- version.segments.map.with_index { |seg, i| seg.zero? ? 0 : i }.max
201
-
202
- version.segments.map.with_index do |_, index|
203
- if index < index_to_update
204
- version_to_be_permitted.segments[index]
205
- elsif index == index_to_update
206
- version_to_be_permitted.segments[index] + 1
207
- else 0
208
- end
209
- end.join(".")
210
- end
211
-
212
- def version_class
213
- Utils::Go::Version
214
- end
215
-
216
- def requirement_class
217
- Utils::Go::Requirement
218
- end
219
- end
220
- end
221
- end
222
- end
223
- end
@@ -1,168 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "toml-rb"
4
- require "open3"
5
- require "dependabot/shared_helpers"
6
- require "dependabot/update_checkers/go/dep"
7
- require "dependabot/errors"
8
-
9
- module Dependabot
10
- module UpdateCheckers
11
- module Go
12
- class Dep
13
- class VersionResolver
14
- NOT_FOUND_REGEX =
15
- /failed to list versions for (?<repo_url>.*?):\s+/.freeze
16
- INDEX_OUT_OF_RANGE_REGEX =
17
- /panic: runtime error: index out of range.*findValidVersion/m.freeze
18
-
19
- def initialize(dependency:, dependency_files:, credentials:)
20
- @dependency = dependency
21
- @dependency_files = dependency_files
22
- @credentials = credentials
23
- end
24
-
25
- def latest_resolvable_version
26
- if defined?(@latest_resolvable_version)
27
- return @latest_resolvable_version
28
- end
29
-
30
- @latest_resolvable_version = fetch_latest_resolvable_version
31
- end
32
-
33
- private
34
-
35
- attr_reader :dependency, :dependency_files, :credentials
36
-
37
- def fetch_latest_resolvable_version
38
- base_directory = File.join("src", "project",
39
- dependency_files.first.directory)
40
- base_parts = base_directory.split("/").length
41
- updated_version =
42
- SharedHelpers.in_a_temporary_directory(base_directory) do |dir|
43
- write_temporary_dependency_files
44
-
45
- SharedHelpers.with_git_configured(credentials: credentials) do
46
- # Shell out to dep, which handles everything for us, and does
47
- # so without doing an install (so it's fast).
48
- command = "dep ensure -update --no-vendor #{dependency.name}"
49
- dir_parts = dir.realpath.to_s.split("/")
50
- gopath = File.join(dir_parts[0..-(base_parts + 1)])
51
- run_shell_command(command, "GOPATH" => gopath)
52
- end
53
-
54
- new_lockfile_content = File.read("Gopkg.lock")
55
-
56
- get_version_from_lockfile(new_lockfile_content)
57
- end
58
-
59
- updated_version
60
- rescue SharedHelpers::HelperSubprocessFailed => error
61
- handle_dep_errors(error)
62
- end
63
-
64
- def get_version_from_lockfile(lockfile_content)
65
- package = TomlRB.parse(lockfile_content).fetch("projects").
66
- find { |p| p["name"] == dependency.name }
67
-
68
- version = package["version"]
69
-
70
- if version && version_class.correct?(version.sub(/^v?/, ""))
71
- version_class.new(version.sub(/^v?/, ""))
72
- elsif version
73
- version
74
- else
75
- package.fetch("revision")
76
- end
77
- end
78
-
79
- def handle_dep_errors(error)
80
- if error.message.match?(NOT_FOUND_REGEX)
81
- url = error.message.match(NOT_FOUND_REGEX).
82
- named_captures.fetch("repo_url")
83
-
84
- raise Dependabot::GitDependenciesNotReachable, url
85
- end
86
-
87
- # A dep bug that probably isn't going to be fixed any time soon :-(
88
- # - https://github.com/golang/dep/issues/1437
89
- # - https://github.com/golang/dep/issues/649
90
- # - https://github.com/golang/dep/issues/2041
91
- # - https://twitter.com/miekg/status/996682296739745792
92
- return if error.message.match?(INDEX_OUT_OF_RANGE_REGEX)
93
-
94
- raise
95
- end
96
-
97
- def run_shell_command(command, env = {})
98
- start = Time.now
99
- stdout, process = Open3.capture2e(env, command)
100
- time_taken = start - Time.now
101
-
102
- # Raise an error with the output from the shell session if dep
103
- # returns a non-zero status
104
- return if process.success?
105
-
106
- raise SharedHelpers::HelperSubprocessFailed.new(
107
- message: stdout,
108
- error_context: {
109
- command: command,
110
- time_taken: time_taken,
111
- process_exit_value: process.to_s
112
- }
113
- )
114
- end
115
-
116
- def write_temporary_dependency_files
117
- dependency_files.each do |file|
118
- path = file.name
119
- FileUtils.mkdir_p(Pathname.new(path).dirname)
120
- File.write(file.name, file.content)
121
- end
122
-
123
- File.write("hello.go", dummy_app_content)
124
- end
125
-
126
- def dummy_app_content
127
- base = "package main\n\n"\
128
- "import \"fmt\"\n\n"
129
-
130
- packages_to_import.each { |nm| base += "import \"#{nm}\"\n\n" }
131
-
132
- base + "func main() {\n fmt.Printf(\"hello, world\\n\")\n}"
133
- end
134
-
135
- def packages_to_import
136
- return [] unless lockfile
137
-
138
- parsed_lockfile = TomlRB.parse(lockfile.content)
139
-
140
- # If the lockfile was created using dep v0.5.0+ then it will tell us
141
- # exactly which packages to import
142
- if parsed_lockfile.dig("solve-meta", "input-imports")
143
- return parsed_lockfile.dig("solve-meta", "input-imports")
144
- end
145
-
146
- # Otherwise we have no way of knowing, so import everything in the
147
- # lockfile that isn't marked as internal
148
- parsed_lockfile.fetch("projects").flat_map do |dep|
149
- dep["packages"].map do |package|
150
- next if package.start_with?("internal")
151
-
152
- package == "." ? dep["name"] : File.join(dep["name"], package)
153
- end.compact
154
- end
155
- end
156
-
157
- def lockfile
158
- @lockfile = dependency_files.find { |f| f.name == "Gopkg.lock" }
159
- end
160
-
161
- def version_class
162
- Utils.version_class_for_package_manager(dependency.package_manager)
163
- end
164
- end
165
- end
166
- end
167
- end
168
- end