Module: Dependabot::NpmAndYarn::Helpers
- Extended by:
- T::Sig
- Defined in:
- lib/dependabot/npm_and_yarn/helpers.rb
Constant Summary collapse
- YARN_PATH_NOT_FOUND =
/^.*(?<error>The "yarn-path" option has been set \(in [^)]+\), but the specified location doesn't exist)/
Class Method Summary collapse
- .dependencies_with_all_versions_metadata(dependency_set) ⇒ Object
- .fetch_yarnrc_yml_value(key, default_value) ⇒ Object
- .handle_subprocess_failure(error) ⇒ Object
- .npm8?(package_lock) ⇒ Boolean
- .npm_version_numeric(lockfile) ⇒ Object
- .pnpm_lockfile_version(pnpm_lock) ⇒ Object
- .pnpm_version_numeric(pnpm_lock) ⇒ Object
-
.run_npm_command(command, fingerprint: command) ⇒ Object
Run single npm command returning stdout/stderr.
-
.run_pnpm_command(command, fingerprint: nil) ⇒ Object
Run single pnpm command returning stdout/stderr.
-
.run_yarn_command(command, fingerprint: nil) ⇒ Object
Setup yarn and run a single yarn command returning stdout/stderr.
-
.run_yarn_commands(*commands) ⇒ Object
Run any number of yarn commands while ensuring that ‘enableScripts` is set to false.
- .setup_yarn_berry ⇒ Object
- .yarn_4_or_higher? ⇒ Boolean
- .yarn_berry?(yarn_lock) ⇒ Boolean
- .yarn_berry_args ⇒ Object
- .yarn_berry_disable_scripts? ⇒ Boolean
- .yarn_berry_skip_build? ⇒ Boolean
- .yarn_major_version ⇒ Object
- .yarn_offline_cache? ⇒ Boolean
- .yarn_version_numeric(yarn_lock) ⇒ Object
- .yarn_zero_install? ⇒ Boolean
Class Method Details
.dependencies_with_all_versions_metadata(dependency_set) ⇒ Object
218 219 220 221 222 223 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 218 def self.(dependency_set) dependency_set.dependencies.map do |dependency| dependency.[:all_versions] = dependency_set.all_versions_for_name(dependency.name) dependency end end |
.fetch_yarnrc_yml_value(key, default_value) ⇒ Object
52 53 54 55 56 57 58 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 52 def self.fetch_yarnrc_yml_value(key, default_value) if File.exist?(".yarnrc.yml") && (yarnrc = YAML.load_file(".yarnrc.yml")) yarnrc.fetch(key, default_value) else default_value end end |
.handle_subprocess_failure(error) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 103 def self.handle_subprocess_failure(error) = error. if YARN_PATH_NOT_FOUND.match?() error = T.must(T.must(YARN_PATH_NOT_FOUND.match())[:error]).sub(Dir.pwd, ".") raise MisconfiguredTooling.new("Yarn", error) end if .include?("Internal Error") && .include?(".yarnrc.yml") raise MisconfiguredTooling.new("Invalid .yarnrc.yml file", ) end raise end |
.npm8?(package_lock) ⇒ Boolean
61 62 63 64 65 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 61 def self.npm8?(package_lock) return true unless package_lock npm_version_numeric(package_lock) == 8 end |
.npm_version_numeric(lockfile) ⇒ Object
18 19 20 21 22 23 24 25 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 18 def self.npm_version_numeric(lockfile) lockfile_content = T.must(lockfile.content) return 8 if JSON.parse(lockfile_content)["lockfileVersion"].to_i >= 2 6 rescue JSON::ParserError 6 end |
.pnpm_lockfile_version(pnpm_lock) ⇒ Object
213 214 215 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 213 def self.pnpm_lockfile_version(pnpm_lock) pnpm_lock.content.match(/^lockfileVersion: ['"]?(?<version>[\d.]+)/)[:version] end |
.pnpm_version_numeric(pnpm_lock) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 40 def self.pnpm_version_numeric(pnpm_lock) if pnpm_lockfile_version(pnpm_lock).to_f >= 9.0 9 elsif pnpm_lockfile_version(pnpm_lock).to_f >= 6.0 8 elsif pnpm_lockfile_version(pnpm_lock).to_f >= 5.4 7 else 6 end end |
.run_npm_command(command, fingerprint: command) ⇒ Object
Run single npm command returning stdout/stderr.
NOTE: Needs to be explicitly run through corepack to respect the ‘packageManager` setting in `package.json`, because corepack does not add shims for NPM.
192 193 194 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 192 def self.run_npm_command(command, fingerprint: command) SharedHelpers.run_shell_command("corepack npm #{command}", fingerprint: "corepack npm #{fingerprint}") end |
.run_pnpm_command(command, fingerprint: nil) ⇒ Object
Run single pnpm command returning stdout/stderr
203 204 205 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 203 def self.run_pnpm_command(command, fingerprint: nil) SharedHelpers.run_shell_command("pnpm #{command}", fingerprint: "pnpm #{fingerprint || command}") end |
.run_yarn_command(command, fingerprint: nil) ⇒ Object
Setup yarn and run a single yarn command returning stdout/stderr
197 198 199 200 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 197 def self.run_yarn_command(command, fingerprint: nil) setup_yarn_berry run_single_yarn_command(command, fingerprint: fingerprint) end |
.run_yarn_commands(*commands) ⇒ Object
Run any number of yarn commands while ensuring that ‘enableScripts` is set to false. Yarn commands should not be ran outside of this helper to ensure that postinstall scripts are never executed, as they could contain malicious code.
182 183 184 185 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 182 def self.run_yarn_commands(*commands) setup_yarn_berry commands.each { |cmd, fingerprint| run_single_yarn_command(cmd, fingerprint: fingerprint) } end |
.setup_yarn_berry ⇒ Object
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 156 def self.setup_yarn_berry # Always disable immutable installs so yarn's CI detection doesn't prevent updates. run_single_yarn_command("config set enableImmutableInstalls false") # Do not generate a cache if offline cache disabled. Otherwise side effects may confuse further checks run_single_yarn_command("config set enableGlobalCache true") unless yarn_berry_skip_build? # We never want to execute postinstall scripts, either set this config or mode=skip-build must be set run_single_yarn_command("config set enableScripts false") if yarn_berry_disable_scripts? if (http_proxy = ENV.fetch("HTTP_PROXY", false)) run_single_yarn_command("config set httpProxy #{http_proxy}", fingerprint: "config set httpProxy <proxy>") end if (https_proxy = ENV.fetch("HTTPS_PROXY", false)) run_single_yarn_command("config set httpsProxy #{https_proxy}", fingerprint: "config set httpsProxy <proxy>") end return unless (ca_file_path = ENV.fetch("NODE_EXTRA_CA_CERTS", false)) if yarn_4_or_higher? run_single_yarn_command("config set httpsCaFilePath #{ca_file_path}") else run_single_yarn_command("config set caFilePath #{ca_file_path}") end end |
.yarn_4_or_higher? ⇒ Boolean
152 153 154 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 152 def self.yarn_4_or_higher? yarn_major_version >= 4 end |
.yarn_berry?(yarn_lock) ⇒ Boolean
68 69 70 71 72 73 74 75 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 68 def self.yarn_berry?(yarn_lock) return false if yarn_lock.nil? || yarn_lock.content.nil? yaml = YAML.safe_load(T.must(yarn_lock.content)) yaml.key?("__metadata") rescue StandardError false end |
.yarn_berry_args ⇒ Object
129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 129 def self.yarn_berry_args if yarn_major_version == 2 "" elsif yarn_berry_skip_build? "--mode=skip-build" else # We only want this mode if the cache is not being updated/managed # as this improperly leaves old versions in the cache "--mode=update-lockfile" end end |
.yarn_berry_disable_scripts? ⇒ Boolean
147 148 149 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 147 def self.yarn_berry_disable_scripts? yarn_major_version == 2 || !yarn_zero_install? end |
.yarn_berry_skip_build? ⇒ Boolean
142 143 144 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 142 def self.yarn_berry_skip_build? yarn_major_version >= 3 && (yarn_zero_install? || yarn_offline_cache?) end |
.yarn_major_version ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 78 def self.yarn_major_version retries = 0 output = run_single_yarn_command("--version") Version.new(output).major rescue Dependabot::SharedHelpers::HelperSubprocessFailed => e # Should never happen, can probably be removed once this settles raise "Failed to replace ENV, not sure why" if T.must(retries).positive? = e. missing_env_var_regex = %r{Environment variable not found \((?:[^)]+)\) in #{Dir.pwd}/(?<path>\S+)} if .match?(missing_env_var_regex) match = T.must(.match(missing_env_var_regex)) path = T.must(match.named_captures["path"]) File.write(path, File.read(path).gsub(/\$\{[^}-]+\}/, "")) retries = T.must(retries) + 1 retry end handle_subprocess_failure(e) end |
.yarn_offline_cache? ⇒ Boolean
123 124 125 126 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 123 def self.yarn_offline_cache? yarn_cache_dir = fetch_yarnrc_yml_value("cacheFolder", ".yarn/cache") File.exist?(yarn_cache_dir) && (fetch_yarnrc_yml_value("nodeLinker", "") == "node-modules") end |
.yarn_version_numeric(yarn_lock) ⇒ Object
28 29 30 31 32 33 34 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 28 def self.yarn_version_numeric(yarn_lock) if yarn_berry?(yarn_lock) 3 else 1 end end |
.yarn_zero_install? ⇒ Boolean
118 119 120 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 118 def self.yarn_zero_install? File.exist?(".pnp.cjs") end |