-
Notifications
You must be signed in to change notification settings - Fork 136
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use CODEOWNERS and changes to determine who can approve
At the moment all matches from codeowners has to be available to approve
- Loading branch information
Showing
2 changed files
with
218 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,124 @@ | |
# testing this here is more convenient | ||
from marge.job import CannotMerge, _get_reviewer_names_and_emails | ||
|
||
CODEOWNERS = """ | ||
# This is an example code owners file, lines starting with a `#` will | ||
# be ignored. | ||
* @default-codeowner @test-user1 | ||
* @test-user1 @ebert | ||
unmatched/* @test5 | ||
""" | ||
|
||
CODEOWNERS_FULL = """ | ||
# This is an example code owners file, lines starting with a `#` will | ||
# be ignored. | ||
# app/ @commented-rule | ||
# We can specify a default match using wildcards: | ||
* @default-codeowner | ||
# Rules defined later in the file take precedence over the rules | ||
# defined before. | ||
# This will match all files for which the file name ends in `.rb` | ||
*.rb @ruby-owner | ||
# Files with a `#` can still be accesssed by escaping the pound sign | ||
\#file_with_pound.rb @owner-file-with-pound | ||
# Multiple codeowners can be specified, separated by spaces or tabs | ||
CODEOWNERS @multiple @code @owners | ||
# Both usernames or email addresses can be used to match | ||
# users. Everything else will be ignored. For example this will | ||
# specify `@legal` and a user with email `[email protected]` as the | ||
# owner for the LICENSE file | ||
LICENSE @legal this_does_not_match [email protected] | ||
# Group names can be used to match groups and nested groups to specify | ||
# them as owners for a file | ||
README @group @group/with-nested/subgroup | ||
# Ending a path in a `/` will specify the code owners for every file | ||
# nested in that directory, on any level | ||
/docs/ @all-docs | ||
# Ending a path in `/*` will specify code owners for every file in | ||
# that directory, but not nested deeper. This will match | ||
# `docs/index.md` but not `docs/projects/index.md` | ||
/docs/* @root-docs | ||
# This will make a `lib` directory nested anywhere in the repository | ||
# match | ||
lib/ @lib-owner | ||
# This will only match a `config` directory in the root of the | ||
# repository | ||
/config/ @config-owner | ||
# If the path contains spaces, these need to be escaped like this: | ||
path\ with\ spaces/ @space-owner | ||
""" | ||
|
||
AWARDS = [ | ||
{ | ||
"id": 1, | ||
"name": "thumbsdown", | ||
"user": { | ||
"name": "Test User 1", | ||
"username": "test-user1", | ||
"id": 3, | ||
"state": "active", | ||
}, | ||
"created_at": "2020-04-24T13:16:23.614Z", | ||
"updated_at": "2020-04-24T13:16:23.614Z", | ||
"awardable_id": 11, | ||
"awardable_type": "MergeRequest" | ||
}, | ||
{ | ||
"id": 2, | ||
"name": "thumbsup", | ||
"user": { | ||
"name": "Roger Ebert", | ||
"username": "ebert", | ||
"id": 2, | ||
"state": "active", | ||
}, | ||
"created_at": "2020-04-24T13:16:23.614Z", | ||
"updated_at": "2020-04-24T13:16:23.614Z", | ||
"awardable_id": 12, | ||
"awardable_type": "MergeRequest" | ||
} | ||
] | ||
|
||
CHANGES = { | ||
"changes": [ | ||
{ | ||
"old_path": "README", | ||
"new_path": "README", | ||
"a_mode": "100644", | ||
"b_mode": "100644", | ||
"new_file": False, | ||
"renamed_file": False, | ||
"deleted_file": False, | ||
"diff": "", | ||
}, | ||
{ | ||
"old_path": "main.go", | ||
"new_path": "main.go", | ||
"a_mode": "100644", | ||
"b_mode": "100644", | ||
"new_file": False, | ||
"renamed_file": False, | ||
"deleted_file": False, | ||
"diff": "" | ||
} | ||
] | ||
} | ||
|
||
|
||
INFO = { | ||
"id": 5, | ||
"iid": 6, | ||
|
@@ -83,17 +201,20 @@ def test_fetch_from_merge_request(self): | |
)) | ||
assert approvals.info == INFO | ||
|
||
@patch('marge.approvals.Approvals.get_awards_ce', Mock(return_value=AWARDS)) | ||
@patch('marge.approvals.Approvals.get_changes_ce', Mock(return_value=CHANGES)) | ||
def test_fetch_from_merge_request_ce_compat(self): | ||
api = self.api | ||
api.version = Mock(return_value=Version.parse('9.2.3')) | ||
api.call = Mock() | ||
api.repo_file_get = Mock(return_value=CODEOWNERS) | ||
|
||
merge_request = MergeRequest(api, {'id': 74, 'iid': 6, 'project_id': 1234}) | ||
approvals = merge_request.fetch_approvals() | ||
|
||
api.call.assert_not_called() | ||
assert approvals.info == { | ||
'id': 74, 'iid': 6, 'project_id': 1234, 'approvals_left': 0, 'approved_by': [], | ||
'id': 74, 'iid': 6, 'project_id': 1234, 'approvals_left': 2, 'approved_by': [AWARDS[1]], | ||
} | ||
|
||
def test_properties(self): | ||
|
@@ -140,3 +261,48 @@ def test_approvals_succeeds_with_independent_author(self, user_fetch_by_id): | |
'Administrator <root@localhost>', | ||
'Roger Ebert <[email protected]>', | ||
] | ||
|
||
def test_approvals_ce_get_codeowners_full(self): | ||
api = self.api | ||
api.version = Mock(return_value=Version.parse('9.2.3')) | ||
api.repo_file_get = Mock(return_value=CODEOWNERS_FULL) | ||
|
||
approvals = Approvals(api, {'id': 74, 'iid': 6, 'project_id': 1234}) | ||
|
||
assert approvals.get_codeowners_ce() == { | ||
'#file_with_pound.rb': {'owner-file-with-pound'}, | ||
'*': {'default-codeowner'}, | ||
'*.rb': {'ruby-owner'}, | ||
'/config/': {'config-owner'}, | ||
'/docs/': {'all-docs'}, | ||
'/docs/*': {'root-docs'}, | ||
'CODEOWNERS': {'owners', 'multiple', 'code'}, | ||
'LICENSE': {'this_does_not_match', '[email protected]', 'legal'}, | ||
'README': {'group', 'group/with-nested/subgroup'}, | ||
'lib/': {'lib-owner'}, | ||
'path with spaces/': {'space-owner'} | ||
} | ||
|
||
def test_approvals_ce_get_codeowners_wildcard(self): | ||
api = self.api | ||
api.version = Mock(return_value=Version.parse('9.2.3')) | ||
api.repo_file_get = Mock(return_value=CODEOWNERS) | ||
|
||
approvals = Approvals(api, {'id': 74, 'iid': 6, 'project_id': 1234}) | ||
|
||
assert approvals.get_codeowners_ce() == {'*': set(['default-codeowner', 'test-user1', 'ebert']), 'unmatched/*': {'test5'}} | ||
|
||
@patch('marge.approvals.Approvals.get_awards_ce', Mock(return_value=AWARDS)) | ||
@patch('marge.approvals.Approvals.get_changes_ce', Mock(return_value=CHANGES)) | ||
def test_approvals_ce(self): | ||
api = self.api | ||
api.version = Mock(return_value=Version.parse('9.2.3')) | ||
api.repo_file_get = Mock(return_value=CODEOWNERS) | ||
|
||
merge_request = MergeRequest(api, {'id': 74, 'iid': 6, 'project_id': 1234}) | ||
approvals = merge_request.fetch_approvals() | ||
|
||
result = approvals.get_approvers_ce() | ||
|
||
assert result['approvals_left'] == 2 | ||
assert len(result['approved_by']) == 1 |