Owner: vapier Bug: b/27541699

Clone this repo:
  1. 4c4e78c drop "Android" from the Test error message am: e6f9679d01 am: b13c1acffd by Mike Frysinger · 2 months ago master
  2. b13c1ac drop "Android" from the Test error message am: e6f9679d01 by Mike Frysinger · 2 months ago
  3. e6f9679 drop "Android" from the Test error message by Mike Frysinger · 2 months ago o-preview android-o-preview-1
  4. 7e74f70 Add support for automatic fixups to google-java-format am: d1fd88b71c am: 1f99b7db96 by Luis Hector Chavez · 4 months ago
  5. 086a189 Add support for automatic fixups to google-java-format am: d1fd88b71c by Luis Hector Chavez · 4 months ago

AOSP Presubmit Hooks

This repo holds hooks that get run by repo during the upload phase. They perform various checks automatically such as running linters on your code.

Note: Currently all hooks are disabled by default. Each repo must explicitly turn on any hook it wishes to enforce.

Usage

Normally these execute automatically when you run repo upload. If you want to run them by hand, you can execute pre-upload.py directly. By default, that will scan the active repo and process all commits that haven't yet been merged. See its help for more info.

Bypassing

Sometimes you might want to bypass the upload checks. While this is strongly discouraged (often failures you add will affect others and block them too), sometimes there are valid reasons for this. You can simply use the option --no-verify when running repo upload to skip all upload checks. This will skip all checks and not just specific ones. It should be used only after having run & evaluated the upload output previously.

Config Files

There are two types of config files:

  • Repo project-wide settings (e.g. all of AOSP). These set up defaults for all projects that are checked out via a single manifest.
  • Project-local settings (e.g. a single .git repo). These control settings for the local project you're working on.

The merging of these config files control the hooks/checks that get run when running repo upload.

GLOBAL-PREUPLOAD.cfg

These are the manifest-wide defaults and can be located in two places:

  • .repo/manifests/GLOBAL-PREUPLOAD.cfg: The manifest git repo. Simply check this in to the manifest git repo and you're done.
  • GLOBAL-PREUPLOAD.cfg: The top level of the repo checkout. For manifests that don‘t have a project checked out at the top level, you can use repo’s <copyfile> directive.

These config files will be loaded first before stacking PREUPLOAD.cfg settings on top.

PREUPLOAD.cfg

This file are checked in the top of a specific git repository. Stacking them in subdirectories (to try and override parent settings) is not supported.

Example

[Options]
ignore_merged_commits = true

[Hook Scripts]
name = script --with args ${PREUPLOAD_FILES}

[Builtin Hooks]
cpplint = true

[Builtin Hooks Options]
cpplint = --filter=-x ${PREUPLOAD_FILES}

[Tool Paths]
clang-format = /usr/bin/clang-format

Environment

Hooks are executed in the top directory of the git repository. All paths should generally be relative to that point.

A few environment variables are set so scripts don't need to discover things.

  • REPO_PROJECT: The name of the project. e.g. platform/tools/repohooks
  • REPO_PATH: The path to the project relative to the root. e.g. tools/repohooks
  • REPO_REMOTE: The name of the git remote. e.g. aosp.
  • REPO_LREV: The name of the remote revision, translated to a local tracking branch. This is typically latest commit in the remote-tracking branch. e.g. ec044d3e9b608ce275f02092f86810a3ba13834e
  • REPO_RREV: The remote revision. e.g. master
  • PREUPLOAD_COMMIT: The commit that is currently being checked. e.g. 1f89dce0468448fa36f632d2fc52175cd6940a91

Placeholders

A few keywords are recognized to pass down settings. These are not environment variables, but are expanded inline. Files with whitespace and such will be expanded correctly via argument positions, so do not try to force your own quote handling.

  • ${PREUPLOAD_FILES}: List of files to operate on.
  • ${PREUPLOAD_COMMIT}: Commit hash.
  • ${PREUPLOAD_COMMIT_MESSAGE}: Commit message.

Some variables are available to make it easier to handle OS differences. These are automatically expanded for you:

  • ${REPO_ROOT}: The absolute path of the root of the repo checkout.
  • ${BUILD_OS}: The string darwin-x86 for macOS and the string linux-x86 for Linux/x86.

[Options]

This section allows for setting options that affect the overall behavior of the pre-upload checks. The following options are recognized:

  • ignore_merged_commits: If set to true, the hooks will not run on commits that are merged. Hooks will still run on the merge commit itself.

[Hook Scripts]

This section allows for completely arbitrary hooks to run on a per-repo basis.

The key can be any name (as long as the syntax is valid), as can the program that is executed. The key is used as the name of the hook for reporting purposes, so it should be at least somewhat descriptive.

[Hook Scripts]
my_first_hook = program --gogog ${PREUPLOAD_FILES}
another_hook = funtimes --i-need "some space" ${PREUPLOAD_FILES}
some_fish = linter --ate-a-cat ${PREUPLOAD_FILES}
some_cat = formatter --cat-commit ${PREUPLOAD_COMMIT}
some_dog = tool --no-cat-in-commit-message ${PREUPLOAD_COMMIT_MESSAGE}

[Builtin Hooks]

This section allows for turning on common/builtin hooks. There are a bunch of canned hooks already included geared towards AOSP style guidelines.

  • checkpatch: Run commits through the Linux kernel's checkpatch.pl script.
  • clang_format: Run git-clang-format against the commit. The default style is file.
  • commit_msg_bug_field: Require a valid Bug: line.
  • commit_msg_changeid_field: Require a valid Change-Id: Gerrit line.
  • commit_msg_test_field: Require a Test: line.
  • cpplint: Run through the cpplint tool (for C++ code).
  • gofmt: Run Go code through gofmt.
  • google_java_format: Run Java code through google-java-format
  • jsonlint: Verify JSON code is sane.
  • pylint: Run Python code through pylint.
  • xmllint: Run XML code through xmllint.

Note: Builtin hooks tend to match specific filenames (e.g. .json). If no files match in a specific commit, then the hook will be skipped for that commit.

[Builtin Hooks]
# Turn on cpplint checking.
cpplint = true
# Turn off gofmt checking.
gofmt = false

[Builtin Hooks Options]

Used to customize the behavior of specific [Builtin Hooks]. Any arguments set here will be passed directly to the linter in question. This will completely override any existing default options, so be sure to include everything you need (especially ${PREUPLOAD_FILES} -- see below).

Quoting is handled naturally. i.e. use "a b c" to pass an argument with whitespace.

See Placeholders for variables you can expand automatically.

[Builtin Hooks Options]
# Pass more filter args to cpplint.
cpplint = --filter=-x ${PREUPLOAD_FILES}

[Tool Paths]

Some builtin hooks need to call external executables to work correctly. By default it will call those tools from the user's $PATH, but the paths of those executables can be overridden through [Tool Paths]. This is helpful to provide consistent behavior for developers across different OS and Linux distros/versions. The following tools are recognized:

  • clang-format: used for the clang_format builtin hook.
  • cpplint: used for the cpplint builtin hook.
  • git-clang-format: used for the clang_format builtin hook.
  • gofmt: used for the gofmt builtin hook.
  • google-java-format: used for the google_java_format builtin hook.
  • google-java-format-diff: used for the google_java_format builtin hook.
  • pylint: used for the pylint builtin hook.

See Placeholders for variables you can expand automatically.

[Tool Paths]
# Pass absolute paths.
clang-format = /usr/bin/clang-format
# Or paths relative to the top of the git project.
clang-format = prebuilts/bin/clang-format
# Or paths relative to the repo root.
clang-format = ${REPO_ROOT}/prebuilts/clang/host/${BUILD_OS}/clang-stable/bin/clang-format

Hook Developers

These are notes for people updating the pre-upload.py hook itself:

  • Don't worry about namespace collisions. The pre-upload.py script is loaded and exec-ed in its own context. The only entry-point that matters is main.
  • New hooks can be added in rh/hooks.py. Be sure to keep the list up-to-date with the documentation in this file.

TODO/Limitations

  • pylint should support per-repo pylintrc files.
  • Some checkers operate on the files as they exist in the filesystem. This is not easy to fix because the linters require not just the modified file but the entire repo in order to perform full checks. e.g. pylint needs to know what other modules exist locally to verify their API. We can support this case by doing a full checkout of the repo in a temp dir, but this can slow things down a lot. Will need to consider a PREUPLOAD.cfg knob.
  • We need to add pylint tool to the AOSP manifest and use that local copy instead of relying on the version that is in $PATH.
  • Should make file extension filters configurable. All hooks currently declare their own list of files like .cc and .py and .xml.
  • Add more checkers.
    • clang-check: Runs static analyzers against code.
    • License checking (like require AOSP header).
    • Whitespace checking (trailing/tab mixing/etc...).
    • Long line checking.
    • Commit message checks (correct format/BUG/TEST/SOB tags/etc...).
    • Markdown (gitiles) validator.
    • Spell checker.