| # Gemini Code-Assist Context for the LUCI Go Repository |
| |
| This document provides essential context for AI models interacting with the |
| `luci-go` codebase. |
| |
| ## 1. Project Overview |
| |
| The `luci-go` repository contains the Go source code for the Layered Universal |
| Continuous Integration (LUCI) system. |
| |
| ## 2. Key Technologies and Libraries |
| |
| - **Go:** The primary programming language. The specific version is defined in |
| the `go.mod` file. |
| - **Protocol Buffers (Protobuf):** Used for defining data structures and service |
| interfaces. |
| - **pRPC and gRPC:** The primary RPC framework for communication between |
| services. |
| - **Google Cloud Platform (GCP):** The primary deployment target, with extensive |
| use of services like Cloud Spanner, Cloud Tasks, Cloud Storage, BigQuery, |
| Cloud Pub/Sub, Cloud Scheduler. While Cloud Run is the preferred runtime for |
| new services, most still run on AppEngine or Google Kubernetes Engine. |
| - **Depot Tools:** The primary toolchain for managing the repository, including |
| code reviews and presubmit checks. |
| - **Golangci-lint:** Used for linting Go code. Configuration is in the |
| `.golangci.yaml` files located in each service's directory. |
| - **Staticcheck:** Used for static analysis of Go code. Configuration is in the |
| `staticcheck.conf` file. |
| |
| ## 3. Project Structure and Conventions |
| |
| ### Key Directories |
| |
| The repository is organized into a series of top-level directories, each |
| corresponding to a specific LUCI service or component. For example, the |
| `buildbucket` directory contains the source code for the Buildbucket service, |
| and the `auth` directory contains the authentication libraries. |
| |
| There are also folders which contain key shared code, notably: |
| |
| - `common/`: Contains common libraries and utilities shared across multiple |
| services. |
| - `server/`: Contains common LUCI server framework code. This framework takes |
| care of many common concerns that would not be handled by net/http, like: |
| SIGTERM handling/graceful draining, request deadlines, panic catching, load |
| shedding, monitoring, health-checking, authorisation of Cloud Scheduler/Tasks |
| pushes. |
| |
| The main UI for LUCI is served by MILO which can be found in `milo/` directory. |
| The frontend code lives in `milo/ui/`; see `milo/ui/GEMINI.md` for details |
| navigating this part of the repo. |
| |
| ### Code Conventions |
| |
| #### Formatting |
| Go code is formatted using `gofmt`. |
| |
| ### Linting |
| Code is linted using `golangci-lint` and `staticcheck`. |
| |
| ### Testing |
| Tests are written using the standard Go testing framework. Test |
| files are named with the `_test.go` suffix. Tests use the the |
| `go.chromium.org/luci/common/testing/ftt` library (see |
| `common/testing/ftt/doc.go`) for nesting test cases and the `assert` library |
| from `go.chromium.org/luci/common/testing/truth/assert` for assertions. |
| |
| See `resultdb/internal/services/recorder/create_root_invocation_test.go` for |
| an example how these libraries are typically used. |
| |
| ### API Surface |
| |
| When modifying the API surface, the following style guide applies: |
| |
| - **API Surface/Protos:** We follow the Google AIPs (API Improvement Proposals) |
| available at https://google.aip.dev/{NUMBER}. This includes: |
| |
| - AIP-121: Resource-oriented design |
| - AIP-122: Resource names |
| - AIP-131: Standard methods: Get |
| - AIP-132: Standard methods: List (an implementation of the AIP-132 order_by |
| clause is available at `common/data/aip132/`) |
| - AIP-133: Standard methods: Create |
| - AIP-134: Standard methods: Update |
| - AIP-135: Standard methods: Delete |
| - AIP-136: Custom methods |
| - AIP-140: Field names |
| - AIP-154: Resource freshness validation (etags for race-free updates) |
| - AIP-155: Request identification (for retry-safe Create/Update RPCs) |
| - AIP-157: Partial responses (for views/field masks) |
| - AIP-158: Pagination |
| - AIP-160: Filtering (an implementation of an AIP-160 filter-parser in go is |
| available at `common/data/aip160/`). |
| - AIP-210: Unicode |
| - AIP-211: Authorization checks |
| - AIP-231: Batch methods: Get |
| - AIP-233: Batch methods: Create |
| |
| - **Request validation:** ALL fields (except OUTPUT_ONLY fields - see AIP-203) |
| in the request should be validated by the server. Validation should comprise |
| at MINIMUM: |
| |
| - Verifying a value is set, if it must be set. |
| - If the field is of a type that allows variable-length data (e.g. a string, |
| bytes or a repeated type), the field is within an allowed length limit (if |
| no length limit exists, one should be created and documented on the proto). |
| In addition to the per-field length limit, a length limit may sometimes also |
| be applied at a higher-level, such as on the total size of a test result or |
| a set of properties. This can be enforced by setting a limit on the size as |
| measured by proto.Size(). |
| - If the value is a string, that it comes from a known alphabet. At minimum, |
| strings should be valid UTF-8 and be in Unicode Normal Form C. In most cases |
| they should also be limited to printable Unicode characters only. See |
| AIP-210 - Unicode. For identifiers, it is common to restrict to a narrow set |
| of inputs such as `[a-z][a-zA-Z0-9]*`, which takes care of most of the |
| checks above. |
| - It is generally preferable that fields are validated in the order that they |
| are specified in the proto, to make it easy to verify that all fields have |
| in fact been validated. |
| |
| - **Errors construction:** |
| |
| - Validation errors for an invalid request input should be annotated with the |
| path of the problematic input. For example, |
| `books: [0]: my_description: non-printable character at byte index 2` for a |
| validation issue with the field books[0].my_description. Typically this is |
| achieved by a hierarchy of validation methods which annotate one part of the |
| path each, e.g. ValidateRequest(request) calls ValidateBooks(request.books) |
| and annotates any errors with `errors.Fmt("books: %w", err)`, ValidateBooks |
| calls ValidateBook(books[0]) and annotates its error (if any) with |
| `errors.Fmt("[0]: %w")`, and so on. |
| |
| - For monitoring purposes, API usability, and to ensure clients can correctly |
| implement retry logic, it is important that all errors that are not internal |
| server errors (codes.Internal) are annotated with the correct status code. |
| This allows separating client from server errors, and retriable from |
| non-retriable (permanent) errors. Services can achieve this by ensuring |
| errors that are not internal server errors are annotated with the |
| appropriate code using the appstatus package: |
| |
| For example (using `go.chromium.org/luci/grpc/appstatus`): |
| `appstatus.Errorf(codes.NotFound, "book %q not found", bookName)` |
| |
| - **Tests:** All RPCs exposed by a service must have associated tests to ensure |
| required access controls are in place, invalid inputs are rejected, and both |
| normal and non-normal situations are correctly handled. |
| |
| - At minimum the following tests should exist: |
| |
| - For security, a test to validate the service enforces authorisation checks |
| (e.g. denies access to callers without the required access). These should |
| also ensure the returned authorisation errors are understandable (i.e. |
| describes the missing permissions or group membership) and have the |
| correct status code (codes.PermissionDenied). |
| - Tests to validate the service validates the request input, including each |
| field (that is not OUTPUT_ONLY). These should ensure returned errors have |
| the correct field path (see above), an understandable human-readable error |
| and are annotated with codes.InvalidArgument. |
| - Tests to validate each non-normal path (e.g. request valid but entity does |
| not exist). Each such test should again ensure the presence of the correct |
| status code. |
| - Tests to validate the happy path. |
| |
| - When validating a response that is expected to error, both the error message |
| and code should be checked. |
| |
| If the appstatus error has already been converted to a grpc code in the |
| service postlude using `appstatus.GRPCifyAndLog(ctx, err)`, the code can be |
| asserted using: |
| |
| `assert.That(t, err, grpccode.ShouldBe(codes.NotFound))` |
| |
| Where grpccode is `go.chromium.org/luci/grpc/grpcutil/testing/grpccode`. |
| |
| Alternatively for standalone methods that contribute to an RPC response, |
| codes can be asserted using an assertion like: |
| `assert.That(t, appstatus.Code(err), should.Equal(codes.NotFound))` |
| |
| - Test cases should try and keep cause and effect clear by factoring out |
| common/repeated test setup. The ideal test case changes only the properties |
| the test is trying to exercise and asserts the expected response. |
| |
| - A contemperanous (but not perfect) example of how the above can be achieved |
| is: `resultdb/internal/services/recorder/create_root_invocation_test.go`. |
| |
| ## 4. Build and Test |
| |
| ### Build |
| |
| The project is built using the standard Go toolchain. |
| |
| As builds of the entire repository can take some time, it is best to only |
| build the directory you need, e.g. if working on ResultDB: |
| |
| `go build go.chromium.org/luci/resultdb/...` |
| |
| ### Protocol buffers bindings |
| |
| After changing protos, regenerate proto buffer go bindings by running |
| `go generate` against the directory, for example: |
| |
| `go generate go.chromium.org/luci/resultdb/proto/v1` |
| |
| ### Test |
| |
| Tests are run using the `go test` command. As most tests are integration |
| tests, you must set the `INTEGRATION_TESTS=1` environment variable to run them. |
| |
| For example, to run all tests under resultdb/, run the following command: |
| `INTEGRATION_TESTS=1 go test go.chromium.org/luci/resultdb/...` |
| |
| Running all tests in `go.chromium.org/luci/...` takes a long time, so it is best |
| to run only the directory you need. |
| |
| ## 5. Contribution and Code Review |
| |
| Changes from the current git branch are uploaded for review using the |
| `git cl upload` command (`git cl` is provided by the `depot_tools` repository). |
| |
| Git commit messages follow a format like: |
| |
| ``` |
| [ResultDB] Add validation for BatchCreateTestResults RPC. |
| |
| A longer description appears here. |
| |
| BUG=b:{BUG_NUMBER} |
| TEST=INTEGRATION_TESTS=1 go test go.chromium.org/luci/resultdb/... |
| ``` |