summaryrefslogtreecommitdiff
path: root/lib/gitlab/changelog/config.rb
blob: 0538fe68474224e4de0e483acd71beae874aa63a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# frozen_string_literal: true

module Gitlab
  module Changelog
    # Configuration settings used when generating changelogs.
    class Config
      # When rendering changelog entries, authors are not included.
      AUTHORS_NONE = 'none'

      # The path to the configuration file as stored in the project's Git
      # repository.
      FILE_PATH = '.gitlab/changelog_config.yml'

      # The default date format to use for formatting release dates.
      DEFAULT_DATE_FORMAT = '%Y-%m-%d'

      # The default template to use for generating release sections.
      DEFAULT_TEMPLATE = File.read(File.join(__dir__, 'template.tpl'))

      # The regex to use for extracting the version from a Git tag.
      #
      # This regex is based on the official semantic versioning regex (as found
      # on https://semver.org/), with the addition of allowing a "v" at the
      # start of a tag name.
      #
      # We default to a strict regex as we simply don't know what kind of data
      # users put in their tags. As such, using simpler patterns (e.g. just
      # `\d+` for the major version) could lead to unexpected results.
      #
      # We use a String here as `Gitlab::UntrustedRegexp` is a mutable object.
      DEFAULT_TAG_REGEX = '^v?(?P<major>0|[1-9]\d*)' \
        '\.(?P<minor>0|[1-9]\d*)' \
        '\.(?P<patch>0|[1-9]\d*)' \
        '(?:-(?P<pre>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))' \
        '?(?:\+(?P<meta>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'

      attr_accessor :date_format, :categories, :template, :tag_regex

      def self.from_git(project)
        if (yaml = project.repository.changelog_config)
          from_hash(project, YAML.safe_load(yaml))
        else
          new(project)
        end
      end

      def self.from_hash(project, hash)
        config = new(project)

        if (date = hash['date_format'])
          config.date_format = date
        end

        if (template = hash['template'])
          config.template =
            begin
              TemplateParser::Parser.new.parse_and_transform(template)
            rescue TemplateParser::Error => e
              raise Error, e.message
            end
        end

        if (categories = hash['categories'])
          if categories.is_a?(Hash)
            config.categories = categories
          else
            raise Error, 'The "categories" configuration key must be a Hash'
          end
        end

        if (regex = hash['tag_regex'])
          config.tag_regex = regex
        end

        config
      end

      def initialize(project)
        @project = project
        @date_format = DEFAULT_DATE_FORMAT
        @template =
          begin
            TemplateParser::Parser.new.parse_and_transform(DEFAULT_TEMPLATE)
          rescue TemplateParser::Error => e
            raise Error, e.message
          end
        @categories = {}
        @tag_regex = DEFAULT_TAG_REGEX
      end

      def contributor?(user)
        @project.team.contributor?(user)
      end

      def category(name)
        @categories[name] || name
      end

      def format_date(date)
        date.strftime(@date_format)
      end
    end
  end
end