Skip to content

Commit

Permalink
Integrate gh-pre tool in mk
Browse files Browse the repository at this point in the history
  • Loading branch information
ssbarnea committed Jun 17, 2024
1 parent 7d5a38e commit 6071ee6
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .config/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ showconfig
showlocals
ssbarnea
superfences
tablerender
taskfile
taskfiles
testenv
timeago
toxenv
toxinidir
typer
Expand Down
2 changes: 2 additions & 0 deletions .config/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ gitpython>=3.1.26
packaging>=22 # pytest, tox, build
pip>=21.0.1 # py_package
pluggy>=1.5.0 # typer and pytest indirect
pyyaml>=5.1
rich>=10.11.0 # typer indirect
setuptools # py_package due to running setup.py
shellingham>=1.5.4
subprocess-tee>=0.4.1
tomli>=2.0.1 ; python_version < "3.11" # tox
twine>=3.4.1 # py_package
typer-config>=1.4.0
typer>=0.12.2 # completion tests will fail with older
2 changes: 2 additions & 0 deletions .github/lower-constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ gitpython==3.1.26
packaging==22
pip==23.2 # py_package (before installation might fail, including with tox)
pluggy==1.5.0
pyyaml==5.1
rich==10.11.0 # typer indirect
setuptools # py_package due to running setup.py
shellingham==1.5.4
subprocess-tee==0.4.1
tomli >= 2.0.1 ; python_version < "3.11" # tox v4
twine==3.4.1 # py_package
typer-config==1.4.0
typer==0.12.2
4 changes: 3 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,21 @@ repos:
- packaging
- rich
- subprocess-tee
- typer-config
- typer>=0.12.2
- repo: https://github.com/pycqa/pylint
rev: v3.2.3
hooks:
- id: pylint
additional_dependencies:
- gitpython
- click-help-colors
- diskcache
- gitpython
- importlib-metadata
- pytest
- rich
- subprocess-tee
- typer-config
- typer>=0.12.2
- repo: https://github.com/jazzband/pip-tools
rev: 7.4.1
Expand Down
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ keywords = ["mk"]

[project.scripts]
mk = "mk.__main__:cli"
pre = "mk.pre:app"

[project.entry-points."mk_tools"]
ansible = "mk.tools.ansible:AnsibleTool"
Expand All @@ -52,6 +53,7 @@ shell = "mk.tools.shell:ShellTool"
taskfile = "mk.tools.taskfile:TaskfileTool"
tox = "mk.tools.tox:ToxTool"
nox = "mk.tools.nox:NoxTool"
pre = "mk.tools.pre:PreTool"

[project.urls]
homepage = "https://github.com/pycontribs/mk"
Expand Down Expand Up @@ -140,10 +142,10 @@ lint.ignore = [
]
lint.select = ["ALL"]

[tool.ruff.flake8-pytest-style]
[tool.ruff.lint.flake8-pytest-style]
parametrize-values-type = "tuple"

[tool.ruff.isort]
[tool.ruff.lint.isort]
known-first-party = ["mk"]

[tool.ruff.lint.per-file-ignores]
Expand Down
126 changes: 126 additions & 0 deletions src/mk/pre.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""Expose features related to git repositories."""

from __future__ import annotations

import datetime
import json
import os
from subprocess import run
from typing import Annotated

import typer
from rich import box
from rich.console import Console
from rich.panel import Panel
from typer_config.decorators import use_yaml_config


class TyperApp(typer.Typer):
"""Our App."""

repos: list[str]


app = TyperApp()
console = Console()


@app.callback(invoke_without_command=True)
@use_yaml_config(
default_value=os.path.expanduser("~/pre.yml"),
param_help="Configuration file (~/pre.yml).",
)
# https://github.com/tiangolo/typer/issues/533
def default(repos: Annotated[list[str], typer.Option()] = []) -> None:
"""Implicit entry point."""
if repos is None:
repos = []
# breakpoint()
app.repos = list(filter(lambda s: not s.startswith("_"), repos))


@app.command()
def releases() -> None:
"""Pre helps you chain releases on github."""
for repo in app.repos:
repo_link = f"[markdown.link][link=https://github.com/{repo}]{repo}[/][/]"
result = run(
f'gh api repos/{repo}/releases --jq "[.[] | select(.draft)"]',
text=True,
shell=True, # noqa: S602
capture_output=True,
check=True,
)
drafts = json.loads(result.stdout)
if not drafts or (
isinstance(drafts, dict) and drafts["message"] == "Not Found"
):
console.print(f"🟢 {repo_link} [dim]has no draft release.[/]")
continue
for draft in drafts:
created = datetime.datetime.fromisoformat(draft["created_at"]).replace(
tzinfo=None,
)
age = (datetime.datetime.now(tz=datetime.timezone.utc) - created).days
if not draft["body"].strip():
console.print(f"🟢 {repo_link} [dim]has an empty draft release.[/]")
continue

md = Panel(draft["body"].replace("\n\n", "\n").strip("\n"), box=box.MINIMAL)
msg = (
f"🟠 {repo_link} draft release "
f"[link={draft['html_url']}][markdown.link]{draft['tag_name']}[/][/]"
f" created [repr.number]{age}[/] days ago:\n"
)
console.print(msg, highlight=False, end="")
console.print(md, style="dim")


@app.command()
def prs() -> None:
"""List pending pull-request."""
# for user in TEAM:
# --review-requested=@{user}
# --owner=ansible --owner=ansible-community
cmd = (
"GH_PAGER= gh search prs --draft=false --state=open --limit=100 --sort=updated"
)
cmd += "".join(f" --repo={repo}" for repo in app.repos)
cmd += (
" --template '{{range .}}{{tablerow .repository.nameWithOwner (timeago .updatedAt) "
'.title (hyperlink .url (printf "#%v" .number) ) }}{{end}}{{tablerender}}\' '
"--json title,url,repository,updatedAt,number"
)
console.print(f"[dim]{cmd}[/]", highlight=False)
os.system(cmd) # noqa: S605


@app.command()
def alerts() -> None:
"""List open alerts."""
for repo in app.repos:
cmd = "GH_PAGER= gh "
cmd += f"api /repos/{repo}/dependabot/alerts"
cmd += " --jq='.[] | select(.state!=\"fixed\") | .html_url'"
result = run(
cmd,
text=True,
shell=True, # noqa: S602
capture_output=True,
check=False,
)
if result.returncode:
console.print(
f"[dim]{cmd}[/dim] failed with {result.returncode}\n"
f"{result.stdout}\n\n{result.stderr}",
)
else:
if result.stdout:
console.print(result.stdout)
if result.stderr:
console.print(result.stderr)


if __name__ == "__main__":
# execute only if run as a script
app()
35 changes: 35 additions & 0 deletions src/mk/tools/pre.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import annotations

from pathlib import Path

from mk.exec import run_or_fail
from mk.tools import Action, Tool


class PreTool(Tool):
name = "pre"

def run(self, action: Action | None = None):
if action:
run_or_fail(["pre", action.name], tee=True)

def is_present(self, path: Path) -> bool:
return True

def actions(self) -> list[Action]:
# for command in app.registered_commands:
# print(command.name, command.short_help, command.short_help, command.epilog, command.short_help)
# breakpoint()
return [
Action(name="prs", description="[dim]Show open PRs[/dim]", tool=self),
Action(
name="drafts",
description="[dim]Show draft releases[/dim]",
tool=self,
),
Action(
name="alerts",
description="[dim]Show dependabot security alerts[/dim]",
tool=self,
),
]
12 changes: 12 additions & 0 deletions test/pre.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
repos:
- ansible/ansible-compat
- ansible/ansible-lint
- ansible/ansible-navigator
- ansible/ansible-creator
- ansible/molecule
- ansible/tox-ansible
- ansible/pytest-ansible
- ansible/ansible-development-environment
- ansible/ansible-dev-tools
- ansible/creator-ee
14 changes: 14 additions & 0 deletions test/test_pre.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Tests"""

from typer.testing import CliRunner

from mk.pre import app

runner = CliRunner()


def test_main() -> None:
"""CLI Tests"""
result = runner.invoke(app, ["--help", "--config=test/pre.yml"])
assert result.exit_code == 0, result.stdout
assert "Pre helps you chain releases on github." in result.stdout

0 comments on commit 6071ee6

Please sign in to comment.