RULEAPI-640: Add language(s) label(s) to automatically created PR

This commit is contained in:
Arseniy Zaostrovnykh 2021-06-10 11:03:05 +02:00 committed by GitHub
parent 08c011b06a
commit b0c064cfb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 136 additions and 26 deletions

View File

@ -1,12 +1,11 @@
import os
import tempfile
from typing import Optional
from pathlib import Path
import click
from rspec_tools.checklinks import check_html_links
from rspec_tools.errors import InvalidArgumenError, RuleNotFoundError, RuleValidationError
from rspec_tools.create_rule import RuleCreator, build_github_repository_url
from rspec_tools.errors import RuleNotFoundError, RuleValidationError
from rspec_tools.create_rule import create_new_rule
from rspec_tools.rules import RulesRepository
from rspec_tools.validation.metadata import validate_metadata
from rspec_tools.validation.description import validate_section_names
@ -36,20 +35,7 @@ def check_links(d):
def create_rule(languages: str, user: Optional[str]):
'''Create a new rule.'''
token = os.environ.get('GITHUB_TOKEN')
url = build_github_repository_url(token, user)
config = {}
if user:
config['user.name'] = user
config['user.email'] = f'{user}@users.noreply.github.com'
lang_list = [lang.strip() for lang in languages.split(',')]
if len(languages.strip()) == 0 or len(lang_list) == 0:
raise InvalidArgumenError('Invalid argument for "languages". At least one language should be provided.')
# TODO: accept only valid languages
with tempfile.TemporaryDirectory() as tmpdirname:
rule_creator = RuleCreator(url, tmpdirname, config)
rule_number = rule_creator.reserve_rule_number()
pull_request = rule_creator.create_new_rule_pull_request(token, rule_number, lang_list, user=user)
create_new_rule(languages, token, user)
@cli.command()

View File

@ -1,12 +1,14 @@
from rspec_tools.errors import GitError
import click
import tempfile
from git import Repo
from git.remote import PushInfo
from github import Github
from github.PullRequest import PullRequest
from pathlib import Path
from typing import Final, Iterable, Optional
from typing import Final, Iterable, Optional, Callable
from contextlib import contextmanager
from rspec_tools.utils import parse_and_validate_language_list, get_labels_for_languages
from rspec_tools.utils import copy_directory_content
@ -21,6 +23,28 @@ def extract_repository_name(url):
url_end = url.split('/')[-2:]
return '/'.join(url_end).removesuffix('.git')
def authGithub(token: str) -> Callable[[Optional[str]], Github]:
def ret(user: Optional[str]):
if user:
return Github(user, token)
else:
return Github(token)
return ret
def create_new_rule(languages: str, token: str, user: Optional[str]):
url = build_github_repository_url(token, user)
config = {}
if user:
config['user.name'] = user
config['user.email'] = f'{user}@users.noreply.github.com'
lang_list = parse_and_validate_language_list(languages)
label_list = get_labels_for_languages(lang_list)
with tempfile.TemporaryDirectory() as tmpdirname:
rule_creator = RuleCreator(url, tmpdirname, config)
rule_number = rule_creator.reserve_rule_number()
pull_request = rule_creator.create_new_rule_pull_request(authGithub(token), rule_number, lang_list, label_list, user=user)
class RuleCreator:
''' Create a new Rule in a repository following the official Github 'rspec' repository structure.'''
MASTER_BRANCH: Final[str] = 'master'
@ -111,15 +135,12 @@ class RuleCreator:
self._fill_in_the_blanks_in_the_template(rule_dir, rule_number)
def create_new_rule_pull_request(self, token: str, rule_number: int, languages: Iterable[str], *, user: Optional[str]) -> PullRequest:
def create_new_rule_pull_request(self, githubApi: Callable[[Optional[str]], Github], rule_number: int, languages: Iterable[str], labels: Iterable[str], *, user: Optional[str]) -> PullRequest:
branch_name = self.create_new_rule_branch(rule_number, languages)
click.echo(f'Created rule Branch {branch_name}')
repository_url = extract_repository_name(self.origin_url)
if user:
github = Github(user, token)
else:
github = Github(token)
github = githubApi(user)
github_repo = github.get_repo(repository_url)
first_lang = next(iter(languages))
pull_request = github_repo.create_pull(
@ -133,6 +154,7 @@ class RuleCreator:
# Note: It is not possible to get the authenticated user using get_user() from a github action.
login = user if user else github.get_user().login
pull_request.add_to_assignees(login)
pull_request.add_to_labels(*labels)
click.echo(f'Pull request assigned to {login}')
return pull_request

View File

@ -4,7 +4,7 @@ class RuleNotFoundError(ClickException):
def __init__(self, id):
super().__init__(f'No rule has ID {id}')
class InvalidArgumenError(ClickException):
class InvalidArgumentError(ClickException):
'''Exception raised when an invalid argument is given to a CLI command.'''
def __init__(self, message):
super().__init__(message)
@ -17,4 +17,4 @@ class GitError(ClickException):
class RuleValidationError(ClickException):
'''Exception raised when a rule did not pass validation.'''
def __init__(self, message):
super().__init__(message)
super().__init__(message)

View File

@ -1,9 +1,70 @@
from rspec_tools.errors import InvalidArgumentError
from pathlib import Path
import shutil
SUPPORTED_LANGUAGES_FILENAME = '../supported_languages.adoc'
LANG_TO_LABEL = {'abap': 'abap',
'apex': 'slang',
'cfamily': 'cfamily',
'cobol': 'cobol',
'csharp': 'dotnet',
'css': 'css',
'flex': 'flex',
'go': 'slang',
'html': 'html',
'java': 'java',
'javascript': 'jsts',
'kotlin': 'kotlin',
'php': 'php',
'pli': 'pli',
'plsql': 'plsql',
'python': 'python',
'rpg': 'rpg',
'ruby': 'slang',
'rust': 'rust',
'scala': 'slang',
'solidity': 'solidity',
'swift': 'swift',
'tsql': 'tsql',
'vb6': 'vb6',
'vbnet': 'dotnet',
'cloudformation': 'iac',
'terraform': 'iac',
'xml': 'xml',
}
def copy_directory_content(src:Path, dest:Path):
for item in src.iterdir():
if (item.is_dir()):
shutil.copytree(item, dest)
else:
shutil.copy2(item, dest)
def load_valid_languages():
with open(SUPPORTED_LANGUAGES_FILENAME, 'r') as supported_langs_file:
supported_langs = supported_langs_file.read()
supported_langs = supported_langs.replace(' or', '')
supported_langs = supported_langs.replace('`', '')
supported_langs = supported_langs.replace(' ', '')
supported_langs = supported_langs.replace('\n', '')
return supported_langs.split(',')
def get_mapped_languages():
'''Get all the languages we have a label for.
Necessary to make sure all valid languages are mapped (see test_utils.py).'''
return LANG_TO_LABEL.keys();
def parse_and_validate_language_list(languages):
lang_list = [lang.strip() for lang in languages.split(',')]
if len(languages.strip()) == 0 or len(lang_list) == 0:
raise InvalidArgumentError('Invalid argument for "languages". At least one language should be provided.')
valid_langs = load_valid_languages()
for lang in lang_list:
if lang not in valid_langs:
raise InvalidArgumentError(f"Unsupported language: \"{lang}\". See {SUPPORTED_LANGUAGES_FILENAME} for the list of supported languages.")
return lang_list
def get_labels_for_languages(lang_list):
labels = [LANG_TO_LABEL[lang] for lang in lang_list]
return list(set(labels))

View File

@ -1,8 +1,11 @@
from git import Repo, Head
from rspec_tools.errors import InvalidArgumentError
from pathlib import Path
from typing import Optional
from unittest.mock import Mock, patch
import pytest
from rspec_tools.create_rule import RuleCreator
from rspec_tools.create_rule import RuleCreator, create_new_rule
@pytest.fixture
def git_config():
@ -134,3 +137,41 @@ def test_create_new_single_lang_rule_branch(rule_creator: RuleCreator, mock_rspe
relative_path = lang_item.relative_to(lang_root)
actual_content = rule_dir.joinpath(lang, relative_path).read_text()
assert actual_content == expected_content
def test_create_new_rule_pr(rule_creator: RuleCreator):
'''Test create_new_rule_branch adds the right user and labels.'''
rule_number = rule_creator.reserve_rule_number()
languages = ['cfamily']
ghMock = Mock()
ghRepoMock = Mock()
pullMock = Mock()
ghRepoMock.create_pull = Mock(return_value=pullMock)
ghMock.get_repo = Mock(return_value=ghRepoMock)
def mockGithub(user: Optional[str]):
return ghMock
rule_creator.create_new_rule_pull_request(mockGithub, rule_number, languages, ['mylab', 'other-lab'], user='testuser')
ghRepoMock.create_pull.assert_called_once();
assert ghRepoMock.create_pull.call_args.kwargs['title'].startswith('Create rule S')
pullMock.add_to_assignees.assert_called_with('testuser');
pullMock.add_to_labels.assert_called_with('mylab', 'other-lab');
@patch('rspec_tools.create_rule.RuleCreator')
def test_create_new_rule(mockRuleCreator):
mockRuleCreator.return_value = Mock()
mockRuleCreator.return_value.create_new_rule_pull_request = Mock()
prMock = mockRuleCreator.return_value.create_new_rule_pull_request
create_new_rule('cfamily,php', 'my token', 'testuser')
prMock.assert_called_once()
assert set(prMock.call_args.args[2]) == set(['cfamily', 'php'])
assert set(prMock.call_args.args[3]) == set(['cfamily', 'php'])
@patch('rspec_tools.create_rule.RuleCreator')
def test_create_new_rule_unsupported_language(mockRuleCreator):
mockRuleCreator.return_value = Mock()
mockRuleCreator.return_value.create_new_rule_pull_request = Mock()
prMock = mockRuleCreator.return_value.create_new_rule_pull_request
with pytest.raises(InvalidArgumentError):
create_new_rule('russian,php', 'my token', 'testuser')