lib/circleci/bundle/update/pr.rb in circleci-bundle-update-pr-1.11.3 vs lib/circleci/bundle/update/pr.rb in circleci-bundle-update-pr-1.12.0

- old
+ new

@@ -7,54 +7,104 @@ module Update module Pr def self.create_if_needed(git_username: nil, git_email: nil, git_branches: ["master"], assignees: nil, reviewers: nil, labels: nil) raise_if_env_unvalid! - return unless need?(git_branches) - repo_full_name = "#{ENV['CIRCLE_PROJECT_USERNAME']}/#{ENV['CIRCLE_PROJECT_REPONAME']}" - now = Time.now - branch = "bundle-update-#{now.strftime('%Y%m%d%H%M%S')}" + if skip? + puts 'Skip because it has already existed.' + return + end + + unless need_to_commit?(git_branches) + puts 'No changes due to bundle update' + return + end + git_username ||= client.user.login git_email ||= "#{git_username}@users.noreply.#{github_host}" - create_branch(git_username, git_email, branch, repo_full_name) - pull_request = create_pull_request(repo_full_name, branch, now) - add_labels(repo_full_name, pull_request[:number], labels) if labels - update_pull_request_body(repo_full_name, pull_request[:number]) - add_assignees(repo_full_name, pull_request[:number], assignees) if assignees - request_review(repo_full_name, pull_request[:number], reviewers) if reviewers + branch = create_branch(git_username, git_email) + pull_request = create_pull_request(branch) + add_labels(pull_request[:number], labels) if labels + update_pull_request_body(pull_request[:number]) + add_assignees(pull_request[:number], assignees) if assignees + request_review(pull_request[:number], reviewers) if reviewers end - def self.need?(git_branches) + BRANCH_PREFIX = 'bundle-update-'.freeze + TITLE_PREFIX = 'bundle update at '.freeze + + def self.raise_if_env_unvalid! + raise "$CIRCLE_PROJECT_USERNAME isn't set" unless ENV['CIRCLE_PROJECT_USERNAME'] + raise "$CIRCLE_PROJECT_REPONAME isn't set" unless ENV['CIRCLE_PROJECT_REPONAME'] + raise "$GITHUB_ACCESS_TOKEN isn't set" unless ENV['GITHUB_ACCESS_TOKEN'] + if ENV['ENTERPRISE_OCTOKIT_ACCESS_TOKEN'] && !ENV['ENTERPRISE_OCTOKIT_API_ENDPOINT'] + raise "$ENTERPRISE_OCTOKIT_API_ENDPOINT isn't set" + end + if !ENV['ENTERPRISE_OCTOKIT_ACCESS_TOKEN'] && ENV['ENTERPRISE_OCTOKIT_API_ENDPOINT'] + raise "$ENTERPRISE_OCTOKIT_ACCESS_TOKEN isn't set" + end + end + private_class_method :raise_if_env_unvalid! + + # Has 'bundle update PR' already existed? + # + # @return [Boolean] + def self.skip? + client.pull_requests(repo_full_name).find do |pr| + pr.title =~ /\A#{TITLE_PREFIX}/ && pr.head.ref =~ /\A#{BRANCH_PREFIX}\d+/ + end != nil + end + private_class_method :skip? + + # Does it need to commit due to bundle update? + # + # @param git_branches [Array<String>] + # @return [Boolean] + def self.need_to_commit?(git_branches) return false unless git_branches.include?(ENV['CIRCLE_BRANCH']) unless system("bundle update && bundle update --ruby") raise "Unable to execute `bundle update && bundle update --ruby`" end `git status -sb 2> /dev/null`.include?("Gemfile.lock") end - private_class_method :need? + private_class_method :need_to_commit? - def self.create_branch(git_username, git_email, branch, repo_full_name) + # Create remote branch for bundle update PR + # + # @return [String] remote branch name. e.g. bundle-update-20180929154455 + def self.create_branch(git_username, git_email) + branch = "#{BRANCH_PREFIX}#{now.strftime('%Y%m%d%H%M%S')}" remote = "https://#{github_access_token}@#{github_host}/#{repo_full_name}" system("git remote add github-url-with-token #{remote}") system("git config user.name #{git_username}") system("git config user.email #{git_email}") system("git add Gemfile.lock") system("git commit -m '$ bundle update && bundle update --ruby'") system("git branch -M #{branch}") system("git push -q github-url-with-token #{branch}") + branch end private_class_method :create_branch - def self.create_pull_request(repo_full_name, branch, now) - title = "bundle update at #{now.strftime('%Y-%m-%d %H:%M:%S %Z')}" + # Create bundle update PR + # + # @param branch [String] branch name + # @return [Sawyer::Resource] The newly created pull request + def self.create_pull_request(branch) + title = "#{TITLE_PREFIX}#{now.strftime('%Y-%m-%d %H:%M:%S %Z')}" client.create_pull_request(repo_full_name, ENV['CIRCLE_BRANCH'], branch, title) end private_class_method :create_pull_request - def self.update_pull_request_body(repo_full_name, pr_number) + def self.add_labels(pr_number, labels) + client.add_labels_to_an_issue(repo_full_name, pr_number, labels) + end + private_class_method :add_labels + + def self.update_pull_request_body(pr_number) ENV["OCTOKIT_ACCESS_TOKEN"] = ENV["GITHUB_ACCESS_TOKEN"] compare_linker = CompareLinker.new(repo_full_name, pr_number) compare_linker.formatter = CompareLinker::Formatter::Markdown.new body = <<-EOB @@ -67,25 +117,20 @@ client.update_pull_request(repo_full_name, pr_number, body: body) end private_class_method :update_pull_request_body - def self.add_assignees(repo_full_name, pr_number, assignees) + def self.add_assignees(pr_number, assignees) client.add_assignees(repo_full_name, pr_number, assignees) end private_class_method :add_assignees - def self.request_review(repo_full_name, pr_number, reviewers) + def self.request_review(pr_number, reviewers) client.request_pull_request_review(repo_full_name, pr_number, reviewers) end private_class_method :request_review - def self.add_labels(repo_full_name, pr_number, labels) - client.add_labels_to_an_issue(repo_full_name, pr_number, labels) - end - private_class_method :add_labels - def self.client if enterprise? Octokit::Client.new(access_token: ENV['ENTERPRISE_OCTOKIT_ACCESS_TOKEN'], api_endpoint: ENV['ENTERPRISE_OCTOKIT_API_ENDPOINT']) else @@ -102,30 +147,33 @@ def self.github_access_token enterprise? ? ENV['ENTERPRISE_OCTOKIT_ACCESS_TOKEN'] : ENV['GITHUB_ACCESS_TOKEN'] end private_class_method :github_access_token + # Get repository full name + # + # @return [String] e.g. 'masutaka/circleci-bundle-update-pr' + def self.repo_full_name + @repo_full_name ||= "#{ENV['CIRCLE_PROJECT_USERNAME']}/#{ENV['CIRCLE_PROJECT_REPONAME']}" + end + private_class_method :repo_full_name + def self.github_host # A format like https://github.com/masutaka/circleci-bundle-update-pr.git return $1 if ENV['CIRCLE_REPOSITORY_URL'] =~ %r{https://(.+?)/} # A format like git@github.com:masutaka/compare_linker.git return $1 if ENV['CIRCLE_REPOSITORY_URL'] =~ %r{([^@]+?):} 'github.com' end private_class_method :github_host - def self.raise_if_env_unvalid! - raise "$CIRCLE_PROJECT_USERNAME isn't set" unless ENV['CIRCLE_PROJECT_USERNAME'] - raise "$CIRCLE_PROJECT_REPONAME isn't set" unless ENV['CIRCLE_PROJECT_REPONAME'] - raise "$GITHUB_ACCESS_TOKEN isn't set" unless ENV['GITHUB_ACCESS_TOKEN'] - if ENV['ENTERPRISE_OCTOKIT_ACCESS_TOKEN'] && !ENV['ENTERPRISE_OCTOKIT_API_ENDPOINT'] - raise "$ENTERPRISE_OCTOKIT_API_ENDPOINT isn't set" - end - if !ENV['ENTERPRISE_OCTOKIT_ACCESS_TOKEN'] && ENV['ENTERPRISE_OCTOKIT_API_ENDPOINT'] - raise "$ENTERPRISE_OCTOKIT_ACCESS_TOKEN isn't set" - end + # Get unified current time + # + # @return [Time] + def self.now + @now ||= Time.now end - private_class_method :raise_if_env_unvalid! + private_class_method :now end end end end