# frozen_string_literal: true module Gitlab module QA module Support class GitlabVersionInfo VERSION_PATTERN = /^(?\d+\.\d+\.\d+)/ COMPONENT_PATTERN = /^(?\d+)\.(?\d+)\.(?\d+)/ # Get previous gitlab version # # @param [String] current_version # @param [String] edition GitLab edition - ee or ce def initialize(current_version, edition) @current_version = current_version @edition = edition @logger = Runtime::Logger.logger end # Get N - 1 version number # # @param [String] semver_component version number component for previous version detection - major|minor|patch # @return [Gem::Version] def previous_version(semver_component) case semver_component when "major" previous_major when "minor" previous_minor when "patch" previous_patch else raise("Unsupported semver component, must be major|minor|patch") end end # Get latest patch for specific version number # # @example # latest_patch(Gem::Version.new("14.10")) => "14.10.5" # # @param [Gem::Version] version # @return [String] def latest_patch(version) versions.find { |ver| ver.to_s.match?(/^#{version}/) } end private attr_reader :current_version, :edition, :logger # Current versions major version # # @return [Integer] def current_major @current_major ||= current_version.match(COMPONENT_PATTERN)[:major].to_i end # Current versions minor version # # @return [Integer] def current_minor @current_minor ||= current_version.match(COMPONENT_PATTERN)[:minor].to_i end # Current versions patch version # # @return [Integer] def current_patch @current_patch ||= current_version.match(COMPONENT_PATTERN)[:patch].to_i end # Previous major version # # @return [String] def previous_major return fallback_major unless tags versions.find { |version| version.to_s.start_with?((current_major - 1).to_s) } end # Previous first major version image # # @return [String] def fallback_major previous_fallback_version(current_major - 1) end # Previous minor version # # @return [String] def previous_minor return fallback_minor unless tags return previous_major if current_minor.zero? versions.find { |version| version.to_s.match?(/^#{current_major}\.#{current_minor - 1}/) } end # Previous first minor version # # @return [String] def fallback_minor return previous_fallback_version(current_major, current_minor - 1) unless current_minor.zero? previous_major end # Previous patch version # # @return [String] def previous_patch return fallback_patch unless tags return previous_minor if current_patch.zero? versions.find { |version| version.to_s.match?(/^#{current_major}\.#{current_minor}\.#{current_patch - 1}/) } end # Previous first patch version # # @return [String] def fallback_patch return previous_fallback_version(current_major, current_minor, current_patch - 1) unless current_patch.zero? previous_minor end # Version number from docker tag # # @param [String] tag # @return [String] def version(tag) tag.match(VERSION_PATTERN)[:version] end # Fallback version # # @param [Integer] major_component # @param [Integer] minor_component # @param [Integer] patch_component # @return [Gem::Version] def previous_fallback_version(major_component, minor_component = 0, patch_component = 0) Gem::Version.new("#{major_component}.#{minor_component}.#{patch_component}") end # All available gitlab versions # # @return [Array] def versions @versions = tags .map { |tag| Gem::Version.new(tag.match(VERSION_PATTERN)[:version]) } .sort .reverse # reverse array so first match by .find always returns latest version end # All available docker tags # # @return [Array] def tags return @tags if defined?(@tags) logger.info("Fetching docker tags from 'gitlab/gitlab-#{edition}' registry") response = HttpRequest.make_http_request( url: "https://registry.hub.docker.com/v2/namespaces/gitlab/repositories/gitlab-#{edition}/tags?page_size=1000", fail_on_error: false ) unless response.code == 200 logger.error(" failed to fetch docker tags - code: #{response.code}, response: '#{response.body}'") return @tags = nil end @tags = JSON .parse(response.body, symbolize_names: true) .fetch(:results) .map { |tag| tag[:name] } .grep(VERSION_PATTERN) end end end end end