Skip to content

Commit

Permalink
pass generated notices into the pr message in operations.
Browse files Browse the repository at this point in the history
  • Loading branch information
kbukum1 committed Aug 14, 2024
1 parent d530548 commit e6a6ea7
Show file tree
Hide file tree
Showing 11 changed files with 1,456 additions and 45 deletions.
14 changes: 13 additions & 1 deletion updater/lib/dependabot/updater/group_update_creation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
require "dependabot/dependency_change_builder"
require "dependabot/updater/dependency_group_change_batch"
require "dependabot/workspace"
require "dependabot/updater/security_update_helpers"
require "dependabot/notices"

# This module contains the methods required to build a DependencyChange for
# a single DependencyGroup.
Expand All @@ -22,6 +24,7 @@ class Updater
module GroupUpdateCreation
extend T::Sig
extend T::Helpers
include PullRequestHelpers

abstract!

Expand Down Expand Up @@ -52,6 +55,14 @@ def compile_all_dependency_changes_for(group)
)
original_dependencies = dependency_snapshot.dependencies

notices = []

# Add a deprecation notice if the package manager is deprecated
add_deprecation_notice(
notices: notices,
package_manager: dependency_snapshot.package_manager
)

Dependabot.logger.info("Updating the #{job.source.directory} directory.")
group.dependencies.each do |dependency|
# We still want to update a dependency if it's been updated in another manifest files,
Expand Down Expand Up @@ -108,7 +119,8 @@ def compile_all_dependency_changes_for(group)
job: job,
updated_dependencies: group_changes.updated_dependencies,
updated_dependency_files: group_changes.updated_dependency_files,
dependency_group: group
dependency_group: group,
notices: notices
)

if Experiments.enabled?("dependency_change_validation") && !dependency_change.all_have_previous_version?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# frozen_string_literal: true

require "dependabot/updater/security_update_helpers"
require "dependabot/notices"

# This class implements our strategy for updating a single, insecure dependency
# to a secure version. We attempt to make the smallest version update possible,
Expand All @@ -12,6 +13,7 @@ module Operations
class CreateSecurityUpdatePullRequest
extend T::Sig
include SecurityUpdateHelpers
include PullRequestHelpers

sig { params(job: Job).returns(T::Boolean) }
def self.applies_to?(job:)
Expand Down Expand Up @@ -43,6 +45,8 @@ def initialize(service:, job:, dependency_snapshot:, error_handler:)
@error_handler = error_handler
# TODO: Collect @created_pull_requests on the Job object?
@created_pull_requests = T.let([], T::Array[T::Array[T::Hash[String, String]]])

@pr_notices = T.let([], T::Array[Dependabot::Notice])
end

# TODO: We currently tolerate multiple dependencies for this operation
Expand All @@ -55,6 +59,12 @@ def initialize(service:, job:, dependency_snapshot:, error_handler:)
def perform
Dependabot.logger.info("Starting security update job for #{job.source.repo}")

# Add a deprecation notice if the package manager is deprecated
add_deprecation_notice(
notices: @pr_notices,
package_manager: dependency_snapshot.package_manager
)

target_dependencies = dependency_snapshot.job_dependencies

if target_dependencies.empty?
Expand Down Expand Up @@ -169,7 +179,8 @@ def check_and_create_pull_request(dependency)
job: job,
dependency_files: dependency_snapshot.dependency_files,
updated_dependencies: updated_deps,
change_source: checker.dependency
change_source: checker.dependency,
notices: @pr_notices
)

create_pull_request(dependency_change)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# typed: strict
# frozen_string_literal: true

require "dependabot/updater/security_update_helpers"
require "dependabot/notices"

# This class implements our strategy for 'refreshing' an existing Pull Request
# that updates an insecure dependency.
#
Expand All @@ -16,6 +19,7 @@ module Operations
class RefreshSecurityUpdatePullRequest
extend T::Sig
include SecurityUpdateHelpers
include PullRequestHelpers

sig { params(job: Job).returns(T::Boolean) }
def self.applies_to?(job:)
Expand All @@ -41,10 +45,21 @@ def initialize(service:, job:, dependency_snapshot:, error_handler:)
@job = job
@dependency_snapshot = dependency_snapshot
@error_handler = error_handler

@pr_notices = T.let([], T::Array[Dependabot::Notice])
end

sig { void }
def perform
Dependabot.logger.info("Starting update job for #{job.source.repo}")
Dependabot.logger.info("Checking and updating security pull requests...")

# Add a deprecation notice if the package manager is deprecated
add_deprecation_notice(
notices: @pr_notices,
package_manager: dependency_snapshot.package_manager
)

check_and_update_pull_request(dependencies)
rescue StandardError => e
error_handler.handle_dependency_error(error: e, dependency: dependencies.last)
Expand Down Expand Up @@ -142,7 +157,8 @@ def check_and_update_pull_request(dependencies)
job: job,
dependency_files: dependency_snapshot.dependency_files,
updated_dependencies: updated_deps,
change_source: checker.dependency
change_source: checker.dependency,
notices: @pr_notices
)

# NOTE: Gradle, Maven and Nuget dependency names can be case-insensitive
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# typed: true
# typed: strict
# frozen_string_literal: true

require "dependabot/updater/security_update_helpers"
require "dependabot/notices"

# This class implements our strategy for 'refreshing' an existing Pull Request
# that updates a dependnency to the latest permitted version.
#
Expand All @@ -12,6 +15,10 @@ module Dependabot
class Updater
module Operations
class RefreshVersionUpdatePullRequest
extend T::Sig
include PullRequestHelpers

sig { params(job: Dependabot::Job).returns(T::Boolean) }
def self.applies_to?(job:)
return false if job.security_updates_only?
# If we haven't been given metadata about the dependencies present
Expand All @@ -21,45 +28,75 @@ def self.applies_to?(job:)
job.updating_a_pull_request?
end

sig { returns(Symbol) }
def self.tag_name
:update_version_pr
end

sig do
params(
service: Dependabot::Service,
job: Dependabot::Job,
dependency_snapshot: Dependabot::DependencySnapshot,
error_handler: ErrorHandler
).void
end
def initialize(service:, job:, dependency_snapshot:, error_handler:)
@service = service
@job = job
@dependency_snapshot = dependency_snapshot
@error_handler = error_handler

return unless job.source.directory.nil? && job.source.directories.count == 1
@pr_notices = T.let([], T::Array[Dependabot::Notice])

job.source.directory = job.source.directories.first
return unless job.source.directory.nil? && job.source.directories&.count == 1

job.source.directory = job.source.directories&.first
end

sig { void }
def perform
Dependabot.logger.info("Starting PR update job for #{job.source.repo}")
Dependabot.logger.info("Starting update job for #{job.source.repo}")
Dependabot.logger.info("Checking and updating versions pull requests...")
dependency = dependencies.last

# Add a deprecation notice if the package manager is deprecated
add_deprecation_notice(
notices: @pr_notices,
package_manager: dependency_snapshot.package_manager
)

check_and_update_pull_request(dependencies)
rescue StandardError => e
error_handler.handle_dependency_error(error: e, dependency: dependency)
end

private

sig { returns(Dependabot::Job) }
attr_reader :job
sig { returns(Dependabot::Service) }
attr_reader :service
sig { returns(Dependabot::DependencySnapshot) }
attr_reader :dependency_snapshot
sig { returns(Dependabot::Updater::ErrorHandler) }
attr_reader :error_handler
attr_reader :created_pull_requests

sig { returns(T::Array[Dependabot::Dependency]) }
def dependencies
dependency_snapshot.job_dependencies
end

# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
sig do
params(dependencies: T::Array[Dependabot::Dependency]).void
end
def check_and_update_pull_request(dependencies)
if dependencies.count != job.dependencies.count
job_dependencies = T.must(job.dependencies)

if job_dependencies.count.zero? || dependencies.count != job_dependencies.count
# If the job dependencies mismatch the parsed dependencies, then
# we should close the PR as at least one thing we changed has been
# removed from the project.
Expand All @@ -73,10 +110,19 @@ def check_and_update_pull_request(dependencies)
# Note: Gradle, Maven and Nuget dependency names can be case-insensitive
# and the dependency name in the security advisory often doesn't match
# what users have specified in their manifest.
lead_dep_name = job.dependencies.first.downcase
lead_dep_name = T.must(job_dependencies.first).downcase
lead_dependency = dependencies.find do |dep|
dep.name.downcase == lead_dep_name
end

if lead_dependency.nil?
# If the lead dependency is not found, it indicates that one of the dependencies
# we attempted to update has been removed from the project. Therefore, we should
# close the PR.
close_pull_request(reason: :dependency_removed)
return
end

checker = update_checker_for(lead_dependency, raise_on_ignored: raise_on_ignored?(lead_dependency))
log_checking_for_update(lead_dependency)

Expand All @@ -99,13 +145,14 @@ def check_and_update_pull_request(dependencies)
job: job,
dependency_files: dependency_snapshot.dependency_files,
updated_dependencies: updated_deps,
change_source: checker.dependency
change_source: checker.dependency,
notices: @pr_notices
)

# NOTE: Gradle, Maven and Nuget dependency names can be case-insensitive
# and the dependency name in the security advisory often doesn't match
# what users have specified in their manifest.
job_dependencies = job.dependencies.map(&:downcase)
job_dependencies = job_dependencies.map(&:downcase)
if dependency_change.updated_dependencies.map { |x| x.name.downcase } != job_dependencies
# The dependencies being updated have changed. Close the existing
# multi-dependency PR and try creating a new one.
Expand All @@ -121,32 +168,43 @@ def check_and_update_pull_request(dependencies)
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength

sig { params(dependency_change: Dependabot::DependencyChange).void }
def create_pull_request(dependency_change)
Dependabot.logger.info("Submitting #{dependency_change.updated_dependencies.map(&:name).join(', ')} " \
"pull request for creation")

service.create_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
end

sig { params(dependency_change: Dependabot::DependencyChange).void }
def update_pull_request(dependency_change)
Dependabot.logger.info("Submitting #{dependency_change.updated_dependencies.map(&:name).join(', ')} " \
"pull request for update")

service.update_pull_request(dependency_change, dependency_snapshot.base_commit_sha)
end

sig { params(reason: Symbol).void }
def close_pull_request(reason:)
job_dependencies = T.must(job.dependencies)

reason_string = reason.to_s.tr("_", " ")
Dependabot.logger.info("Telling backend to close pull request for " \
"#{job.dependencies.join(', ')} - #{reason_string}")
service.close_pull_request(job.dependencies, reason)
"#{job_dependencies.join(', ')} - #{reason_string}")
service.close_pull_request(job_dependencies, reason)
end

sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
def raise_on_ignored?(dependency)
job.ignore_conditions_for(dependency).any?
end

sig do
params(dependency: Dependabot::Dependency, raise_on_ignored: T::Boolean)
.returns(Dependabot::UpdateCheckers::Base)
end
def update_checker_for(dependency, raise_on_ignored:)
Dependabot::UpdateCheckers.for_package_manager(job.package_manager).new(
dependency: dependency,
Expand All @@ -161,13 +219,17 @@ def update_checker_for(dependency, raise_on_ignored:)
)
end

sig { params(dependency: Dependabot::Dependency).void }
def log_checking_for_update(dependency)
Dependabot.logger.info(
"Checking if #{dependency.name} #{dependency.version} needs updating"
)
job.log_ignore_conditions_for(dependency)
end

sig do
params(dependency: Dependabot::Dependency, checker: Dependabot::UpdateCheckers::Base).returns(T::Boolean)
end
def all_versions_ignored?(dependency, checker)
Dependabot.logger.info("Latest version is #{checker.latest_version}")
false
Expand All @@ -176,6 +238,9 @@ def all_versions_ignored?(dependency, checker)
true
end

sig do
params(checker: Dependabot::UpdateCheckers::Base).returns(Symbol)
end
def requirements_to_unlock(checker)
if !checker.requirements_unlocked_or_can_be?
if checker.can_update?(requirements_to_unlock: :none) then :none
Expand All @@ -189,6 +254,9 @@ def requirements_to_unlock(checker)
end
end

sig do
params(requirements_to_unlock: Symbol, checker: Dependabot::UpdateCheckers::Base).void
end
def log_requirements_for_update(requirements_to_unlock, checker)
Dependabot.logger.info("Requirements to unlock #{requirements_to_unlock}")

Expand All @@ -199,6 +267,10 @@ def log_requirements_for_update(requirements_to_unlock, checker)
)
end

sig do
params(updated_dependencies: T::Array[Dependabot::Dependency])
.returns(T.nilable(T::Array[T::Hash[String, T.untyped]]))
end
def existing_pull_request(updated_dependencies)
new_pr_set = updated_dependencies.to_set do |dep|
{
Expand Down
Loading

0 comments on commit e6a6ea7

Please sign in to comment.