Example #1
0
def make_dependabot(repo_path: pathlib.Path, templates: Environment) -> List[str]:
	"""
	Add configuration for ``dependabot`` to the desired repo.

	https://dependabot.com/

	:param repo_path: Path to the repository root.
	:param templates:

	.. deprecated:: 2020.12.11
	"""

	dependabot_file = PathPlus(repo_path / ".dependabot" / "config.yml")
	dependabot_file.parent.maybe_make()

	update_configs = {
			"package_manager": "python",
			"directory": '/',
			"update_schedule": "weekly",
			"default_reviewers": [templates.globals["assignee"]],
			}

	config = {"version": 1, "update_configs": [update_configs]}

	dependabot_file.write_lines([
			f"# {templates.globals['managed_message']}",
			"---",
			_round_trip_dump(config),
			])

	return [dependabot_file.relative_to(repo_path).as_posix()]
Example #2
0
def make_dependabotv2(repo_path: pathlib.Path, templates: Environment) -> List[str]:
	"""
	Add configuration for ``dependabot`` to the desired repo.

	https://dependabot.com/

	:param repo_path: Path to the repository root.
	:param templates:

	.. versionadded:: 2020.12.11
	"""

	dependabot_file = PathPlus(repo_path / ".github" / "dependabot.yml")
	dependabot_file.parent.maybe_make()

	updates = {
			"package-ecosystem": "pip",
			"directory": '/',
			"schedule": {"interval": "weekly"},
			"reviewers": [templates.globals["assignee"]],
			}

	config = {"version": 2, "updates": [updates]}

	dependabot_file.write_lines([
			f"# {templates.globals['managed_message']}",
			"---",
			_round_trip_dump(config),
			])

	return [dependabot_file.relative_to(repo_path).as_posix()]
Example #3
0
def make_automerge_action(repo_path: pathlib.Path, templates: Environment) -> List[str]:
	"""
	Add configuration for https://github.com/pascalgn/automerge-action to the desired repo.

	:param repo_path: Path to the repository root.
	:param templates:
	"""

	dot_github = PathPlus(repo_path / ".github")
	(dot_github / "workflows").maybe_make(parents=True)

	automerge_workflow = dot_github / "workflows" / "automerge.yml"

	pr_types = [
			"labeled",
			"unlabeled",
			"synchronize",
			"opened",
			"edited",
			"ready_for_review",
			"reopened",
			"unlocked",
			]
	steps = [{
			"name": "automerge",
			"uses": "pascalgn/[email protected]",
			"env": {
					"GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}",
					"MERGE_METHOD": "squash",
					},
			}]

	config: MutableMapping[str, Any] = {
			"name": "automerge",
			"on": {
					"pull_request": {"types": pr_types},
					"pull_request_review": {"types": ["submitted"]},
					"check_suite": {"types": ["completed"]},
					"status": {},
					},
			"jobs": {"automerge": {"runs-on": "ubuntu-latest", "steps": steps}}
			}

	automerge_workflow.write_lines([
			f"# {templates.globals['managed_message']}",
			"---",
			_round_trip_dump(config),
			])

	return [automerge_workflow.relative_to(repo_path).as_posix()]
Example #4
0
def make_auto_assign_action(repo_path: pathlib.Path, templates: Environment) -> List[str]:
	"""
	Add configuration for ``auto-assign`` to the desired repo.

	https://github.com/kentaro-m/auto-assign

	:param repo_path: Path to the repository root.
	:param templates:
	"""

	dot_github = PathPlus(repo_path / ".github")
	(dot_github / "workflows").maybe_make(parents=True)

	assign_workflow = dot_github / "workflows" / "assign.yml"
	old_assign_workflow = dot_github / "workflow" / "assign.yml"
	auto_assign_yml = dot_github / "auto_assign.yml"

	if old_assign_workflow.is_file():
		old_assign_workflow.unlink()

	if old_assign_workflow.parent.is_dir():
		old_assign_workflow.parent.rmdir()

	if assign_workflow.is_file():
		assign_workflow.unlink()

	config: MutableMapping[str, Any] = {
			"addReviewers": True,
			"addAssignees": True,
			}

	# A list of reviewers to be added to pull requests (GitHub user name)
	config["reviewers"] = [templates.globals["assignee"]]

	# A number of reviewers added to the pull request
	# Set 0 to add all the reviewers
	config["numberOfReviewers"] = 0

	# A list of assignees, overrides reviewers if set
	# assignees:
	#   - assigneeA

	# A number of assignees to add to the pull request
	# Set to 0 to add assignees.
	# Uses numberOfReviewers if unset.
	# numberOfAssignees: 2

	# more settings at https://github.com/marketplace/actions/auto-assign-action

	auto_assign_yml.write_lines([
			f"# {templates.globals['managed_message']}",
			"---",
			_round_trip_dump(config),
			"# more settings at https://github.com/marketplace/actions/auto-assign-action",
			])

	return [
			assign_workflow.relative_to(repo_path).as_posix(),
			old_assign_workflow.relative_to(repo_path).as_posix(),
			auto_assign_yml.relative_to(repo_path).as_posix(),
			]
Example #5
0
def wizard() -> None:
    """
	Run the wizard 🧙 to create a 'repo_helper.yml' file.
	"""

    # stdlib
    import datetime
    import getpass
    import os
    import socket

    # 3rd party
    from apeye.email_validator import EmailSyntaxError, validate_email
    from consolekit.terminal_colours import Fore
    from domdf_python_tools.paths import PathPlus
    from dulwich.errors import NotGitRepository
    from ruamel.yaml import scalarstring
    from southwark.repo import Repo

    # this package
    from repo_helper.utils import _round_trip_dump, license_lookup

    path = PathPlus.cwd()
    config_file = path / "repo_helper.yml"

    try:
        r = Repo(path)
    except NotGitRepository:

        with Fore.RED:
            click.echo(f"The directory {path} is not a git repository.")
            click.echo(
                "You may need to run 'git init' in that directory first.")

        raise click.Abort

    # ---------- intro ----------
    click.echo(
        "This wizard 🧙‍will guide you through creating a 'repo_helper.yml' configuration file."
    )
    click.echo(f"This will be created in '{config_file}'.")
    if not confirm("Do you want to continue?"):
        raise click.Abort()

    # ---------- file exists warning ----------
    if config_file.is_file():
        click.echo(
            f"\nWoah! That file already exists. It will be overwritten if you continue!"
        )
        if not confirm("Are you sure you want to continue?"):
            raise click.Abort()

    click.echo("\nDefault options are indicated in [square brackets].")

    # ---------- modname ----------
    click.echo("\nThe name of the library/project.")
    modname = prompt("Name")

    # ---------- name ----------
    click.echo("\nThe name of the author.")
    click.echo("The author is usually the person who wrote the library.")

    git_config = r.get_config_stack()

    try:
        default_author = git_config.get(("user", ), "name").decode("UTF-8")
    except KeyError:
        try:
            getpass_user = getpass.getuser()
            default_author = os.getenv(
                "GIT_AUTHOR_NAME",
                default=os.getenv("GIT_COMMITTER_NAME", default=getpass_user),
            )
        except ImportError:
            # Usually USERNAME is not set when trying getpass.getuser()
            default_author = ''

    author = prompt("Name", default=default_author)

    # ---------- email ----------
    try:
        default_email = git_config.get(("user", ), "email").decode("UTF-8")
    except KeyError:
        default_email = os.getenv(
            "GIT_AUTHOR_EMAIL",
            default=os.getenv("GIT_COMMITTER_EMAIL",
                              default=f"{author}@{socket.gethostname()}"))

    click.echo(
        "\nThe email address of the author. This will be shown on PyPI, amongst other places."
    )

    while True:
        try:
            email = validate_email(prompt("Email",
                                          default=default_email)).email
            break
        except EmailSyntaxError:
            click.echo("That is not a valid email address.")

    # ---------- username ----------
    click.echo("\nThe username of the author.")
    click.echo(
        "(repo_helper naïvely assumes that you use the same username on GitHub as on other sites.)"
    )
    username = prompt("Username", default=author)
    # TODO: validate username

    # ---------- version ----------
    click.echo("\nThe version number of the library, in semver format.")
    version = prompt("Version number", default="0.0.0")

    # ---------- copyright_years ----------
    click.echo("\nThe copyright years for the library.")
    copyright_years = prompt("Copyright years",
                             default=str(datetime.datetime.today().year),
                             type=str)

    # ---------- license_ ----------
    click.echo("""
The SPDX identifier for the license this library is distributed under.
Not all SPDX identifiers are allowed as not all map to PyPI Trove classifiers."""
               )
    while True:
        license_ = prompt("License")

        if license_ in license_lookup:
            break
        else:
            click.echo("That is not a valid identifier.")

    # ---------- short_desc ----------
    click.echo("\nEnter a short, one-line description for the project.")
    short_desc = prompt("Description")

    # ---------- writeout ----------

    data = {
        "modname": modname,
        "copyright_years": copyright_years,
        "author": author,
        "email": email,
        "username": username,
        "version": str(version),
        "license": license_,
        "short_desc": short_desc,
    }

    data = {
        k: scalarstring.SingleQuotedScalarString(v)
        for k, v in data.items()
    }

    config_file.write_lines([
        "# Configuration for 'repo_helper' (https://github.com/repo-helper/repo_helper)",
        "---",
        _round_trip_dump(data),
        "enable_conda: false",
    ])

    click.echo(f"""
The options you provided have been written to the file {config_file}.
You can configure additional options in that file.

The schema for the Yaml file can be found at:
	https://github.com/repo-helper/repo_helper/blob/master/repo_helper/repo_helper_schema.json
You may be able to configure your code editor to validate your configuration file against that schema.

repo_helper can now be run with the 'repo_helper' command in the repository root.

Be seeing you!
""")