Skip to content

Commit

Permalink
Merge branch 'main' into fix/pnpm-lock-lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
thavaahariharangit authored Nov 28, 2024
2 parents 30184d9 + 5d96f2d commit c72d5af
Show file tree
Hide file tree
Showing 22 changed files with 427 additions and 99 deletions.
1 change: 1 addition & 0 deletions composer/lib/dependabot/composer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
require "dependabot/composer/version"
require "dependabot/composer/helpers"
require "dependabot/composer/package_manager"
require "dependabot/composer/language"

require "dependabot/pull_request_creator/labeler"
Dependabot::PullRequestCreator::Labeler
Expand Down
14 changes: 7 additions & 7 deletions composer/lib/dependabot/composer/file_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ class FileFetcher < Dependabot::FileFetchers::Base
require_relative "helpers"

def self.required_files_in?(filenames)
filenames.include?("composer.json")
filenames.include?(PackageManager::MANIFEST_FILENAME)
end

def self.required_files_message
"Repo must contain a composer.json."
"Repo must contain a #{PackageManager::MANIFEST_FILENAME}."
end

def ecosystem_versions
{
package_managers: {
"composer" => Helpers.composer_version(parsed_composer_json, parsed_lockfile)
PackageManager::NAME => Helpers.composer_version(parsed_composer_json, parsed_lockfile)
}
}
end
Expand All @@ -45,20 +45,20 @@ def fetch_files
private

def composer_json
@composer_json ||= fetch_file_from_host("composer.json")
@composer_json ||= fetch_file_from_host(PackageManager::MANIFEST_FILENAME)
end

def composer_lock
return @composer_lock if defined?(@composer_lock)

@composer_lock = fetch_file_if_present("composer.lock")
@composer_lock = fetch_file_if_present(PackageManager::LOCKFILE_FILENAME)
end

# NOTE: This is fetched but currently unused
def auth_json
return @auth_json if defined?(@auth_json)

@auth_json = fetch_support_file("auth.json")
@auth_json = fetch_support_file(PackageManager::AUTH_FILENAME)
end

def artifact_dependencies
Expand Down Expand Up @@ -106,7 +106,7 @@ def path_dependencies
directories = path.end_with?("*") ? expand_path(path) : [path]

directories.each do |dir|
file = File.join(dir, "composer.json")
file = File.join(dir, PackageManager::MANIFEST_FILENAME)

begin
composer_json_files << fetch_file_with_root_fallback(file)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def initialize(path:, directory:, lockfile:)
end

def dependency_file
filename = File.join(path, "composer.json")
filename = File.join(path, PackageManager::MANIFEST_FILENAME)

# Current we just return `nil` if a path dependency can't be built.
# In future we may wish to change that to a raise. (We'll get errors
Expand Down
18 changes: 12 additions & 6 deletions composer/lib/dependabot/composer/file_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@ def build_manifest_dependency(name, req, keys)
version: dependency_version(name: name, type: group),
requirements: [{
requirement: req,
file: "composer.json",
file: PackageManager::MANIFEST_FILENAME,
source: dependency_source(
name: name,
type: group,
requirement: req
),
groups: [group]
}],
package_manager: "composer"
package_manager: PackageManager::NAME
)
end

Expand Down Expand Up @@ -141,7 +141,7 @@ def build_lockfile_dependency(name, version, keys)
name: name,
version: version,
requirements: [],
package_manager: "composer",
package_manager: PackageManager::NAME,
subdependency_metadata: [{
production: keys.fetch(:group) != "development"
}]
Expand Down Expand Up @@ -223,7 +223,7 @@ def package?(name)

sig { override.void }
def check_required_files
raise "No composer.json!" unless get_original_file("composer.json")
raise "No #{PackageManager::MANIFEST_FILENAME}!" unless get_original_file(PackageManager::MANIFEST_FILENAME)
end

sig { returns(T.nilable(T::Hash[String, T.untyped])) }
Expand Down Expand Up @@ -252,12 +252,18 @@ def parsed_composer_json

sig { returns(T.nilable(Dependabot::DependencyFile)) }
def composer_json
@composer_json ||= T.let(get_original_file("composer.json"), T.nilable(Dependabot::DependencyFile))
@composer_json ||= T.let(
get_original_file(PackageManager::MANIFEST_FILENAME),
T.nilable(Dependabot::DependencyFile)
)
end

sig { returns(T.nilable(Dependabot::DependencyFile)) }
def lockfile
@lockfile ||= T.let(get_original_file("composer.lock"), T.nilable(Dependabot::DependencyFile))
@lockfile ||= T.let(
get_original_file(PackageManager::LOCKFILE_FILENAME),
T.nilable(Dependabot::DependencyFile)
)
end

sig { returns(String) }
Expand Down
6 changes: 3 additions & 3 deletions composer/lib/dependabot/composer/file_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def updated_dependency_files
private

def check_required_files
raise "No composer.json!" unless get_original_file("composer.json")
raise "No #{PackageManager::MANIFEST_FILENAME}!" unless get_original_file(PackageManager::MANIFEST_FILENAME)
end

def updated_composer_json_content
Expand All @@ -66,11 +66,11 @@ def updated_lockfile_content
end

def composer_json
@composer_json ||= get_original_file("composer.json")
@composer_json ||= get_original_file(PackageManager::MANIFEST_FILENAME)
end

def lockfile
@lockfile ||= get_original_file("composer.lock")
@lockfile ||= get_original_file(PackageManager::LOCKFILE_FILENAME)
end
end
end
Expand Down
34 changes: 17 additions & 17 deletions composer/lib/dependabot/composer/file_updater/lockfile_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def generate_updated_lockfile_content
SharedHelpers.in_a_temporary_directory(base_directory) do
write_temporary_dependency_files

updated_content = run_update_helper.fetch("composer.lock")
updated_content = run_update_helper.fetch(PackageManager::LOCKFILE_FILENAME)

updated_content = post_process_lockfile(updated_content)
raise "Expected content to change!" if lockfile.content == updated_content
Expand Down Expand Up @@ -256,9 +256,9 @@ def write_temporary_dependency_files
File.write(file.name, file.content)
end

File.write("composer.json", locked_composer_json_content)
File.write("composer.lock", lockfile.content)
File.write("auth.json", auth_json.content) if auth_json
File.write(PackageManager::MANIFEST_FILENAME, locked_composer_json_content)
File.write(PackageManager::LOCKFILE_FILENAME, lockfile.content)
File.write(PackageManager::AUTH_FILENAME, auth_json.content) if auth_json
end

def locked_composer_json_content
Expand Down Expand Up @@ -288,7 +288,7 @@ def lock_dependencies_being_updated(original_content)
next content unless Composer::Version.correct?(updated_req)

old_req =
dep.requirements.find { |r| r[:file] == "composer.json" }
dep.requirements.find { |r| r[:file] == PackageManager::MANIFEST_FILENAME }
&.fetch(:requirement)

# When updating a subdep there won't be an old requirement
Expand Down Expand Up @@ -379,11 +379,11 @@ def replace_patches(updated_content)
def replace_content_hash(content)
existing_hash = JSON.parse(content).fetch("content-hash")
SharedHelpers.in_a_temporary_directory do
File.write("composer.json", updated_composer_json_content)
File.write(PackageManager::MANIFEST_FILENAME, updated_composer_json_content)

content_hash =
SharedHelpers.run_helper_subprocess(
command: "php #{php_helper_path}",
command: "#{Language::NAME} #{php_helper_path}",
function: "get_content_hash",
env: credentials_env,
args: [Dir.pwd]
Expand Down Expand Up @@ -466,25 +466,25 @@ def git_credentials

def registry_credentials
credentials
.select { |cred| cred.fetch("type") == "composer_repository" }
.select { |cred| cred.fetch("type") == PackageManager::REPOSITORY_KEY }
.select { |cred| cred["password"] }
end

def initial_platform
platform_php = parsed_composer_json.dig("config", "platform", "php")
platform_php = Helpers.capture_platform_php(parsed_composer_json)

platform = {}
platform["php"] = [platform_php] if platform_php.is_a?(String) && requirement_valid?(platform_php)
platform[Language::NAME] = [platform_php] if platform_php.is_a?(String) && requirement_valid?(platform_php)

# NOTE: We *don't* include the require-dev PHP version in our initial
# platform. If we fail to resolve with the PHP version specified in
# `require` then it will be picked up in a subsequent iteration.
requirement_php = parsed_composer_json.dig("require", "php")
requirement_php = Helpers.php_constraint(parsed_composer_json)
return platform unless requirement_php.is_a?(String)
return platform unless requirement_valid?(requirement_php)

platform["php"] ||= []
platform["php"] << requirement_php
platform[Language::NAME] ||= []
platform[Language::NAME] << requirement_php
platform
end

Expand All @@ -505,16 +505,16 @@ def parsed_lockfile

def composer_json
@composer_json ||=
dependency_files.find { |f| f.name == "composer.json" }
dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME }
end

def lockfile
@lockfile ||=
dependency_files.find { |f| f.name == "composer.lock" }
dependency_files.find { |f| f.name == PackageManager::LOCKFILE_FILENAME }
end

def auth_json
@auth_json ||= dependency_files.find { |f| f.name == "auth.json" }
@auth_json ||= dependency_files.find { |f| f.name == PackageManager::AUTH_FILENAME }
end

def artifact_dependencies
Expand All @@ -524,7 +524,7 @@ def artifact_dependencies

def path_dependencies
@path_dependencies ||=
dependency_files.select { |f| f.name.end_with?("/composer.json") }
dependency_files.select { |f| f.name.end_with?("/#{PackageManager::MANIFEST_FILENAME}") }
end
end
end
Expand Down
32 changes: 28 additions & 4 deletions composer/lib/dependabot/composer/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ module Helpers
def self.composer_version(composer_json, parsed_lockfile = nil)
# If the parsed lockfile has a plugin API version, we return either V1 or V2
# based on the major version of the lockfile.
if parsed_lockfile && parsed_lockfile["plugin-api-version"]
version = Composer::Version.new(parsed_lockfile["plugin-api-version"])
if parsed_lockfile && parsed_lockfile[PackageManager::PLUGIN_API_VERSION_KEY]
version = Composer::Version.new(parsed_lockfile[PackageManager::PLUGIN_API_VERSION_KEY])
major_version = version.canonical_segments.first

return major_version.nil? || major_version > 1 ? V2 : V1
Expand Down Expand Up @@ -89,11 +89,35 @@ def self.extract_and_clean_dependency_url(message, regex)
nil
end

# Capture the platform PHP version from composer.json
sig { params(parsed_composer_json: T::Hash[String, T.untyped]).returns(T.nilable(String)) }
def self.capture_platform_php(parsed_composer_json)
capture_platform(parsed_composer_json, Language::NAME)
end

# Capture the platform extension from composer.json
sig { params(parsed_composer_json: T::Hash[String, T.untyped], name: String).returns(T.nilable(String)) }
def self.capture_platform(parsed_composer_json, name)
parsed_composer_json.dig(PackageManager::CONFIG_KEY, PackageManager::PLATFORM_KEY, name)
end

# Capture PHP version constraint from composer.json
sig { params(parsed_composer_json: T::Hash[String, T.untyped]).returns(T.nilable(String)) }
def self.php_constraint(parsed_composer_json)
dependency_constraint(parsed_composer_json, Language::NAME)
end

# Capture extension version constraint from composer.json
sig { params(parsed_composer_json: T::Hash[String, T.untyped], name: String).returns(T.nilable(String)) }
def self.dependency_constraint(parsed_composer_json, name)
parsed_composer_json.dig(PackageManager::REQUIRE_KEY, name)
end

sig { params(composer_json: T::Hash[String, T.untyped]).returns(T::Boolean) }
def self.invalid_v2_requirement?(composer_json)
return false unless composer_json.key?("require")
return false unless composer_json.key?(PackageManager::REQUIRE_KEY)

composer_json["require"].keys.any? do |key|
composer_json[PackageManager::REQUIRE_KEY].keys.any? do |key|
key !~ PLATFORM_PACKAGE_REGEX && key !~ COMPOSER_V2_NAME_REGEX
end
end
Expand Down
34 changes: 34 additions & 0 deletions composer/lib/dependabot/composer/language.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# typed: strong
# frozen_string_literal: true

require "sorbet-runtime"
require "dependabot/ecosystem"
require "dependabot/composer/version"

module Dependabot
module Composer
class Language < Dependabot::Ecosystem::VersionManager
extend T::Sig

NAME = "php"

sig { params(raw_version: String).void }
def initialize(raw_version)
super(
NAME,
Version.new(raw_version),
)
end

sig { returns(T::Boolean) }
def deprecated?
false
end

sig { returns(T::Boolean) }
def unsupported?
false
end
end
end
end
31 changes: 22 additions & 9 deletions composer/lib/dependabot/composer/package_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,35 @@
module Dependabot
module Composer
ECOSYSTEM = "composer"
PACKAGE_MANAGER = "composer"

# Keep versions in ascending order
SUPPORTED_COMPOSER_VERSIONS = T.let([Version.new("2")].freeze, T::Array[Dependabot::Version])

DEPRECATED_COMPOSER_VERSIONS = T.let([
Version.new("1")
].freeze, T::Array[Dependabot::Version])

class PackageManager < Dependabot::Ecosystem::VersionManager
extend T::Sig

NAME = "composer"
MANIFEST_FILENAME = "composer.json"
LOCKFILE_FILENAME = "composer.lock"
AUTH_FILENAME = "auth.json"
DEPENDENCY_NAME = "composer/composer"

REQUIRE_KEY = "require"
CONFIG_KEY = "config"
PLATFORM_KEY = "platform"
PLUGIN_API_VERSION_KEY = "plugin-api-version"
REPOSITORY_KEY = "composer_repository"

# Keep versions in ascending order
SUPPORTED_COMPOSER_VERSIONS = T.let([Version.new("2")].freeze, T::Array[Dependabot::Version])

# Currently, we don't support any deprecated versions of Composer
# When a version is going to be unsupported, it will be added here for a while to give users time to upgrade
# Example for deprecation:
# DEPRECATED_COMPOSER_VERSIONS = T.let([Version.new("1")].freeze, T::Array[Dependabot::Version])
DEPRECATED_COMPOSER_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])

sig { params(raw_version: String).void }
def initialize(raw_version)
super(
PACKAGE_MANAGER,
NAME,
Version.new(raw_version),
DEPRECATED_COMPOSER_VERSIONS,
SUPPORTED_COMPOSER_VERSIONS,
Expand Down
4 changes: 2 additions & 2 deletions composer/lib/dependabot/composer/update_checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ def git_dependency?

def composer_file
composer_file =
dependency_files.find { |f| f.name == "composer.json" }
raise "No composer.json!" unless composer_file
dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME }
raise "No #{PackageManager::MANIFEST_FILENAME}!" unless composer_file

composer_file
end
Expand Down
Loading

0 comments on commit c72d5af

Please sign in to comment.