diff --git a/common/lib/dependabot/pull_request_creator.rb b/common/lib/dependabot/pull_request_creator.rb index b18a2b4101..8030476391 100644 --- a/common/lib/dependabot/pull_request_creator.rb +++ b/common/lib/dependabot/pull_request_creator.rb @@ -12,6 +12,7 @@ class PullRequestCreator require "dependabot/pull_request_creator/message_builder" require "dependabot/pull_request_creator/branch_namer" require "dependabot/pull_request_creator/labeler" + require "dependabot/pull_request_creator/labelers/factory" class RepoNotFound < StandardError; end @@ -225,7 +226,7 @@ def branch_namer def labeler @labeler ||= - Labeler.new( + Dependabot::PullRequestCreator::Labelers::Factory.for_source( source: source, custom_labels: custom_labels, credentials: credentials, diff --git a/common/lib/dependabot/pull_request_creator/labeler.rb b/common/lib/dependabot/pull_request_creator/labeler.rb index b1ab9b8bc5..c97fcb048b 100644 --- a/common/lib/dependabot/pull_request_creator/labeler.rb +++ b/common/lib/dependabot/pull_request_creator/labeler.rb @@ -2,6 +2,7 @@ require "octokit" require "dependabot/pull_request_creator" + module Dependabot class PullRequestCreator class Labeler @@ -26,12 +27,11 @@ def register_label_details(package_manager, label_details) end end - def initialize(source:, custom_labels:, credentials:, dependencies:, + def initialize(source:, custom_labels:, dependencies:, includes_security_fixes:, label_language:, automerge_candidate:) @source = source @custom_labels = custom_labels - @credentials = credentials @dependencies = dependencies @includes_security_fixes = includes_security_fixes @label_language = label_language @@ -53,29 +53,13 @@ def labels_for_pr ].compact.uniq end - def label_pull_request(pull_request_number) - create_default_labels_if_required - - return if labels_for_pr.none? - raise "Only GitHub!" unless source.provider == "github" - - github_client_for_source.add_labels_to_an_issue( - source.repo, - pull_request_number, - labels_for_pr - ) - rescue Octokit::UnprocessableEntity, Octokit::NotFound - retry_count ||= 0 - retry_count += 1 - raise if retry_count > 10 - - sleep(rand(1..1.99)) - retry + def label_pull_request(_pull_request_number) + raise "Only GitHub!" end private - attr_reader :source, :custom_labels, :credentials, :dependencies + attr_reader :source, :custom_labels, :dependencies def label_language? @label_language @@ -226,183 +210,37 @@ def language_label_exists? end def language_label - label_name = - self.class.label_details_for_package_manager(package_manager). - fetch(:name) - labels.find { |l| l.casecmp(label_name).zero? } + labels.find { |l| l.casecmp(language_name).zero? } end def labels - @labels ||= - case source.provider - when "github" then fetch_github_labels - when "gitlab" then fetch_gitlab_labels - when "azure" then fetch_azure_labels - else raise "Unsupported provider #{source.provider}" - end - end - - def fetch_github_labels - client = github_client_for_source - - labels = - client. - labels(source.repo, per_page: 100). - map(&:name) - - next_link = client.last_response.rels[:next] - - while next_link - next_page = next_link.get - labels += next_page.data.map(&:name) - next_link = next_page.rels[:next] - end - - labels - end - - def fetch_gitlab_labels - gitlab_client_for_source. - labels(source.repo, per_page: 100). - auto_paginate. - map(&:name) - end - - def fetch_azure_labels - langauge_name = - self.class.label_details_for_package_manager(package_manager). - fetch(:name) - - @labels = [ - *@labels, - DEFAULT_DEPENDENCIES_LABEL, - DEFAULT_SECURITY_LABEL, - langauge_name - ].uniq + raise "Unsupported provider #{source.provider}" end def create_dependencies_label - case source.provider - when "github" then create_github_dependencies_label - when "gitlab" then create_gitlab_dependencies_label - when "azure" then @labels # Azure does not have centralised labels - else raise "Unsupported provider #{source.provider}" - end + raise "Unsupported provider #{source.provider}" end def create_security_label - case source.provider - when "github" then create_github_security_label - when "gitlab" then create_gitlab_security_label - when "azure" then @labels # Azure does not have centralised labels - else raise "Unsupported provider #{source.provider}" - end + raise "Unsupported provider #{source.provider}" end def create_language_label - case source.provider - when "github" then create_github_language_label - when "gitlab" then create_gitlab_language_label - when "azure" then @labels # Azure does not have centralised labels - else raise "Unsupported provider #{source.provider}" - end - end - - def create_github_dependencies_label - github_client_for_source.add_label( - source.repo, DEFAULT_DEPENDENCIES_LABEL, "0366d6", - description: "Pull requests that update a dependency file", - accept: "application/vnd.github.symmetra-preview+json" - ) - @labels = [*@labels, DEFAULT_DEPENDENCIES_LABEL].uniq - rescue Octokit::UnprocessableEntity => e - raise unless e.errors.first.fetch(:code) == "already_exists" - - @labels = [*@labels, DEFAULT_DEPENDENCIES_LABEL].uniq - end - - def create_gitlab_dependencies_label - gitlab_client_for_source.create_label( - source.repo, DEFAULT_DEPENDENCIES_LABEL, "#0366d6", - description: "Pull requests that update a dependency file" - ) - @labels = [*@labels, DEFAULT_DEPENDENCIES_LABEL].uniq - end - - def create_github_security_label - github_client_for_source.add_label( - source.repo, DEFAULT_SECURITY_LABEL, "ee0701", - description: "Pull requests that address a security vulnerability", - accept: "application/vnd.github.symmetra-preview+json" - ) - @labels = [*@labels, DEFAULT_SECURITY_LABEL].uniq - rescue Octokit::UnprocessableEntity => e - raise unless e.errors.first.fetch(:code) == "already_exists" - - @labels = [*@labels, DEFAULT_SECURITY_LABEL].uniq - end - - def create_gitlab_security_label - gitlab_client_for_source.create_label( - source.repo, DEFAULT_SECURITY_LABEL, "#ee0701", - description: "Pull requests that address a security vulnerability" - ) - @labels = [*@labels, DEFAULT_SECURITY_LABEL].uniq - end - - def create_github_language_label - langauge_name = - self.class.label_details_for_package_manager(package_manager). - fetch(:name) - github_client_for_source.add_label( - source.repo, - langauge_name, - self.class.label_details_for_package_manager(package_manager). - fetch(:colour), - description: "Pull requests that update #{langauge_name.capitalize} "\ - "code", - accept: "application/vnd.github.symmetra-preview+json" - ) - @labels = [*@labels, langauge_name].uniq - rescue Octokit::UnprocessableEntity => e - raise unless e.errors.first.fetch(:code) == "already_exists" - - @labels = [*@labels, langauge_name].uniq - end - - def create_gitlab_language_label - langauge_name = - self.class.label_details_for_package_manager(package_manager). - fetch(:name) - gitlab_client_for_source.create_label( - source.repo, - langauge_name, - "#" + self.class.label_details_for_package_manager(package_manager). - fetch(:colour) - ) - @labels = [*@labels, langauge_name].uniq - end - - def github_client_for_source - @github_client_for_source ||= - Dependabot::Clients::GithubWithRetries.for_source( - source: source, - credentials: credentials - ) - end - - def gitlab_client_for_source - @gitlab_client_for_source ||= - Dependabot::Clients::GitlabWithRetries.for_source( - source: source, - credentials: credentials - ) + raise "Unsupported provider #{source.provider}" end def package_manager @package_manager ||= dependencies.first.package_manager end + def language_name + @language_name ||= self.class.label_details_for_package_manager(package_manager).fetch(:name) + end + + def colour + @colour ||= self.class.label_details_for_package_manager(package_manager).fetch(:colour) + end + def version_class Utils.version_class_for_package_manager(package_manager) end diff --git a/common/lib/dependabot/pull_request_creator/labelers/azure.rb b/common/lib/dependabot/pull_request_creator/labelers/azure.rb new file mode 100644 index 0000000000..f1ef015cee --- /dev/null +++ b/common/lib/dependabot/pull_request_creator/labelers/azure.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require "dependabot/pull_request_creator/labeler" + +module Dependabot + class PullRequestCreator + module Labelers + class Azure < Labeler + @package_manager_labels = {} + + # Azure does not have centralised labels + def labels + @labels ||= [ + DEFAULT_DEPENDENCIES_LABEL, + DEFAULT_SECURITY_LABEL, + language_name + ] + end + + def create_dependencies_label + labels + end + + def create_security_label + labels + end + + def create_language_label + labels + end + end + end + end +end diff --git a/common/lib/dependabot/pull_request_creator/labelers/factory.rb b/common/lib/dependabot/pull_request_creator/labelers/factory.rb new file mode 100644 index 0000000000..cbeb0a2466 --- /dev/null +++ b/common/lib/dependabot/pull_request_creator/labelers/factory.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Dependabot + class PullRequestCreator + module Labelers + class Factory + class << self + def for_source(source:, custom_labels:, credentials:, includes_security_fixes:, dependencies:, + label_language:, automerge_candidate:) + labeler_params = { + custom_labels: custom_labels, + includes_security_fixes: includes_security_fixes, + dependencies: dependencies, + label_language: label_language, + automerge_candidate: automerge_candidate + } + + case source.provider + when "github" then github_labeler(source, credentials, labeler_params) + when "gitlab" then gitlab_labeler(source, credentials, labeler_params) + when "azure" then azure_labeler(source, labeler_params) + when "bitbucket" then base_labeler(source, labeler_params) + when "codecommit" then base_labeler(source, labeler_params) + else raise "Unsupported provider '#{source.provider}'." + end + end + + private + + def github_labeler(source, credentials, labeler_params) + require "dependabot/pull_request_creator/labelers/github" + require "dependabot/clients/github_with_retries" + client = Dependabot::Clients::GithubWithRetries.for_source( + source: source, + credentials: credentials + ) + Dependabot::PullRequestCreator::Labelers::Github.new( + source: source, + client: client, + **labeler_params + ) + end + + def gitlab_labeler(source, credentials, labeler_params) + require "dependabot/pull_request_creator/labelers/gitlab" + require "dependabot/clients/gitlab_with_retries" + client = Dependabot::Clients::GitlabWithRetries.for_source( + source: source, + credentials: credentials + ) + Dependabot::PullRequestCreator::Labelers::Gitlab.new( + source: source, + client: client, + **labeler_params + ) + end + + def azure_labeler(source, labeler_params) + require "dependabot/pull_request_creator/labelers/azure" + Dependabot::PullRequestCreator::Labelers::Azure.new( + source: source, + **labeler_params + ) + end + + def base_labeler(source, labeler_params) + require "dependabot/pull_request_creator/labeler" + Dependabot::PullRequestCreator.Labeler.new( + source: source, + **labeler_params + ) + end + end + end + end + end +end diff --git a/common/lib/dependabot/pull_request_creator/labelers/github.rb b/common/lib/dependabot/pull_request_creator/labelers/github.rb new file mode 100644 index 0000000000..45f86e3c6e --- /dev/null +++ b/common/lib/dependabot/pull_request_creator/labelers/github.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require "octokit" +require "dependabot/pull_request_creator/labeler" + +module Dependabot + class PullRequestCreator + module Labelers + class Github < Labeler + @package_manager_labels = {} + + def initialize(source:, custom_labels:, dependencies:, + includes_security_fixes:, label_language:, + automerge_candidate:, client:) + super( + source: source, + custom_labels: custom_labels, + includes_security_fixes: includes_security_fixes, + dependencies: dependencies, + label_language: label_language, + automerge_candidate: automerge_candidate + ) + @client = client + end + + def labels + @labels ||= fetch_labels + end + + def label_pull_request(pull_request_number) + create_default_labels_if_required + + return if labels_for_pr.none? + + @client.add_labels_to_an_issue( + source.repo, + pull_request_number, + labels_for_pr + ) + rescue Octokit::UnprocessableEntity, Octokit::NotFound + retry_count ||= 0 + retry_count += 1 + raise if retry_count > 10 + + sleep(rand(1..1.99)) + retry + end + + def create_dependencies_label + create_label( + DEFAULT_DEPENDENCIES_LABEL, + "0366d6", + "Pull requests that update a dependency file" + ) + end + + def create_security_label + create_label( + DEFAULT_SECURITY_LABEL, + "ee0701", + "Pull requests that address a security vulnerability" + ) + end + + def create_language_label + create_label( + language_name, + colour, + "Pull requests that update #{language_name.capitalize} code" + ) + end + + private + + def fetch_labels + labels ||= @client.labels(source.repo, per_page: 100).map(&:name) + + next_link = @client.last_response.rels[:next] + + while next_link + next_page = next_link.get + labels += next_page.data.map(&:name) + next_link = next_page.rels[:next] + end + + labels + end + + def create_label(label, colour, description) + @client.add_label( + source.repo, + label, + colour, + description: description, + accept: "application/vnd.github.symmetra-preview+json" + ) + @labels = [*@labels, label].uniq + rescue Octokit::UnprocessableEntity => e + raise unless e.errors.first.fetch(:code) == "already_exists" + + @labels = [*@labels, label].uniq + end + end + end + end +end diff --git a/common/lib/dependabot/pull_request_creator/labelers/gitlab.rb b/common/lib/dependabot/pull_request_creator/labelers/gitlab.rb new file mode 100644 index 0000000000..1a1620de10 --- /dev/null +++ b/common/lib/dependabot/pull_request_creator/labelers/gitlab.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require "dependabot/pull_request_creator/labeler" + +module Dependabot + class PullRequestCreator + module Labelers + class Gitlab < Labeler + @package_manager_labels = {} + + def initialize(source:, custom_labels:, dependencies:, + includes_security_fixes:, label_language:, + automerge_candidate:, client:) + super( + source: source, + custom_labels: custom_labels, + includes_security_fixes: includes_security_fixes, + dependencies: dependencies, + label_language: label_language, + automerge_candidate: automerge_candidate + ) + @client = client + end + + def labels + @labels ||= fetch_labels + end + + def create_dependencies_label + create_label( + DEFAULT_DEPENDENCIES_LABEL, + "0366d6", + "Pull requests that update a dependency file" + ) + end + + def create_security_label + create_label( + DEFAULT_SECURITY_LABEL, + "ee0701", + "Pull requests that address a security vulnerability" + ) + end + + def create_language_label + create_label( + language_name, + colour, + "Pull requests that update #{language_name.capitalize} code" + ) + end + + private + + def fetch_labels + @client.labels(source.repo, per_page: 100).auto_paginate.map(&:name) + end + + def create_label(label, colour, description) + @client.create_label( + source.repo, + label, + '#' + colour, + description: description + ) + @labels = [*@labels, label].uniq + end + end + end + end +end diff --git a/common/spec/dependabot/pull_request_creator/azure_spec.rb b/common/spec/dependabot/pull_request_creator/azure_spec.rb index 4cd3973bed..300cd2c8e9 100644 --- a/common/spec/dependabot/pull_request_creator/azure_spec.rb +++ b/common/spec/dependabot/pull_request_creator/azure_spec.rb @@ -4,6 +4,7 @@ require "dependabot/dependency" require "dependabot/dependency_file" require "dependabot/pull_request_creator/azure" +require "dependabot/pull_request_creator/labelers/azure" RSpec.describe Dependabot::PullRequestCreator::Azure do subject(:creator) do @@ -44,9 +45,8 @@ let(:assignee) { nil } let(:milestone) { nil } let(:labeler) do - Dependabot::PullRequestCreator::Labeler.new( + Dependabot::PullRequestCreator::Labelers::Azure.new( source: source, - credentials: credentials, custom_labels: custom_labels, includes_security_fixes: false, dependencies: [dependency], diff --git a/common/spec/dependabot/pull_request_creator/github_spec.rb b/common/spec/dependabot/pull_request_creator/github_spec.rb index 440820b39d..ea9239c062 100644 --- a/common/spec/dependabot/pull_request_creator/github_spec.rb +++ b/common/spec/dependabot/pull_request_creator/github_spec.rb @@ -4,6 +4,7 @@ require "dependabot/dependency" require "dependabot/dependency_file" require "dependabot/pull_request_creator/github" +require "dependabot/pull_request_creator/labelers/github" RSpec.describe Dependabot::PullRequestCreator::Github do subject(:creator) do @@ -51,15 +52,21 @@ let(:assignees) { nil } let(:milestone) { nil } let(:require_up_to_date_base) { false } + let(:client) do + Dependabot::Clients::GithubWithRetries.for_source( + source: source, + credentials: credentials + ) + end let(:labeler) do - Dependabot::PullRequestCreator::Labeler.new( + Dependabot::PullRequestCreator::Labelers::Github.new( source: source, - credentials: credentials, custom_labels: custom_labels, includes_security_fixes: false, dependencies: [dependency], label_language: false, - automerge_candidate: false + automerge_candidate: false, + client: client ) end let(:custom_labels) { nil } diff --git a/common/spec/dependabot/pull_request_creator/gitlab_spec.rb b/common/spec/dependabot/pull_request_creator/gitlab_spec.rb index d775e407b3..2bcbdee3d9 100644 --- a/common/spec/dependabot/pull_request_creator/gitlab_spec.rb +++ b/common/spec/dependabot/pull_request_creator/gitlab_spec.rb @@ -4,6 +4,7 @@ require "dependabot/dependency" require "dependabot/dependency_file" require "dependabot/pull_request_creator/gitlab" +require "dependabot/pull_request_creator/labelers/gitlab" RSpec.describe Dependabot::PullRequestCreator::Gitlab do subject(:creator) do @@ -45,15 +46,21 @@ let(:approvers) { nil } let(:assignees) { nil } let(:milestone) { nil } + let(:client) do + Dependabot::Clients::GitlabWithRetries.for_source( + source: source, + credentials: credentials + ) + end let(:labeler) do - Dependabot::PullRequestCreator::Labeler.new( + Dependabot::PullRequestCreator::Labelers::Gitlab.new( source: source, - credentials: credentials, custom_labels: custom_labels, includes_security_fixes: false, dependencies: [dependency], label_language: false, - automerge_candidate: false + automerge_candidate: false, + client: client ) end let(:custom_labels) { nil } diff --git a/common/spec/dependabot/pull_request_creator/labeler_spec.rb b/common/spec/dependabot/pull_request_creator/labeler_spec.rb index 3d98f489e6..8ba7d3db0e 100644 --- a/common/spec/dependabot/pull_request_creator/labeler_spec.rb +++ b/common/spec/dependabot/pull_request_creator/labeler_spec.rb @@ -5,10 +5,11 @@ require "dependabot/dependency_file" require "dependabot/source" require "dependabot/pull_request_creator/labeler" +require "dependabot/pull_request_creator/labelers/factory" -RSpec.describe Dependabot::PullRequestCreator::Labeler do +RSpec.describe Dependabot::PullRequestCreator::Labelers::Factory do subject(:labeler) do - described_class.new( + described_class.for_source( source: source, credentials: credentials, custom_labels: custom_labels, @@ -58,7 +59,7 @@ let(:repo_api_url) { "https://api.github.com/repos/#{source.repo}" } describe ".package_manager_labels" do - subject { described_class.package_manager_labels } + subject { labeler.package_manager_labels } it { is_expected.to eq("dummy" => { colour: "ce2d2d", name: "ruby" }) } end diff --git a/common/spec/dependabot/pull_request_creator_spec.rb b/common/spec/dependabot/pull_request_creator_spec.rb index 82761e6173..999ba56e5d 100644 --- a/common/spec/dependabot/pull_request_creator_spec.rb +++ b/common/spec/dependabot/pull_request_creator_spec.rb @@ -197,7 +197,7 @@ author_details: author_details, signature_key: signature_key, custom_headers: nil, - labeler: instance_of(described_class::Labeler), + labeler: instance_of(described_class::Labelers::Github), reviewers: reviewers, assignees: assignees, milestone: milestone, @@ -212,7 +212,7 @@ let(:source) { Dependabot::Source.new(provider: "gitlab", repo: "gc/bp") } let(:dummy_creator) { instance_double(described_class::Gitlab) } - it "delegates to PullRequestCreator::Github with correct params" do + it "delegates to PullRequestCreator::Gitlab with correct params" do expect(described_class::Gitlab). to receive(:new). with( @@ -225,7 +225,7 @@ pr_description: "PR msg", pr_name: "PR name", author_details: author_details, - labeler: instance_of(described_class::Labeler), + labeler: instance_of(described_class::Labelers::Gitlab), approvers: reviewers, assignees: nil, milestone: milestone @@ -253,7 +253,7 @@ pr_description: "PR msg", pr_name: "PR name", author_details: author_details, - labeler: instance_of(described_class::Labeler), + labeler: instance_of(described_class::Labelers::Azure), work_item: 123 ).and_return(dummy_creator) expect(dummy_creator).to receive(:create) @@ -311,7 +311,7 @@ author_details: author_details, signature_key: signature_key, custom_headers: nil, - labeler: instance_of(described_class::Labeler), + labeler: instance_of(described_class::Labelers::Github), reviewers: reviewers, assignees: assignees, milestone: milestone,