README.md
1# AOSP Preupload Hooks
2
3This repo holds hooks that get run by repo during the upload phase. They
4perform various checks automatically such as running linters on your code.
5
6Note: Currently all hooks are disabled by default. Each repo must explicitly
7turn on any hook it wishes to enforce.
8
9[TOC]
10
11## Usage
12
13Normally these execute automatically when you run `repo upload`. If you want to
14run them by hand, you can execute `pre-upload.py` directly. By default, that
15will scan the active repo and process all commits that haven't yet been merged.
16See its help for more info.
17
18### Bypassing
19
20Sometimes you might want to bypass the upload checks. While this is **strongly
21discouraged** (often failures you add will affect others and block them too),
22sometimes there are valid reasons for this. You can simply use the option
23`--no-verify` when running `repo upload` to skip all upload checks. This will
24skip **all** checks and not just specific ones. It should be used only after
25having run & evaluated the upload output previously.
26
27# Config Files
28
29There are two types of config files:
30* Repo project-wide settings (e.g. all of AOSP). These set up defaults for all
31 projects that are checked out via a single manifest.
32* Project-local settings (e.g. a single .git repo). These control settings for
33 the local project you're working on.
34
35The merging of these config files control the hooks/checks that get run when
36running `repo upload`.
37
38## GLOBAL-PREUPLOAD.cfg
39
40These are the manifest-wide defaults and can be located in two places:
41* `.repo/manifests/GLOBAL-PREUPLOAD.cfg`: The manifest git repo.
42 Simply check this in to the manifest git repo and you're done.
43* `GLOBAL-PREUPLOAD.cfg`: The top level of the repo checkout.
44 For manifests that don't have a project checked out at the top level,
45 you can use repo's `<copyfile>` directive.
46
47These config files will be loaded first before stacking `PREUPLOAD.cfg`
48settings on top.
49
50## PREUPLOAD.cfg
51
52This file is checked in the top of a specific git repository. Stacking them
53in subdirectories (to try and override parent settings) is not supported.
54
55## Example
56
57```
58# Per-project `repo upload` hook settings.
59# https://android.googlesource.com/platform/tools/repohooks
60
61[Options]
62ignore_merged_commits = true
63
64[Hook Scripts]
65name = script --with args ${PREUPLOAD_FILES}
66
67[Builtin Hooks]
68cpplint = true
69
70[Builtin Hooks Options]
71cpplint = --filter=-x ${PREUPLOAD_FILES}
72
73[Tool Paths]
74clang-format = /usr/bin/clang-format
75```
76
77## Environment
78
79Hooks are executed in the top directory of the git repository. All paths should
80generally be relative to that point.
81
82A few environment variables are set so scripts don't need to discover things.
83
84* `REPO_PROJECT`: The name of the project.
85 e.g. `platform/tools/repohooks`
86* `REPO_PATH`: The path to the project relative to the root.
87 e.g. `tools/repohooks`
88* `REPO_REMOTE`: The name of the git remote.
89 e.g. `aosp`.
90* `REPO_LREV`: The name of the remote revision, translated to a local tracking
91 branch. This is typically latest commit in the remote-tracking branch.
92 e.g. `ec044d3e9b608ce275f02092f86810a3ba13834e`
93* `REPO_RREV`: The remote revision.
94 e.g. `master`
95* `PREUPLOAD_COMMIT`: The commit that is currently being checked.
96 e.g. `1f89dce0468448fa36f632d2fc52175cd6940a91`
97
98## Placeholders
99
100A few keywords are recognized to pass down settings. These are **not**
101environment variables, but are expanded inline. Files with whitespace and
102such will be expanded correctly via argument positions, so do not try to
103force your own quote handling.
104
105* `${PREUPLOAD_FILES}`: List of files to operate on.
106* `${PREUPLOAD_FILES_PREFIXED}`: A list of files to operate on.
107 Any string preceding/attached to the keyword ${PREUPLOAD_FILES_PREFIXED}
108 will be repeated for each file automatically. If no string is preceding/attached
109 to the keyword, the previous argument will be repeated before each file.
110* `${PREUPLOAD_COMMIT}`: Commit hash.
111* `${PREUPLOAD_COMMIT_MESSAGE}`: Commit message.
112
113Some variables are available to make it easier to handle OS differences. These
114are automatically expanded for you:
115
116* `${REPO_PATH}`: The path to the project relative to the root.
117* `${REPO_ROOT}`: The absolute path of the root of the repo checkout. If the
118 project is in a submanifest, this points to the root of the submanifest.
119* `${REPO_OUTER_ROOT}`: The absolute path of the root of the repo checkout.
120 This always points to the root of the overall repo checkout.
121* `${BUILD_OS}`: The string `darwin-x86` for macOS and the string `linux-x86`
122 for Linux/x86.
123
124### Examples
125
126Here are some examples of using the placeholders.
127Consider this sample config file.
128```
129[Hook Scripts]
130lister = ls ${PREUPLOAD_FILES}
131checker prefix = check --file=${PREUPLOAD_FILES_PREFIXED}
132checker flag = check --file ${PREUPLOAD_FILES_PREFIXED}
133```
134With a commit that changes `path1/file1` and `path2/file2`, then this will run
135programs with the arguments:
136* ['ls', 'path1/file1', 'path2/file2']
137* ['check', '--file=path1/file1', '--file=path2/file2']
138* ['check', '--file', 'path1/file1', '--file', 'path2/file2']
139
140## [Options]
141
142This section allows for setting options that affect the overall behavior of the
143pre-upload checks. The following options are recognized:
144
145* `ignore_merged_commits`: If set to `true`, the hooks will not run on commits
146 that are merged. Hooks will still run on the merge commit itself.
147
148## [Hook Scripts]
149
150This section allows for completely arbitrary hooks to run on a per-repo basis.
151
152The key can be any name (as long as the syntax is valid), as can the program
153that is executed. The key is used as the name of the hook for reporting purposes,
154so it should be at least somewhat descriptive.
155
156Whitespace in the key name is OK!
157
158The keys must be unique as duplicates will silently clobber earlier values.
159
160You do not need to send stderr to stdout. The tooling will take care of
161merging them together for you automatically.
162
163```
164[Hook Scripts]
165my first hook = program --gogog ${PREUPLOAD_FILES}
166another hook = funtimes --i-need "some space" ${PREUPLOAD_FILES}
167some fish = linter --ate-a-cat ${PREUPLOAD_FILES}
168some cat = formatter --cat-commit ${PREUPLOAD_COMMIT}
169some dog = tool --no-cat-in-commit-message ${PREUPLOAD_COMMIT_MESSAGE}
170```
171
172## [Builtin Hooks]
173
174This section allows for turning on common/builtin hooks. There are a bunch of
175canned hooks already included geared towards AOSP style guidelines.
176
177* `aidl_format`: Run AIDL files (.aidl) through `aidl-format`.
178* `android_test_mapping_format`: Validate TEST_MAPPING files in Android source
179 code. Refer to go/test-mapping for more details.
180* `bpfmt`: Run Blueprint files (.bp) through `bpfmt`.
181* `checkpatch`: Run commits through the Linux kernel's `checkpatch.pl` script.
182* `clang_format`: Run git-clang-format against the commit. The default style is
183 `file`.
184* `commit_msg_bug_field`: Require a valid `Bug:` line.
185* `commit_msg_changeid_field`: Require a valid `Change-Id:` Gerrit line.
186* `commit_msg_prebuilt_apk_fields`: Require badging and build information for
187 prebuilt APKs.
188* `commit_msg_relnote_field_format`: Check for possible misspellings of the
189 `Relnote:` field and that multiline release notes are properly formatted with
190 quotes.
191* `commit_msg_relnote_for_current_txt`: Check that CLs with changes to
192 current.txt or public_plus_experimental_current.txt also contain a
193 `Relnote:` field in the commit message.
194* `commit_msg_test_field`: Require a `Test:` line.
195* `cpplint`: Run through the cpplint tool (for C++ code).
196* `gofmt`: Run Go code through `gofmt`.
197* `google_java_format`: Run Java code through
198 [`google-java-format`](https://github.com/google/google-java-format)
199* `jsonlint`: Verify JSON code is sane.
200* `ktfmt`: Run Kotlin code through `ktfmt`. Supports an additional option
201 --include-dirs, which if specified will limit enforcement to only files under
202 the specified directories.
203* `pylint`: Alias of `pylint2`. Will change to `pylint3` by end of 2019.
204* `pylint2`: Run Python code through `pylint` using Python 2.
205* `pylint3`: Run Python code through `pylint` using Python 3.
206* `rustfmt`: Run Rust code through `rustfmt`.
207* `xmllint`: Run XML code through `xmllint`.
208
209Note: Builtin hooks tend to match specific filenames (e.g. `.json`). If no
210files match in a specific commit, then the hook will be skipped for that commit.
211
212```
213[Builtin Hooks]
214# Turn on cpplint checking.
215cpplint = true
216# Turn off gofmt checking.
217gofmt = false
218```
219
220## [Builtin Hooks Options]
221
222Used to customize the behavior of specific `[Builtin Hooks]`. Any arguments set
223here will be passed directly to the linter in question. This will completely
224override any existing default options, so be sure to include everything you need
225(especially `${PREUPLOAD_FILES}` -- see below).
226
227Quoting is handled naturally. i.e. use `"a b c"` to pass an argument with
228whitespace.
229
230See [Placeholders](#Placeholders) for variables you can expand automatically.
231
232```
233[Builtin Hooks Options]
234# Pass more filter args to cpplint.
235cpplint = --filter=-x ${PREUPLOAD_FILES}
236```
237
238## [Builtin Hooks Exclude Paths]
239
240*** note
241This section can only be added to the repo project-wide settings
242[GLOBAL-PREUPLOAD.cfg].
243***
244
245Used to explicitly exclude some projects when processing a hook. With this
246section, it is possible to define a hook that should apply to the majority of
247projects except a few.
248
249An entry must completely match the project's `REPO_PATH`. The paths can use the
250[shell-style wildcards](https://docs.python.org/library/fnmatch.html) and
251quotes. For advanced cases, it is possible to use a [regular
252expression](https://docs.python.org/howto/regex.html) by using the `^` prefix.
253
254```
255[Builtin Hooks Exclude Paths]
256# Run cpplint on all projects except ones under external/ and vendor/.
257# The "external" and "vendor" projects, if they exist, will still run cpplint.
258cpplint = external/* vendor/*
259
260# Run rustfmt on all projects except ones under external/. All projects under
261# hardware/ will be excluded except for ones starting with hardware/google (due to
262# the negative regex match).
263rustfmt = external/ ^hardware/(!?google)
264```
265
266## [Tool Paths]
267
268Some builtin hooks need to call external executables to work correctly. By
269default it will call those tools from the user's `$PATH`, but the paths of those
270executables can be overridden through `[Tool Paths]`. This is helpful to
271provide consistent behavior for developers across different OS and Linux
272distros/versions. The following tools are recognized:
273
274* `aidl-format`: used for the `aidl_format` builtin hook.
275* `android-test-mapping-format`: used for the `android_test_mapping_format`
276 builtin hook.
277* `bpfmt`: used for the `bpfmt` builtin hook.
278* `clang-format`: used for the `clang_format` builtin hook.
279* `cpplint`: used for the `cpplint` builtin hook.
280* `git-clang-format`: used for the `clang_format` builtin hook.
281* `gofmt`: used for the `gofmt` builtin hook.
282* `google-java-format`: used for the `google_java_format` builtin hook.
283* `google-java-format-diff`: used for the `google_java_format` builtin hook.
284* `ktfmt`: used for the `ktfmt` builtin hook.
285* `pylint`: used for the `pylint` builtin hook.
286* `rustfmt`: used for the `rustfmt` builtin hook.
287
288See [Placeholders](#Placeholders) for variables you can expand automatically.
289
290```
291[Tool Paths]
292# Pass absolute paths.
293clang-format = /usr/bin/clang-format
294# Or paths relative to the top of the git project.
295clang-format = prebuilts/bin/clang-format
296# Or paths relative to the repo root.
297clang-format = ${REPO_ROOT}/prebuilts/clang/host/${BUILD_OS}/clang-stable/bin/clang-format
298```
299
300# Hook Developers
301
302These are notes for people updating the `pre-upload.py` hook itself:
303
304* Don't worry about namespace collisions. The `pre-upload.py` script is loaded
305 and exec-ed in its own context. The only entry-point that matters is `main`.
306* New hooks can be added in `rh/hooks.py`. Be sure to keep the list up-to-date
307 with the documentation in this file.
308
309## Warnings
310
311If the return code of a hook is 77, then it is assumed to be a warning. The
312output will be printed to the terminal, but uploading will still be allowed
313without a bypass being required.
314
315# TODO/Limitations
316
317* `pylint` should support per-directory pylintrc files.
318* Some checkers operate on the files as they exist in the filesystem. This is
319 not easy to fix because the linters require not just the modified file but the
320 entire repo in order to perform full checks. e.g. `pylint` needs to know what
321 other modules exist locally to verify their API. We can support this case by
322 doing a full checkout of the repo in a temp dir, but this can slow things down
323 a lot. Will need to consider a `PREUPLOAD.cfg` knob.
324* We need to add `pylint` tool to the AOSP manifest and use that local copy
325 instead of relying on the version that is in $PATH.
326* Should make file extension filters configurable. All hooks currently declare
327 their own list of files like `.cc` and `.py` and `.xml`.
328* Add more checkers.
329 * `clang-check`: Runs static analyzers against code.
330 * License checking (like require AOSP header).
331 * Whitespace checking (trailing/tab mixing/etc...).
332 * Long line checking.
333 * Commit message checks (correct format/BUG/TEST/SOB tags/etc...).
334 * Markdown (gitiles) validator.
335 * Spell checker.
336