diff --git a/Containerfile b/Containerfile index 0539f24..3881091 100644 --- a/Containerfile +++ b/Containerfile @@ -1,7 +1,9 @@ -FROM registry.access.redhat.com/ubi8/python-39 +FROM registry.fedoraproject.org/fedora:41 USER root +RUN dnf update -y && dnf install -y python3-pip git make diffutils && dnf clean all + ENV GO_VERSION=1.23.2 RUN curl -Ls https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz | \ tar -C /usr/local -zxvf - @@ -13,8 +15,5 @@ RUN python -m pip install . WORKDIR /working RUN rm -rf /src -RUN chown default . -RUN chmod 0777 . -USER default -ENTRYPOINT [ "/opt/app-root/bin/merge-bot" ] +ENTRYPOINT [ "/usr/local/bin/merge-bot" ] diff --git a/src/merge_bot/cli.py b/src/merge_bot/cli.py index 5c3a1a9..ac617e1 100755 --- a/src/merge_bot/cli.py +++ b/src/merge_bot/cli.py @@ -168,6 +168,13 @@ def parse_cli_arguments(testing_args=None): required=False, help="When enabled, the bot will update and vendor the go modules in a separate commit", ) + parser.add_argument( + "--run-make", + action="store_true", + default=False, + required=False, + help="When enabled, the bot will run `make merge-bot` and submit any change in a separate commit", + ) if testing_args is not None: args = parser.parse_args(testing_args) diff --git a/src/merge_bot/merge_bot.py b/src/merge_bot/merge_bot.py index 6e9048e..e8741b9 100755 --- a/src/merge_bot/merge_bot.py +++ b/src/merge_bot/merge_bot.py @@ -53,6 +53,14 @@ class RepoException(Exception): pass +class MakeException(Exception): + """An non-fatal error when running make merge-bot, the PR will be sent but + the user should be informed that the make failed, has to manually run + the command and fix the issue.""" + + pass + + def check_conflict(repo): unmerged_blobs = repo.index.unmerged_blobs() @@ -158,6 +166,41 @@ def grab_go_version_from_go_mod(): return "" +# Run `make merge-bot` to allow projects to run custom logic when merging +# upstream changes. This is useful for projects that need to update vendored +# dependencies or run other tasks after an upstream merge. +# This is a best effort, and if the target doesn't exist, we ignore the error. +def commit_run_make(repo): + try: + proc = subprocess.run( + "make merge-bot", shell=True, check=True, capture_output=True + ) + logging.debug(f"make merge-bot output: {proc.stdout.decode()}") + except subprocess.CalledProcessError as err: + if "No rule to make target" not in err.stderr.decode(): + raise MakeException( + f"Error running `make merge-bot`. Manual intervention is needed: {err}: {err.stderr.decode()}" + ) + else: + logging.info("No make target for merge-bot, skipping") + + if repo.is_dirty(): + commit(repo, "CARRY: running make merge-bot") + + return + + +def commit(repo, msg): + try: + repo.git.add(all=True) + repo.git.commit("-m", msg) + except Exception as err: + err.extra_info = "Unable to commit changes in git" + raise err + + return + + def commit_go_mod_updates(repo): try: go_version = grab_go_version_from_go_mod() @@ -179,14 +222,7 @@ def commit_go_mod_updates(repo): ) if repo.is_dirty(): - try: - repo.git.add(all=True) - repo.git.commit( - "-m", "Updating and vendoring go modules after an upstream merge" - ) - except Exception as err: - err.extra_info = "Unable to commit go module changes in git" - raise err + commit(repo, "Updating and vendoring go modules after an upstream merge") return @@ -351,6 +387,7 @@ def run( gh_cloner_key, slack_webhook, update_go_modules=False, + run_make=False, ): logging.basicConfig( format="%(levelname)s - %(message)s", stream=sys.stdout, level=logging.INFO @@ -414,6 +451,17 @@ def run( if update_go_modules: commit_go_mod_updates(gitwd) + + if run_make: + commit_run_make(gitwd) + + except MakeException as ex: + # We don't want to fail the merge if make fails, but we should inform + # the user that they need to run make merge-bot manually and fix the + # issue. + logging.warning(ex) + pass + except RepoException as ex: logging.error(ex) message_slack( diff --git a/src/merge_bot/test.py b/src/merge_bot/test.py index 2bd5682..7898661 100644 --- a/src/merge_bot/test.py +++ b/src/merge_bot/test.py @@ -17,6 +17,7 @@ "github-cloner-key": "/credentials/gh-cloner-key", "slack-webhook": "/credentials/slack-webhook", "update-go-modules": None, + "run-make": None, } @@ -74,6 +75,7 @@ def test_valid_cli_argmuents(self): self.assertEqual(args.github_cloner_key, "/credentials/gh-cloner-key") self.assertEqual(args.slack_webhook, "/credentials/slack-webhook") self.assertEqual(args.update_go_modules, True) + self.assertEqual(args.run_make, True) def test_invalid_branch(self): for branch in ("dest", "source", "merge"):