diff options
-rw-r--r-- | docs/repo-hooks.md | 40 | ||||
-rw-r--r-- | hooks.py | 1 | ||||
-rw-r--r-- | progress.py | 5 | ||||
-rw-r--r-- | subcmds/init.py | 1 | ||||
-rw-r--r-- | subcmds/sync.py | 17 |
5 files changed, 64 insertions, 0 deletions
diff --git a/docs/repo-hooks.md b/docs/repo-hooks.md index cbb1aac7..a56f261c 100644 --- a/docs/repo-hooks.md +++ b/docs/repo-hooks.md | |||
@@ -133,3 +133,43 @@ def main(project_list, worktree_list=None, **kwargs): | |||
133 | kwargs: Leave this here for forward-compatibility. | 133 | kwargs: Leave this here for forward-compatibility. |
134 | """ | 134 | """ |
135 | ``` | 135 | ``` |
136 | |||
137 | ### post-sync | ||
138 | |||
139 | This hook runs when `repo sync` completes without errors. | ||
140 | |||
141 | Note: This includes cases where no actual checkout may occur. The hook will still run. | ||
142 | For example: | ||
143 | - `repo sync -n` performs network fetches only and skips the checkout phase. | ||
144 | - `repo sync <project>` only updates the specified project(s). | ||
145 | - Partial failures may still result in a successful exit. | ||
146 | |||
147 | This hook is useful for post-processing tasks such as setting up git hooks, | ||
148 | bootstrapping configuration files, or running project initialization logic. | ||
149 | |||
150 | The hook is defined using the existing `<repo-hooks>` manifest block and is | ||
151 | optional. If the hook script fails or is missing, `repo sync` will still | ||
152 | complete successfully, and the error will be printed as a warning. | ||
153 | |||
154 | Example: | ||
155 | |||
156 | ```xml | ||
157 | <project name="myorg/dev-tools" path="tools" revision="main" /> | ||
158 | <repo-hooks in-project="myorg/dev-tools" enabled-list="post-sync"> | ||
159 | <hook name="post-sync" /> | ||
160 | </repo-hooks> | ||
161 | ``` | ||
162 | |||
163 | The `post-sync.py` file should be defined like: | ||
164 | |||
165 | ```py | ||
166 | def main(repo_topdir=None, **kwargs): | ||
167 | """Main function invoked directly by repo. | ||
168 | |||
169 | We must use the name "main" as that is what repo requires. | ||
170 | |||
171 | Args: | ||
172 | repo_topdir: The absolute path to the top-level directory of the repo workspace. | ||
173 | kwargs: Leave this here for forward-compatibility. | ||
174 | """ | ||
175 | ``` | ||
@@ -25,6 +25,7 @@ from git_refs import HEAD | |||
25 | # The API we've documented to hook authors. Keep in sync with repo-hooks.md. | 25 | # The API we've documented to hook authors. Keep in sync with repo-hooks.md. |
26 | _API_ARGS = { | 26 | _API_ARGS = { |
27 | "pre-upload": {"project_list", "worktree_list"}, | 27 | "pre-upload": {"project_list", "worktree_list"}, |
28 | "post-sync": {"repo_topdir"}, | ||
28 | } | 29 | } |
29 | 30 | ||
30 | 31 | ||
diff --git a/progress.py b/progress.py index a386f426..31a4890a 100644 --- a/progress.py +++ b/progress.py | |||
@@ -101,6 +101,7 @@ class Progress: | |||
101 | self._units = units | 101 | self._units = units |
102 | self._elide = elide and _TTY | 102 | self._elide = elide and _TTY |
103 | self._quiet = quiet | 103 | self._quiet = quiet |
104 | self._ended = False | ||
104 | 105 | ||
105 | # Only show the active jobs section if we run more than one in parallel. | 106 | # Only show the active jobs section if we run more than one in parallel. |
106 | self._show_jobs = False | 107 | self._show_jobs = False |
@@ -211,6 +212,10 @@ class Progress: | |||
211 | self.update(inc=0) | 212 | self.update(inc=0) |
212 | 213 | ||
213 | def end(self): | 214 | def end(self): |
215 | if self._ended: | ||
216 | return | ||
217 | self._ended = True | ||
218 | |||
214 | self._update_event.set() | 219 | self._update_event.set() |
215 | if not _TTY or IsTraceToStderr() or self._quiet: | 220 | if not _TTY or IsTraceToStderr() or self._quiet: |
216 | return | 221 | return |
diff --git a/subcmds/init.py b/subcmds/init.py index fb6d3eb5..f5a3892a 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -127,6 +127,7 @@ to update the working directory files. | |||
127 | return { | 127 | return { |
128 | "REPO_MANIFEST_URL": "manifest_url", | 128 | "REPO_MANIFEST_URL": "manifest_url", |
129 | "REPO_MIRROR_LOCATION": "reference", | 129 | "REPO_MIRROR_LOCATION": "reference", |
130 | "REPO_GIT_LFS": "git_lfs", | ||
130 | } | 131 | } |
131 | 132 | ||
132 | def _SyncManifest(self, opt): | 133 | def _SyncManifest(self, opt): |
diff --git a/subcmds/sync.py b/subcmds/sync.py index 20d75dc8..250925f4 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -68,6 +68,7 @@ from git_config import GetUrlCookieFile | |||
68 | from git_refs import HEAD | 68 | from git_refs import HEAD |
69 | from git_refs import R_HEADS | 69 | from git_refs import R_HEADS |
70 | import git_superproject | 70 | import git_superproject |
71 | from hooks import RepoHook | ||
71 | import platform_utils | 72 | import platform_utils |
72 | from progress import elapsed_str | 73 | from progress import elapsed_str |
73 | from progress import jobs_str | 74 | from progress import jobs_str |
@@ -623,6 +624,7 @@ later is required to fix a server side protocol bug. | |||
623 | action="store_true", | 624 | action="store_true", |
624 | help=optparse.SUPPRESS_HELP, | 625 | help=optparse.SUPPRESS_HELP, |
625 | ) | 626 | ) |
627 | RepoHook.AddOptionGroup(p, "post-sync") | ||
626 | 628 | ||
627 | def _GetBranch(self, manifest_project): | 629 | def _GetBranch(self, manifest_project): |
628 | """Returns the branch name for getting the approved smartsync manifest. | 630 | """Returns the branch name for getting the approved smartsync manifest. |
@@ -1847,6 +1849,21 @@ later is required to fix a server side protocol bug. | |||
1847 | except (KeyboardInterrupt, Exception) as e: | 1849 | except (KeyboardInterrupt, Exception) as e: |
1848 | raise RepoUnhandledExceptionError(e, aggregate_errors=errors) | 1850 | raise RepoUnhandledExceptionError(e, aggregate_errors=errors) |
1849 | 1851 | ||
1852 | # Run post-sync hook only after successful sync | ||
1853 | self._RunPostSyncHook(opt) | ||
1854 | |||
1855 | def _RunPostSyncHook(self, opt): | ||
1856 | """Run post-sync hook if configured in manifest <repo-hooks>.""" | ||
1857 | hook = RepoHook.FromSubcmd( | ||
1858 | hook_type="post-sync", | ||
1859 | manifest=self.manifest, | ||
1860 | opt=opt, | ||
1861 | abort_if_user_denies=False, | ||
1862 | ) | ||
1863 | success = hook.Run(repo_topdir=self.client.topdir) | ||
1864 | if not success: | ||
1865 | print("Warning: post-sync hook reported failure.") | ||
1866 | |||
1850 | def _ExecuteHelper(self, opt, args, errors): | 1867 | def _ExecuteHelper(self, opt, args, errors): |
1851 | manifest = self.outer_manifest | 1868 | manifest = self.outer_manifest |
1852 | if not opt.outer_manifest: | 1869 | if not opt.outer_manifest: |