dependabot-common 0.223.0 → 0.225.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: 18ecca84ce32ec9c88e10c027588144887dcbec20f59878d30d8d3c26d0d41f4
4
- data.tar.gz: 4a13a9d395adf5c8567523e6afb21ab4dc2d265b98d30f2c99bee1b19d8c5119
3
+ metadata.gz: eff50d0047fd94f2b62e76e55d42561e17780c26da251ba55cd04f493bce0cb5
4
+ data.tar.gz: 34d2e201f6198e4478a2b13d8e2c12162ae1a2e74b6c66ebfd45e12518379155
5
5
  SHA512:
6
- metadata.gz: 9ce88a617e9e8d0952c76630f8f8c76956a38f66cedc06e2da6f360ceaf9b28a52fef49be84102213a4262d96abf56c7ec55c6d75fc53e6e6b6653d3c8f363b1
7
- data.tar.gz: 4b58cd7780d8c2c4f8d18a0622a7a16d2f0ace2204098d5376b4077fef02a1b6629cea77c4b280f745b4e4946568d9be2014e6e38f31ac088696eb981d69019a
6
+ metadata.gz: 97d3947770789948f293026af11c4a8f065831ebf4c5ee5f06c3e2fe147766b5d4615b2497fbd7dfbfb4589a07df52a91de9c7041fe49cd74795fd3fb6be6437
7
+ data.tar.gz: 640a7855071b6c94d1a8e91972bf4e34792e6ffcf02ac38d1bb6a42e985fdfc3479b994c645fdb24d77fe450f84e39aef0e6bef2ab41c0394989f9c63f792f19
@@ -22,8 +22,6 @@ module Dependabot
22
22
 
23
23
  RETRYABLE_ERRORS = [InternalServerError, BadGateway, ServiceNotAvailable].freeze
24
24
 
25
- MAX_PR_DESCRIPTION_LENGTH = 3999
26
-
27
25
  #######################
28
26
  # Constructor methods #
29
27
  #######################
@@ -174,7 +172,6 @@ module Dependabot
174
172
  def create_pull_request(pr_name, source_branch, target_branch,
175
173
  pr_description, labels,
176
174
  reviewers = nil, assignees = nil, work_item = nil)
177
- pr_description = truncate_pr_description(pr_description)
178
175
 
179
176
  content = {
180
177
  sourceRefName: "refs/heads/" + source_branch,
@@ -375,19 +372,6 @@ module Dependabot
375
372
  end
376
373
  end
377
374
 
378
- def truncate_pr_description(pr_description)
379
- # Azure DevOps only support descriptions up to 4000 characters in UTF-16
380
- # encoding.
381
- # https://developercommunity.visualstudio.com/content/problem/608770/remove-4000-character-limit-on-pull-request-descri.html
382
- pr_description = pr_description.dup.force_encoding(Encoding::UTF_16)
383
- if pr_description.length > MAX_PR_DESCRIPTION_LENGTH
384
- truncated_msg = (+"...\n\n_Description has been truncated_").force_encoding(Encoding::UTF_16)
385
- truncate_length = MAX_PR_DESCRIPTION_LENGTH - truncated_msg.length
386
- pr_description = (pr_description[0..truncate_length] + truncated_msg)
387
- end
388
- pr_description.force_encoding(Encoding::UTF_8)
389
- end
390
-
391
375
  def tags_creation_forbidden?(response)
392
376
  return if response.body.empty?
393
377
 
@@ -1,16 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dependabot/experiments"
4
+ require "dependabot/config/ignore_condition"
5
+ require "dependabot/logger"
6
+
3
7
  require "wildcard_matcher"
4
8
  require "yaml"
5
9
 
6
10
  module Dependabot
7
11
  class DependencyGroup
12
+ ANY_DEPENDENCY_NAME = "*"
13
+ SECURITY_UPDATES_ONLY = false
14
+
15
+ DEFAULT_UPDATE_TYPES = [
16
+ SEMVER_MAJOR = "major",
17
+ SEMVER_MINOR = "minor",
18
+ SEMVER_PATCH = "patch"
19
+ ].freeze
20
+
21
+ IGNORE_CONDITION_TYPES = {
22
+ SEMVER_MAJOR => Dependabot::Config::IgnoreCondition::MAJOR_VERSION_TYPE,
23
+ SEMVER_MINOR => Dependabot::Config::IgnoreCondition::MINOR_VERSION_TYPE,
24
+ SEMVER_PATCH => Dependabot::Config::IgnoreCondition::PATCH_VERSION_TYPE
25
+ }.freeze
26
+
27
+ class NullIgnoreCondition
28
+ def ignored_versions(_dependency, _security_updates_only)
29
+ []
30
+ end
31
+ end
32
+
8
33
  attr_reader :name, :rules, :dependencies
9
34
 
10
35
  def initialize(name:, rules:)
11
36
  @name = name
12
37
  @rules = rules
13
38
  @dependencies = []
39
+ @ignore_condition = generate_ignore_condition!
14
40
  end
15
41
 
16
42
  def contains?(dependency)
@@ -20,6 +46,18 @@ module Dependabot
20
46
  matches_pattern?(dependency.name) && matches_dependency_type?(dependency)
21
47
  end
22
48
 
49
+ # This method generates ignored versions for the given Dependency based on
50
+ # the any update-types we have defined.
51
+ def ignored_versions_for(dependency)
52
+ @ignore_condition.ignored_versions(dependency, SECURITY_UPDATES_ONLY)
53
+ end
54
+
55
+ def targets_highest_versions_possible?
56
+ return true unless experimental_rules_enabled?
57
+
58
+ update_types.include?(SEMVER_MAJOR)
59
+ end
60
+
23
61
  def to_h
24
62
  { "name" => name }
25
63
  end
@@ -54,5 +92,49 @@ module Dependabot
54
92
  "development"
55
93
  end
56
94
  end
95
+
96
+ def pattern_rules?
97
+ rules.key?("patterns") && rules["patterns"]&.any?
98
+ end
99
+
100
+ def update_types
101
+ rules.fetch("update-types", DEFAULT_UPDATE_TYPES)
102
+ end
103
+
104
+ def generate_ignore_condition!
105
+ return NullIgnoreCondition.new unless experimental_rules_enabled?
106
+
107
+ ignored_update_types = ignored_update_types_for_rules
108
+
109
+ return NullIgnoreCondition.new unless ignored_update_types.any?
110
+
111
+ Dependabot.logger.debug("The #{name} group has set ignores for update-type(s): #{ignored_update_types}")
112
+
113
+ Dependabot::Config::IgnoreCondition.new(
114
+ dependency_name: ANY_DEPENDENCY_NAME,
115
+ update_types: ignored_update_types
116
+ )
117
+ end
118
+
119
+ def ignored_update_types_for_rules
120
+ unless update_types.is_a?(Array)
121
+ raise ArgumentError,
122
+ "The #{name} group has an unexpected value for update-types: '#{update_types}'"
123
+ end
124
+
125
+ unless update_types.any?
126
+ raise ArgumentError,
127
+ "The #{name} group has specified an empty array for update-types."
128
+ end
129
+
130
+ ignored_update_types = DEFAULT_UPDATE_TYPES - update_types
131
+ return [] if ignored_update_types.empty?
132
+
133
+ IGNORE_CONDITION_TYPES.fetch_values(*ignored_update_types)
134
+ end
135
+
136
+ def experimental_rules_enabled?
137
+ Dependabot::Experiments.enabled?(:grouped_updates_experimental_rules)
138
+ end
57
139
  end
58
140
  end
@@ -126,6 +126,7 @@ module Dependabot
126
126
  version: version,
127
127
  requirements: requirements,
128
128
  package_manager: old_dep.package_manager,
129
+ metadata: old_dep.metadata,
129
130
  subdependency_metadata: subdependency_metadata
130
131
  )
131
132
  end
@@ -112,13 +112,17 @@ module Dependabot
112
112
  command = "git ls-remote #{service_pack_uri}"
113
113
  command = SharedHelpers.escape_command(command)
114
114
 
115
- stdout, stderr, process = Open3.capture3(env, command)
116
- # package the command response like a HTTP response so error handling
117
- # remains unchanged
118
- if process.success?
119
- OpenStruct.new(body: stdout, status: 200)
115
+ begin
116
+ stdout, stderr, process = Open3.capture3(env, command)
117
+ # package the command response like a HTTP response so error handling remains unchanged
118
+ rescue Errno::ENOENT => e # thrown when `git` isn't installed...
119
+ OpenStruct.new(body: e.message, status: 500)
120
120
  else
121
- OpenStruct.new(body: stderr, status: 500)
121
+ if process.success?
122
+ OpenStruct.new(body: stdout, status: 200)
123
+ else
124
+ OpenStruct.new(body: stderr, status: 500)
125
+ end
122
126
  end
123
127
  end
124
128
 
@@ -10,6 +10,11 @@ module Dependabot
10
10
  :files, :commit_message, :pr_description, :pr_name,
11
11
  :author_details, :labeler, :reviewers, :assignees, :work_item
12
12
 
13
+ # Azure DevOps limits PR descriptions to a max of 4,000 characters in UTF-16 encoding:
14
+ # https://developercommunity.visualstudio.com/content/problem/608770/remove-4000-character-limit-on-pull-request-descri.html
15
+ PR_DESCRIPTION_MAX_LENGTH = 3_999 # 0 based count
16
+ PR_DESCRIPTION_ENCODING = Encoding::UTF_16
17
+
13
18
  def initialize(source:, branch_name:, base_commit:, credentials:,
14
19
  files:, commit_message:, pr_description:, pr_name:,
15
20
  author_details:, labeler:, reviewers: nil, assignees: nil, work_item: nil)
@@ -10,6 +10,10 @@ module Dependabot
10
10
  :files, :commit_message, :pr_description, :pr_name,
11
11
  :author_details, :labeler
12
12
 
13
+ # CodeCommit limits PR descriptions to a max length of 10,240 characters:
14
+ # https://docs.aws.amazon.com/codecommit/latest/APIReference/API_PullRequest.html
15
+ PR_DESCRIPTION_MAX_LENGTH = 10_239 # 0 based count
16
+
13
17
  def initialize(source:, branch_name:, base_commit:, credentials:,
14
18
  files:, commit_message:, pr_description:, pr_name:,
15
19
  author_details:, labeler:, require_up_to_date_base:)
@@ -9,7 +9,9 @@ module Dependabot
9
9
  class PullRequestCreator
10
10
  # rubocop:disable Metrics/ClassLength
11
11
  class Github
12
- MAX_PR_DESCRIPTION_LENGTH = 65_536 # characters (see #create_pull_request)
12
+ # GitHub limits PR descriptions to a max of 65,536 characters:
13
+ # https://github.com/orgs/community/discussions/27190#discussioncomment-3726017
14
+ PR_DESCRIPTION_MAX_LENGTH = 65_535 # 0 based count
13
15
 
14
16
  attr_reader :source, :branch_name, :base_commit, :credentials,
15
17
  :files, :pr_description, :pr_name, :commit_message,
@@ -349,18 +351,6 @@ module Dependabot
349
351
  end
350
352
 
351
353
  def create_pull_request
352
- # Limit PR description to MAX_PR_DESCRIPTION_LENGTH (65,536) characters
353
- # and truncate with message if over. The API limit is 262,144 bytes
354
- # (https://github.community/t/maximum-length-for-the-comment-body-in-issues-and-pr/148867/2).
355
- # As Ruby strings are UTF-8 encoded, this is a pessimistic limit: it
356
- # presumes the case where all characters are 4 bytes.
357
- pr_description = @pr_description.dup
358
- if pr_description && pr_description.length > MAX_PR_DESCRIPTION_LENGTH
359
- truncated_msg = "...\n\n_Description has been truncated_"
360
- truncate_length = MAX_PR_DESCRIPTION_LENGTH - truncated_msg.length
361
- pr_description = (pr_description[0, truncate_length] + truncated_msg)
362
- end
363
-
364
354
  github_client_for_source.create_pull_request(
365
355
  source.repo,
366
356
  target_branch,
@@ -22,13 +22,16 @@ module Dependabot
22
22
  attr_reader :source, :dependencies, :files, :credentials,
23
23
  :pr_message_header, :pr_message_footer,
24
24
  :commit_message_options, :vulnerabilities_fixed,
25
- :github_redirection_service, :dependency_group
25
+ :github_redirection_service, :dependency_group, :pr_message_max_length,
26
+ :pr_message_encoding, :ignore_conditions
27
+
28
+ TRUNCATED_MSG = "...\n\n_Description has been truncated_"
26
29
 
27
30
  def initialize(source:, dependencies:, files:, credentials:,
28
31
  pr_message_header: nil, pr_message_footer: nil,
29
32
  commit_message_options: {}, vulnerabilities_fixed: {},
30
33
  github_redirection_service: DEFAULT_GITHUB_REDIRECTION_SERVICE,
31
- dependency_group: nil)
34
+ dependency_group: nil, pr_message_max_length: nil, pr_message_encoding: nil, ignore_conditions: [])
32
35
  @dependencies = dependencies
33
36
  @files = files
34
37
  @source = source
@@ -39,8 +42,15 @@ module Dependabot
39
42
  @vulnerabilities_fixed = vulnerabilities_fixed
40
43
  @github_redirection_service = github_redirection_service
41
44
  @dependency_group = dependency_group
45
+ @pr_message_max_length = pr_message_max_length
46
+ @pr_message_encoding = pr_message_encoding
47
+ @ignore_conditions = ignore_conditions
42
48
  end
43
49
 
50
+ attr_writer :pr_message_max_length
51
+
52
+ attr_writer :pr_message_encoding
53
+
44
54
  def pr_name
45
55
  name = dependency_group ? group_pr_name : solo_pr_name
46
56
  name[0] = name[0].capitalize if pr_name_prefixer.capitalize_first_word?
@@ -48,13 +58,49 @@ module Dependabot
48
58
  end
49
59
 
50
60
  def pr_message
51
- suffixed_pr_message_header + commit_message_intro +
52
- metadata_cascades + prefixed_pr_message_footer
61
+ # TODO: Remove unignore_commands? feature flag once we are confident
62
+ # that it is working as expected
63
+ msg = if unignore_commands?
64
+ "#{suffixed_pr_message_header}" \
65
+ "#{commit_message_intro}" \
66
+ "#{metadata_cascades}" \
67
+ "#{ignore_conditions_table}" \
68
+ "#{prefixed_pr_message_footer}"
69
+ else
70
+ "#{suffixed_pr_message_header}" \
71
+ "#{commit_message_intro}" \
72
+ "#{metadata_cascades}" \
73
+ "#{prefixed_pr_message_footer}"
74
+ end
75
+
76
+ truncate_pr_message(msg)
53
77
  rescue StandardError => e
54
78
  Dependabot.logger.error("Error while generating PR message: #{e.message}")
55
79
  suffixed_pr_message_header + prefixed_pr_message_footer
56
80
  end
57
81
 
82
+ def unignore_commands?
83
+ Experiments.enabled?(:unignore_commands)
84
+ end
85
+
86
+ # Truncate PR message as determined by the pr_message_max_length and pr_message_encoding instance variables
87
+ # The encoding is used when calculating length, all messages are returned as ruby UTF_8 encoded string
88
+ def truncate_pr_message(msg)
89
+ return msg if pr_message_max_length.nil?
90
+
91
+ msg = msg.dup
92
+ msg = msg.force_encoding(pr_message_encoding) unless pr_message_encoding.nil?
93
+
94
+ if msg.length > pr_message_max_length
95
+ tr_msg = pr_message_encoding.nil? ? TRUNCATED_MSG : (+TRUNCATED_MSG).dup.force_encoding(pr_message_encoding)
96
+ trunc_length = pr_message_max_length - tr_msg.length
97
+ msg = (msg[0..trunc_length] + tr_msg)
98
+ end
99
+ # if we used a custom encoding for calculating length, then we need to force back to UTF-8
100
+ msg.force_encoding(Encoding::UTF_8) unless pr_message_encoding.nil?
101
+ msg
102
+ end
103
+
58
104
  def commit_message
59
105
  message = commit_subject + "\n\n"
60
106
  message += commit_message_intro
@@ -477,6 +523,46 @@ module Dependabot
477
523
  ).to_s
478
524
  end
479
525
 
526
+ def ignore_conditions_table
527
+ # Return an empty string if ignore_conditions is empty
528
+ return "" if @ignore_conditions.empty?
529
+
530
+ # Filter out the conditions where from_config_file is false and dependency is in @dependencies
531
+ valid_ignore_conditions = @ignore_conditions.select do |ic|
532
+ !ic[:from_config_file] && dependencies.any? { |dep| dep.name == ic[:dependency_name] }
533
+ end
534
+
535
+ # Return an empty string if no valid ignore conditions after filtering
536
+ return "" if valid_ignore_conditions.empty?
537
+
538
+ # Sort them by updated_at (or created_at if updated_at is nil), taking the latest 20
539
+ sorted_ignore_conditions = valid_ignore_conditions.sort_by { |ic| ic[:updated_at] || ic[:created_at] }.last(20)
540
+
541
+ # Map each condition to a row string
542
+ table_rows = sorted_ignore_conditions.map do |ic|
543
+ "| #{ic[:dependency_name]} | [#{ic[:version_requirement]}] |"
544
+ end
545
+
546
+ summary = "Most Recent Ignore Conditions Applied to This Pull Request"
547
+ build_table(summary, table_rows)
548
+ end
549
+
550
+ def build_table(summary, rows)
551
+ table_header = "| Dependency Name | Ignore Conditions |"
552
+ table_divider = "| --- | --- |"
553
+ table_body = rows.join("\n")
554
+ body = "\n#{[table_header, table_divider, table_body].join("\n")}\n"
555
+
556
+ if %w(azure bitbucket codecommit).include?(source.provider)
557
+ "\n##{summary}\n\n#{body}"
558
+ else
559
+ # Build the collapsible section
560
+ msg = "<details>\n<summary>#{summary}</summary>\n\n" \
561
+ "#{[table_header, table_divider, table_body].join("\n")}\n</details>"
562
+ "\n#{msg}\n"
563
+ end
564
+ end
565
+
480
566
  def changelog_url(dependency)
481
567
  metadata_finder(dependency).changelog_url
482
568
  end
@@ -49,7 +49,8 @@ module Dependabot
49
49
  :commit_message_options, :vulnerabilities_fixed,
50
50
  :reviewers, :assignees, :milestone, :branch_name_separator,
51
51
  :branch_name_prefix, :branch_name_max_length, :github_redirection_service,
52
- :custom_headers, :provider_metadata, :dependency_group
52
+ :custom_headers, :provider_metadata, :dependency_group, :pr_message_max_length,
53
+ :pr_message_encoding
53
54
 
54
55
  def initialize(source:, base_commit:, dependencies:, files:, credentials:,
55
56
  pr_message_header: nil, pr_message_footer: nil,
@@ -61,7 +62,8 @@ module Dependabot
61
62
  automerge_candidate: false,
62
63
  github_redirection_service: DEFAULT_GITHUB_REDIRECTION_SERVICE,
63
64
  custom_headers: nil, require_up_to_date_base: false,
64
- provider_metadata: {}, message: nil, dependency_group: nil)
65
+ provider_metadata: {}, message: nil, dependency_group: nil, pr_message_max_length: nil,
66
+ pr_message_encoding: nil)
65
67
  @dependencies = dependencies
66
68
  @source = source
67
69
  @base_commit = base_commit
@@ -88,6 +90,8 @@ module Dependabot
88
90
  @provider_metadata = provider_metadata
89
91
  @message = message
90
92
  @dependency_group = dependency_group
93
+ @pr_message_max_length = pr_message_max_length
94
+ @pr_message_encoding = pr_message_encoding
91
95
 
92
96
  check_dependencies_have_previous_version
93
97
  end
@@ -216,19 +220,32 @@ module Dependabot
216
220
  end
217
221
 
218
222
  def message
219
- @message ||=
220
- MessageBuilder.new(
221
- source: source,
222
- dependencies: dependencies,
223
- files: files,
224
- credentials: credentials,
225
- commit_message_options: commit_message_options,
226
- pr_message_header: pr_message_header,
227
- pr_message_footer: pr_message_footer,
228
- vulnerabilities_fixed: vulnerabilities_fixed,
229
- github_redirection_service: github_redirection_service,
230
- dependency_group: dependency_group
231
- )
223
+ return @message unless @message.nil?
224
+
225
+ case source.provider
226
+ when "github"
227
+ @pr_message_max_length = Github::PR_DESCRIPTION_MAX_LENGTH if @pr_message_max_length.nil?
228
+ when "azure"
229
+ @pr_message_max_length = Azure::PR_DESCRIPTION_MAX_LENGTH if @pr_message_max_length.nil?
230
+ @pr_message_encoding = Azure::PR_DESCRIPTION_ENCODING if @pr_message_encoding.nil?
231
+ when "codecommit"
232
+ @pr_message_max_length = Codecommit::PR_DESCRIPTION_MAX_LENGTH if @pr_message_max_length.nil?
233
+ end
234
+
235
+ @message = MessageBuilder.new(
236
+ source: source,
237
+ dependencies: dependencies,
238
+ files: files,
239
+ credentials: credentials,
240
+ commit_message_options: commit_message_options,
241
+ pr_message_header: pr_message_header,
242
+ pr_message_footer: pr_message_footer,
243
+ vulnerabilities_fixed: vulnerabilities_fixed,
244
+ github_redirection_service: github_redirection_service,
245
+ dependency_group: dependency_group,
246
+ pr_message_max_length: pr_message_max_length,
247
+ pr_message_encoding: pr_message_encoding
248
+ )
232
249
  end
233
250
 
234
251
  def branch_namer
@@ -166,6 +166,7 @@ module Dependabot
166
166
  previous_version: previous_version,
167
167
  previous_requirements: dependency.requirements,
168
168
  package_manager: dependency.package_manager,
169
+ metadata: dependency.metadata,
169
170
  subdependency_metadata: dependency.subdependency_metadata
170
171
  )
171
172
  end
@@ -181,6 +182,7 @@ module Dependabot
181
182
  previous_version: previous_version,
182
183
  previous_requirements: dependency.requirements,
183
184
  package_manager: dependency.package_manager,
185
+ metadata: dependency.metadata,
184
186
  subdependency_metadata: dependency.subdependency_metadata
185
187
  )
186
188
  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.223.0"
4
+ VERSION = "0.225.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.223.0
4
+ version: 0.225.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-07-25 00:00:00.000000000 Z
11
+ date: 2023-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-codecommit
@@ -486,7 +486,7 @@ licenses:
486
486
  - Nonstandard
487
487
  metadata:
488
488
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
489
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.223.0
489
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.225.0
490
490
  post_install_message:
491
491
  rdoc_options: []
492
492
  require_paths: