2017-11-11 02:28:41 +00:00
|
|
|
# repo hooks
|
|
|
|
|
|
|
|
[TOC]
|
|
|
|
|
|
|
|
Repo provides a mechanism to hook specific stages of the runtime with custom
|
|
|
|
python modules. All the hooks live in one git project which is checked out by
|
|
|
|
the manifest (specified during `repo init`), and the manifest itself defines
|
|
|
|
which hooks are registered.
|
|
|
|
|
|
|
|
These are useful to run linters, check formatting, and run quick unittests
|
|
|
|
before allowing a step to proceed (e.g. before uploading a commit to Gerrit).
|
|
|
|
|
|
|
|
A complete example can be found in the Android project. It can be easily
|
|
|
|
re-used by any repo based project and is not specific to Android.<br>
|
|
|
|
https://android.googlesource.com/platform/tools/repohooks
|
|
|
|
|
|
|
|
## Approvals
|
|
|
|
|
|
|
|
When a hook is processed the first time, the user is prompted for approval.
|
|
|
|
We don't want to execute arbitrary code without explicit consent. For manifests
|
|
|
|
fetched via secure protocols (e.g. https://), the user is prompted once. For
|
|
|
|
insecure protocols (e.g. http://), the user is prompted whenever the registered
|
|
|
|
repohooks project is updated and a hook is triggered.
|
|
|
|
|
|
|
|
## Manifest Settings
|
|
|
|
|
2018-10-05 23:26:15 +00:00
|
|
|
For the full syntax, see the [repo manifest format](./manifest-format.md).
|
2017-11-11 02:28:41 +00:00
|
|
|
|
|
|
|
Here's a short example from
|
|
|
|
[Android](https://android.googlesource.com/platform/manifest/+/master/default.xml).
|
|
|
|
The `<project>` line checks out the repohooks git repo to the local
|
|
|
|
`tools/repohooks/` path. The `<repo-hooks>` line says to look in the project
|
|
|
|
with the name `platform/tools/repohooks` for hooks to run during the
|
|
|
|
`pre-upload` phase.
|
|
|
|
|
|
|
|
```xml
|
|
|
|
<project path="tools/repohooks" name="platform/tools/repohooks" />
|
|
|
|
<repo-hooks in-project="platform/tools/repohooks" enabled-list="pre-upload" />
|
|
|
|
```
|
|
|
|
|
|
|
|
## Source Layout
|
|
|
|
|
|
|
|
The repohooks git repo should have a python file with the same name as the hook.
|
|
|
|
So if you want to support the `pre-upload` hook, you'll need to create a file
|
|
|
|
named `pre-upload.py`. Repo will dynamically load that module when processing
|
|
|
|
the hook and then call the `main` function in it.
|
|
|
|
|
|
|
|
Hooks should have their `main` accept `**kwargs` for future compatibility.
|
|
|
|
|
|
|
|
## Runtime
|
|
|
|
|
|
|
|
Hook return values are ignored.
|
|
|
|
|
|
|
|
Any uncaught exceptions from the hook will cause the step to fail. This is
|
|
|
|
intended as a fallback safety check though rather than the normal flow. If
|
|
|
|
you want your hook to trigger a failure, it should call `sys.exit()` (after
|
|
|
|
displaying relevant diagnostics).
|
|
|
|
|
|
|
|
Output (stdout & stderr) are not filtered in any way. Hooks should generally
|
|
|
|
not be too verbose. A short summary is nice, and some status information when
|
|
|
|
long running operations occur, but long/verbose output should be used only if
|
|
|
|
the hook ultimately fails.
|
|
|
|
|
2018-03-21 00:00:14 +00:00
|
|
|
The hook runs from the top level of the repo client where the operation is
|
|
|
|
started.
|
|
|
|
For example, if the repo client is under `~/tree/`, then that is where the hook
|
|
|
|
runs, even if you ran repo in a git repository at `~/tree/src/foo/`, or in a
|
|
|
|
subdirectory of that git repository in `~/tree/src/foo/bar/`.
|
|
|
|
Hooks frequently start off by doing a `os.chdir` to the specific project they're
|
|
|
|
called on (see below) and then changing back to the original dir when they're
|
|
|
|
finished.
|
2017-11-11 02:28:41 +00:00
|
|
|
|
|
|
|
Python's `sys.path` is modified so that the top of repohooks directory comes
|
|
|
|
first. This should help simplify the hook logic to easily allow importing of
|
|
|
|
local modules.
|
|
|
|
|
|
|
|
Repo does not modify the state of the git checkout. This means that the hooks
|
|
|
|
might be running in a dirty git repo with many commits and checked out to the
|
|
|
|
latest one. If the hook wants to operate on specific git commits, it needs to
|
|
|
|
manually discover the list of pending commits, extract the diff/commit, and
|
|
|
|
then check it directly. Hooks should not normally modify the active git repo
|
|
|
|
(such as checking out a specific commit to run checks) without first prompting
|
|
|
|
the user. Although user interaction is discouraged in the common case, it can
|
|
|
|
be useful when deploying automatic fixes.
|
|
|
|
|
2019-06-18 21:23:39 +00:00
|
|
|
### Shebang Handling
|
|
|
|
|
|
|
|
*** note
|
|
|
|
This is intended as a transitional feature. Hooks are expected to eventually
|
|
|
|
migrate to Python 3 only as Python 2 is EOL & deprecated.
|
|
|
|
***
|
|
|
|
|
|
|
|
If the hook is written against a specific version of Python (either 2 or 3),
|
|
|
|
the script can declare that explicitly. Repo will then attempt to execute it
|
|
|
|
under the right version of Python regardless of the version repo itself might
|
|
|
|
be executing under.
|
|
|
|
|
|
|
|
Here are the shebangs that are recognized.
|
|
|
|
|
|
|
|
* `#!/usr/bin/env python` & `#!/usr/bin/python`: The hook is compatible with
|
|
|
|
Python 2 & Python 3. For maximum compatibility, these are recommended.
|
|
|
|
* `#!/usr/bin/env python2` & `#!/usr/bin/python2`: The hook requires Python 2.
|
|
|
|
Version specific names like `python2.7` are also recognized.
|
|
|
|
* `#!/usr/bin/env python3` & `#!/usr/bin/python3`: The hook requires Python 3.
|
|
|
|
Version specific names like `python3.6` are also recognized.
|
|
|
|
|
|
|
|
If no shebang is detected, or does not match the forms above, we assume that the
|
|
|
|
hook is compatible with both Python 2 & Python 3 as if `#!/usr/bin/python` was
|
|
|
|
used.
|
|
|
|
|
2017-11-11 02:28:41 +00:00
|
|
|
## Hooks
|
|
|
|
|
|
|
|
Here are all the points available for hooking.
|
|
|
|
|
|
|
|
### pre-upload
|
|
|
|
|
|
|
|
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):
|
|
|
|
"""Main function invoked directly by repo.
|
|
|
|
|
|
|
|
We must use the name "main" as that is what repo requires.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
project_list: List of projects to run on.
|
|
|
|
worktree_list: A list of directories. It should be the same length as
|
|
|
|
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.
|
|
|
|
kwargs: Leave this here for forward-compatibility.
|
|
|
|
"""
|
|
|
|
```
|