Module: Dependabot::NpmAndYarn::Helpers
- Extended by:
- T::Sig
- Defined in:
- lib/dependabot/npm_and_yarn/helpers.rb
Overview
rubocop:disable Metrics/ModuleLength
Defined Under Namespace
Classes: ReleaseAgeGateSetting
Constant Summary collapse
- YARN_PATH_NOT_FOUND =
/^.*(?<error>The "yarn-path" option has been set \(in [^)]+\), but the specified location doesn't exist)/- NPM_V11 =
NPM Version Constants
11- NPM_V10 =
10- NPM_V8 =
8- NPM_V6 =
6- NPM_DEFAULT_VERSION =
NPM_V11- NPM_MINIMUM_RELEASE_AGE_VERSION =
Minimum npm version that supports the
--min-release-ageCLI flag. "11.10.0"- PNPM_V10 =
PNPM Version Constants
10- PNPM_V9 =
9- PNPM_V8 =
8- PNPM_V7 =
7- PNPM_V6 =
6- PNPM_DEFAULT_VERSION =
PNPM_V10- PNPM_FALLBACK_VERSION =
PNPM_V6- YARN_V3 =
YARN Version Constants
3- YARN_V2 =
2- YARN_V1 =
1- YARN_DEFAULT_VERSION =
YARN_V3- YARN_FALLBACK_VERSION =
YARN_V1- SUPPORTED_COREPACK_PACKAGE_MANAGERS =
corepack supported package managers
%w(npm yarn pnpm).freeze
- COREPACK_SIGNATURE_METADATA_ERROR =
"No compatible signature found in package metadata"- MINUTES_PER_DAY =
pnpm's
minimumReleaseAgeand yarn'snpmMinimalAgeGateare expressed in minutes, whereas dependabot.yml cooldown and npm'smin-release-ageuse days. Used to convert a cooldown floor (days) into the minutes those gates expect. 1440
Class Method Summary collapse
- .build_corepack_env_variables ⇒ Object
- .command_observer(output) ⇒ Object
- .corepack_supported_package_manager?(name) ⇒ Boolean
- .credentials ⇒ Object
- .credentials=(creds) ⇒ Object
- .default_npm_registry?(registry) ⇒ Boolean
- .dependencies_with_all_versions_metadata(dependency_set) ⇒ Object
- .dependency_files ⇒ Object
- .dependency_files=(files) ⇒ Object
- .fallback_to_local_version(name, env: {}) ⇒ Object
- .fetch_yarnrc_yml_value(key, default_value) ⇒ Object
- .handle_subprocess_failure(error) ⇒ Object
- .higher_release_age_gate(cooldown, user_gate) ⇒ Object
- .install(name, version, env: {}) ⇒ Object
- .local_package_manager_version(name) ⇒ Object
- .max_configured_release_age(dependency_files, settings) ⇒ Object
- .merge_corepack_env(env) ⇒ Object
- .node_version ⇒ Object
- .normalize_npm_scope(scope) ⇒ Object
- .npm_supports_min_release_age? ⇒ Boolean
- .npm_version ⇒ Object
- .npm_version_numeric(lockfile) ⇒ Object
- .package_manager_activate(name, version, env: {}) ⇒ Object
- .package_manager_install(name, version, env: {}) ⇒ Object
- .package_manager_run_command(name, command, fingerprint: nil, output_observer: nil, env: nil) ⇒ Object
- .package_manager_version(name, env: nil) ⇒ Object
- .parse_npm8?(package_lock) ⇒ Boolean
- .pnpm_lockfile_version(pnpm_lock) ⇒ Object
- .pnpm_version ⇒ Object
- .pnpm_version_numeric(pnpm_lock) ⇒ Object
- .raise_registry_error_if_not_found(error) ⇒ Object
- .retry_without_signature_verification?(error:, env:) ⇒ Boolean
- .run_corepack_command(full_command, fingerprint:, output_observer: nil, env: nil) ⇒ Object
- .run_corepack_shell_command(full_command, fingerprint:, output_observer:, env: nil) ⇒ Object
- .run_node_command(command, fingerprint: nil) ⇒ Object
- .run_npm_command(command, fingerprint: command, env: nil) ⇒ Object
- .run_pnpm_command(command, fingerprint: nil, env: nil) ⇒ Object
- .run_yarn_command(command, fingerprint: nil, env: nil) ⇒ Object
- .run_yarn_commands(*commands, env: nil) ⇒ Object
- .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_berry_supports_minimal_age_gate? ⇒ Boolean
- .yarn_major_version ⇒ Object
- .yarn_offline_cache? ⇒ Boolean
- .yarn_version_numeric(yarn_lock) ⇒ Object
- .yarn_zero_install? ⇒ Boolean
Class Method Details
.build_corepack_env_variables ⇒ Object
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 770 def self.build_corepack_env_variables return nil unless Dependabot::Experiments.enabled?(:enable_private_registry_for_corepack) return nil if dependency_files.nil? || credentials.nil? files = T.must(dependency_files) creds = T.must(credentials) registry_helper = RegistryHelper.new( { npmrc: files.find { |f| f.name.end_with?(".npmrc") }, yarnrc: files.find { |f| f.name.end_with?(".yarnrc") && !f.name.end_with?(".yarnrc.yml") }, yarnrc_yml: files.find { |f| f.name.end_with?(".yarnrc.yml") } }, creds ) registry_helper.find_corepack_env_variables end |
.command_observer(output) ⇒ Object
489 490 491 492 493 494 495 496 497 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 489 def self.command_observer(output) # Observe the output for specific error return {} unless output.include?("npm ERR! ERESOLVE") { gracefully_stop: true, # value must be a String reason: "NPM Resolution Error" } end |
.corepack_supported_package_manager?(name) ⇒ Boolean
865 866 867 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 865 def self.corepack_supported_package_manager?(name) SUPPORTED_COREPACK_PACKAGE_MANAGERS.include?(name) end |
.credentials ⇒ Object
38 39 40 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 38 def credentials T.cast(Thread.current[:npm_and_yarn_credentials], T.nilable(T::Array[Dependabot::Credential])) end |
.credentials=(creds) ⇒ Object
33 34 35 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 33 def credentials=(creds) Thread.current[:npm_and_yarn_credentials] = creds end |
.default_npm_registry?(registry) ⇒ Boolean
841 842 843 844 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 841 def self.default_npm_registry?(registry) normalized_registry = RegistryHelper.normalize_registry_url(registry) normalized_registry == RegistryHelper::DEFAULT_NPM_REGISTRY end |
.dependencies_with_all_versions_metadata(dependency_set) ⇒ Object
857 858 859 860 861 862 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 857 def self.(dependency_set) dependency_set.dependencies.map do |dependency| dependency.[:all_versions] = dependency_set.all_versions_for_name(dependency.name) dependency end end |
.dependency_files ⇒ Object
28 29 30 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 28 def dependency_files T.cast(Thread.current[:npm_and_yarn_dependency_files], T.nilable(T::Array[Dependabot::DependencyFile])) end |
.dependency_files=(files) ⇒ Object
23 24 25 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 23 def dependency_files=(files) Thread.current[:npm_and_yarn_dependency_files] = files end |
.fallback_to_local_version(name, env: {}) ⇒ Object
624 625 626 627 628 629 630 631 632 633 634 635 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 624 def self.fallback_to_local_version(name, env: {}) return "Corepack does not support #{name}" unless corepack_supported_package_manager?(name) Dependabot.logger.info("Falling back to activate the currently installed version of #{name}.") # Fetch the currently installed version directly from the environment current_version = local_package_manager_version(name) Dependabot.logger.info("Activating currently installed version of #{name}: #{current_version}") # Prepare the existing version package_manager_activate(name, current_version, env: env) end |
.fetch_yarnrc_yml_value(key, default_value) ⇒ Object
289 290 291 292 293 294 295 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 289 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
343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 343 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 |
.higher_release_age_gate(cooldown, user_gate) ⇒ Object
94 95 96 97 98 99 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 94 def self.higher_release_age_gate(cooldown, user_gate) return nil if cooldown.nil? || !cooldown.positive? return nil if user_gate && cooldown <= user_gate cooldown end |
.install(name, version, env: {}) ⇒ Object
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 595 def self.install(name, version, env: {}) Dependabot.logger.info("Installing \"#{name}@#{version}\"") begin # Try to activate the specified version output = package_manager_activate(name, version, env: env) # Confirm success based on the output if output.include?("immediate activation...") Dependabot.logger.info("#{name}@#{version} successfully installed.") Dependabot.logger.info("Activating currently installed version of #{name}: #{version}") else Dependabot.logger.error("Corepack installation output unexpected: #{output}") fallback_to_local_version(name, env: env) end rescue StandardError => e Dependabot.logger.error("Error activating #{name}@#{version}: #{e.}") fallback_to_local_version(name, env: env) end # Verify the installed version installed_version = package_manager_version(name, env: env) installed_version end |
.local_package_manager_version(name) ⇒ Object
671 672 673 674 675 676 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 671 def self.local_package_manager_version(name) Dependabot::SharedHelpers.run_shell_command( "#{name} -v", fingerprint: "#{name} -v" ).strip end |
.max_configured_release_age(dependency_files, settings) ⇒ Object
123 124 125 126 127 128 129 130 131 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 123 def self.max_configured_release_age(dependency_files, settings) values = dependency_files.filter_map do |file| setting = settings.find { |candidate| candidate.filename == File.basename(file.name) } next unless setting configured_release_age(file.content.to_s, setting) end values.max end |
.merge_corepack_env(env) ⇒ Object
761 762 763 764 765 766 767 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 761 def self.merge_corepack_env(env) corepack_env = build_corepack_env_variables return env if corepack_env.nil? || corepack_env.empty? return corepack_env if env.nil? corepack_env.merge(env) end |
.node_version ⇒ Object
500 501 502 503 504 505 506 507 508 509 510 511 512 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 500 def self.node_version version = run_node_command("-v", fingerprint: "-v").strip # Validate the output format (e.g., "v20.18.1" or "20.18.1") if version.match?(/^v?\d+(\.\d+){2}$/) parsed_version = version.strip.delete_prefix("v") # Remove the "v" prefix if present Dependabot.logger.info("Using node version: #{parsed_version}") parsed_version end rescue StandardError => e Dependabot.logger.error("Error retrieving Node.js version: #{e.}") nil end |
.normalize_npm_scope(scope) ⇒ Object
870 871 872 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 870 def self.normalize_npm_scope(scope) scope.start_with?("@") ? scope : "@#{scope}" end |
.npm_supports_min_release_age? ⇒ Boolean
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 272 def self.npm_supports_min_release_age? version = npm_version return false if version.nil? supported = version >= Version.new(NPM_MINIMUM_RELEASE_AGE_VERSION) if supported Dependabot.logger.info("npm #{version} supports --min-release-age.") else Dependabot.logger.info( "npm #{version} does not support --min-release-age (requires 11.10.0+); the release-age " \ "cooldown gate will not be applied to transitive dependencies." ) end supported end |
.npm_version ⇒ Object
256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 256 def self.npm_version raw = if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn) package_manager_version(NpmPackageManager::NAME, env: merge_corepack_env(nil)) else local_package_manager_version(NpmPackageManager::NAME) end Version.new(raw) rescue StandardError => e Dependabot.logger.warn("Could not determine npm version to gate release-age settings: #{e.}") nil end |
.npm_version_numeric(lockfile) ⇒ Object
162 163 164 165 166 167 168 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 162 def self.npm_version_numeric(lockfile) detected_npm_version = detect_npm_version(lockfile) return NPM_DEFAULT_VERSION if detected_npm_version.nil? || detected_npm_version == NPM_V6 detected_npm_version end |
.package_manager_activate(name, version, env: {}) ⇒ Object
658 659 660 661 662 663 664 665 666 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 658 def self.package_manager_activate(name, version, env: {}) return "Corepack does not support #{name}" unless corepack_supported_package_manager?(name) run_corepack_command( "corepack prepare #{name}@#{version} --activate", fingerprint: "corepack prepare <name>@<version> --activate", env: env ) end |
.package_manager_install(name, version, env: {}) ⇒ Object
646 647 648 649 650 651 652 653 654 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 646 def self.package_manager_install(name, version, env: {}) return "Corepack does not support #{name}" unless corepack_supported_package_manager?(name) run_corepack_command( "corepack install #{name}@#{version} --global --cache-only", fingerprint: "corepack install <name>@<version> --global --cache-only", env: env ) end |
.package_manager_run_command(name, command, fingerprint: nil, output_observer: nil, env: nil) ⇒ Object
703 704 705 706 707 708 709 710 711 712 713 714 715 716 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 703 def self.package_manager_run_command( name, command, fingerprint: nil, output_observer: nil, env: nil ) run_corepack_command( "corepack #{name} #{command}", fingerprint: "corepack #{name} #{fingerprint || command}", output_observer: output_observer, env: env ) end |
.package_manager_version(name, env: nil) ⇒ Object
680 681 682 683 684 685 686 687 688 689 690 691 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 680 def self.package_manager_version(name, env: nil) Dependabot.logger.info("Fetching version for package manager: #{name}") version = package_manager_run_command(name, "-v", env: env).strip Dependabot.logger.info("Installed version of #{name}: #{version}") version rescue StandardError => e Dependabot.logger.error("Error fetching version for package manager #{name}: #{e.}") raise end |
.parse_npm8?(package_lock) ⇒ Boolean
298 299 300 301 302 303 304 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 298 def self.parse_npm8?(package_lock) return true unless package_lock&.content detected_npm = detect_npm_version(package_lock) # For conversion reading properly from npm 6 lockfile we need to check if detected version is npm 6 detected_npm.nil? || detected_npm != NPM_V6 end |
.pnpm_lockfile_version(pnpm_lock) ⇒ Object
849 850 851 852 853 854 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 849 def self.pnpm_lockfile_version(pnpm_lock) match = T.must(pnpm_lock.content).match(/^lockfileVersion: ['"]?(?<version>[\d.]+)/) return match[:version] if match nil end |
.pnpm_version ⇒ Object
239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 239 def self.pnpm_version raw = if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn) package_manager_version(PNPMPackageManager::NAME, env: merge_corepack_env(nil)) else local_package_manager_version(PNPMPackageManager::NAME) end Version.new(raw) rescue StandardError => e Dependabot.logger.warn("Could not determine pnpm version to gate release-age settings: #{e.}") nil end |
.pnpm_version_numeric(pnpm_lock) ⇒ Object
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 214 def self.pnpm_version_numeric(pnpm_lock) lockfile_content = pnpm_lock&.content return PNPM_DEFAULT_VERSION if !lockfile_content || lockfile_content.strip.empty? pnpm_lockfile_version_str = pnpm_lockfile_version(pnpm_lock) return PNPM_FALLBACK_VERSION unless pnpm_lockfile_version_str pnpm_lockfile_version = pnpm_lockfile_version_str.to_f return PNPM_V10 if pnpm_lockfile_version >= 9.0 return PNPM_V8 if pnpm_lockfile_version >= 6.0 return PNPM_V7 if pnpm_lockfile_version >= 5.4 PNPM_FALLBACK_VERSION end |
.raise_registry_error_if_not_found(error) ⇒ Object
815 816 817 818 819 820 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 815 def self.raise_registry_error_if_not_found(error) if error..match?(/Response Code.*:.*404.*\(Not Found\)/) && error..include?("The remote server failed to provide the requested resource") raise RegistryError.new(404, "The remote server failed to provide the requested resource") end end |
.retry_without_signature_verification?(error:, env:) ⇒ Boolean
823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 823 def self.retry_without_signature_verification?(error:, env:) return false unless env registry = env[RegistryHelper::COREPACK_NPM_REGISTRY_ENV] return false unless registry return false if default_npm_registry?(registry) return false unless error..include?(COREPACK_SIGNATURE_METADATA_ERROR) # Retry (disabling verification) only when no integrity keys are # configured. When a replaces-base registry's merged npm + registry keys # were fetched and set, a remaining signature failure is a genuine # integrity problem, so we fail closed rather than silently disabling # verification. If the keys could not be fetched they are left unset, and # this signature-stripping retry still applies as before. !env.key?(RegistryHelper::COREPACK_INTEGRITY_KEYS_ENV) end |
.run_corepack_command(full_command, fingerprint:, output_observer: nil, env: nil) ⇒ Object
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 730 def self.run_corepack_command(full_command, fingerprint:, output_observer: nil, env: nil) run_corepack_shell_command( full_command, fingerprint: fingerprint, output_observer: output_observer, env: env ) rescue StandardError => e Dependabot.logger.error("Error running package manager command: #{full_command}, Error: #{e.}") raise_registry_error_if_not_found(e) if retry_without_signature_verification?(error: e, env: env) retry_env = T.must(env).merge(RegistryHelper::COREPACK_INTEGRITY_KEYS_ENV => "") Dependabot.logger.warn( "Corepack signature verification failed against the configured private registry. " \ "Retrying once with COREPACK_INTEGRITY_KEYS disabled for this command only." ) return run_corepack_shell_command( full_command, fingerprint: fingerprint, output_observer: output_observer, env: retry_env ) end raise end |
.run_corepack_shell_command(full_command, fingerprint:, output_observer:, env: nil) ⇒ Object
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 797 def self.run_corepack_shell_command(full_command, fingerprint:, output_observer:, env: nil) if output_observer return Dependabot::SharedHelpers.run_shell_command( full_command, fingerprint: fingerprint, output_observer: output_observer, env: env ).strip end Dependabot::SharedHelpers.run_shell_command( full_command, fingerprint: fingerprint, env: env ).strip end |
.run_node_command(command, fingerprint: nil) ⇒ Object
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 515 def self.run_node_command(command, fingerprint: nil) full_command = "node #{command}" Dependabot.logger.info("Running node command: #{full_command}") result = Dependabot::SharedHelpers.run_shell_command( full_command, fingerprint: "node #{fingerprint || command}" ) Dependabot.logger.info("Command executed successfully: #{full_command}") result rescue StandardError => e Dependabot.logger.error("Error running node command: #{full_command}, Error: #{e.}") raise end |
.run_npm_command(command, fingerprint: command, env: nil) ⇒ Object
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 466 def self.run_npm_command(command, fingerprint: command, env: nil) merged_env = merge_corepack_env(env) if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn) package_manager_run_command( NpmPackageManager::NAME, command, fingerprint: fingerprint, output_observer: ->(output) { command_observer(output) }, env: merged_env ) else Dependabot::SharedHelpers.run_shell_command( "npm #{command}", fingerprint: "npm #{fingerprint}", output_observer: ->(output) { command_observer(output) } ) end end |
.run_pnpm_command(command, fingerprint: nil, env: nil) ⇒ Object
553 554 555 556 557 558 559 560 561 562 563 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 553 def self.run_pnpm_command(command, fingerprint: nil, env: nil) if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn) merged_env = merge_corepack_env(env) package_manager_run_command(PNPMPackageManager::NAME, command, fingerprint: fingerprint, env: merged_env) else Dependabot::SharedHelpers.run_shell_command( "pnpm #{command}", fingerprint: "pnpm #{fingerprint || command}" ) end end |
.run_yarn_command(command, fingerprint: nil, env: nil) ⇒ Object
540 541 542 543 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 540 def self.run_yarn_command(command, fingerprint: nil, env: nil) setup_yarn_berry run_single_yarn_command(command, fingerprint: fingerprint, env: env) end |
.run_yarn_commands(*commands, env: nil) ⇒ Object
447 448 449 450 451 452 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 447 def self.run_yarn_commands(*commands, env: nil) setup_yarn_berry commands.each do |cmd, fingerprint| run_single_yarn_command(cmd, fingerprint: fingerprint, env: env) if cmd end end |
.setup_yarn_berry ⇒ Object
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 420 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
392 393 394 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 392 def self.yarn_4_or_higher? yarn_major_version >= 4 end |
.yarn_berry?(yarn_lock) ⇒ Boolean
307 308 309 310 311 312 313 314 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 307 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
369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 369 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
387 388 389 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 387 def self.yarn_berry_disable_scripts? yarn_major_version == YARN_V2 || !yarn_zero_install? end |
.yarn_berry_skip_build? ⇒ Boolean
382 383 384 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 382 def self.yarn_berry_skip_build? yarn_major_version >= YARN_V3 && (yarn_zero_install? || yarn_offline_cache?) end |
.yarn_berry_supports_minimal_age_gate? ⇒ Boolean
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 397 def self.yarn_berry_supports_minimal_age_gate? version = Version.new(run_single_yarn_command("--version")) supported = version >= Version.new("4.10.0") if supported Dependabot.logger.info("Yarn #{version} supports npmMinimalAgeGate.") else Dependabot.logger.info( "Yarn #{version} does not support npmMinimalAgeGate (requires 4.10.0+). " \ "YARN_NPM_MINIMAL_AGE_GATE will not be set." ) end supported rescue StandardError => e Dependabot.logger.warn( "Could not determine Yarn version to check npmMinimalAgeGate support: #{e.}. " \ "Assuming unsupported (returning false). YARN_NPM_MINIMAL_AGE_GATE will not be set, so the " \ "security-update `=0` bypass cannot be applied — any release-age gate configured in " \ ".yarnrc.yml (npmMinimalAgeGate) or enforced by the registry may still block security updates." ) false end |
.yarn_major_version ⇒ Object
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 317 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
363 364 365 366 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 363 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
198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 198 def self.yarn_version_numeric(yarn_lock) lockfile_content = yarn_lock&.content return YARN_DEFAULT_VERSION if lockfile_content.nil? || lockfile_content.strip.empty? if yarn_berry?(yarn_lock) YARN_DEFAULT_VERSION else YARN_FALLBACK_VERSION end end |
.yarn_zero_install? ⇒ Boolean
358 359 360 |
# File 'lib/dependabot/npm_and_yarn/helpers.rb', line 358 def self.yarn_zero_install? File.exist?(".pnp.cjs") end |