From a2f33f1be2772d2204cb8c9a17ca7cc16b17346a Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Tue, 27 Aug 2024 21:14:13 -0500 Subject: [PATCH] wip: Include manifest diff in PR description --- updatebot.py | 69 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/updatebot.py b/updatebot.py index c9f477e..5c3a287 100644 --- a/updatebot.py +++ b/updatebot.py @@ -5,6 +5,7 @@ import logging import functools import os import re +import subprocess import tempfile import threading import tomllib @@ -122,6 +123,10 @@ class BaseProject(abc.ABC, pydantic.BaseModel): def apply_update(self, path: Path, version: str) -> None: raise NotImplementedError + @abc.abstractmethod + def diff(self, path: Path) -> str: + raise NotImplementedError + class KustomizeProject(BaseProject): kind: Literal['kustomize'] @@ -153,6 +158,26 @@ class KustomizeProject(BaseProject): with filepath.open('wb') as f: yaml.dump(kustomization, f) + def diff(self, path: Path) -> str: + try: + p = subprocess.run( + ['kubectl', 'diff', '-k', '.'], + cwd=path, + check=False, + capture_output=True, + stdin=subprocess.DEVNULL, + encoding='utf-8', + ) + except FileNotFoundError as e: + log.warning('Cannot compute manifest diff: %s', e) + return '' + if not p.stdout and p.returncode != 0: + log.error('Error computing manifest diff: %s', p.stderr) + return '' + else: + assert p.stdout is not None + return p.stdout + class DirectoryProject(BaseProject): kind: Literal['dir'] | Literal['directory'] @@ -195,7 +220,11 @@ class RepoConfig(pydantic.BaseModel): return data['clone_url'] def create_pr( - self, title: str, source_branch: str, target_branch: str + self, + title: str, + source_branch: str, + target_branch: str, + body: Optional[str] = None, ) -> None: session = _get_session() r = session.post( @@ -207,6 +236,7 @@ class RepoConfig(pydantic.BaseModel): 'title': title, 'base': target_branch, 'head': source_branch, + 'body': body, }, ) log.log(TRACE, '%r', r.content) @@ -237,7 +267,7 @@ class Arguments: def update_project( repo: git.Repo, name: str, project: Project -) -> Optional[git.Commit]: +) -> tuple[Optional[git.Commit], Optional[str]]: basedir = Path(repo.working_dir) log.debug('Checking for latest version of %s', name) latest = project.source.get_latest_version() @@ -250,10 +280,12 @@ def update_project( repo.index.add(str(path)) c = repo.index.commit(f'{name}: Update to {latest}') log.info('Commited %s %s', str(c)[:7], c.summary) - return c + log.debug('Computing manifest diff') + diff = project.diff(path) + return (c, diff) else: log.info('No changes to commit') - return None + return (None, None) def parse_args() -> Arguments: @@ -324,8 +356,11 @@ def main() -> None: log.debug('Checking out new branch: %s', args.branch_name) repo.heads[0].checkout(force=True, B=args.branch_name) title = None + pr_desc = '' for project in projects: - commit = update_project(repo, project, config.projects[project]) + commit, diff = update_project( + repo, project, config.projects[project] + ) if commit and not title: if not isinstance(commit.summary, str): title = bytes(commit.summary).decode( @@ -333,17 +368,29 @@ def main() -> None: ) else: title = commit.summary + if diff: + pr_desc += diff if not title: log.info('No changes made') return - repo.head.reference.set_tracking_branch( - git.RemoteReference( - repo, f'refs/remotes/origin/{args.branch_name}' - ) - ) if not args.dry_run: + repo.head.reference.set_tracking_branch( + git.RemoteReference( + repo, f'refs/remotes/origin/{args.branch_name}' + ) + ) repo.remote().push(force=True) - config.repo.create_pr(title, args.branch_name, config.repo.branch) + if pr_desc: + pr_desc = ( + '
\nManifest diff\n\n' + f'```diff\n{pr_desc}```\n' + '
' + ) + config.repo.create_pr( + title, args.branch_name, config.repo.branch, pr_desc + ) + elif pr_desc: + print(pr_desc) if __name__ == '__main__':