diff options
Diffstat (limited to 'app/finders/repositories/changelog_tag_finder.rb')
-rw-r--r-- | app/finders/repositories/changelog_tag_finder.rb | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/app/finders/repositories/changelog_tag_finder.rb b/app/finders/repositories/changelog_tag_finder.rb new file mode 100644 index 00000000000..3c110e6c65d --- /dev/null +++ b/app/finders/repositories/changelog_tag_finder.rb @@ -0,0 +1,82 @@ +# 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 |