dependabot-npm_and_yarn 0.131.2 → 0.133.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/lib/npm6/updater.js +1 -0
- data/helpers/lib/npm7/index.js +5 -0
- data/helpers/lib/npm7/peer-dependency-checker.js +77 -0
- data/helpers/package.json +1 -1
- data/helpers/test/npm7/conflicting-dependency-parser.test.js +67 -0
- data/helpers/test/npm7/fixtures/conflicting-dependency-parser/deeply-nested/package-lock.json +1291 -0
- data/helpers/test/npm7/fixtures/conflicting-dependency-parser/deeply-nested/package.json +14 -0
- data/helpers/test/npm7/fixtures/conflicting-dependency-parser/nested/package-lock.json +411 -0
- data/helpers/test/npm7/fixtures/conflicting-dependency-parser/nested/package.json +14 -0
- data/helpers/test/npm7/fixtures/conflicting-dependency-parser/simple/package-lock.json +64 -0
- data/helpers/test/npm7/fixtures/conflicting-dependency-parser/simple/package.json +14 -0
- data/helpers/test/npm7/fixtures/peer-dependency-checker/peer_dependency/package-lock.json +290 -0
- data/helpers/test/npm7/fixtures/peer-dependency-checker/peer_dependency/package.json +23 -0
- data/helpers/test/npm7/fixtures/peer-dependency-checker/peer_dependency_multiple/package-lock.json +965 -0
- data/helpers/test/npm7/fixtures/peer-dependency-checker/peer_dependency_multiple/package.json +10 -0
- data/helpers/test/npm7/helpers.js +21 -0
- data/helpers/test/npm7/peer-dependency-checker.test.js +107 -0
- data/helpers/yarn.lock +1258 -1271
- data/lib/dependabot/npm_and_yarn/dependency_files_filterer.rb +3 -3
- data/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb +40 -16
- data/lib/dependabot/npm_and_yarn/file_updater.rb +9 -19
- data/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb +409 -167
- data/lib/dependabot/npm_and_yarn/update_checker/subdependency_version_resolver.rb +1 -2
- data/lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb +1 -2
- metadata +19 -4
@@ -20,7 +20,7 @@ module Dependabot
|
|
20
20
|
dependency_files.select do |file|
|
21
21
|
package_files_requiring_update.include?(file) ||
|
22
22
|
package_required_lockfile?(file) ||
|
23
|
-
|
23
|
+
workspaces_lockfile?(file)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -51,8 +51,8 @@ module Dependabot
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
55
|
-
return false unless
|
54
|
+
def workspaces_lockfile?(lockfile)
|
55
|
+
return false unless ["yarn.lock", "package-lock.json"].include?(lockfile.name)
|
56
56
|
return false unless parsed_root_package_json["workspaces"]
|
57
57
|
|
58
58
|
updated_dependencies_in_lockfile?(lockfile)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "dependabot/dependency_file"
|
4
4
|
require "dependabot/npm_and_yarn/file_parser"
|
5
|
+
require "dependabot/npm_and_yarn/helpers"
|
5
6
|
|
6
7
|
module Dependabot
|
7
8
|
module NpmAndYarn
|
@@ -23,23 +24,9 @@ module Dependabot
|
|
23
24
|
potential_lockfiles_for_manifest(manifest_name).each do |lockfile|
|
24
25
|
details =
|
25
26
|
if [*package_locks, *shrinkwraps].include?(lockfile)
|
26
|
-
|
27
|
-
parsed_lockfile.dig("dependencies", dependency_name)
|
27
|
+
npm_lockfile_details(lockfile, dependency_name, manifest_name)
|
28
28
|
else
|
29
|
-
|
30
|
-
details_candidates =
|
31
|
-
parsed_yarn_lock.
|
32
|
-
select { |k, _| k.split(/(?<=\w)\@/)[0] == dependency_name }
|
33
|
-
|
34
|
-
# If there's only one entry for this dependency, use it, even if
|
35
|
-
# the requirement in the lockfile doesn't match
|
36
|
-
if details_candidates.one?
|
37
|
-
details_candidates.first.last
|
38
|
-
else
|
39
|
-
details_candidates.find do |k, _|
|
40
|
-
k.split(/(?<=\w)\@/)[1..-1].join("@") == requirement
|
41
|
-
end&.last
|
42
|
-
end
|
29
|
+
yarn_lockfile_details(lockfile, dependency_name, requirement, manifest_name)
|
43
30
|
end
|
44
31
|
|
45
32
|
return details if details
|
@@ -65,6 +52,43 @@ module Dependabot
|
|
65
52
|
compact
|
66
53
|
end
|
67
54
|
|
55
|
+
def npm_lockfile_details(lockfile, dependency_name, manifest_name)
|
56
|
+
parsed_lockfile = parse_package_lock(lockfile)
|
57
|
+
|
58
|
+
if Helpers.npm_version(lockfile.content) == "npm7"
|
59
|
+
parsed_lockfile.dig(
|
60
|
+
"packages",
|
61
|
+
node_modules_path(manifest_name, dependency_name)
|
62
|
+
)&.slice("version", "resolved", "integrity", "dev")
|
63
|
+
else
|
64
|
+
parsed_lockfile.dig("dependencies", dependency_name)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def yarn_lockfile_details(lockfile, dependency_name, requirement, _manifest_name)
|
69
|
+
parsed_yarn_lock = parse_yarn_lock(lockfile)
|
70
|
+
details_candidates =
|
71
|
+
parsed_yarn_lock.
|
72
|
+
select { |k, _| k.split(/(?<=\w)\@/)[0] == dependency_name }
|
73
|
+
|
74
|
+
# If there's only one entry for this dependency, use it, even if
|
75
|
+
# the requirement in the lockfile doesn't match
|
76
|
+
if details_candidates.one?
|
77
|
+
details_candidates.first.last
|
78
|
+
else
|
79
|
+
details_candidates.find do |k, _|
|
80
|
+
k.split(/(?<=\w)\@/)[1..-1].join("@") == requirement
|
81
|
+
end&.last
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def node_modules_path(manifest_name, dependency_name)
|
86
|
+
return "node_modules/#{dependency_name}" if manifest_name == "package.json"
|
87
|
+
|
88
|
+
workspace_path = manifest_name.gsub("/package.json", "")
|
89
|
+
File.join(workspace_path, "node_modules", dependency_name)
|
90
|
+
end
|
91
|
+
|
68
92
|
def yarn_lock_dependencies
|
69
93
|
dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new
|
70
94
|
|
@@ -117,11 +117,11 @@ module Dependabot
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def package_lock_changed?(package_lock)
|
120
|
-
package_lock.content !=
|
120
|
+
package_lock.content != updated_lockfile_content(package_lock)
|
121
121
|
end
|
122
122
|
|
123
123
|
def shrinkwrap_changed?(shrinkwrap)
|
124
|
-
shrinkwrap.content !=
|
124
|
+
shrinkwrap.content != updated_lockfile_content(shrinkwrap)
|
125
125
|
end
|
126
126
|
|
127
127
|
def updated_manifest_files
|
@@ -150,7 +150,7 @@ module Dependabot
|
|
150
150
|
|
151
151
|
updated_files << updated_file(
|
152
152
|
file: package_lock,
|
153
|
-
content:
|
153
|
+
content: updated_lockfile_content(package_lock)
|
154
154
|
)
|
155
155
|
end
|
156
156
|
|
@@ -159,7 +159,7 @@ module Dependabot
|
|
159
159
|
|
160
160
|
updated_files << updated_file(
|
161
161
|
file: shrinkwrap,
|
162
|
-
content:
|
162
|
+
content: updated_lockfile_content(shrinkwrap)
|
163
163
|
)
|
164
164
|
end
|
165
165
|
|
@@ -181,25 +181,15 @@ module Dependabot
|
|
181
181
|
)
|
182
182
|
end
|
183
183
|
|
184
|
-
def
|
185
|
-
@
|
186
|
-
@
|
187
|
-
npm_lockfile_updater.updated_lockfile_content(package_lock)
|
188
|
-
end
|
189
|
-
|
190
|
-
def updated_shrinkwrap_content(shrinkwrap)
|
191
|
-
@updated_shrinkwrap_content ||= {}
|
192
|
-
@updated_shrinkwrap_content[shrinkwrap.name] ||=
|
193
|
-
npm_lockfile_updater.updated_lockfile_content(shrinkwrap)
|
194
|
-
end
|
195
|
-
|
196
|
-
def npm_lockfile_updater
|
197
|
-
@npm_lockfile_updater ||=
|
184
|
+
def updated_lockfile_content(file)
|
185
|
+
@updated_lockfile_content ||= {}
|
186
|
+
@updated_lockfile_content[file.name] ||=
|
198
187
|
NpmLockfileUpdater.new(
|
188
|
+
lockfile: file,
|
199
189
|
dependencies: dependencies,
|
200
190
|
dependency_files: dependency_files,
|
201
191
|
credentials: credentials
|
202
|
-
)
|
192
|
+
).updated_lockfile.content
|
203
193
|
end
|
204
194
|
|
205
195
|
def updated_package_json_content(file)
|
@@ -17,45 +17,54 @@ module Dependabot
|
|
17
17
|
require_relative "npmrc_builder"
|
18
18
|
require_relative "package_json_updater"
|
19
19
|
|
20
|
-
def initialize(dependencies:, dependency_files:, credentials:)
|
20
|
+
def initialize(lockfile:, dependencies:, dependency_files:, credentials:)
|
21
|
+
@lockfile = lockfile
|
21
22
|
@dependencies = dependencies
|
22
23
|
@dependency_files = dependency_files
|
23
24
|
@credentials = credentials
|
24
25
|
end
|
25
26
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
@updated_lockfile_content ||= {}
|
31
|
-
@updated_lockfile_content[lockfile.name] ||=
|
32
|
-
SharedHelpers.in_a_temporary_directory do
|
33
|
-
path = Pathname.new(lockfile.name).dirname.to_s
|
34
|
-
lockfile_name = Pathname.new(lockfile.name).basename.to_s
|
35
|
-
write_temporary_dependency_files(lockfile.name)
|
36
|
-
updated_files = Dir.chdir(path) do
|
37
|
-
run_current_npm_update(lockfile_name: lockfile_name, lockfile_content: lockfile.content)
|
38
|
-
end
|
39
|
-
updated_content = updated_files.fetch(lockfile_name)
|
40
|
-
post_process_npm_lockfile(lockfile.content, updated_content)
|
41
|
-
end
|
42
|
-
rescue SharedHelpers::HelperSubprocessFailed => e
|
43
|
-
handle_npm_updater_error(e, lockfile)
|
27
|
+
def updated_lockfile
|
28
|
+
updated_file = lockfile.dup
|
29
|
+
updated_file.content = updated_lockfile_content
|
30
|
+
updated_file
|
44
31
|
end
|
45
32
|
|
46
33
|
private
|
47
34
|
|
48
|
-
attr_reader :dependencies, :dependency_files, :credentials
|
35
|
+
attr_reader :lockfile, :dependencies, :dependency_files, :credentials
|
49
36
|
|
50
|
-
UNREACHABLE_GIT =
|
51
|
-
|
52
|
-
FORBIDDEN_PACKAGE =
|
53
|
-
%r{(?<package_req>[^/]+) - (Forbidden|Unauthorized)}.freeze
|
37
|
+
UNREACHABLE_GIT = /fatal: repository '(?<url>.*)' not found/.freeze
|
38
|
+
FORBIDDEN_GIT = /fatal: Authentication failed for '(?<url>.*)'/.freeze
|
39
|
+
FORBIDDEN_PACKAGE = %r{(?<package_req>[^/]+) - (Forbidden|Unauthorized)}.freeze
|
54
40
|
FORBIDDEN_PACKAGE_403 = %r{^403\sForbidden\s
|
55
41
|
-\sGET\shttps?://(?<source>[^/]+)/(?<package_req>[^/\s]+)}x.freeze
|
56
42
|
MISSING_PACKAGE = %r{(?<package_req>[^/]+) - Not found}.freeze
|
57
43
|
INVALID_PACKAGE = /Can't install (?<package_req>.*): Missing/.freeze
|
58
44
|
|
45
|
+
# TODO: look into fixing this in npm, seems like a bug in the git
|
46
|
+
# downloader introduced in npm 7
|
47
|
+
#
|
48
|
+
# NOTE: error message returned from arborist/npm 7 when trying to
|
49
|
+
# fetching a invalid/non-existent git ref
|
50
|
+
NPM7_MISSING_GIT_REF = /already exists and is not an empty directory/.freeze
|
51
|
+
NPM6_MISSING_GIT_REF = /did not match any file\(s\) known to git/.freeze
|
52
|
+
|
53
|
+
def updated_lockfile_content
|
54
|
+
return lockfile.content if npmrc_disables_lockfile?
|
55
|
+
return lockfile.content unless updatable_dependencies.any?
|
56
|
+
|
57
|
+
@updated_lockfile_content ||=
|
58
|
+
SharedHelpers.in_a_temporary_directory do
|
59
|
+
write_temporary_dependency_files
|
60
|
+
updated_files = Dir.chdir(lockfile_directory) { run_current_npm_update }
|
61
|
+
updated_lockfile_content = updated_files.fetch(lockfile_basename)
|
62
|
+
post_process_npm_lockfile(updated_lockfile_content)
|
63
|
+
end
|
64
|
+
rescue SharedHelpers::HelperSubprocessFailed => e
|
65
|
+
handle_npm_updater_error(e)
|
66
|
+
end
|
67
|
+
|
59
68
|
def top_level_dependencies
|
60
69
|
dependencies.select(&:top_level?)
|
61
70
|
end
|
@@ -64,18 +73,14 @@ module Dependabot
|
|
64
73
|
dependencies.reject(&:top_level?)
|
65
74
|
end
|
66
75
|
|
67
|
-
def updatable_dependencies
|
68
|
-
lockfile_dir = Pathname.new(lockfile.name).dirname.to_s
|
76
|
+
def updatable_dependencies
|
69
77
|
dependencies.reject do |dependency|
|
70
|
-
dependency_up_to_date?(
|
71
|
-
top_level_dependency_update_not_required?(dependency,
|
72
|
-
lockfile_dir)
|
78
|
+
dependency_up_to_date?(dependency) || top_level_dependency_update_not_required?(dependency)
|
73
79
|
end
|
74
80
|
end
|
75
81
|
|
76
|
-
def lockfile_dependencies
|
77
|
-
@lockfile_dependencies ||=
|
78
|
-
@lockfile_dependencies[lockfile.name] ||=
|
82
|
+
def lockfile_dependencies
|
83
|
+
@lockfile_dependencies ||=
|
79
84
|
NpmAndYarn::FileParser.new(
|
80
85
|
dependency_files: [lockfile, *package_files],
|
81
86
|
source: nil,
|
@@ -83,9 +88,8 @@ module Dependabot
|
|
83
88
|
).parse
|
84
89
|
end
|
85
90
|
|
86
|
-
def dependency_up_to_date?(
|
87
|
-
existing_dep = lockfile_dependencies
|
88
|
-
find { |dep| dep.name == dependency.name }
|
91
|
+
def dependency_up_to_date?(dependency)
|
92
|
+
existing_dep = lockfile_dependencies.find { |dep| dep.name == dependency.name }
|
89
93
|
|
90
94
|
# If the dependency is missing but top level it should be treated as
|
91
95
|
# not up to date
|
@@ -96,99 +100,204 @@ module Dependabot
|
|
96
100
|
existing_dep&.version == dependency.version
|
97
101
|
end
|
98
102
|
|
99
|
-
# Prevent changes to
|
103
|
+
# NOTE: Prevent changes to npm 6 lockfiles when the dependency has been
|
100
104
|
# required in a package.json outside the current folder (e.g. lerna
|
101
|
-
# proj)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
dependency.top_level? && requirements_for_path.empty?
|
105
|
+
# proj). npm 7 introduces workspace support so we explitly want to
|
106
|
+
# update the root lockfile and check if the dependency is in the
|
107
|
+
# lockfile
|
108
|
+
def top_level_dependency_update_not_required?(dependency)
|
109
|
+
dependency.top_level? &&
|
110
|
+
!dependency_in_package_json?(dependency) &&
|
111
|
+
!dependency_in_lockfile?(dependency)
|
110
112
|
end
|
111
113
|
|
112
|
-
def run_current_npm_update
|
113
|
-
|
114
|
-
{ name: d.name, version: d.version, requirements: d.requirements }
|
115
|
-
end
|
116
|
-
|
117
|
-
run_npm_updater(
|
118
|
-
lockfile_name: lockfile_name,
|
119
|
-
top_level_dependency_updates: top_level_dependency_updates,
|
120
|
-
lockfile_content: lockfile_content
|
121
|
-
)
|
114
|
+
def run_current_npm_update
|
115
|
+
run_npm_updater(top_level_dependencies: top_level_dependencies)
|
122
116
|
end
|
123
117
|
|
124
|
-
def run_previous_npm_update
|
118
|
+
def run_previous_npm_update
|
125
119
|
previous_top_level_dependencies = top_level_dependencies.map do |d|
|
126
|
-
|
120
|
+
Dependabot::Dependency.new(
|
127
121
|
name: d.name,
|
122
|
+
package_manager: d.package_manager,
|
128
123
|
version: d.previous_version,
|
129
|
-
|
130
|
-
|
124
|
+
previous_version: d.previous_version,
|
125
|
+
requirements: d.previous_requirements,
|
126
|
+
previous_requirements: d.previous_requirements
|
127
|
+
)
|
131
128
|
end
|
132
129
|
|
133
|
-
run_npm_updater(
|
134
|
-
lockfile_name: lockfile_name,
|
135
|
-
top_level_dependency_updates: previous_top_level_dependencies,
|
136
|
-
lockfile_content: lockfile_content
|
137
|
-
)
|
130
|
+
run_npm_updater(top_level_dependencies: previous_top_level_dependencies)
|
138
131
|
end
|
139
132
|
|
140
|
-
def run_npm_updater(
|
133
|
+
def run_npm_updater(top_level_dependencies:)
|
141
134
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
142
|
-
if
|
143
|
-
run_npm_top_level_updater(
|
144
|
-
lockfile_name: lockfile_name,
|
145
|
-
top_level_dependency_updates: top_level_dependency_updates,
|
146
|
-
lockfile_content: lockfile_content
|
147
|
-
)
|
135
|
+
if top_level_dependencies.any?
|
136
|
+
run_npm_top_level_updater(top_level_dependencies: top_level_dependencies)
|
148
137
|
else
|
149
|
-
run_npm_subdependency_updater
|
138
|
+
run_npm_subdependency_updater
|
150
139
|
end
|
151
140
|
end
|
152
141
|
end
|
153
142
|
|
154
|
-
def run_npm_top_level_updater(
|
155
|
-
|
156
|
-
|
143
|
+
def run_npm_top_level_updater(top_level_dependencies:)
|
144
|
+
if npm7?
|
145
|
+
run_npm_7_top_level_updater(top_level_dependencies: top_level_dependencies)
|
146
|
+
else
|
147
|
+
SharedHelpers.run_helper_subprocess(
|
148
|
+
command: NativeHelpers.helper_path,
|
149
|
+
function: "npm6:update",
|
150
|
+
args: [
|
151
|
+
Dir.pwd,
|
152
|
+
lockfile_basename,
|
153
|
+
top_level_dependencies.map(&:to_h)
|
154
|
+
]
|
155
|
+
)
|
156
|
+
end
|
157
|
+
end
|
157
158
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
159
|
+
def run_npm_7_top_level_updater(top_level_dependencies:)
|
160
|
+
dependencies_in_current_package_json = top_level_dependencies.any? do |dependency|
|
161
|
+
dependency_in_package_json?(dependency)
|
162
|
+
end
|
163
|
+
|
164
|
+
# NOTE: When updating a dependency in a nested workspace project we
|
165
|
+
# need to run `npm install` without any arguments to update the root
|
166
|
+
# level lockfile after having updated the nested packages package.json
|
167
|
+
# requirement, otherwise npm will add the dependency as a new
|
168
|
+
# top-level dependency to the root lockfile.
|
169
|
+
install_args = ""
|
170
|
+
if dependencies_in_current_package_json
|
171
|
+
# TODO: Update the npm 6 updater to use these args as we currently
|
172
|
+
# do the same in the js updater helper, we've kept it seperate for
|
173
|
+
# the npm 7 rollout
|
174
|
+
install_args = top_level_dependencies.map { |dependency| npm_install_args(dependency) }
|
175
|
+
end
|
176
|
+
|
177
|
+
# NOTE: npm options
|
178
|
+
# - `--force` ignores checks for platform (os, cpu) and engines
|
179
|
+
# - `--dry-run=false` the updater sets a global .npmrc with dry-run:
|
180
|
+
# true to work around an issue in npm 6, we don't want that here
|
181
|
+
# - `--ignore-scripts` disables prepare and prepack scripts which are
|
182
|
+
# run when installing git dependencies
|
183
|
+
command = [
|
184
|
+
"npm",
|
185
|
+
"install",
|
186
|
+
*install_args,
|
187
|
+
"--force",
|
188
|
+
"--dry-run",
|
189
|
+
"false",
|
190
|
+
"--ignore-scripts",
|
191
|
+
"--package-lock-only"
|
192
|
+
].join(" ")
|
193
|
+
SharedHelpers.run_shell_command(command)
|
194
|
+
{ lockfile_basename => File.read(lockfile_basename) }
|
195
|
+
end
|
196
|
+
|
197
|
+
def run_npm_subdependency_updater
|
198
|
+
if npm7?
|
199
|
+
run_npm_7_subdependency_updater
|
200
|
+
else
|
201
|
+
SharedHelpers.run_helper_subprocess(
|
202
|
+
command: NativeHelpers.helper_path,
|
203
|
+
function: "npm6:updateSubdependency",
|
204
|
+
args: [Dir.pwd, lockfile_basename, sub_dependencies.map(&:to_h)]
|
205
|
+
)
|
206
|
+
end
|
167
207
|
end
|
168
208
|
|
169
|
-
def
|
170
|
-
|
171
|
-
|
209
|
+
def run_npm_7_subdependency_updater
|
210
|
+
dependency_names = sub_dependencies.map(&:name)
|
211
|
+
# NOTE: npm options
|
212
|
+
# - `--force` ignores checks for platform (os, cpu) and engines
|
213
|
+
# - `--dry-run=false` the updater sets a global .npmrc with dry-run: true to
|
214
|
+
# work around an issue in npm 6, we don't want that here
|
215
|
+
# - `--ignore-scripts` disables prepare and prepack scripts which are run
|
216
|
+
# when installing git dependencies
|
217
|
+
command = [
|
218
|
+
"npm",
|
219
|
+
"update",
|
220
|
+
*dependency_names,
|
221
|
+
"--force",
|
222
|
+
"--dry-run",
|
223
|
+
"false",
|
224
|
+
"--ignore-scripts",
|
225
|
+
"--package-lock-only"
|
226
|
+
].join(" ")
|
227
|
+
SharedHelpers.run_shell_command(command)
|
228
|
+
{ lockfile_basename => File.read(lockfile_basename) }
|
229
|
+
end
|
230
|
+
|
231
|
+
def updated_version_requirement_for_dependency(dependency)
|
232
|
+
flattenend_manifest_dependencies[dependency.name]
|
233
|
+
end
|
234
|
+
|
235
|
+
# TODO: Add the raw updated requirement to the Dependency instance
|
236
|
+
# instead of fishing it out of the updated package json, we need to do
|
237
|
+
# this because we don't store the same requirement in
|
238
|
+
# Dependency#requirements for git dependencies - see PackageJsonUpdater
|
239
|
+
def flattenend_manifest_dependencies
|
240
|
+
return @flattenend_manifest_dependencies if defined?(@flattenend_manifest_dependencies)
|
241
|
+
|
242
|
+
@flattenend_manifest_dependencies =
|
243
|
+
NpmAndYarn::FileParser::DEPENDENCY_TYPES.inject({}) do |deps, type|
|
244
|
+
deps.merge(parsed_package_json[type] || {})
|
245
|
+
end
|
246
|
+
end
|
172
247
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
248
|
+
def npm_install_args(dependency)
|
249
|
+
git_requirement = dependency.requirements.find { |req| req[:source] && req[:source][:type] == "git" }
|
250
|
+
|
251
|
+
if git_requirement
|
252
|
+
# NOTE: For git dependencies we loose some information about the
|
253
|
+
# requirement that's only available in the package.json, e.g. when
|
254
|
+
# specifying a semver tag:
|
255
|
+
# `dependabot/depeendabot-core#semver:^0.1` - this is required to
|
256
|
+
# pass the correct install argument to `npm install`
|
257
|
+
updated_version_requirement = updated_version_requirement_for_dependency(dependency)
|
258
|
+
updated_version_requirement ||= git_requirement[:source][:url]
|
259
|
+
|
260
|
+
# NOTE: Git is configured to auth over https while updating
|
261
|
+
updated_version_requirement = updated_version_requirement.gsub(
|
262
|
+
%r{git\+ssh://git@(.*?)[:/]}, 'https://\1/'
|
263
|
+
)
|
264
|
+
|
265
|
+
# NOTE: Keep any semver range that has already been updated by the
|
266
|
+
# PackageJsonUpdater when installing the new version
|
267
|
+
if updated_version_requirement.include?(dependency.version)
|
268
|
+
"#{dependency.name}@#{updated_version_requirement}"
|
269
|
+
else
|
270
|
+
"#{dependency.name}@#{updated_version_requirement.sub(/#.*/, '')}##{dependency.version}"
|
271
|
+
end
|
272
|
+
else
|
273
|
+
"#{dependency.name}@#{dependency.version}"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def dependency_in_package_json?(dependency)
|
278
|
+
dependency.requirements.any? do |req|
|
279
|
+
req[:file] == package_json.name
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def dependency_in_lockfile?(dependency)
|
284
|
+
lockfile_dependencies.any? do |dep|
|
285
|
+
dep.name == dependency.name
|
286
|
+
end
|
178
287
|
end
|
179
288
|
|
180
289
|
# rubocop:disable Metrics/AbcSize
|
181
290
|
# rubocop:disable Metrics/CyclomaticComplexity
|
182
291
|
# rubocop:disable Metrics/PerceivedComplexity
|
183
292
|
# rubocop:disable Metrics/MethodLength
|
184
|
-
def handle_npm_updater_error(error
|
293
|
+
def handle_npm_updater_error(error)
|
185
294
|
error_message = error.message
|
186
295
|
if error_message.match?(MISSING_PACKAGE)
|
187
296
|
package_name = error_message.match(MISSING_PACKAGE).
|
188
297
|
named_captures["package_req"]
|
189
298
|
sanitized_name = sanitize_package_name(package_name)
|
190
299
|
sanitized_error = error_message.gsub(package_name, sanitized_name)
|
191
|
-
handle_missing_package(sanitized_name, sanitized_error
|
300
|
+
handle_missing_package(sanitized_name, sanitized_error)
|
192
301
|
end
|
193
302
|
|
194
303
|
# Invalid package: When the package.json doesn't include a name or
|
@@ -198,9 +307,9 @@ module Dependabot
|
|
198
307
|
# workspace project)
|
199
308
|
sub_dep_local_path_error = "does not contain a package.json file"
|
200
309
|
if error_message.match?(INVALID_PACKAGE) ||
|
201
|
-
error_message.
|
310
|
+
error_message.include?("Invalid package name") ||
|
202
311
|
error_message.include?(sub_dep_local_path_error)
|
203
|
-
raise_resolvability_error(error_message
|
312
|
+
raise_resolvability_error(error_message)
|
204
313
|
end
|
205
314
|
|
206
315
|
# TODO: Move this logic to the version resolver and check if a new
|
@@ -222,9 +331,9 @@ module Dependabot
|
|
222
331
|
# This happens if a new version has been published but npm is having
|
223
332
|
# consistency issues and the version isn't fully available on all
|
224
333
|
# queries
|
225
|
-
if error_message.
|
334
|
+
if error_message.include?("No matching vers") &&
|
226
335
|
dependencies_in_error_message?(error_message) &&
|
227
|
-
resolvable_before_update?
|
336
|
+
resolvable_before_update?
|
228
337
|
|
229
338
|
# Raise a bespoke error so we can capture and ignore it if
|
230
339
|
# we're trying to create a new PR (which will be created
|
@@ -237,7 +346,7 @@ module Dependabot
|
|
237
346
|
named_captures["package_req"]
|
238
347
|
sanitized_name = sanitize_package_name(package_name)
|
239
348
|
sanitized_error = error_message.gsub(package_name, sanitized_name)
|
240
|
-
handle_missing_package(sanitized_name, sanitized_error
|
349
|
+
handle_missing_package(sanitized_name, sanitized_error)
|
241
350
|
end
|
242
351
|
|
243
352
|
# Some private registries return a 403 when the user is readonly
|
@@ -246,13 +355,11 @@ module Dependabot
|
|
246
355
|
named_captures["package_req"]
|
247
356
|
sanitized_name = sanitize_package_name(package_name)
|
248
357
|
sanitized_error = error_message.gsub(package_name, sanitized_name)
|
249
|
-
handle_missing_package(sanitized_name, sanitized_error
|
358
|
+
handle_missing_package(sanitized_name, sanitized_error)
|
250
359
|
end
|
251
360
|
|
252
|
-
if error_message.match
|
253
|
-
dependency_url =
|
254
|
-
error_message.match(UNREACHABLE_GIT).
|
255
|
-
named_captures.fetch("url")
|
361
|
+
if (git_error = error_message.match(UNREACHABLE_GIT) || error_message.match(FORBIDDEN_GIT))
|
362
|
+
dependency_url = git_error.named_captures.fetch("url")
|
256
363
|
|
257
364
|
raise Dependabot::GitDependenciesNotReachable, dependency_url
|
258
365
|
end
|
@@ -265,17 +372,24 @@ module Dependabot
|
|
265
372
|
# people to re-generate their lockfiles (Future feature idea: add a
|
266
373
|
# way to click-to-fix the lockfile from the issue)
|
267
374
|
if error_message.include?("Cannot read property 'match' of ") &&
|
268
|
-
!resolvable_before_update?
|
269
|
-
raise_missing_lockfile_version_resolvability_error(error_message
|
270
|
-
lockfile)
|
375
|
+
!resolvable_before_update?
|
376
|
+
raise_missing_lockfile_version_resolvability_error(error_message)
|
271
377
|
end
|
272
378
|
|
273
|
-
if (error_message.
|
274
|
-
error_message.include?("
|
379
|
+
if (error_message.include?("No matching vers") ||
|
380
|
+
error_message.include?("404 Not Found") ||
|
275
381
|
error_message.include?("Non-registry package missing package") ||
|
276
|
-
error_message.include?("Invalid tag name")
|
277
|
-
|
278
|
-
|
382
|
+
error_message.include?("Invalid tag name") ||
|
383
|
+
error_message.match?(NPM6_MISSING_GIT_REF) ||
|
384
|
+
error_message.match?(NPM7_MISSING_GIT_REF)) &&
|
385
|
+
!resolvable_before_update?
|
386
|
+
raise_resolvability_error(error_message)
|
387
|
+
end
|
388
|
+
|
389
|
+
# NOTE: This check was introduced in npm7/arborist
|
390
|
+
if error_message.include?("must provide string spec")
|
391
|
+
msg = "Error parsing your package.json manifest: the version requirement must be a string"
|
392
|
+
raise Dependabot::DependencyFileNotParseable, msg
|
279
393
|
end
|
280
394
|
|
281
395
|
raise error
|
@@ -285,17 +399,15 @@ module Dependabot
|
|
285
399
|
# rubocop:enable Metrics/PerceivedComplexity
|
286
400
|
# rubocop:enable Metrics/MethodLength
|
287
401
|
|
288
|
-
def raise_resolvability_error(error_message
|
402
|
+
def raise_resolvability_error(error_message)
|
289
403
|
dependency_names = dependencies.map(&:name).join(", ")
|
290
404
|
msg = "Error whilst updating #{dependency_names} in "\
|
291
405
|
"#{lockfile.path}:\n#{error_message}"
|
292
406
|
raise Dependabot::DependencyFileNotResolvable, msg
|
293
407
|
end
|
294
408
|
|
295
|
-
def raise_missing_lockfile_version_resolvability_error(error_message
|
296
|
-
|
297
|
-
lockfile_dir = Pathname.new(lockfile.name).dirname
|
298
|
-
modules_path = lockfile_dir.join("node_modules")
|
409
|
+
def raise_missing_lockfile_version_resolvability_error(error_message)
|
410
|
+
modules_path = File.join(lockfile_directory, "node_modules")
|
299
411
|
# NOTE: don't include the dependency names to prevent opening
|
300
412
|
# multiple issues for each dependency that fails because we unique
|
301
413
|
# issues on the error message (issue detail) on the backend
|
@@ -310,11 +422,10 @@ module Dependabot
|
|
310
422
|
raise Dependabot::DependencyFileNotResolvable, msg
|
311
423
|
end
|
312
424
|
|
313
|
-
def handle_missing_package(package_name, error_message
|
314
|
-
missing_dep = lockfile_dependencies
|
315
|
-
find { |dep| dep.name == package_name }
|
425
|
+
def handle_missing_package(package_name, error_message)
|
426
|
+
missing_dep = lockfile_dependencies.find { |dep| dep.name == package_name }
|
316
427
|
|
317
|
-
raise_resolvability_error(error_message
|
428
|
+
raise_resolvability_error(error_message) unless missing_dep
|
318
429
|
|
319
430
|
reg = NpmAndYarn::UpdateChecker::RegistryFinder.new(
|
320
431
|
dependency: missing_dep,
|
@@ -336,23 +447,14 @@ module Dependabot
|
|
336
447
|
end
|
337
448
|
end
|
338
449
|
|
339
|
-
def resolvable_before_update?
|
340
|
-
@resolvable_before_update
|
341
|
-
return @resolvable_before_update[lockfile.name] if @resolvable_before_update.key?(lockfile.name)
|
450
|
+
def resolvable_before_update?
|
451
|
+
return @resolvable_before_update if defined?(@resolvable_before_update)
|
342
452
|
|
343
|
-
@resolvable_before_update
|
453
|
+
@resolvable_before_update =
|
344
454
|
begin
|
345
455
|
SharedHelpers.in_a_temporary_directory do
|
346
|
-
write_temporary_dependency_files(
|
347
|
-
|
348
|
-
update_package_json: false
|
349
|
-
)
|
350
|
-
|
351
|
-
lockfile_name = Pathname.new(lockfile.name).basename.to_s
|
352
|
-
path = Pathname.new(lockfile.name).dirname.to_s
|
353
|
-
Dir.chdir(path) do
|
354
|
-
run_previous_npm_update(lockfile_name: lockfile_name, lockfile_content: lockfile.content)
|
355
|
-
end
|
456
|
+
write_temporary_dependency_files(update_package_json: false)
|
457
|
+
Dir.chdir(lockfile_directory) { run_previous_npm_update }
|
356
458
|
end
|
357
459
|
|
358
460
|
true
|
@@ -370,12 +472,10 @@ module Dependabot
|
|
370
472
|
end
|
371
473
|
end
|
372
474
|
|
373
|
-
def write_temporary_dependency_files(
|
374
|
-
|
375
|
-
write_lockfiles(lockfile_name)
|
475
|
+
def write_temporary_dependency_files(update_package_json: true)
|
476
|
+
write_lockfiles
|
376
477
|
|
377
|
-
|
378
|
-
File.write(File.join(dir, ".npmrc"), npmrc_content)
|
478
|
+
File.write(File.join(lockfile_directory, ".npmrc"), npmrc_content)
|
379
479
|
|
380
480
|
package_files.each do |file|
|
381
481
|
path = file.name
|
@@ -388,8 +488,11 @@ module Dependabot
|
|
388
488
|
file.content
|
389
489
|
end
|
390
490
|
|
391
|
-
#
|
392
|
-
#
|
491
|
+
# TODO: Figure out if we need to lock git deps for npm 7 and can
|
492
|
+
# start deprecating this hornets nest
|
493
|
+
#
|
494
|
+
# NOTE: When updating a package-lock.json we have to manually lock
|
495
|
+
# all git dependencies, otherwise npm will (unhelpfully) update them
|
393
496
|
updated_content = lock_git_deps(updated_content)
|
394
497
|
updated_content = replace_ssh_sources(updated_content)
|
395
498
|
updated_content = lock_deps_with_latest_reqs(updated_content)
|
@@ -399,9 +502,9 @@ module Dependabot
|
|
399
502
|
end
|
400
503
|
end
|
401
504
|
|
402
|
-
def write_lockfiles
|
505
|
+
def write_lockfiles
|
403
506
|
excluded_lock =
|
404
|
-
case
|
507
|
+
case lockfile.name
|
405
508
|
when "package-lock.json" then "npm-shrinkwrap.json"
|
406
509
|
when "npm-shrinkwrap.json" then "package-lock.json"
|
407
510
|
end
|
@@ -502,57 +605,166 @@ module Dependabot
|
|
502
605
|
@git_ssh_requirements_to_swap
|
503
606
|
end
|
504
607
|
|
505
|
-
def post_process_npm_lockfile(
|
506
|
-
updated_content =
|
507
|
-
replace_project_metadata(updated_content, original_content)
|
508
|
-
|
608
|
+
def post_process_npm_lockfile(updated_lockfile_content)
|
509
609
|
# Switch SSH requirements back for git dependencies
|
610
|
+
updated_lockfile_content = replace_swapped_git_ssh_requirements(updated_lockfile_content)
|
611
|
+
|
612
|
+
# Switch from details back for git dependencies (they will have
|
613
|
+
# changed because we locked them)
|
614
|
+
updated_lockfile_content = replace_locked_git_dependencies(updated_lockfile_content)
|
615
|
+
|
616
|
+
parsed_updated_lockfile_content = JSON.parse(updated_lockfile_content)
|
617
|
+
|
618
|
+
# Restore lockfile name attribute from the original lockfile
|
619
|
+
updated_lockfile_content = replace_project_name(updated_lockfile_content, parsed_updated_lockfile_content)
|
620
|
+
|
621
|
+
# Restore npm 7 "packages" "name" entry from package.json if previously set
|
622
|
+
updated_lockfile_content = restore_packages_name(updated_lockfile_content, parsed_updated_lockfile_content)
|
623
|
+
|
624
|
+
# Switch back npm 7 lockfile "pacakages" requirements from the package.json
|
625
|
+
updated_lockfile_content = restore_locked_package_dependencies(
|
626
|
+
updated_lockfile_content, parsed_updated_lockfile_content
|
627
|
+
)
|
628
|
+
|
629
|
+
# Switch back the protocol of tarball resolutions if they've changed
|
630
|
+
# (fixes an npm bug, which appears to be applied inconsistently)
|
631
|
+
replace_tarball_urls(updated_lockfile_content)
|
632
|
+
end
|
633
|
+
|
634
|
+
def replace_project_name(updated_lockfile_content, parsed_updated_lockfile_content)
|
635
|
+
current_name = parsed_updated_lockfile_content["name"]
|
636
|
+
original_name = parsed_lockfile["name"]
|
637
|
+
if original_name
|
638
|
+
updated_lockfile_content = replace_lockfile_name_attribute(
|
639
|
+
current_name, original_name, updated_lockfile_content
|
640
|
+
)
|
641
|
+
end
|
642
|
+
updated_lockfile_content
|
643
|
+
end
|
644
|
+
|
645
|
+
def restore_packages_name(updated_lockfile_content, parsed_updated_lockfile_content)
|
646
|
+
return updated_lockfile_content unless npm7?
|
647
|
+
|
648
|
+
current_name = parsed_updated_lockfile_content.dig("packages", "", "name")
|
649
|
+
original_name = parsed_lockfile.dig("packages", "", "name")
|
650
|
+
|
651
|
+
# TODO: Submit a patch to npm fixing this issue making `npm install`
|
652
|
+
# consistent with `npm install --package-lock-only`
|
653
|
+
#
|
654
|
+
# NOTE: This is a workaround for npm adding a `name` attribute to the
|
655
|
+
# packages section in the lockfile because we install using
|
656
|
+
# `--package-lock-only`
|
657
|
+
if !original_name
|
658
|
+
updated_lockfile_content = remove_lockfile_packages_name_attribute(
|
659
|
+
current_name, updated_lockfile_content
|
660
|
+
)
|
661
|
+
elsif original_name && original_name != current_name
|
662
|
+
updated_lockfile_content = replace_lockfile_packages_name_attribute(
|
663
|
+
current_name, original_name, updated_lockfile_content
|
664
|
+
)
|
665
|
+
end
|
666
|
+
|
667
|
+
updated_lockfile_content
|
668
|
+
end
|
669
|
+
|
670
|
+
def replace_lockfile_name_attribute(current_name, original_name, updated_lockfile_content)
|
671
|
+
updated_lockfile_content.sub(
|
672
|
+
/"name":\s"#{current_name}"/,
|
673
|
+
"\"name\": \"#{original_name}\""
|
674
|
+
)
|
675
|
+
end
|
676
|
+
|
677
|
+
def replace_lockfile_packages_name_attribute(current_name, original_name, updated_lockfile_content)
|
678
|
+
packages_key_line = '"": {'
|
679
|
+
updated_lockfile_content.sub(
|
680
|
+
/(#{packages_key_line}[\n\s]+"name":\s)"#{current_name}"/,
|
681
|
+
'\1"' + original_name + '"'
|
682
|
+
)
|
683
|
+
end
|
684
|
+
|
685
|
+
def remove_lockfile_packages_name_attribute(current_name, updated_lockfile_content)
|
686
|
+
packages_key_line = '"": {'
|
687
|
+
updated_lockfile_content.gsub(/(#{packages_key_line})[\n\s]+"name":\s"#{current_name}",/, '\1')
|
688
|
+
end
|
689
|
+
|
690
|
+
# NOTE: This is a workaround to "sync" what's in package.json
|
691
|
+
# requirements and the `packages.""` entry in npm 7 v2 lockfiles. These
|
692
|
+
# get out of sync because we lock git dependencies (that are not being
|
693
|
+
# updated) to a specific sha to prevent unrelated updates and the way we
|
694
|
+
# invoke the `npm install` cli, where we might tell npm to install a
|
695
|
+
# specific versionm e.g. `npm install eslint@1.1.8` but we keep the
|
696
|
+
# `package.json` requirement for eslint at `^1.0.0`, in which case we
|
697
|
+
# need to copy this from the manifest to the lockfile after the update
|
698
|
+
# has finished.
|
699
|
+
def restore_locked_package_dependencies(updated_lockfile_content, parsed_updated_lockfile_content)
|
700
|
+
return updated_lockfile_content unless npm7?
|
701
|
+
|
702
|
+
dependency_names_to_restore = (dependencies.map(&:name) + git_dependencies_to_lock.keys).uniq
|
703
|
+
|
704
|
+
NpmAndYarn::FileParser::DEPENDENCY_TYPES.each do |type|
|
705
|
+
parsed_package_json.fetch(type, {}).each do |dependency_name, original_requirement|
|
706
|
+
next unless dependency_names_to_restore.include?(dependency_name)
|
707
|
+
|
708
|
+
locked_requirement = parsed_updated_lockfile_content.dig("packages", "", type, dependency_name)
|
709
|
+
next unless locked_requirement
|
710
|
+
|
711
|
+
locked_req = %("#{dependency_name}": "#{locked_requirement}")
|
712
|
+
original_req = %("#{dependency_name}": "#{original_requirement}")
|
713
|
+
updated_lockfile_content = updated_lockfile_content.gsub(locked_req, original_req)
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
updated_lockfile_content
|
718
|
+
end
|
719
|
+
|
720
|
+
def replace_swapped_git_ssh_requirements(updated_lockfile_content)
|
510
721
|
git_ssh_requirements_to_swap.each do |req|
|
511
722
|
new_r = req.gsub(%r{git\+ssh://git@(.*?)[:/]}, 'git+https://\1/')
|
512
723
|
old_r = req.gsub(%r{git@(.*?)[:/]}, 'git@\1/')
|
513
|
-
|
724
|
+
updated_lockfile_content = updated_lockfile_content.gsub(new_r, old_r)
|
514
725
|
end
|
515
726
|
|
727
|
+
updated_lockfile_content
|
728
|
+
end
|
729
|
+
|
730
|
+
def replace_locked_git_dependencies(updated_lockfile_content)
|
516
731
|
# Switch from details back for git dependencies (they will have
|
517
732
|
# changed because we locked them)
|
518
|
-
git_dependencies_to_lock.each do |
|
733
|
+
git_dependencies_to_lock.each do |dependency_name, details|
|
519
734
|
next unless details[:version] && details[:from]
|
520
735
|
|
521
736
|
# When locking git dependencies in package.json we set the version
|
522
737
|
# to be the git commit from the lockfile "version" field which
|
523
738
|
# updates the lockfile "from" field to the new git commit when we
|
524
739
|
# run npm install
|
525
|
-
locked_from = %("from": "#{details[:version]}")
|
526
740
|
original_from = %("from": "#{details[:from]}")
|
527
|
-
|
741
|
+
if npm7?
|
742
|
+
# NOTE: The `from` syntax has changed in npm 7 to inclued the dependency name
|
743
|
+
npm7_locked_from = %("from": "#{dependency_name}@#{details[:version]}")
|
744
|
+
updated_lockfile_content = updated_lockfile_content.gsub(npm7_locked_from, original_from)
|
745
|
+
else
|
746
|
+
npm6_locked_from = %("from": "#{details[:version]}")
|
747
|
+
updated_lockfile_content = updated_lockfile_content.gsub(npm6_locked_from, original_from)
|
748
|
+
end
|
528
749
|
end
|
529
750
|
|
530
|
-
|
531
|
-
|
751
|
+
updated_lockfile_content
|
752
|
+
end
|
753
|
+
|
754
|
+
def replace_tarball_urls(updated_lockfile_content)
|
532
755
|
tarball_urls.each do |url|
|
533
756
|
trimmed_url = url.gsub(/(\d+\.)*tgz$/, "")
|
534
757
|
incorrect_url = if url.start_with?("https")
|
535
758
|
trimmed_url.gsub(/^https:/, "http:")
|
536
759
|
else trimmed_url.gsub(/^http:/, "https:")
|
537
760
|
end
|
538
|
-
|
761
|
+
updated_lockfile_content = updated_lockfile_content.gsub(
|
539
762
|
/#{Regexp.quote(incorrect_url)}(?=(\d+\.)*tgz")/,
|
540
763
|
trimmed_url
|
541
764
|
)
|
542
765
|
end
|
543
766
|
|
544
|
-
|
545
|
-
end
|
546
|
-
|
547
|
-
def replace_project_metadata(new_content, old_content)
|
548
|
-
old_name = old_content.match(/(?<="name": ").*(?=",)/)&.to_s
|
549
|
-
|
550
|
-
if old_name
|
551
|
-
new_content = new_content.
|
552
|
-
sub(/(?<="name": ").*(?=",)/, old_name)
|
553
|
-
end
|
554
|
-
|
555
|
-
new_content
|
767
|
+
updated_lockfile_content
|
556
768
|
end
|
557
769
|
|
558
770
|
def tarball_urls
|
@@ -592,6 +804,12 @@ module Dependabot
|
|
592
804
|
npmrc_content.match?(/^package-lock\s*=\s*false/)
|
593
805
|
end
|
594
806
|
|
807
|
+
def npm7?
|
808
|
+
return @npm7 if defined?(@npm7)
|
809
|
+
|
810
|
+
@npm7 = Dependabot::NpmAndYarn::Helpers.npm_version(lockfile.content) == "npm7"
|
811
|
+
end
|
812
|
+
|
595
813
|
def sanitized_package_json_content(content)
|
596
814
|
content.
|
597
815
|
gsub(/\{\{[^\}]*?\}\}/, "something"). # {{ nm }} syntax not allowed
|
@@ -603,6 +821,30 @@ module Dependabot
|
|
603
821
|
package_name.gsub("%2f", "/").gsub("%2F", "/")
|
604
822
|
end
|
605
823
|
|
824
|
+
def lockfile_directory
|
825
|
+
Pathname.new(lockfile.name).dirname.to_s
|
826
|
+
end
|
827
|
+
|
828
|
+
def lockfile_basename
|
829
|
+
Pathname.new(lockfile.name).basename.to_s
|
830
|
+
end
|
831
|
+
|
832
|
+
def parsed_lockfile
|
833
|
+
@parsed_lockfile ||= JSON.parse(lockfile.content)
|
834
|
+
end
|
835
|
+
|
836
|
+
def parsed_package_json
|
837
|
+
return {} unless package_json
|
838
|
+
return @parsed_package_json if defined?(@parsed_package_json)
|
839
|
+
|
840
|
+
@parsed_package_json = JSON.parse(updated_package_json_content(package_json))
|
841
|
+
end
|
842
|
+
|
843
|
+
def package_json
|
844
|
+
package_name = lockfile.name.sub(lockfile_basename, "package.json")
|
845
|
+
package_files.find { |f| f.name == package_name }
|
846
|
+
end
|
847
|
+
|
606
848
|
def package_locks
|
607
849
|
@package_locks ||=
|
608
850
|
dependency_files.
|