From eeba6f268d0220bc928ed7af41d310dfbe215205 Mon Sep 17 00:00:00 2001 From: Rahul Yadav Date: Fri, 10 Jul 2026 08:39:24 -0700 Subject: [PATCH] hooks: pass yes flag when available Pass the -y flag as a keyword argument "yes" to the hook main function. This allows upload hooks (such as auto-fixers) to automatically apply fixes when the -y flag is passed, rather than prompting the user. Bug: 498893733 Change-Id: I12096029aa3af471ba9175314d749deb0ebd1007 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/605261 Commit-Queue: Rahul Yadav Reviewed-by: Mike Frysinger Tested-by: Rahul Yadav --- docs/repo-hooks.md | 11 ++++++++++- hooks.py | 11 +++++++++-- tests/test_hooks.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/docs/repo-hooks.md b/docs/repo-hooks.md index 696ad5a9f..c3adee8ad 100644 --- a/docs/repo-hooks.md +++ b/docs/repo-hooks.md @@ -83,6 +83,13 @@ then check it directly. Hooks should not normally modify the active git repo the user. Although user interaction is discouraged in the common case, it can be useful when deploying automatic fixes. +### Safe Prompts + +If the repo command that triggered the hook supports a "yes" option (e.g., +`repo upload --yes`), this option is propagated to the hook's `main` function +as `yes` parameter (defaulting to `False`). Hooks can use this to bypass +interactive confirmation prompts when they can automatically fix issues. + ### Shebang Handling *** note @@ -119,7 +126,7 @@ This hook runs when people run `repo upload`. The `pre-upload.py` file should be defined like: ```py -def main(project_list, worktree_list=None, **kwargs): +def main(project_list, worktree_list=None, yes=False, **kwargs): """Main function invoked directly by repo. We must use the name "main" as that is what repo requires. @@ -130,6 +137,8 @@ def main(project_list, worktree_list=None, **kwargs): project_list, so that each entry in project_list matches with a directory in worktree_list. If None, we will attempt to calculate the directories automatically. + yes: Whether to answer yes to all safe prompts (see + [Safe Prompts](#safe-prompts)). kwargs: Leave this here for forward-compatibility. """ ``` diff --git a/hooks.py b/hooks.py index 0637dcec8..5c763db5e 100644 --- a/hooks.py +++ b/hooks.py @@ -68,6 +68,7 @@ class RepoHook: allow_all_hooks=False, ignore_hooks=False, abort_if_user_denies=False, + yes=False, ): """RepoHook constructor. @@ -89,6 +90,7 @@ class RepoHook: ignore_hooks: If True, then 'Do not abort action if hooks fail'. abort_if_user_denies: If True, we'll abort running the hook if the user doesn't allow us to run the hook. + yes: If True, then 'Yes' is assumed for any prompts. """ self._hook_type = hook_type self._hooks_project = hooks_project @@ -99,6 +101,7 @@ class RepoHook: self._allow_all_hooks = allow_all_hooks self._ignore_hooks = ignore_hooks self._abort_if_user_denies = abort_if_user_denies + self._yes = yes # Store the full path to the script for convenience. self._script_fullpath = None @@ -374,8 +377,11 @@ class RepoHook: # def main(project_list, **kwargs): # # This allows us to later expand the API without breaking old hooks. - kwargs = kwargs.copy() - kwargs["hook_should_take_kwargs"] = True + kwargs = { + **kwargs, + "hook_should_take_kwargs": True, + "yes": self._yes, + } # See what version of python the hook has been written against. data = open(self._script_fullpath).read() @@ -497,6 +503,7 @@ class RepoHook: "origin" ).url, "bug_url": manifest.contactinfo.bugurl, + "yes": getattr(opt, "yes", False), } ) return cls(*args, **kwargs) diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 14c226c75..33ccba7b6 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -105,3 +105,37 @@ def test_post_sync_argument_validation() -> None: finally: sys.stderr = old_stderr + + +@pytest.mark.parametrize("yes_val", (True, False)) +def test_repo_upload_yes_arg(tmp_path, yes_val: bool) -> None: + """Test that yes is passed in kwargs during hook execution.""" + + class FakeProject: + def __init__(self, worktree): + self.worktree = worktree + self.enabled_repo_hooks = ["pre-upload"] + self.config = None + + hook_file = tmp_path / "pre-upload.py" + + hook_content = """ +def main(project_list, **kwargs): + project_list.append(kwargs.get("yes")) +""" + hook_file.write_text(hook_content) + + hook = hooks.RepoHook( + hook_type="pre-upload", + hooks_project=FakeProject(str(tmp_path)), + repo_topdir=str(tmp_path), + manifest_url="https://gerrit", + allow_all_hooks=True, + yes=yes_val, + ) + + project_list = [] + res = hook.Run(project_list=project_list, worktree_list=[]) + + assert res is True + assert project_list == [yes_val]