dependabot-common 0.220.0 → 0.221.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: