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 e.g. `tools/repohooks` 118* `${REPO_PROJECT}`: The name of the project. 119 e.g. `platform/tools/repohooks` 120* `${REPO_ROOT}`: The absolute path of the root of the repo checkout. If the 121 project is in a submanifest, this points to the root of the submanifest. 122* `${REPO_OUTER_ROOT}`: The absolute path of the root of the repo checkout. 123 This always points to the root of the overall repo checkout. 124* `${BUILD_OS}`: The string `darwin-x86` for macOS and the string `linux-x86` 125 for Linux/x86. 126 127### Examples 128 129Here are some examples of using the placeholders. 130Consider this sample config file. 131``` 132[Hook Scripts] 133lister = ls ${PREUPLOAD_FILES} 134checker prefix = check --file=${PREUPLOAD_FILES_PREFIXED} 135checker flag = check --file ${PREUPLOAD_FILES_PREFIXED} 136``` 137With a commit that changes `path1/file1` and `path2/file2`, then this will run 138programs with the arguments: 139* ['ls', 'path1/file1', 'path2/file2'] 140* ['check', '--file=path1/file1', '--file=path2/file2'] 141* ['check', '--file', 'path1/file1', '--file', 'path2/file2'] 142 143## [Options] 144 145This section allows for setting options that affect the overall behavior of the 146pre-upload checks. The following options are recognized: 147 148* `ignore_merged_commits`: If set to `true`, the hooks will not run on commits 149 that are merged. Hooks will still run on the merge commit itself. 150 151## [Hook Scripts] 152 153This section allows for completely arbitrary hooks to run on a per-repo basis. 154 155The key can be any name (as long as the syntax is valid), as can the program 156that is executed. The key is used as the name of the hook for reporting purposes, 157so it should be at least somewhat descriptive. 158 159Whitespace in the key name is OK! 160 161The keys must be unique as duplicates will silently clobber earlier values. 162 163You do not need to send stderr to stdout. The tooling will take care of 164merging them together for you automatically. 165 166``` 167[Hook Scripts] 168my first hook = program --gogog ${PREUPLOAD_FILES} 169another hook = funtimes --i-need "some space" ${PREUPLOAD_FILES} 170some fish = linter --ate-a-cat ${PREUPLOAD_FILES} 171some cat = formatter --cat-commit ${PREUPLOAD_COMMIT} 172some dog = tool --no-cat-in-commit-message ${PREUPLOAD_COMMIT_MESSAGE} 173``` 174 175## [Builtin Hooks] 176 177This section allows for turning on common/builtin hooks. There are a bunch of 178canned hooks already included geared towards AOSP style guidelines. 179 180* `aidl_format`: Run AIDL files (.aidl) through `aidl-format`. 181* `aosp_license`: Check if all new-added file have valid AOSP license headers. 182* `android_test_mapping_format`: Validate TEST_MAPPING files in Android source 183 code. Refer to go/test-mapping for more details. 184* `bpfmt`: Run Blueprint files (.bp) through `bpfmt`. 185* `checkpatch`: Run commits through the Linux kernel's `checkpatch.pl` script. 186* `clang_format`: Run git-clang-format against the commit. The default style is 187 `file`. 188* `commit_msg_bug_field`: Require a valid `Bug:` line. 189* `commit_msg_changeid_field`: Require a valid `Change-Id:` Gerrit line. 190* `commit_msg_prebuilt_apk_fields`: Require badging and build information for 191 prebuilt APKs. 192* `commit_msg_relnote_field_format`: Check for possible misspellings of the 193 `Relnote:` field and that multiline release notes are properly formatted with 194 quotes. 195* `commit_msg_relnote_for_current_txt`: Check that CLs with changes to 196 current.txt or public_plus_experimental_current.txt also contain a 197 `Relnote:` field in the commit message. 198* `commit_msg_test_field`: Require a `Test:` line. 199* `cpplint`: Run through the cpplint tool (for C++ code). 200* `gofmt`: Run Go code through `gofmt`. 201* `google_java_format`: Run Java code through 202 [`google-java-format`](https://github.com/google/google-java-format). 203 Supports an additional option --include-dirs, which if specified will limit 204 enforcement to only files under the specified directories. 205* `jsonlint`: Verify JSON code is sane. 206* `ktfmt`: Run Kotlin code through `ktfmt`. Supports an additional option 207 --include-dirs, which if specified will limit enforcement to only files under 208 the specified directories. 209* `pylint`: Alias of `pylint3`. 210* `pylint2`: Run Python code through `pylint` using Python 2. 211* `pylint3`: Run Python code through `pylint` using Python 3. 212* `rustfmt`: Run Rust code through `rustfmt`. 213* `xmllint`: Run XML code through `xmllint`. 214 215Note: Builtin hooks tend to match specific filenames (e.g. `.json`). If no 216files match in a specific commit, then the hook will be skipped for that commit. 217 218``` 219[Builtin Hooks] 220# Turn on cpplint checking. 221cpplint = true 222# Turn off gofmt checking. 223gofmt = false 224``` 225 226## [Builtin Hooks Options] 227 228Used to customize the behavior of specific `[Builtin Hooks]`. Any arguments set 229here will be passed directly to the linter in question. This will completely 230override any existing default options, so be sure to include everything you need 231(especially `${PREUPLOAD_FILES}` -- see below). 232 233Quoting is handled naturally. i.e. use `"a b c"` to pass an argument with 234whitespace. 235 236See [Placeholders](#Placeholders) for variables you can expand automatically. 237 238``` 239[Builtin Hooks Options] 240# Pass more filter args to cpplint. 241cpplint = --filter=-x ${PREUPLOAD_FILES} 242``` 243 244## [Builtin Hooks Exclude Paths] 245 246*** note 247This section can only be added to the repo project-wide settings 248[GLOBAL-PREUPLOAD.cfg]. 249*** 250 251Used to explicitly exclude some projects when processing a hook. With this 252section, it is possible to define a hook that should apply to the majority of 253projects except a few. 254 255An entry must completely match the project's `REPO_PATH`. The paths can use the 256[shell-style wildcards](https://docs.python.org/library/fnmatch.html) and 257quotes. For advanced cases, it is possible to use a [regular 258expression](https://docs.python.org/howto/regex.html) by using the `^` prefix. 259 260``` 261[Builtin Hooks Exclude Paths] 262# Run cpplint on all projects except ones under external/ and vendor/. 263# The "external" and "vendor" projects, if they exist, will still run cpplint. 264cpplint = external/* vendor/* 265 266# Run rustfmt on all projects except ones under external/. All projects under 267# hardware/ will be excluded except for ones starting with hardware/google (due to 268# the negative regex match). 269rustfmt = external/ ^hardware/(!?google) 270``` 271 272## [Tool Paths] 273 274Some builtin hooks need to call external executables to work correctly. By 275default it will call those tools from the user's `$PATH`, but the paths of those 276executables can be overridden through `[Tool Paths]`. This is helpful to 277provide consistent behavior for developers across different OS and Linux 278distros/versions. The following tools are recognized: 279 280* `aidl-format`: used for the `aidl_format` builtin hook. 281* `android-test-mapping-format`: used for the `android_test_mapping_format` 282 builtin hook. 283* `bpfmt`: used for the `bpfmt` builtin hook. 284* `clang-format`: used for the `clang_format` builtin hook. 285* `cpplint`: used for the `cpplint` builtin hook. 286* `git-clang-format`: used for the `clang_format` builtin hook. 287* `gofmt`: used for the `gofmt` builtin hook. 288* `google-java-format`: used for the `google_java_format` builtin hook. 289* `google-java-format-diff`: used for the `google_java_format` builtin hook. 290* `ktfmt`: used for the `ktfmt` builtin hook. 291* `pylint`: used for the `pylint` builtin hook. 292* `rustfmt`: used for the `rustfmt` builtin hook. 293 294See [Placeholders](#Placeholders) for variables you can expand automatically. 295 296``` 297[Tool Paths] 298# Pass absolute paths. 299clang-format = /usr/bin/clang-format 300# Or paths relative to the top of the git project. 301clang-format = prebuilts/bin/clang-format 302# Or paths relative to the repo root. 303clang-format = ${REPO_ROOT}/prebuilts/clang/host/${BUILD_OS}/clang-stable/bin/clang-format 304``` 305 306# Hook Developers 307 308These are notes for people updating the `pre-upload.py` hook itself: 309 310* Don't worry about namespace collisions. The `pre-upload.py` script is loaded 311 and exec-ed in its own context. The only entry-point that matters is `main`. 312* New hooks can be added in `rh/hooks.py`. Be sure to keep the list up-to-date 313 with the documentation in this file. 314 315## Warnings 316 317If the return code of a hook is 77, then it is assumed to be a warning. The 318output will be printed to the terminal, but uploading will still be allowed 319without a bypass being required. 320 321# TODO/Limitations 322 323* Some checkers operate on the files as they exist in the filesystem. This is 324 not easy to fix because the linters require not just the modified file but the 325 entire repo in order to perform full checks. e.g. `pylint` needs to know what 326 other modules exist locally to verify their API. We can support this case by 327 doing a full checkout of the repo in a temp dir, but this can slow things down 328 a lot. Will need to consider a `PREUPLOAD.cfg` knob. 329* We need to add `pylint` tool to the AOSP manifest and use that local copy 330 instead of relying on the version that is in $PATH. 331* Should make file extension filters configurable. All hooks currently declare 332 their own list of files like `.cc` and `.py` and `.xml`. 333* Add more checkers. 334 * `clang-check`: Runs static analyzers against code. 335 * Whitespace checking (trailing/tab mixing/etc...). 336 * Long line checking. 337 * Commit message checks (correct format/BUG/TEST/SOB tags/etc...). 338 * Markdown (gitiles) validator. 339 * Spell checker. 340