Contributing
We are thrilled that you are interested in contributing to the API linter. This software is fully open-sourced, licensed under the Apache license, and we do accept contributions.
The most common way to contribute is by writing a new linter rule.
Setup
The API linter is written in Go, so you will need the Go language
installed. The version of Go you need is officially documented in our
go.mod
file. Most of the time, we will most likely support the most
recent two versions.
Once you have Go installed, you can clone the repository the usual way, and then follow up by running the tests:
$ git clone https://github.com/googleapis/api-linter
$ cd api-linter
$ go test ./...
Note: Unless you have commit bit, you will likely need to make your own fork in GitHub in order to send us pull requests (in which case you clone your fork instead).
Writing rules
One of the best ways to contribute is by writing a new lint rule. Rules are
located in the rules/
directory. Rules are grouped into packages based on the
AIP that mandates the behavior.
Important: All linter rules must have a corresponding AIP that mandates the behavior. There are no exceptions to this.
Additionally, we observe the following guidelines around rules:
- One rule per file.
- The filename must correspond to the last segment of the rule name (with hyphens converted to underscores).
- The rule must have a corresponding test (or tests). Each rule package must maintain 100% coverage of statements.
- All rules must have a three part name. The first part is the “rule group”
(such as
core
), the second part is the AIP number, zero-padded, and the third part is a unique name for the rule. The name only has to be unique within the scope of the AIP.- If word separation is needed,
kebab-case
must be used.
- If word separation is needed,
- Every rule must have corresponding documentation. Documentation lives in
the
docs/
directory, and powers this documentation site using GitHub Pages.
We have a CI lint to remind you to do so.
Writing a rule is straightforward: the linter employs a visitor pattern that goes to every descriptor in the proto file and runs each lint rule against it. Most rules are run against only a certain type of descriptor (for example, a “message rule” is run against all of the message descriptors, but not services or fields).
Consider a bare-bones message rule:
var myRule = &lint.MessageRule{
Name: lint.NewRuleName(0, "my-rule"),
LintMessage: func(m *desc.MessageDescriptor) []lint.Problem {
// This lint rule does nothing and always passes.
return nil
},
}
The actual lint function takes a protoreflect descriptor. Beyond this, the
function is free-form; the developer can check anything desired and return a
slice of Problem
objects.
Registering rules
Once a rule is written, it must be registered with the rule registry, which
is defined in rules.go
.
There are two steps:
- In the registry in
rules.go
, ensure that the corresponding AIP package is imported, and that itsAddRules
function is called. - In the AIP package, ensure that the new rule is included in the
AddRules
function.
We have a CI lint to remind you to do so.
Documentation
Rule documentation is the primary purpose of this site, and it is important
that all rules are documented. This documentation is written in Markdown, and
goes in the docs/rules/
directory. The naming convention is
{aip}-{rule_name}.md
:
{aip}
is the four-digit AIP number (zero-padded if needed!){rule_name}
is the final component of the rule name in the rule itself.
The actual Markdown document is fairly boilerplate, and copy and paste from another file is reasonable.
The top of the file must include the proper “front matter” for GitHub Pages. The format is:
---
rule:
aip: 0
name: [core, '0000', my-rule]
---
- The AIP number must be included as an integer, and must be set as a
string in the
name
array (quotes are required to keep the YAML parser from interpreting it as an integer and dropping leading zeroes). - The
name
field must be the name of the rule (as passed tolint.NewRuleName
in array form).
In addition to that, when providing protobuf examples, it is often useful to mark one as being explicitly “incorrect” (or “correct”). Do this by beginning the code block with a special comment:
// Incorrect.
message BadThing {
// ...
}
If a proto block begins with a comment that says only Incorrect.
or
Correct.
, it picks up different styling when viewing in GitHub Pages.
Releases
Releases are handled automatically by release-please by sending PRs
after changes starting with fix:
or feat:
have been merged.
example release pr.
If a manual release is desired, simply open a pull request with an empty commit and the proper conventional commit message for the desired semver bump. For example:
# releasing a minor version
git commit --allow-empty -m 'feat: new minor release'
# releasing a patch version
git commit --allow-empty -m 'fix: new patch release'