mirror of
https://gerrit.googlesource.com/git-repo
synced 2024-12-21 07:16:21 +00:00
f7c51606f0
As we convert repo to support Python 3, the version of Python that we use might not be the version that repo hooks users have written for. Since repo upgrades are not immediate, and not easily under direct control of end users (relative to the projects maintaining the hook code), allow hook authors to declare the version of Python that they want to use. Now repo will read the shebang from the hook script and compare it against the version of Python repo itself is running under. If they differ, we'll try to execute a separate instance of Python and have it load & execute the hook. If things are compatible, then we still use the inprocess execution logic that we have today. This allows repo hook users to upgrade on their own schedule (they could even upgrade to Python 3 ahead of us) without having to worry about their supported version being exactly in sync with repo's. Bug: https://crbug.com/gerrit/10418 Change-Id: I97c7c96b64fb2ee465c39b90e9bdcc76394a146a Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/228432 Reviewed-by: David Pursehouse <dpursehouse@collab.net> Tested-by: Mike Frysinger <vapier@google.com>
136 lines
5.5 KiB
Markdown
136 lines
5.5 KiB
Markdown
# 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
|
|
|
|
For the full syntax, see the [repo manifest format](./manifest-format.md).
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
### 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.
|
|
|
|
## 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.
|
|
"""
|
|
```
|