This document provides essential context for AI models interacting with the luci-go codebase.
The luci-go repository contains the Go source code for the Layered Universal Continuous Integration (LUCI) system.
go.mod file..golangci.yaml files located in each service's directory.staticcheck.conf file.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.
Go code is formatted using gofmt.
Code is linted using golangci-lint and staticcheck.
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.
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:
common/data/aip132/)common/data/aip160/).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:
[a-z][a-zA-Z0-9]*, which takes care of most of the checks above.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:
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.
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/...
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
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.
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/...