diff --git a/.gitlab/changelog_config.yml b/.gitlab/changelog_config.yml index 6069cd17a084063960976731517f3ac460d5968c..f6a041cced971e262a7ac654c1ee81531df862a0 100644 --- a/.gitlab/changelog_config.yml +++ b/.gitlab/changelog_config.yml @@ -11,6 +11,8 @@ categories: security: Security performance: Performance other: Other +include_groups: + - gitlab-org/gitlab-core-team/community-members template: | {% if categories %} {% each categories %} @@ -18,7 +20,7 @@ template: | {% each entries %} - [{{ title }}]({{ commit.reference }})\ - {% if author.contributor %} by {{ author.reference }}{% end %}\ + {% if author.credit %} by {{ author.reference }}{% end %}\ {% if commit.trailers.MR %}\ ([merge request]({{ commit.trailers.MR }}))\ {% else %}\ diff --git a/app/services/repositories/changelog_service.rb b/app/services/repositories/changelog_service.rb index bac3fdf36da7aa2845b25e1e02ffaab39be41fd7..96db00fbc1ba95a5d0cdffefb7004b000cb0e50b 100644 --- a/app/services/repositories/changelog_service.rb +++ b/app/services/repositories/changelog_service.rb @@ -61,7 +61,7 @@ def initialize( # rubocop: enable Metrics/ParameterLists def execute - config = Gitlab::Changelog::Config.from_git(@project) + config = Gitlab::Changelog::Config.from_git(@project, @user) from = start_of_commit_range(config) # For every entry we want to only include the merge request that diff --git a/doc/api/repositories.md b/doc/api/repositories.md index a669bb5177f898efc0caf4da049c9ab1624264a2..1c9136d22aca9eb098a1c0406737eae6a64d3307 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -448,6 +448,10 @@ You can set the following variables in this file: - `template`: a custom template to use for generating the changelog data. - `categories`: a hash that maps raw category names to the names to use in the changelog. +- `include_groups`: a list of group full paths containing users whose + contributions should be credited regardless of project membership. The user + generating the changelog must have access to each group or the members will + not be credited. Using the default settings, generating a changelog results in a section along the lines of the following: @@ -508,7 +512,7 @@ follows: {% each entries %} - [{{ title }}]({{ commit.reference }})\ -{% if author.contributor %} by {{ author.reference }}{% end %}\ +{% if author.credit %} by {{ author.reference }}{% end %}\ {% if merge_request %} ([merge request]({{ merge_request.reference }})){% end %} {% end %} @@ -598,7 +602,7 @@ template: | {% each entries %} - [{{ title }}]({{ commit.reference }})\ - {% if author.contributor %} by {{ author.reference }}{% end %} + {% if author.credit %} by {{ author.reference }}{% end %} {% end %} @@ -634,8 +638,11 @@ In an entry, the following variables are available (here `foo.bar` means that - `commit.trailers`: an object containing all the Git trailers that were present in the commit body. - `author.reference`: a reference to the commit author (for example, `@alice`). -- `author.contributor`: a boolean set to `true` when the author is an external - contributor, otherwise this is set to `false`. +- `author.contributor`: a boolean set to `true` when the author is not a project + member, otherwise `false`. +- `author.credit`: a boolean set to `true` when `author.contributor` is `true` or + when `include_groups` is configured, and the author is a member of one of the + groups. - `merge_request.reference`: a reference to the merge request that first introduced the change (for example, `gitlab-org/gitlab!50063`). diff --git a/lib/gitlab/changelog/config.rb b/lib/gitlab/changelog/config.rb index d25094d9b372af56cdf56c5b3de520055e7002e8..fd5d701b858f82f3f8989f0030b920058aeaf123 100644 --- a/lib/gitlab/changelog/config.rb +++ b/lib/gitlab/changelog/config.rb @@ -34,17 +34,17 @@ class Config '(?:-(?P
(?: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[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
- attr_accessor :date_format, :categories, :template, :tag_regex
+ attr_accessor :date_format, :categories, :template, :tag_regex, :always_credit_user_ids
- def self.from_git(project)
+ def self.from_git(project, user = nil)
if (yaml = project.repository.changelog_config)
- from_hash(project, YAML.safe_load(yaml))
+ from_hash(project, YAML.safe_load(yaml), user)
else
new(project)
end
end
- def self.from_hash(project, hash)
+ def self.from_hash(project, hash, user = nil)
config = new(project)
if (date = hash['date_format'])
@@ -72,6 +72,14 @@ def self.from_hash(project, hash)
config.tag_regex = regex
end
+ config.always_credit_user_ids = Set.new
+ if (group_paths = Array(hash['include_groups']))
+ group_paths.each do |group_path|
+ group = Group.find_by_full_path(group_path)
+ config.always_credit_user_ids.merge(group&.users_ids_of_direct_members&.compact) if user&.can?(:read_group, group)
+ end
+ end
+
config
end
@@ -92,6 +100,10 @@ def contributor?(user)
@project.team.contributor?(user&.id)
end
+ def always_credit_author?(user)
+ always_credit_user_ids&.include?(user&.id) || false
+ end
+
def category(name)
@categories[name] || name
end
diff --git a/lib/gitlab/changelog/release.rb b/lib/gitlab/changelog/release.rb
index c0b6a5c5679ece4b3317584908480ec2ad7289f9..a0d598c746447d3648681f6930c162d37850359f 100644
--- a/lib/gitlab/changelog/release.rb
+++ b/lib/gitlab/changelog/release.rb
@@ -42,6 +42,7 @@ def add_entry(
'reference' => author.to_reference(full: true),
'contributor' => @config.contributor?(author)
}
+ entry['author']['credit'] = entry['author']['contributor'] || @config.always_credit_author?(author)
end
if merge_request
diff --git a/lib/gitlab/changelog/template.tpl b/lib/gitlab/changelog/template.tpl
index 584939dff51b3bccfd138c62aa02ab9242a03d0f..68c1c624394c3a46ce9653e0692b94e39563610b 100644
--- a/lib/gitlab/changelog/template.tpl
+++ b/lib/gitlab/changelog/template.tpl
@@ -4,7 +4,7 @@
{% each entries %}
- [{{ title }}]({{ commit.reference }})\
-{% if author.contributor %} by {{ author.reference }}{% end %}\
+{% if author.credit %} by {{ author.reference }}{% end %}\
{% if merge_request %} ([merge request]({{ merge_request.reference }})){% end %}
{% end %}
diff --git a/spec/lib/gitlab/changelog/config_spec.rb b/spec/lib/gitlab/changelog/config_spec.rb
index ff5a084bb867efac65a7b68a95b4ba5aeb795d62..c410ba4d1167ddac5607c7bba87c8b9a756dcb54 100644
--- a/spec/lib/gitlab/changelog/config_spec.rb
+++ b/spec/lib/gitlab/changelog/config_spec.rb
@@ -15,7 +15,7 @@
expect(described_class)
.to receive(:from_hash)
- .with(project, 'date_format' => '%Y')
+ .with(project, { 'date_format' => '%Y' }, nil)
described_class.from_git(project)
end
@@ -35,12 +35,25 @@
describe '.from_hash' do
it 'sets the configuration according to a Hash' do
+ user1 = create(:user)
+ user2 = create(:user)
+ user3 = create(:user)
+ group = create(:group, path: 'group')
+ group2 = create(:group, path: 'group-path')
+ group.add_developer(user1)
+ group.add_developer(user2)
+ group2.add_developer(user3)
+
config = described_class.from_hash(
project,
- 'date_format' => 'foo',
- 'template' => 'bar',
- 'categories' => { 'foo' => 'bar' },
- 'tag_regex' => 'foo'
+ {
+ 'date_format' => 'foo',
+ 'template' => 'bar',
+ 'categories' => { 'foo' => 'bar' },
+ 'tag_regex' => 'foo',
+ 'include_groups' => %w[group group-path non-existent-group]
+ },
+ user1
)
expect(config.date_format).to eq('foo')
@@ -49,6 +62,7 @@
expect(config.categories).to eq({ 'foo' => 'bar' })
expect(config.tag_regex).to eq('foo')
+ expect(config.always_credit_user_ids).to match_array([user1.id, user2.id, user3.id])
end
it 'raises Error when the categories are not a Hash' do
@@ -122,4 +136,55 @@
expect(config.format_date(time)).to eq('2021-01-05')
end
end
+
+ describe '#always_credit_author?' do
+ let_it_be(:group_member) { create(:user) }
+ let_it_be(:non_group_member) { create(:user) }
+ let_it_be(:group) { create(:group, :private, path: 'group') }
+
+ before do
+ group.add_developer(group_member)
+ end
+
+ context 'when include_groups is defined' do
+ context 'when user generating changelog has access to group' do
+ it 'returns whether author should always be credited' do
+ config = described_class.from_hash(
+ project,
+ { 'include_groups' => ['group'] },
+ group_member
+ )
+
+ expect(config.always_credit_author?(group_member)).to eq(true)
+ expect(config.always_credit_author?(non_group_member)).to eq(false)
+ end
+ end
+
+ context 'when user generating changelog has no access to group' do
+ it 'always returns false' do
+ config = described_class.from_hash(
+ project,
+ { 'include_groups' => ['group'] },
+ non_group_member
+ )
+
+ expect(config.always_credit_author?(group_member)).to eq(false)
+ expect(config.always_credit_author?(non_group_member)).to eq(false)
+ end
+ end
+ end
+
+ context 'when include_groups is not defined' do
+ it 'always returns false' do
+ config = described_class.from_hash(
+ project,
+ {},
+ group_member
+ )
+
+ expect(config.always_credit_author?(group_member)).to eq(false)
+ expect(config.always_credit_author?(non_group_member)).to eq(false)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/changelog/release_spec.rb b/spec/lib/gitlab/changelog/release_spec.rb
index f95244d67500b5aa2ac5f6be4f637345e4e9d253..d84348216402499c5ea544a81039e9568fcaf93c 100644
--- a/spec/lib/gitlab/changelog/release_spec.rb
+++ b/spec/lib/gitlab/changelog/release_spec.rb
@@ -94,6 +94,30 @@
end
end
+ context 'when the author should always be credited' do
+ it 'includes the author' do
+ allow(config).to receive(:contributor?).with(author).and_return(false)
+ allow(config).to receive(:always_credit_author?).with(author).and_return(true)
+
+ release.add_entry(
+ title: 'Entry title',
+ commit: commit,
+ category: 'fixed',
+ author: author
+ )
+
+ expect(release.to_markdown).to eq(<<~OUT)
+ ## 1.0.0 (2021-01-05)
+
+ ### fixed (1 change)
+
+ - [Entry title](#{commit.to_reference(full: true)}) \
+ by #{author.to_reference(full: true)}
+
+ OUT
+ end
+ end
+
context 'when a category has no entries' do
it "isn't included in the output" do
config.categories['kittens'] = 'Kittens'