83 lines
2.4 KiB
Ruby
83 lines
2.4 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
module Repositories
|
||
|
# A finder class for getting the tag of the last release before a given
|
||
|
# version, used when generating changelogs.
|
||
|
#
|
||
|
# Imagine a project with the following tags:
|
||
|
#
|
||
|
# * v1.0.0
|
||
|
# * v1.1.0
|
||
|
# * v2.0.0
|
||
|
#
|
||
|
# If the version supplied is 2.1.0, the tag returned will be v2.0.0. And when
|
||
|
# the version is 1.1.1, or 1.2.0, the returned tag will be v1.1.0.
|
||
|
#
|
||
|
# To obtain the tags, this finder requires a regular expression (using the re2
|
||
|
# syntax) to be provided. This regex must produce the following named
|
||
|
# captures:
|
||
|
#
|
||
|
# - major (required)
|
||
|
# - minor (required)
|
||
|
# - patch (required)
|
||
|
# - pre
|
||
|
# - meta
|
||
|
#
|
||
|
# If the `pre` group has a value, the tag is ignored. If any of the required
|
||
|
# capture groups don't have a value, the tag is also ignored.
|
||
|
class ChangelogTagFinder
|
||
|
def initialize(project, regex: Gitlab::Changelog::Config::DEFAULT_TAG_REGEX)
|
||
|
@project = project
|
||
|
@regex = regex
|
||
|
end
|
||
|
|
||
|
def execute(new_version)
|
||
|
tags = {}
|
||
|
versions = [new_version]
|
||
|
|
||
|
begin
|
||
|
regex = Gitlab::UntrustedRegexp.new(@regex)
|
||
|
rescue RegexpError => ex
|
||
|
# The error messages produced by default are not very helpful, so we
|
||
|
# raise a better one here. We raise the specific error here so its
|
||
|
# message is displayed in the API (where we catch this specific
|
||
|
# error).
|
||
|
raise(
|
||
|
Gitlab::Changelog::Error,
|
||
|
"The regular expression to use for finding the previous tag for a version is invalid: #{ex.message}"
|
||
|
)
|
||
|
end
|
||
|
|
||
|
@project.repository.tags.each do |tag|
|
||
|
matches = regex.match(tag.name)
|
||
|
|
||
|
next unless matches
|
||
|
|
||
|
# When using this class for generating changelog data for a range of
|
||
|
# commits, we want to compare against the tag of the last _stable_
|
||
|
# release; not some random RC that came after that.
|
||
|
next if matches[:pre]
|
||
|
|
||
|
major = matches[:major]
|
||
|
minor = matches[:minor]
|
||
|
patch = matches[:patch]
|
||
|
build = matches[:meta]
|
||
|
|
||
|
next unless major && minor && patch
|
||
|
|
||
|
version = "#{major}.#{minor}.#{patch}"
|
||
|
version += "+#{build}" if build
|
||
|
|
||
|
tags[version] = tag
|
||
|
versions << version
|
||
|
end
|
||
|
|
||
|
VersionSorter.sort!(versions)
|
||
|
|
||
|
index = versions.index(new_version)
|
||
|
|
||
|
tags[versions[index - 1]] if index&.positive?
|
||
|
end
|
||
|
end
|
||
|
end
|