Overview
gcl and git-cl will check for and run presubmit scripts before you upload and/or commit your changes. Presubmit scripts are a way of applying automatic verification to your changes before they are reviewed and/or before they are committed.
Presubmit scripts can perform automated checks on the files in your change and the description of your change, and either fail your attempt to upload or commit, show a warning that you must acknowledge before uploading/committing, or simply show an informational message as part of the output of gcl.
Examples of things presubmit scripts may be useful for include:
- Ensuring your change is linked to a bug via a BUG= line.
- Ensuring you have run cpplint.py or automatically running cpplint for you.
- Enforcing coding conventions.
- Preventing you from breaking dependency rules e.g. by including a header that code in your directory is not supposed to depend upon.
- Warning you if you haven't built your client and/or run unit tests within the last X hours.
Design
When you run gcl upload or gcl commit, gcl will look for all files named PRESUBMIT.py in folders enclosing the files in your change, up to the repository root.
For each such file, it will load the file into the Python interpreter and then call either the CheckChangeOnUpload or CheckChangeOnCommit function depending on whether you are calling [gcl upload] or [gcl commit].
The same applies to git-cl upload, git-cl dcommit and git-cl push.
The functions must match these method signatures; you do not need to define both functions if you're only interested in one type of event:
def CheckChangeOnUpload(input_api, output_api):
pass
def CheckChangeOnCommit(input_api, output_api):
pass
The input_api parameter is an object through which you can get information about the change. Using the output_api you can create result objects.
Both CheckChangeOnXXX functions must return a list or tuple of result objects, or an empty list or tuple if there is nothing to report. The types of result objects you may use are output_api.PresubmitError (a critical error), output_api.PresubmitPromptWarning (a warning the user must acknowledge before the command will continue) and output_api.PresubmitNotifyResult (a message that should be shown). Each takes a message parameter, and optional "items" and "long_text" parameters.
The input_api parameter is an object of type presubmit.InputApi. This object can be used to transform from local to repository paths and vice versa, and to get information on the files in the change that are contained in the same directory as your PRESUBMIT.py file or subdirectories thereof.
The input_api.change object represents the change itself. Using this object you can retrieve the description of the change, any key-value pairs in the description (e.g. BUG=123), and details on all of the files in the change (not just the ones contained by the directory your PRESUBMIT.py file resides in).
An input_api.canned_checks object contains a set of ready-made checks that you can easily add to your presubmit script.
Files in the API are represented by an AffectedFile object through which you can query the LocalPath(), ServerPath(), and the Action() being performed ('A', 'M' or 'D').
Caveats
Please note that you should not use any functions or attributes on the API objects that begin with an underscore (_) as these are private functions that may change, whereas all public functions will be retained through future versions of the API.
Also note that presubmit scripts are a best-effort kind of thing; they do not prevent users from submitting without running the scripts (in fact there is a --no_presubmit flag to gcl that skips presubmit checks) and, since they use the local copy of the PRESUBMIT.py files, users must sync their clients before the latest presubmit checks will run in their clients.
Details and Example
The most detailed documentation for the presubmit API is in its implementation code.
The canned checks are good examples of what you can do with the presubmit API.
An example simple file might be as follows:
def CheckChange(input_api, output_api, committing):
results = []
results += input_api.canned_checks.CheckDoNotSubmit(input_api, output_api)
results += input_api.canned_checks.CheckChangeHasNoTabs(input_api, output_api)
results += input_api.canned_checks.CheckLongLines(input_api, output_api)
# Require a BUG= line and a HOW_TO_TEST= line.
if not input_api.change.BUG or not input_api.change.HOW_TO_TEST:
results += [output_api.PresubmitError(
'Must provide a BUG= line and a HOW_TO_TEST line.')]
return results
def CheckChangeOnUpload(input_api, output_api):
return CheckChange(input_api, output_api, False)
def CheckChangeOnCommit(input_api, output_api):
return CheckChange(input_api, output_api, True)
You can look at the
chromium's PRESUBMIT.py or
chromium-os PRESUBMIT.py as a more complete examples.