github.com/googleapis/api-linter@v1.65.2/docs/contributing.md (about)

     1  ---
     2  ---
     3  
     4  # Contributing
     5  
     6  We are thrilled that you are interested in contributing to the API linter. This
     7  software is fully open-sourced, licensed under the Apache license, and we do
     8  accept contributions.
     9  
    10  The most common way to contribute is by writing a new linter rule.
    11  
    12  ## Setup
    13  
    14  The API linter is written in [Go][], so you will need the Go language
    15  installed. The version of Go you need is officially documented in our
    16  [`go.mod`][] file. Most of the time, we will most likely support the most
    17  recent two versions.
    18  
    19  Once you have Go installed, you can clone the repository the usual way, and
    20  then follow up by running the tests:
    21  
    22  ```bash
    23  $ git clone https://github.com/googleapis/api-linter
    24  $ cd api-linter
    25  $ go test ./...
    26  ```
    27  
    28  **Note:** Unless you have commit bit, you will likely need to make your own
    29  fork in GitHub in order to send us pull requests (in which case you clone your
    30  fork instead).
    31  
    32  ## Writing rules
    33  
    34  One of the best ways to contribute is by writing a new lint rule. Rules are
    35  located in the `rules/` directory. Rules are grouped into packages based on the
    36  [AIP][] that mandates the behavior.
    37  
    38  **Important:** **All** linter rules **must** have a corresponding AIP that
    39  mandates the behavior. There are no exceptions to this.
    40  
    41  Additionally, we observe the following guidelines around rules:
    42  
    43  - One rule per file.
    44    - The filename **must** correspond to the last segment of the rule name (with
    45      hyphens converted to underscores).
    46    - The rule must have a corresponding test (or tests). Each rule package
    47      **must** maintain 100% coverage of statements.
    48  - All rules **must** have a three part name. The first part is the "rule group"
    49    (such as `core`), the second part is the AIP number, zero-padded, and the
    50    third part is a unique name for the rule. The name only has to be unique
    51    within the scope of the AIP.
    52    - If word separation is needed, `kebab-case` **must** be used.
    53  - Every rule **must** have corresponding documentation. Documentation lives in
    54    the `docs/` directory, and powers this documentation site using GitHub Pages.
    55  
    56  We have a CI lint to remind you to do so.
    57  
    58  Writing a rule is straightforward: the linter employs a [visitor pattern][]
    59  that goes to every descriptor in the proto file and runs each lint rule against
    60  it. Most rules are run against only a certain _type_ of descriptor (for
    61  example, a "message rule" is run against all of the message descriptors, but
    62  not services or fields).
    63  
    64  Consider a bare-bones message rule:
    65  
    66  ```go
    67  var myRule = &lint.MessageRule{
    68    Name: lint.NewRuleName(0, "my-rule"),
    69    LintMessage: func(m *desc.MessageDescriptor) []lint.Problem {
    70      // This lint rule does nothing and always passes.
    71      return nil
    72    },
    73  }
    74  ```
    75  
    76  The actual lint function takes a [protoreflect][] descriptor. Beyond this, the
    77  function is free-form; the developer can check anything desired and return a
    78  slice of [`Problem`][] objects.
    79  
    80  ## Registering rules
    81  
    82  Once a rule is written, it must be _registered_ with the rule registry, which
    83  is defined in [`rules.go`][].
    84  
    85  There are two steps:
    86  
    87  1. In the registry in `rules.go`, ensure that the corresponding AIP package is
    88     imported, and that its `AddRules` function is called.
    89  2. In the AIP package, ensure that the new rule is included in the `AddRules`
    90     function.
    91  
    92  We have a CI lint to remind you to do so.
    93  
    94  ## Documentation
    95  
    96  Rule documentation is the primary purpose of this site, and it is important
    97  that all rules are documented. This documentation is written in Markdown, and
    98  goes in the `docs/rules/` directory. The naming convention is
    99  `{aip}-{rule_name}.md`:
   100  
   101  - `{aip}` is the _four-digit_ AIP number (zero-padded if needed!)
   102  - `{rule_name}` is the final component of the rule name in the rule itself.
   103  
   104  The actual Markdown document is fairly boilerplate, and copy and paste from
   105  another file is reasonable.
   106  
   107  The top of the file **must** include the proper "front matter" for GitHub
   108  Pages. The format is:
   109  
   110      ---
   111      rule:
   112        aip: 0
   113        name: [core, '0000', my-rule]
   114      ---
   115  
   116  - The AIP number **must** be included as an integer, and **must** be set as a
   117    string in the `name` array (quotes are required to keep the YAML parser from
   118    interpreting it as an integer and dropping leading zeroes).
   119  - The `name` field **must** be the name of the rule (as passed to
   120    `lint.NewRuleName` in array form).
   121  
   122  In addition to that, when providing protobuf examples, it is often useful to
   123  mark one as being explicitly "incorrect" (or "correct"). Do this by beginning
   124  the code block with a special comment:
   125  
   126  ```
   127  // Incorrect.
   128  message BadThing {
   129    // ...
   130  }
   131  ```
   132  
   133  If a proto block _begins with_ a comment that says only `Incorrect.` or
   134  `Correct.`, it picks up different styling when viewing in GitHub Pages.
   135  
   136  ## Releases
   137  
   138  Releases are handled automatically by [release-please][] by sending PRs
   139  after changes starting with `fix:` or `feat:` have been merged.
   140  [example release pr][].
   141  
   142  If a manual release is desired, simply open a pull request with an empty commit
   143  and the proper conventional commit message for the desired semver bump. For example:
   144  
   145  ```sh
   146  # releasing a minor version
   147  git commit --allow-empty -m 'feat: new minor release'
   148  
   149  # releasing a patch version
   150  git commit --allow-empty -m 'fix: new patch release'
   151  ```
   152  
   153  <!-- prettier-ignore-start -->
   154  [aip]: https://aip.dev/
   155  [go]: https://golang.org/
   156  [`go.mod`]: https://github.com/googleapis/api-linter/blob/main/go.mod
   157  [`problem`]: https://godoc.org/github.com/googleapis/api-linter/lint#Problem
   158  [protoreflect]: https://godoc.org/github.com/jhump/protoreflect
   159  [`rules.go`]: https://github.com/googleapis/api-linter/blob/main/rules/rules.go
   160  [visitor pattern]: https://en.wikipedia.org/wiki/Visitor_pattern
   161  [release-please]: https://github.com/googleapis/release-please
   162  [example release pr]: https://github.com/googleapis/api-linter/pull/1290
   163  <!-- prettier-ignore-end -->