lib/release_tools/public_release/cng_image_release.rb (188 lines of code) (raw):

# frozen_string_literal: true module ReleaseTools module PublicRelease # A release of our CNG image using the API. class CNGImageRelease include Release attr_reader :version, :client, :release_metadata DEFAULT_UBI_VERSION = '8' GITLAB_FIPS_MINIMUM_VERSION = '15.1.0' # When https://gitlab.com/gitlab-org/build/CNG/-/merge_requests/981 was introduced GITLAB_MAILROOM_MINIMUM_VERSION = '15.11.0' # When https://gitlab.com/gitlab-org/gitlab/-/merge_requests/116494 was merged UBI_DROP_SUFFIX_VERSION = '17.3.0' # When https://gitlab.com/gitlab-org/build/CNG/-/merge_requests/1888 was merged VERSION_FILES = [ Project::Gitaly.version_file, Project::GitlabShell.version_file, Project::GitlabPages.version_file, Project::GitlabElasticsearchIndexer.version_file, Project::Kas.version_file ].freeze # Variables that stores version information. # Even though it says GITLAB_REF_SLUG, because of what CNG expects in # tag/release pipelines, it stores the non-slugified form of the tag. CNG # expects the slug form only when pulling GitLab rails from a branch, not # when from a tag. Hence, this variable is under `GITLAB_VERSION_FILES` # and not `GITLAB_SLUG_FILES`. GITLAB_VERSION_FILES = %w[GITLAB_VERSION GITLAB_REF_SLUG].freeze # Variables that stores version slug information. GITLAB_SLUG_FILES = %w[GITLAB_ASSETS_TAG].freeze VARIABLES_FILE = 'ci_files/variables.yml' def initialize( version, client: GitlabClient, release_metadata: ReleaseMetadata.new, ubi_version: DEFAULT_UBI_VERSION, commit: nil ) @version = version.to_ee @gitlab_version = Version.new(@version.to_normalized_version) @client = client @release_metadata = release_metadata @ubi_version = ubi_version @commit = commit end def execute logger.info('Starting release of CNG', version: version) create_target_branch return if SharedStatus.dry_run? ce_tag, ee_tag, ubi_tag, fips_tag = update_component_versions add_release_data_for_tags(ce_tag, ee_tag, ubi_tag, fips_tag) notify_slack(project, version) end def update_component_versions logger.info( 'Updating component versions', project: project_path, branch: target_branch ) ce_data, ee_data = distribution_component_versions [ create_ce_tag(ce_data), create_ee_tag(ee_data), create_ubi_tag, create_fips_tag ] end def create_ce_tag(variables) find_or_create_tag(version.to_ce.tag, variables) end def create_ee_tag(variables) find_or_create_tag(version.to_ee.tag, variables) end def create_ubi_tag tag = if version >= Version.new(UBI_DROP_SUFFIX_VERSION) && Feature.enabled?(:drop_ubi_version_suffix) version.tag(ee: true).gsub(/-ee$/, "-ubi") else version.tag(ee: true).gsub(/-ee$/, "-ubi#{@ubi_version}") end logger.info('Creating UBI tag', tag: tag, project: project_path) # UBI tags contain the same data as EE, so we don't need to bump any # version files. client.find_or_create_tag( project_path, tag, target_branch, message: "Version #{tag}" ) end def create_fips_tag return unless version >= Version.new(GITLAB_FIPS_MINIMUM_VERSION) tag = version.tag(ee: true).gsub(/-ee$/, "-fips") logger.info('Creating FIPS tag', tag: tag, project: project_path) # FIPS tags contain the same data as EE, so we don't need to bump any # version files. client.find_or_create_tag( project_path, tag, target_branch, message: "Version #{tag}" ) end def find_or_create_tag(tag, variables) Retriable.with_context(:api) do client.tag(project_path, tag: tag) rescue Gitlab::Error::NotFound logger.info('Creating new tag', tag: tag, project: project_path) commit_version_files( target_branch, { VARIABLES_FILE => YAML.dump(variables) } ) client.create_tag(project_path, tag, target_branch, "Version #{tag}") end end def distribution_component_versions variables_yaml = read_file(VARIABLES_FILE) versions = component_versions ce_data = YAML.safe_load(variables_yaml) ee_data = YAML.safe_load(variables_yaml) ce_data['variables'].merge!(versions) ee_data['variables'].merge!(versions) # For the version of GitLab itself, we only want the -ee suffix for an # EE CNG release. GITLAB_VERSION_FILES.each do |file| ce_data['variables'][file] = @gitlab_version.tag(ee: false) ee_data['variables'][file] = @gitlab_version.tag(ee: true) end GITLAB_SLUG_FILES.each do |file| ce_data['variables'][file] = @gitlab_version.slug.tag(ee: false) ee_data['variables'][file] = @gitlab_version.slug.tag(ee: true) end [ce_data, ee_data] end def component_versions gemfile_parser = GemfileParser.new(read_ee_file('Gemfile.lock')) components = {} VERSION_FILES.each do |file| components[file] = version_string(read_ee_file(file)) end Project::GitlabEe.gems.each do |pattern, file| # Ensure ci/variables.yml are always used for ignored versions next if ignore?(file) components[file] = gemfile_parser.gem_version(pattern) end components end def add_release_data_for_tags(ce_tag, ee_tag, ubi_tag, fips_tag) meta_version = version.to_normalized_version logger.info( 'Recording release data', project: project_path, version: meta_version, ce_tag: ce_tag.name, ee_tag: ee_tag.name, ubi_tag: ubi_tag.name, fips_tag: fips_tag&.name ) add_release_data('cng-ce', meta_version, ce_tag) add_release_data('cng-ee', meta_version, ee_tag) add_release_data('cng-ee-ubi', meta_version, ubi_tag) add_release_data('cng-ee-fips', meta_version, fips_tag) if fips_tag end def add_release_data(name, version, tag) release_metadata.add_release( name: name, version: version, sha: tag.commit.id, ref: tag.name, tag: true ) end def read_ee_file(file) path = Project::GitlabEe.canonical_or_security_path branch = @gitlab_version.stable_branch(ee: true) read_file(file, path, branch) end def read_file(file, project = project_path, branch = target_branch) content = Retriable.with_context(:api) do client.file_contents(project, file, branch) end content.strip end def version_string(version) # If it looks like SemVer, assume it's a tag, which we prepend with `v` if version.match?(/\A\d+\.\d+\.\d+/) "v#{version}" else version end end def project Project::CNGImage end def ignore?(file) ignored = Project::GitlabEe.ignore_versions ignored -= ['MAILROOM_VERSION'] if version >= Version.new(GITLAB_MAILROOM_MINIMUM_VERSION) ignored.include?(file) end def source_for_target_branch if @commit logger.info('Using specific commit', project: project_path, commit: @commit) end @commit || project.default_branch end end end end