Commit 092b2447 authored by Lee Tickett's avatar Lee Tickett

Include author in commit message from core team community members

Changelog: fixed
parent 2108cc52
...@@ -11,6 +11,8 @@ categories: ...@@ -11,6 +11,8 @@ categories:
security: Security security: Security
performance: Performance performance: Performance
other: Other other: Other
include_groups:
- gitlab-org/gitlab-core-team/community-members
template: | template: |
{% if categories %} {% if categories %}
{% each categories %} {% each categories %}
...@@ -18,7 +20,7 @@ template: | ...@@ -18,7 +20,7 @@ template: |
{% each entries %} {% each entries %}
- [{{ title }}]({{ commit.reference }})\ - [{{ title }}]({{ commit.reference }})\
{% if author.contributor %} by {{ author.reference }}{% end %}\ {% if author.credit %} by {{ author.reference }}{% end %}\
{% if commit.trailers.MR %}\ {% if commit.trailers.MR %}\
([merge request]({{ commit.trailers.MR }}))\ ([merge request]({{ commit.trailers.MR }}))\
{% else %}\ {% else %}\
......
...@@ -61,7 +61,7 @@ module Repositories ...@@ -61,7 +61,7 @@ module Repositories
# rubocop: enable Metrics/ParameterLists # rubocop: enable Metrics/ParameterLists
def execute def execute
config = Gitlab::Changelog::Config.from_git(@project) config = Gitlab::Changelog::Config.from_git(@project, @user)
from = start_of_commit_range(config) from = start_of_commit_range(config)
# For every entry we want to only include the merge request that # For every entry we want to only include the merge request that
......
...@@ -448,6 +448,10 @@ You can set the following variables in this file: ...@@ -448,6 +448,10 @@ You can set the following variables in this file:
- `template`: a custom template to use for generating the changelog data. - `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 - `categories`: a hash that maps raw category names to the names to use in the
changelog. 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 Using the default settings, generating a changelog results in a section along
the lines of the following: the lines of the following:
...@@ -508,7 +512,7 @@ follows: ...@@ -508,7 +512,7 @@ follows:
{% each entries %} {% each entries %}
- [{{ title }}]({{ commit.reference }})\ - [{{ 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 %} {% if merge_request %} ([merge request]({{ merge_request.reference }})){% end %}
{% end %} {% end %}
...@@ -598,7 +602,7 @@ template: | ...@@ -598,7 +602,7 @@ template: |
{% each entries %} {% each entries %}
- [{{ title }}]({{ commit.reference }})\ - [{{ title }}]({{ commit.reference }})\
{% if author.contributor %} by {{ author.reference }}{% end %} {% if author.credit %} by {{ author.reference }}{% end %}
{% end %} {% end %}
...@@ -634,8 +638,11 @@ In an entry, the following variables are available (here `foo.bar` means that ...@@ -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 - `commit.trailers`: an object containing all the Git trailers that were present
in the commit body. in the commit body.
- `author.reference`: a reference to the commit author (for example, `@alice`). - `author.reference`: a reference to the commit author (for example, `@alice`).
- `author.contributor`: a boolean set to `true` when the author is an external - `author.contributor`: a boolean set to `true` when the author is not a project
contributor, otherwise this is set to `false`. 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 - `merge_request.reference`: a reference to the merge request that first
introduced the change (for example, `gitlab-org/gitlab!50063`). introduced the change (for example, `gitlab-org/gitlab!50063`).
......
...@@ -34,17 +34,17 @@ module Gitlab ...@@ -34,17 +34,17 @@ module Gitlab
'(?:-(?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<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-]+)*))?$' '?(?:\+(?P<meta>[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) if (yaml = project.repository.changelog_config)
from_hash(project, YAML.safe_load(yaml)) from_hash(project, YAML.safe_load(yaml), user)
else else
new(project) new(project)
end end
end end
def self.from_hash(project, hash) def self.from_hash(project, hash, user = nil)
config = new(project) config = new(project)
if (date = hash['date_format']) if (date = hash['date_format'])
...@@ -72,6 +72,14 @@ module Gitlab ...@@ -72,6 +72,14 @@ module Gitlab
config.tag_regex = regex config.tag_regex = regex
end 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 config
end end
...@@ -92,6 +100,10 @@ module Gitlab ...@@ -92,6 +100,10 @@ module Gitlab
@project.team.contributor?(user&.id) @project.team.contributor?(user&.id)
end end
def always_credit_author?(user)
always_credit_user_ids&.include?(user&.id) || false
end
def category(name) def category(name)
@categories[name] || name @categories[name] || name
end end
......
...@@ -42,6 +42,7 @@ module Gitlab ...@@ -42,6 +42,7 @@ module Gitlab
'reference' => author.to_reference(full: true), 'reference' => author.to_reference(full: true),
'contributor' => @config.contributor?(author) 'contributor' => @config.contributor?(author)
} }
entry['author']['credit'] = entry['author']['contributor'] || @config.always_credit_author?(author)
end end
if merge_request if merge_request
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
{% each entries %} {% each entries %}
- [{{ title }}]({{ commit.reference }})\ - [{{ 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 %} {% if merge_request %} ([merge request]({{ merge_request.reference }})){% end %}
{% end %} {% end %}
......
...@@ -15,7 +15,7 @@ RSpec.describe Gitlab::Changelog::Config do ...@@ -15,7 +15,7 @@ RSpec.describe Gitlab::Changelog::Config do
expect(described_class) expect(described_class)
.to receive(:from_hash) .to receive(:from_hash)
.with(project, 'date_format' => '%Y') .with(project, { 'date_format' => '%Y' }, nil)
described_class.from_git(project) described_class.from_git(project)
end end
...@@ -35,12 +35,25 @@ RSpec.describe Gitlab::Changelog::Config do ...@@ -35,12 +35,25 @@ RSpec.describe Gitlab::Changelog::Config do
describe '.from_hash' do describe '.from_hash' do
it 'sets the configuration according to a 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( config = described_class.from_hash(
project, project,
{
'date_format' => 'foo', 'date_format' => 'foo',
'template' => 'bar', 'template' => 'bar',
'categories' => { 'foo' => 'bar' }, 'categories' => { 'foo' => 'bar' },
'tag_regex' => 'foo' 'tag_regex' => 'foo',
'include_groups' => %w[group group-path non-existent-group]
},
user1
) )
expect(config.date_format).to eq('foo') expect(config.date_format).to eq('foo')
...@@ -49,6 +62,7 @@ RSpec.describe Gitlab::Changelog::Config do ...@@ -49,6 +62,7 @@ RSpec.describe Gitlab::Changelog::Config do
expect(config.categories).to eq({ 'foo' => 'bar' }) expect(config.categories).to eq({ 'foo' => 'bar' })
expect(config.tag_regex).to eq('foo') expect(config.tag_regex).to eq('foo')
expect(config.always_credit_user_ids).to match_array([user1.id, user2.id, user3.id])
end end
it 'raises Error when the categories are not a Hash' do it 'raises Error when the categories are not a Hash' do
...@@ -122,4 +136,55 @@ RSpec.describe Gitlab::Changelog::Config do ...@@ -122,4 +136,55 @@ RSpec.describe Gitlab::Changelog::Config do
expect(config.format_date(time)).to eq('2021-01-05') expect(config.format_date(time)).to eq('2021-01-05')
end end
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 end
...@@ -94,6 +94,30 @@ RSpec.describe Gitlab::Changelog::Release do ...@@ -94,6 +94,30 @@ RSpec.describe Gitlab::Changelog::Release do
end end
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 context 'when a category has no entries' do
it "isn't included in the output" do it "isn't included in the output" do
config.categories['kittens'] = 'Kittens' config.categories['kittens'] = 'Kittens'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment