dependabot-common 0.220.0 → 0.221.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffac7b072d3084a0eaa087c7bf4cdf6bf8f180a7312a55bd99fd5648320bb38f
4
- data.tar.gz: af8cb21616a02e1a3368ae839f591fd2dfed3154bd4b138fe13843447f9306d6
3
+ metadata.gz: 883ba90c6d526f51118f6fabc2082e4ddd09cd73338f103978e2c59a63c6ef67
4
+ data.tar.gz: e86edce90676becde42edc6fdc7c7a17304ddab77966905d059a3ae11934d8f6
5
5
  SHA512:
6
- metadata.gz: 3073fb55361f0b46820a01071dc35e14fa91bdbbe653cd581256dde2889df9abeef87bd6787d5c32fd619aaa728736db98116dce9ca357d132e7814941f949dd
7
- data.tar.gz: 3a1c6297dcd5425c3838550486066ece3ca2f89f7d1386adc0fa29e1afa681d17a0c30ed8fe987c672e25727af75319e072e1db4668fed0d35b4f4220457fdb9
6
+ metadata.gz: a806d1c610355284b4a98a4e3b6ae5ad7f070be18af6fadf23ed98b2ac842eeb55ccc09f4e90c5483b2ab42821ece37c1c3390133817f61e52c4fc580fda111d
7
+ data.tar.gz: 87ca2cbe54385a9ebd526ea9016ef652f183eb7b9b5d8be0849cd5ad7513ba5ce57071081c95788e3eb92600696ca801fa2abbbe020c32d537c26157bcb38d3b
@@ -5,7 +5,8 @@ require "pathname"
5
5
  module Dependabot
6
6
  class DependencyFile
7
7
  attr_accessor :name, :content, :directory, :type, :support_file,
8
- :symlink_target, :content_encoding, :operation, :mode
8
+ :vendored_file, :symlink_target, :content_encoding,
9
+ :operation, :mode
9
10
 
10
11
  class ContentEncoding
11
12
  UTF_8 = "utf-8"
@@ -19,7 +20,7 @@ module Dependabot
19
20
  end
20
21
 
21
22
  def initialize(name:, content:, directory: "/", type: "file",
22
- support_file: false, symlink_target: nil,
23
+ support_file: false, vendored_file: false, symlink_target: nil,
23
24
  content_encoding: ContentEncoding::UTF_8, deleted: false,
24
25
  operation: Operation::UPDATE, mode: nil)
25
26
  @name = name
@@ -27,6 +28,7 @@ module Dependabot
27
28
  @directory = clean_directory(directory)
28
29
  @symlink_target = symlink_target
29
30
  @support_file = support_file
31
+ @vendored_file = vendored_file
30
32
  @content_encoding = content_encoding
31
33
  @operation = operation
32
34
 
@@ -94,6 +96,10 @@ module Dependabot
94
96
  @support_file
95
97
  end
96
98
 
99
+ def vendored_file?
100
+ @vendored_file
101
+ end
102
+
97
103
  def deleted
98
104
  @operation == Operation::DELETE
99
105
  end
@@ -14,7 +14,10 @@ module Dependabot
14
14
 
15
15
  def contains?(dependency)
16
16
  @dependencies.include?(dependency) if @dependencies.any?
17
- rules.any? { |rule| WildcardMatcher.match?(rule, dependency.name) }
17
+ positive_match = rules["patterns"].any? { |rule| WildcardMatcher.match?(rule, dependency.name) }
18
+ negative_match = rules["exclude-patterns"]&.any? { |rule| WildcardMatcher.match?(rule, dependency.name) }
19
+
20
+ positive_match && !negative_match
18
21
  end
19
22
 
20
23
  def to_h
@@ -102,7 +102,7 @@ module Dependabot
102
102
  raise Dependabot::RepoNotFound, source
103
103
  end
104
104
 
105
- def package_manager_version
105
+ def ecosystem_versions
106
106
  nil
107
107
  end
108
108
 
@@ -91,13 +91,7 @@ module Dependabot
91
91
  @combined = if @combined
92
92
  combined_dependency(@combined, dep)
93
93
  else
94
- Dependency.new(
95
- name: dep.name,
96
- version: dep.version,
97
- requirements: dep.requirements,
98
- package_manager: dep.package_manager,
99
- subdependency_metadata: dep.subdependency_metadata
100
- )
94
+ dep
101
95
  end
102
96
 
103
97
  index_of_same_version =
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/dependency_file"
4
+
5
+ # This class provides a utility to check for arbitary modified files within a
6
+ # git directory that need to be wrapped as Dependabot::DependencyFile object
7
+ # and returned as along with anything managed by the FileUpdater itself.
8
+ module Dependabot
9
+ module FileUpdaters
10
+ class ArtifactUpdater
11
+ # @param repo_contents_path [String, nil] the path we cloned the repository into
12
+ # @param target_directory [String, nil] the path within a project directory we should inspect for changes
13
+ def initialize(repo_contents_path:, target_directory:)
14
+ @repo_contents_path = repo_contents_path
15
+ @target_directory = target_directory
16
+ end
17
+
18
+ # Returns any files that have changed within the path composed from:
19
+ # :repo_contents_path/:base_directory/:target_directory
20
+ #
21
+ # @param base_directory [String] Update config base directory
22
+ # @param only_paths [Array<String>, nil] An optional list of specific paths to check, if this is nil we will
23
+ # return every change we find within the `base_directory`
24
+ # @return [Array<Dependabot::DependencyFile>]
25
+ def updated_files(base_directory:, only_paths: nil)
26
+ return [] unless repo_contents_path && target_directory
27
+
28
+ Dir.chdir(repo_contents_path) do
29
+ # rubocop:disable Performance/DeletePrefix
30
+ relative_dir = Pathname.new(base_directory).sub(%r{\A/}, "").join(target_directory)
31
+ # rubocop:enable Performance/DeletePrefix
32
+
33
+ status = SharedHelpers.run_shell_command(
34
+ "git status --untracked-files all --porcelain v1 #{relative_dir}",
35
+ fingerprint: "git status --untracked-files all --porcelain v1 <relative_dir>"
36
+ )
37
+ changed_paths = status.split("\n").map(&:split)
38
+ changed_paths.filter_map do |type, path|
39
+ project_root = Pathname.new(File.expand_path(File.join(Dir.pwd, base_directory)))
40
+ file_path = Pathname.new(path).expand_path.relative_path_from(project_root)
41
+
42
+ # Skip this file if we are looking for specific paths and this isn't on the list
43
+ next if only_paths && !only_paths.include?(file_path.to_s)
44
+
45
+ # The following types are possible to be returned:
46
+ # M = Modified = Default for DependencyFile
47
+ # D = Deleted
48
+ # ?? = Untracked = Created
49
+ operation = Dependabot::DependencyFile::Operation::UPDATE
50
+ operation = Dependabot::DependencyFile::Operation::DELETE if type == "D"
51
+ operation = Dependabot::DependencyFile::Operation::CREATE if type == "??"
52
+
53
+ encoded_content, encoding = get_encoded_file_contents(path, operation)
54
+
55
+ create_dependency_file(
56
+ name: file_path.to_s,
57
+ content: encoded_content,
58
+ directory: base_directory,
59
+ operation: operation,
60
+ content_encoding: encoding
61
+ )
62
+ end
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ TEXT_ENCODINGS = %w(us-ascii utf-8).freeze
69
+
70
+ attr_reader :repo_contents_path, :target_directory
71
+
72
+ def get_encoded_file_contents(path, operation)
73
+ encoded_content = nil
74
+ encoding = ""
75
+
76
+ return encoded_content, encoding if operation == Dependabot::DependencyFile::Operation::DELETE
77
+
78
+ encoded_content = File.read(path)
79
+
80
+ if binary_file?(path)
81
+ encoding = Dependabot::DependencyFile::ContentEncoding::BASE64
82
+ encoded_content = Base64.encode64(encoded_content)
83
+ end
84
+
85
+ [encoded_content, encoding]
86
+ end
87
+
88
+ def binary_file?(path)
89
+ return false unless File.exist?(path)
90
+
91
+ command = SharedHelpers.escape_command("file -b --mime-encoding #{path}")
92
+ encoding = `#{command}`.strip
93
+
94
+ !TEXT_ENCODINGS.include?(encoding)
95
+ end
96
+
97
+ def create_dependency_file(parameters)
98
+ Dependabot::DependencyFile.new(**parameters)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,78 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dependabot/dependency_file"
4
-
4
+ require "dependabot/file_updaters/artifact_updater"
5
+
6
+ # This class is a specialisation of the ArtifactUpdater which should be used
7
+ # for vendored files so any DependencyFile objects it creates are properly
8
+ # flagged.
9
+ #
10
+ # This flagging ensures that the Updater will handle them correctly when
11
+ # compiling grouped updates.
5
12
  module Dependabot
6
13
  module FileUpdaters
7
- class VendorUpdater
14
+ class VendorUpdater < ArtifactUpdater
15
+ # This provides backwards compatability for anyone who used this class
16
+ # before the base ArtifactUpdater class was introduced and aligns the
17
+ # method's public signatures with it's special-case domain.
8
18
  def initialize(repo_contents_path:, vendor_dir:)
9
19
  @repo_contents_path = repo_contents_path
10
20
  @vendor_dir = vendor_dir
21
+ super(repo_contents_path: @repo_contents_path, target_directory: @vendor_dir)
11
22
  end
12
23
 
13
- # Returns changed files in the vendor/cache folder
14
- #
15
- # @param base_directory [String] Update config base directory
16
- # @return [Array<Dependabot::DependencyFile>]
17
- def updated_vendor_cache_files(base_directory:)
18
- return [] unless repo_contents_path && vendor_dir
19
-
20
- Dir.chdir(repo_contents_path) do
21
- # rubocop:disable Performance/DeletePrefix
22
- relative_dir = Pathname.new(base_directory).sub(%r{\A/}, "").join(vendor_dir)
23
- # rubocop:enable Performance/DeletePrefix
24
-
25
- status = SharedHelpers.run_shell_command(
26
- "git status --untracked-files all --porcelain v1 #{relative_dir}",
27
- fingerprint: "git status --untracked-files all --porcelain v1 <relative_dir>"
28
- )
29
- changed_paths = status.split("\n").map(&:split)
30
- changed_paths.map do |type, path|
31
- # The following types are possible to be returned:
32
- # M = Modified = Default for DependencyFile
33
- # D = Deleted
34
- # ?? = Untracked = Created
35
- operation = Dependabot::DependencyFile::Operation::UPDATE
36
- operation = Dependabot::DependencyFile::Operation::DELETE if type == "D"
37
- operation = Dependabot::DependencyFile::Operation::CREATE if type == "??"
38
- encoding = ""
39
- encoded_content = File.read(path) unless operation == Dependabot::DependencyFile::Operation::DELETE
40
- if binary_file?(path)
41
- encoding = Dependabot::DependencyFile::ContentEncoding::BASE64
42
- if operation != Dependabot::DependencyFile::Operation::DELETE
43
- encoded_content = Base64.encode64(encoded_content)
44
- end
45
- end
46
-
47
- project_root =
48
- Pathname.new(File.expand_path(File.join(Dir.pwd, base_directory)))
49
- file_path =
50
- Pathname.new(path).expand_path.relative_path_from(project_root)
51
-
52
- Dependabot::DependencyFile.new(
53
- name: file_path.to_s,
54
- content: encoded_content,
55
- directory: base_directory,
56
- operation: operation,
57
- content_encoding: encoding
58
- )
59
- end
60
- end
61
- end
24
+ alias updated_vendor_cache_files updated_files
62
25
 
63
26
  private
64
27
 
65
- TEXT_ENCODINGS = %w(us-ascii utf-8).freeze
66
-
67
- attr_reader :repo_contents_path, :vendor_dir
68
-
69
- def binary_file?(path)
70
- return false unless File.exist?(path)
71
-
72
- command = SharedHelpers.escape_command("file -b --mime-encoding #{path}")
73
- encoding = `#{command}`.strip
74
-
75
- !TEXT_ENCODINGS.include?(encoding)
28
+ def create_dependency_file(parameters)
29
+ Dependabot::DependencyFile.new(**parameters.merge({ vendored_file: true }))
76
30
  end
77
31
  end
78
32
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ class PullRequestCreator
5
+ class BranchNamer
6
+ class Base
7
+ attr_reader :dependencies, :files, :target_branch, :separator, :prefix, :max_length
8
+
9
+ def initialize(dependencies:, files:, target_branch:, separator: "/",
10
+ prefix: "dependabot", max_length: nil)
11
+ @dependencies = dependencies
12
+ @files = files
13
+ @target_branch = target_branch
14
+ @separator = separator
15
+ @prefix = prefix
16
+ @max_length = max_length
17
+ end
18
+
19
+ private
20
+
21
+ def sanitize_branch_name(ref_name)
22
+ # General git ref validation
23
+ sanitized_name = sanitize_ref(ref_name)
24
+
25
+ # Some users need branch names without slashes
26
+ sanitized_name = sanitized_name.gsub("/", separator)
27
+
28
+ # Shorten the ref in case users refs have length limits
29
+ if max_length && (sanitized_name.length > max_length)
30
+ sha = Digest::SHA1.hexdigest(sanitized_name)[0, max_length]
31
+ sanitized_name[[max_length - sha.size, 0].max..] = sha
32
+ end
33
+
34
+ sanitized_name
35
+ end
36
+
37
+ def sanitize_ref(ref)
38
+ # This isn't a complete implementation of git's ref validation, but it
39
+ # covers most cases that crop up. Its list of allowed characters is a
40
+ # bit stricter than git's, but that's for cosmetic reasons.
41
+ ref.
42
+ # Remove forbidden characters (those not already replaced elsewhere)
43
+ gsub(%r{[^A-Za-z0-9/\-_.(){}]}, "").
44
+ # Slashes can't be followed by periods
45
+ gsub(%r{/\.}, "/dot-").squeeze(".").squeeze("/").
46
+ # Trailing periods are forbidden
47
+ sub(/\.$/, "")
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,32 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dependabot/pull_request_creator/branch_namer/base"
4
+
3
5
  module Dependabot
4
6
  class PullRequestCreator
5
7
  class BranchNamer
6
- class DependencyGroupStrategy
8
+ class DependencyGroupStrategy < Base
7
9
  def initialize(dependencies:, files:, target_branch:, dependency_group:,
8
10
  separator: "/", prefix: "dependabot", max_length: nil)
9
- @dependencies = dependencies
10
- @files = files
11
- @target_branch = target_branch
11
+ super(
12
+ dependencies: dependencies,
13
+ files: files,
14
+ target_branch: target_branch,
15
+ separator: separator,
16
+ prefix: prefix,
17
+ max_length: max_length
18
+ )
19
+
12
20
  @dependency_group = dependency_group
13
- @separator = separator
14
- @prefix = prefix
15
- @max_length = max_length
16
21
  end
17
22
 
18
- # FIXME: Incorporate max_length truncation once we allow user config
19
- #
20
- # For now, we are using a placeholder DependencyGroup with a
21
- # fixed-length name, so we can punt on handling truncation until
22
- # we determine the strict validation rules for names
23
23
  def new_branch_name
24
- File.join(prefixes, group_name_with_dependency_digest).gsub("/", separator)
24
+ sanitize_branch_name(File.join(prefixes, group_name_with_dependency_digest))
25
25
  end
26
26
 
27
27
  private
28
28
 
29
- attr_reader :dependencies, :dependency_group, :files, :target_branch, :separator, :prefix, :max_length
29
+ attr_reader :dependency_group
30
30
 
31
31
  def prefixes
32
32
  [
@@ -3,23 +3,12 @@
3
3
  require "digest"
4
4
 
5
5
  require "dependabot/metadata_finders"
6
+ require "dependabot/pull_request_creator/branch_namer/base"
6
7
 
7
8
  module Dependabot
8
9
  class PullRequestCreator
9
10
  class BranchNamer
10
- class SoloStrategy
11
- attr_reader :dependencies, :files, :target_branch, :separator, :prefix, :max_length
12
-
13
- def initialize(dependencies:, files:, target_branch:, separator: "/",
14
- prefix: "dependabot", max_length: nil)
15
- @dependencies = dependencies
16
- @files = files
17
- @target_branch = target_branch
18
- @separator = separator
19
- @prefix = prefix
20
- @max_length = max_length
21
- end
22
-
11
+ class SoloStrategy < Base
23
12
  def new_branch_name
24
13
  @name ||=
25
14
  begin
@@ -39,16 +28,7 @@ module Dependabot
39
28
  "#{dependency_name_part}-#{branch_version_suffix}"
40
29
  end
41
30
 
42
- # Some users need branch names without slashes
43
- sanitized_name = sanitize_ref(File.join(prefixes, @name).gsub("/", separator))
44
-
45
- # Shorten the ref in case users refs have length limits
46
- if @max_length && (sanitized_name.length > @max_length)
47
- sha = Digest::SHA1.hexdigest(sanitized_name)[0, @max_length]
48
- sanitized_name[[@max_length - sha.size, 0].max..] = sha
49
- end
50
-
51
- sanitized_name
31
+ sanitize_branch_name(File.join(prefixes, @name))
52
32
  end
53
33
 
54
34
  private
@@ -189,19 +169,6 @@ module Dependabot
189
169
  def requirements_changed?(dependency)
190
170
  (dependency.requirements - dependency.previous_requirements).any?
191
171
  end
192
-
193
- def sanitize_ref(ref)
194
- # This isn't a complete implementation of git's ref validation, but it
195
- # covers most cases that crop up. Its list of allowed characters is a
196
- # bit stricter than git's, but that's for cosmetic reasons.
197
- ref.
198
- # Remove forbidden characters (those not already replaced elsewhere)
199
- gsub(%r{[^A-Za-z0-9/\-_.(){}]}, "").
200
- # Slashes can't be followed by periods
201
- gsub(%r{/\.}, "/dot-").squeeze(".").squeeze("/").
202
- # Trailing periods are forbidden
203
- sub(/\.$/, "")
204
- end
205
172
  end
206
173
  end
207
174
  end
@@ -315,13 +315,21 @@ module Dependabot
315
315
  def group_intro
316
316
  update_count = dependencies.map(&:name).uniq.count
317
317
 
318
- msg = "Bumps the #{dependency_group.name} group#{pr_name_directory} with #{update_count} update"
319
- msg += if update_count > 1
320
- "s: #{dependency_links[0..-2].join(', ')} and #{dependency_links[-1]}."
318
+ msg = "Bumps the #{dependency_group.name} group#{pr_name_directory} " \
319
+ "with #{update_count} update#{update_count > 1 ? 's' : ''}:"
320
+
321
+ msg += if update_count >= 5
322
+ header = %w(Package Update)
323
+ rows = dependencies.map { |dep| [dependency_link(dep), dependency_version_update(dep)] }
324
+ "\n\n#{table([header] + rows)}"
325
+ elsif update_count > 1
326
+ " #{dependency_links[0..-2].join(', ')} and #{dependency_links[-1]}."
321
327
  else
322
- ": #{dependency_links.first}."
328
+ " #{dependency_links.first}."
323
329
  end
324
330
 
331
+ msg += "\n"
332
+
325
333
  msg
326
334
  end
327
335
 
@@ -389,6 +397,10 @@ module Dependabot
389
397
  end
390
398
  end
391
399
 
400
+ def dependency_version_update(dependency)
401
+ "#{dependency.humanized_previous_version} to #{dependency.humanized_version}"
402
+ end
403
+
392
404
  def metadata_links
393
405
  return metadata_links_for_dep(dependencies.first) if dependencies.count == 1
394
406
 
@@ -413,6 +425,24 @@ module Dependabot
413
425
  msg
414
426
  end
415
427
 
428
+ def table(rows)
429
+ [
430
+ table_header(rows[0]),
431
+ rows[1..].map { |r| table_row(r) }
432
+ ].join("\n")
433
+ end
434
+
435
+ def table_header(row)
436
+ [
437
+ table_row(row),
438
+ table_row(["---"] * row.count)
439
+ ].join("\n")
440
+ end
441
+
442
+ def table_row(row)
443
+ "| #{row.join(' | ')} |"
444
+ end
445
+
416
446
  def metadata_cascades
417
447
  return metadata_cascades_for_dep(dependencies.first) if dependencies.one?
418
448
 
@@ -12,6 +12,7 @@ require "tmpdir"
12
12
  require "dependabot/simple_instrumentor"
13
13
  require "dependabot/utils"
14
14
  require "dependabot/errors"
15
+ require "dependabot/workspace"
15
16
  require "dependabot"
16
17
 
17
18
  module Dependabot
@@ -23,17 +24,22 @@ module Dependabot
23
24
  "(+https://github.com/dependabot/dependabot-core)"
24
25
  SIGKILL = 9
25
26
 
26
- def self.in_a_temporary_repo_directory(directory = "/",
27
- repo_contents_path = nil,
28
- &block)
27
+ def self.in_a_temporary_repo_directory(directory = "/", repo_contents_path = nil, &block)
29
28
  if repo_contents_path
30
- path = Pathname.new(File.join(repo_contents_path, directory)).
31
- expand_path
32
- reset_git_repo(repo_contents_path)
33
- # Handle missing directories by creating an empty one and relying on the
34
- # file fetcher to raise a DependencyFileNotFound error
35
- FileUtils.mkdir_p(path)
36
- Dir.chdir(path) { yield(path) }
29
+ # If a workspace has been defined to allow orcestration of the git repo
30
+ # by the runtime we should defer to it, otherwise we prepare the folder
31
+ # for direct use and yield.
32
+ if Dependabot::Workspace.active_workspace
33
+ Dependabot::Workspace.active_workspace.change(&block)
34
+ else
35
+ path = Pathname.new(File.join(repo_contents_path, directory)).expand_path
36
+ reset_git_repo(repo_contents_path)
37
+ # Handle missing directories by creating an empty one and relying on the
38
+ # file fetcher to raise a DependencyFileNotFound error
39
+ FileUtils.mkdir_p(path)
40
+
41
+ Dir.chdir(path) { yield(path) }
42
+ end
37
43
  else
38
44
  in_a_temporary_directory(directory, &block)
39
45
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ module Workspace
5
+ class Base
6
+ attr_reader :change_attempts, :path
7
+
8
+ def initialize(path)
9
+ @path = path
10
+ @change_attempts = []
11
+ end
12
+
13
+ def changed?
14
+ changes.any?
15
+ end
16
+
17
+ def changes
18
+ change_attempts.select(&:success?)
19
+ end
20
+
21
+ def failed_change_attempts
22
+ change_attempts.select(&:error?)
23
+ end
24
+
25
+ def change(memo = nil)
26
+ Dir.chdir(path) { yield(path) }
27
+ rescue StandardError => e
28
+ capture_failed_change_attempt(memo, e)
29
+ clean # clean up any failed changes
30
+ raise e
31
+ end
32
+
33
+ def store_change(memo = nil); end
34
+
35
+ def to_patch
36
+ ""
37
+ end
38
+
39
+ def reset!; end
40
+
41
+ protected
42
+
43
+ def capture_failed_change_attempt(memo = nil, error = nil); end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ module Workspace
5
+ class ChangeAttempt
6
+ attr_reader :diff, :error, :id, :memo, :workspace
7
+
8
+ def initialize(workspace, id:, memo:, diff: nil, error: nil)
9
+ @workspace = workspace
10
+ @id = id
11
+ @memo = memo
12
+ @diff = diff
13
+ @error = error
14
+ end
15
+
16
+ def success?
17
+ error.nil?
18
+ end
19
+
20
+ def error?
21
+ error
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/workspace/base"
4
+ require "dependabot/workspace/change_attempt"
5
+
6
+ module Dependabot
7
+ module Workspace
8
+ class Git < Base
9
+ USER = "dependabot[bot]"
10
+ EMAIL = "#{USER}@users.noreply.github.com"
11
+
12
+ attr_reader :initial_head_sha
13
+
14
+ def initialize(path)
15
+ super(path)
16
+ @initial_head_sha = head_sha
17
+ configure_git
18
+ end
19
+
20
+ def changed?
21
+ changes.any? || !changed_files.empty?
22
+ end
23
+
24
+ def to_patch
25
+ run_shell_command("git diff --patch #{@initial_head_sha}.. .")
26
+ end
27
+
28
+ def reset!
29
+ reset(initial_head_sha)
30
+ clean
31
+ run_shell_command("git stash clear")
32
+ @change_attempts = []
33
+
34
+ nil
35
+ end
36
+
37
+ def store_change(memo = nil)
38
+ return nil if changed_files.empty?
39
+
40
+ debug("store_change - before: #{current_commit}")
41
+ sha, diff = commit(memo)
42
+
43
+ change_attempts << ChangeAttempt.new(self, id: sha, memo: memo, diff: diff)
44
+ ensure
45
+ debug("store_change - after: #{current_commit}")
46
+ end
47
+
48
+ protected
49
+
50
+ def capture_failed_change_attempt(memo = nil, error = nil)
51
+ return nil if changed_files(ignored_mode: "matching").empty? && error.nil?
52
+
53
+ sha, diff = stash(memo)
54
+ change_attempts << ChangeAttempt.new(self, id: sha, memo: memo, diff: diff, error: error)
55
+ end
56
+
57
+ private
58
+
59
+ def configure_git
60
+ run_shell_command(%(git config user.name "#{USER}"), allow_unsafe_shell_command: true)
61
+ run_shell_command(%(git config user.email "#{EMAIL}"), allow_unsafe_shell_command: true)
62
+ end
63
+
64
+ def head_sha
65
+ run_shell_command("git rev-parse HEAD").strip
66
+ end
67
+
68
+ def last_stash_sha
69
+ run_shell_command("git rev-parse refs/stash").strip
70
+ end
71
+
72
+ def current_commit
73
+ # Avoid emiting the user's commit message to logs if Dependabot hasn't made any changes
74
+ return "Initial SHA: #{initial_head_sha}" if changes.empty?
75
+
76
+ # Prints out the last commit in the format "<short-ref> <commit-message>"
77
+ run_shell_command(%(git log -1 --pretty="%h% B"), allow_unsafe_shell_command: true).strip
78
+ end
79
+
80
+ def changed_files(ignored_mode: "traditional")
81
+ run_shell_command("git status --untracked-files=all --ignored=#{ignored_mode} --short .").strip
82
+ end
83
+
84
+ def stash(memo = nil)
85
+ msg = memo || "workspace change attempt"
86
+ run_shell_command("git add --all --force .")
87
+ run_shell_command(%(git stash push --all -m "#{msg}"), allow_unsafe_shell_command: true)
88
+
89
+ sha = last_stash_sha
90
+ diff = run_shell_command("git stash show --patch #{sha}")
91
+
92
+ [sha, diff]
93
+ end
94
+
95
+ def commit(memo = nil)
96
+ run_shell_command("git add #{path}")
97
+ diff = run_shell_command("git diff --cached .")
98
+
99
+ msg = memo || "workspace change"
100
+ run_shell_command(%(git commit -m "#{msg}"), allow_unsafe_shell_command: true)
101
+
102
+ [head_sha, diff]
103
+ end
104
+
105
+ def reset(sha)
106
+ run_shell_command("git reset --hard #{sha}")
107
+ end
108
+
109
+ def clean
110
+ run_shell_command("git clean -fx .")
111
+ end
112
+
113
+ def run_shell_command(*args, **kwargs)
114
+ Dir.chdir(path) { SharedHelpers.run_shell_command(*args, **kwargs) }
115
+ end
116
+
117
+ def debug(message)
118
+ Dependabot.logger.debug("[workspace] #{message}")
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/workspace/git"
4
+
5
+ module Dependabot
6
+ module Workspace
7
+ @active_workspace = nil
8
+
9
+ class << self
10
+ attr_accessor :active_workspace
11
+ end
12
+
13
+ def self.setup(repo_contents_path:, directory:)
14
+ Dependabot.logger.debug("Setting up workspace in #{repo_contents_path}")
15
+
16
+ full_path = Pathname.new(File.join(repo_contents_path, directory)).expand_path
17
+ # Handle missing directories by creating an empty one and relying on the
18
+ # file fetcher to raise a DependencyFileNotFound error
19
+ FileUtils.mkdir_p(full_path)
20
+
21
+ @active_workspace = Dependabot::Workspace::Git.new(full_path)
22
+ end
23
+
24
+ def self.store_change(memo:)
25
+ return unless @active_workspace
26
+
27
+ Dependabot.logger.debug("Storing change to workspace: #{memo}")
28
+
29
+ @active_workspace.store_change(memo)
30
+ end
31
+
32
+ def self.cleanup!
33
+ return unless @active_workspace
34
+
35
+ Dependabot.logger.debug("Cleaning up current workspace")
36
+
37
+ @active_workspace.reset!
38
+ @active_workspace = nil
39
+ end
40
+ end
41
+ end
data/lib/dependabot.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dependabot
4
- VERSION = "0.220.0"
4
+ VERSION = "0.221.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.220.0
4
+ version: 0.221.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-12 00:00:00.000000000 Z
11
+ date: 2023-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-codecommit
@@ -248,14 +248,14 @@ dependencies:
248
248
  requirements:
249
249
  - - "~>"
250
250
  - !ruby/object:Gem::Version
251
- version: 1.7.1
251
+ version: 1.8.0
252
252
  type: :development
253
253
  prerelease: false
254
254
  version_requirements: !ruby/object:Gem::Requirement
255
255
  requirements:
256
256
  - - "~>"
257
257
  - !ruby/object:Gem::Version
258
- version: 1.7.1
258
+ version: 1.8.0
259
259
  - !ruby/object:Gem::Dependency
260
260
  name: gpgme
261
261
  requirement: !ruby/object:Gem::Requirement
@@ -354,34 +354,6 @@ dependencies:
354
354
  - - "~>"
355
355
  - !ruby/object:Gem::Version
356
356
  version: 1.17.1
357
- - !ruby/object:Gem::Dependency
358
- name: simplecov
359
- requirement: !ruby/object:Gem::Requirement
360
- requirements:
361
- - - "~>"
362
- - !ruby/object:Gem::Version
363
- version: 0.22.0
364
- type: :development
365
- prerelease: false
366
- version_requirements: !ruby/object:Gem::Requirement
367
- requirements:
368
- - - "~>"
369
- - !ruby/object:Gem::Version
370
- version: 0.22.0
371
- - !ruby/object:Gem::Dependency
372
- name: simplecov-console
373
- requirement: !ruby/object:Gem::Requirement
374
- requirements:
375
- - - "~>"
376
- - !ruby/object:Gem::Version
377
- version: 0.9.1
378
- type: :development
379
- prerelease: false
380
- version_requirements: !ruby/object:Gem::Requirement
381
- requirements:
382
- - - "~>"
383
- - !ruby/object:Gem::Version
384
- version: 0.9.1
385
357
  - !ruby/object:Gem::Dependency
386
358
  name: stackprof
387
359
  requirement: !ruby/object:Gem::Requirement
@@ -458,6 +430,7 @@ files:
458
430
  - lib/dependabot/file_parsers/base/dependency_set.rb
459
431
  - lib/dependabot/file_updaters.rb
460
432
  - lib/dependabot/file_updaters/README.md
433
+ - lib/dependabot/file_updaters/artifact_updater.rb
461
434
  - lib/dependabot/file_updaters/base.rb
462
435
  - lib/dependabot/file_updaters/vendor_updater.rb
463
436
  - lib/dependabot/git_commit_checker.rb
@@ -474,6 +447,7 @@ files:
474
447
  - lib/dependabot/pull_request_creator/azure.rb
475
448
  - lib/dependabot/pull_request_creator/bitbucket.rb
476
449
  - lib/dependabot/pull_request_creator/branch_namer.rb
450
+ - lib/dependabot/pull_request_creator/branch_namer/base.rb
477
451
  - lib/dependabot/pull_request_creator/branch_namer/dependency_group_strategy.rb
478
452
  - lib/dependabot/pull_request_creator/branch_namer/solo_strategy.rb
479
453
  - lib/dependabot/pull_request_creator/codecommit.rb
@@ -502,13 +476,17 @@ files:
502
476
  - lib/dependabot/update_checkers/version_filters.rb
503
477
  - lib/dependabot/utils.rb
504
478
  - lib/dependabot/version.rb
479
+ - lib/dependabot/workspace.rb
480
+ - lib/dependabot/workspace/base.rb
481
+ - lib/dependabot/workspace/change_attempt.rb
482
+ - lib/dependabot/workspace/git.rb
505
483
  - lib/wildcard_matcher.rb
506
484
  homepage: https://github.com/dependabot/dependabot-core
507
485
  licenses:
508
486
  - Nonstandard
509
487
  metadata:
510
488
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
511
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.220.0
489
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.221.0
512
490
  post_install_message:
513
491
  rdoc_options: []
514
492
  require_paths: