dependabot-composer 0.89.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/shared_helpers"
4
+ require "dependabot/composer/update_checker"
5
+ require "dependabot/composer/version"
6
+ require "dependabot/composer/native_helpers"
7
+
8
+ module Dependabot
9
+ module Composer
10
+ class UpdateChecker
11
+ class VersionResolver
12
+ VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/.freeze
13
+ SOURCE_TIMED_OUT_REGEX =
14
+ /The "(?<url>[^"]+packages\.json)".*timed out/.freeze
15
+
16
+ def initialize(credentials:, dependency:, dependency_files:,
17
+ requirements_to_unlock:, latest_allowable_version:)
18
+ @credentials = credentials
19
+ @dependency = dependency
20
+ @dependency_files = dependency_files
21
+ @requirements_to_unlock = requirements_to_unlock
22
+ @latest_allowable_version = latest_allowable_version
23
+ end
24
+
25
+ def latest_resolvable_version
26
+ @latest_resolvable_version ||= fetch_latest_resolvable_version
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :credentials, :dependency, :dependency_files,
32
+ :requirements_to_unlock, :latest_allowable_version
33
+
34
+ def fetch_latest_resolvable_version
35
+ version = fetch_latest_resolvable_version_string
36
+ return if version.nil?
37
+ return unless Composer::Version.correct?(version)
38
+
39
+ Composer::Version.new(version)
40
+ end
41
+
42
+ def fetch_latest_resolvable_version_string
43
+ base_directory = dependency_files.first.directory
44
+ SharedHelpers.in_a_temporary_directory(base_directory) do
45
+ File.write("composer.json", prepared_composer_json_content)
46
+ File.write("composer.lock", lockfile.content) if lockfile
47
+
48
+ run_update_checker
49
+ end
50
+ rescue SharedHelpers::HelperSubprocessFailed => error
51
+ retry_count ||= 0
52
+ retry_count += 1
53
+ retry if retry_count < 2 && error.message.include?("404 Not Found")
54
+ retry if retry_count < 2 && error.message.include?("timed out")
55
+ handle_composer_errors(error)
56
+ end
57
+
58
+ def run_update_checker
59
+ SharedHelpers.with_git_configured(credentials: credentials) do
60
+ SharedHelpers.run_helper_subprocess(
61
+ command: "php -d memory_limit=-1 #{php_helper_path}",
62
+ function: "get_latest_resolvable_version",
63
+ args: [
64
+ Dir.pwd,
65
+ dependency.name.downcase,
66
+ git_credentials,
67
+ registry_credentials
68
+ ]
69
+ )
70
+ end
71
+ end
72
+
73
+ def prepared_composer_json_content
74
+ content = composer_file.content
75
+
76
+ content.gsub(
77
+ /"#{Regexp.escape(dependency.name)}"\s*:\s*".*"/,
78
+ %("#{dependency.name}": "#{updated_version_requirement_string}")
79
+ )
80
+ end
81
+
82
+ def updated_version_requirement_string
83
+ lower_bound =
84
+ if requirements_to_unlock == :none
85
+ dependency.requirements.first&.fetch(:requirement) || ">= 0"
86
+ elsif dependency.version
87
+ ">= #{dependency.version}"
88
+ else
89
+ version_for_requirement =
90
+ dependency.requirements.map { |r| r[:requirement] }.compact.
91
+ reject { |req_string| req_string.start_with?("<") }.
92
+ select { |req_string| req_string.match?(VERSION_REGEX) }.
93
+ map { |req_string| req_string.match(VERSION_REGEX) }.
94
+ select { |version| Gem::Version.correct?(version) }.
95
+ max_by { |version| Gem::Version.new(version) }
96
+
97
+ ">= #{version_for_requirement || 0}"
98
+ end
99
+
100
+ # Add the latest_allowable_version as an upper bound. This means
101
+ # ignore conditions are considered when checking for the latest
102
+ # resolvable version.
103
+ #
104
+ # NOTE: This isn't perfect. If v2.x is ignored and v3 is out but
105
+ # unresolvable then the `latest_allowable_version` will be v3, and
106
+ # we won't be ignoring v2.x releases like we should be.
107
+ return lower_bound unless latest_allowable_version
108
+
109
+ lower_bound + ", <= #{latest_allowable_version}"
110
+ end
111
+
112
+ # rubocop:disable Metrics/PerceivedComplexity
113
+ # rubocop:disable Metrics/AbcSize
114
+ # rubocop:disable Metrics/CyclomaticComplexity
115
+ # rubocop:disable Metrics/MethodLength
116
+ def handle_composer_errors(error)
117
+ if error.message.start_with?("Failed to execute git clone")
118
+ dependency_url =
119
+ error.message.match(/--mirror '(?<url>.*?)'/).
120
+ named_captures.fetch("url")
121
+ raise Dependabot::GitDependenciesNotReachable, dependency_url
122
+ elsif error.message.start_with?("Failed to clone")
123
+ dependency_url =
124
+ error.message.match(/Failed to clone (?<url>.*?) via/).
125
+ named_captures.fetch("url")
126
+ raise Dependabot::GitDependenciesNotReachable, dependency_url
127
+ elsif error.message.start_with?("Could not parse version")
128
+ raise Dependabot::DependencyFileNotResolvable, error.message
129
+ elsif error.message.include?("requested PHP extension")
130
+ extensions = error.message.scan(/\sext\-.*?\s/).map(&:strip).uniq
131
+ msg = "Dependabot's installed extensions didn't match those "\
132
+ "required by your application.\n\n"\
133
+ "Please add the following extensions to the platform "\
134
+ "config in your composer.json to allow Dependabot to run: "\
135
+ "#{extensions.join(', ')}.\n\n"\
136
+ "The full error raised was:\n\n#{error.message}"
137
+ raise Dependabot::DependencyFileNotResolvable, msg
138
+ elsif error.message.include?("package requires php") ||
139
+ error.message.include?("cannot require itself")
140
+ raise Dependabot::DependencyFileNotResolvable, error.message
141
+ elsif error.message.include?("No driver found to handle VCS") &&
142
+ !error.message.include?("@") && !error.message.include?("://")
143
+ msg = "Dependabot detected a VCS requirement with a local path, "\
144
+ "rather than a URL. Dependabot does not support this "\
145
+ "setup.\n\nThe underlying error was:\n\n#{error.message}"
146
+ raise Dependabot::DependencyFileNotResolvable, msg
147
+ elsif error.message.include?("requirements could not be resolved")
148
+ # We should raise a Dependabot::DependencyFileNotResolvable error
149
+ # here, but can't confidently distinguish between cases where we
150
+ # can't install and cases where we can't update. For now, we
151
+ # therefore just ignore the dependency.
152
+ nil
153
+ elsif error.message.include?("URL required authentication") ||
154
+ error.message.include?("403 Forbidden")
155
+ source =
156
+ error.message.match(%r{https?://(?<source>[^/]+)/}).
157
+ named_captures.fetch("source")
158
+ raise Dependabot::PrivateSourceAuthenticationFailure, source
159
+ elsif error.message.match?(SOURCE_TIMED_OUT_REGEX)
160
+ url = error.message.match(SOURCE_TIMED_OUT_REGEX).
161
+ named_captures.fetch("url")
162
+ raise if url.include?("packagist.org")
163
+
164
+ source = url.gsub(%r{/packages.json$}, "")
165
+ raise Dependabot::PrivateSourceTimedOut, source
166
+ elsif error.message.start_with?("Allowed memory size")
167
+ raise Dependabot::OutOfMemory
168
+ elsif error.message.start_with?("Package not found in updated") &&
169
+ !dependency.top_level?
170
+ # If we can't find the dependency in the composer.lock after an
171
+ # update, but it was originally a sub-dependency, it's because the
172
+ # dependency is no longer required and is just cruft in the
173
+ # composer.json. In this case we just ignore the dependency.
174
+ nil
175
+ elsif error.message.include?("stefandoorn/sitemap-plugin-1.0.0.0")
176
+ # We get a recurring error when attempting to update this repo
177
+ # which doesn't recur locally and we can't figure out how to fix!
178
+ #
179
+ # Package is not installed: stefandoorn/sitemap-plugin-1.0.0.0
180
+ nil
181
+ else
182
+ raise error
183
+ end
184
+ end
185
+ # rubocop:enable Metrics/PerceivedComplexity
186
+ # rubocop:enable Metrics/AbcSize
187
+ # rubocop:enable Metrics/CyclomaticComplexity
188
+ # rubocop:enable Metrics/MethodLength
189
+
190
+ def php_helper_path
191
+ NativeHelpers.composer_helper_path
192
+ end
193
+
194
+ def composer_file
195
+ @composer_file ||=
196
+ dependency_files.find { |f| f.name == "composer.json" }
197
+ end
198
+
199
+ def lockfile
200
+ @lockfile ||=
201
+ dependency_files.find { |f| f.name == "composer.lock" }
202
+ end
203
+
204
+ def git_credentials
205
+ credentials.select { |cred| cred["type"] == "git_source" }
206
+ end
207
+
208
+ def registry_credentials
209
+ credentials.select { |cred| cred["type"] == "composer_repository" }
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/utils"
4
+ require "rubygems_version_patch"
5
+
6
+ # PHP pre-release versions use 1.0.1-rc1 syntax, which Gem::Version
7
+ # converts into 1.0.1.pre.rc1. We override the `to_s` method to stop that
8
+ # alteration.
9
+
10
+ module Dependabot
11
+ module Composer
12
+ class Version < Gem::Version
13
+ def initialize(version)
14
+ @version_string = version.to_s
15
+ super
16
+ end
17
+
18
+ def to_s
19
+ @version_string
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ Dependabot::Utils.
26
+ register_version_class("composer", Dependabot::Composer::Version)
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dependabot-composer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.89.0
5
+ platform: ruby
6
+ authors:
7
+ - Dependabot
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-01-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dependabot-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.89.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.89.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-its
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec_junit_formatter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.61'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.61'
111
+ - !ruby/object:Gem::Dependency
112
+ name: vcr
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '4.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: webmock
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.4'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.4'
139
+ description: Automated dependency management for Ruby, JavaScript, Python, PHP, Elixir,
140
+ Rust, Java, .NET, Elm and Go
141
+ email: support@dependabot.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - helpers/.php_cs
147
+ - helpers/bin/run.php
148
+ - helpers/build
149
+ - helpers/composer.json
150
+ - helpers/composer.lock
151
+ - helpers/php/.php_cs
152
+ - helpers/setup.sh
153
+ - helpers/src/DependabotInstallationManager.php
154
+ - helpers/src/DependabotPluginManager.php
155
+ - helpers/src/ExceptionIO.php
156
+ - helpers/src/Hasher.php
157
+ - helpers/src/UpdateChecker.php
158
+ - helpers/src/Updater.php
159
+ - lib/dependabot/composer.rb
160
+ - lib/dependabot/composer/file_fetcher.rb
161
+ - lib/dependabot/composer/file_parser.rb
162
+ - lib/dependabot/composer/file_updater.rb
163
+ - lib/dependabot/composer/file_updater/lockfile_updater.rb
164
+ - lib/dependabot/composer/file_updater/manifest_updater.rb
165
+ - lib/dependabot/composer/metadata_finder.rb
166
+ - lib/dependabot/composer/native_helpers.rb
167
+ - lib/dependabot/composer/requirement.rb
168
+ - lib/dependabot/composer/update_checker.rb
169
+ - lib/dependabot/composer/update_checker/requirements_updater.rb
170
+ - lib/dependabot/composer/update_checker/version_resolver.rb
171
+ - lib/dependabot/composer/version.rb
172
+ homepage: https://github.com/dependabot/dependabot-core
173
+ licenses:
174
+ - Nonstandard
175
+ metadata: {}
176
+ post_install_message:
177
+ rdoc_options: []
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: 2.5.0
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: 2.5.0
190
+ requirements: []
191
+ rubygems_version: 3.0.2
192
+ signing_key:
193
+ specification_version: 4
194
+ summary: PHP (Composer) support for dependabot-core
195
+ test_files: []