lib/release_tools/commits.rb (71 lines of code) (raw):
# frozen_string_literal: true
module ReleaseTools
class Commits
include ::SemanticLogger::Loggable
MAX_COMMITS_TO_CHECK = 100
def initialize(project, ref: project.default_branch, client: ReleaseTools::GitlabClient)
@project = project
@ref = ref
@client = client
end
def merge_base(other_ref)
@client.merge_base(@project.auto_deploy_path, [@ref, other_ref])&.id
end
# Get the latest commit for `ref`
def latest
commit_list.first
end
delegate :detect, to: :commit_list
# Find a commit with a passing build on production that also exists on dev.
# The definition of success changes based on the operation, a predicate block
# must be provided that receives a commit id and returns if it is a success.
#
# @param since_last_auto_deploy [boolean] if true, it will limit the search to
# the merge base with the SHA of last auto_deploy package
# @yieldparam commit_id [string] The commit ID to validate for success
# @yieldreturn [boolean] whatever the provided commit should be considered successful
def latest_successful_on_build(since_last_auto_deploy: true)
limit = find_last_auto_deploy_limit_sha if since_last_auto_deploy
commit_list.detect do |commit|
if commit.id == limit
logger.info('Reached the limit commit', project: @project.auto_deploy_path, limit: limit)
next true
end
next unless yield(commit.id)
begin
# Hit the dev API with the specified commit to see if it even exists
Retriable.with_context(:mirroring) do
ReleaseTools::GitlabDevClient
.commit(@project.dev_path, ref: commit.id)
end
logger.info(
'Passing commit found on Build',
project: @project.auto_deploy_path,
commit: commit.id
)
rescue Gitlab::Error::Error
logger.debug(
'Commit passed on Canonical, missing on Build',
project: @project.auto_deploy_path,
commit: commit.id
)
false
end
end
end
# @param commit_id [String] is a commit in the commit_list.
# @return [ObjectifiedHash, nil] Returns the next chronologically newer commit after commit_id.
# Returns nil if commit_id doesn't exist in commit_list, or if there is no
# commit newer than commit_id.
def next_commit(commit_id)
index = commit_list.index { |c| c.id == commit_id }
return if index.nil? || index.zero?
# Since the commit_list is in descending chronological order, the previous commit
# in the list is the next newer commit.
commit_list[index - 1]
end
def find_last_auto_deploy_limit_sha
# Helm auto-deploy support is incomplete and we are not tracking it
return unless [Project::GitlabEe, Project::OmnibusGitlab, Project::CNGImage].include?(@project)
return @find_last_auto_deploy_limit_sha if defined?(@find_last_auto_deploy_limit_sha)
product_version = ProductVersion.last_auto_deploy
last_pkg_sha = product_version[@project.metadata_project_name]&.sha
logger.info('Finding auto_deploy limit SHA', project: @project, last_product_version: product_version.version, last_pkg_sha: last_pkg_sha)
# TODO (rpereira2): Raise an error here instead of silently returning nil?
return unless last_pkg_sha
@find_last_auto_deploy_limit_sha =
merge_base(last_pkg_sha).tap do |id|
logger.info('Found auto_deploy limit SHA', limit_sha: id)
end
end
private
def commit_list
@commit_list ||= @client.commits(
@project.auto_deploy_path,
per_page: MAX_COMMITS_TO_CHECK,
ref_name: @ref
)
end
end
end