| # TurboCI |
| |
| TurboCI is an in-development private-access API for Google's own continuous |
| integration of Android, Chrome, ChromeOS, and other large software projects. |
| |
| This repo contains the protobuf definitions for the service API and objects used |
| by the service for integration with Googler-owned Open Source CI related |
| infrastructure in Open Source software such as Chromium. |
| |
| ## Working with the repo |
| |
| ### Getting the repo |
| |
| Just `git clone` it. All dependencies are pulled automatically when running |
| `build.py <anything>`. |
| |
| ### Making Changes |
| |
| This repo uses `buf breaking` to check that your CL doesn't introduce |
| [backwards incompatible changes]. This is verified by the Commit Queue for this |
| project which will be activated when CLs in this repo are uploaded to Gerrit and |
| marked for submission. |
| |
| You can format the protos, run all checks and build the go stubs by running: |
| |
| ``` |
| $ ./build.py all |
| ``` |
| |
| ### Skipping breaking build checks |
| |
| You don't usually want to do this, but it can be OK as long as you know that the |
| affected fields are not being used by anything in production. Please see |
| [backwards incompatible changes] for guidance. |
| |
| To skip breaking change checks, upload your CL and then add the following footer |
| to the CL description in Gerrit: |
| |
| ``` |
| Breaking-Proto-Change-Ok: Some reason. |
| ``` |
| |
| Other checks cannot be skipped. |
| |
| ## Repo layout |
| |
| This repo primarially consists of the `turboci` subfolder with the following |
| proto packages: |
| |
| * `turboci.data.<feature>.v1` - Data messages to be used as the Anys in Check |
| Options and Check Results to support some logical feature. Features should |
| be selected to form logical groups where someone using any message in the |
| namespace is likely to want to use ALL messages in that namespace. It's |
| allowed to use additional hierarchy here to group many related stages |
| together. |
| |
| * `turboci.stages.<function>.v1` - Stage args for some logical function. It's |
| allowed to use additional hierarchy here to group many related stages |
| together. |
| |
| * `turboci.graph.ids.v1` - Identifiers for objects in a TurboCI Graph. This is |
| separate from `turboci.graph.orchestrator.v1` to allow these IDs to be |
| included in other messages without pulling in all the rest of the protos. |
| |
| * `turboci.graph.orchestrator.v1` - The TurboCI Orchestrator Service. |
| Everything interacting with the TurboCI Orchestrator will deal with the |
| messages here, including Checks, Stages and their related messages, but not |
| the contents of the Any fields in this API (e.g. Check Options, Check |
| Results, Stage Args, Edit Reasons, etc.). |
| |
| * `turboci.graph.executor.v1` - This is the RPC interface implemented by |
| TurboCI Executors (external services which the TurboCI Orchestrator connects |
| to in order to actually execute Stages). Very few things need to worry about |
| this API. |
| |
| Additionally, go stubs are generated in the `go` folder. The `google` folder |
| contains just an unmodified copy of `google/api/field_behavior.proto` for |
| standalone compilation/linting. |
| |
| ### GoPackage option |
| |
| Every ...v1 package must have a `go_package` option as follows: |
| |
| Given `package turboci.a.b.c.v1`, a given proto file would have a `go_package` |
| value of: |
| |
| ``` |
| go.chromium.org/turboci/proto/go/a/b/c/v1;cpb |
| ``` |
| |
| Unless this proto file contains a `service` definition, in which case it would |
| have a `go_package` value of: |
| |
| ``` |
| go.chromium.org/turboci/proto/go/a/b/c/v1/grpcpb;cgrpcpb |
| ``` |
| |
| Additionally: |
| |
| * proto files with service definitions must not have any other top-level |
| definitions. |
| * packages are limited to a maximum of one service definition. |
| |
| ### Why this repo layout? |
| |
| There are a couple competing requirements we are trying to achieve: |
| |
| * Programs should always be able to only link the protos they need. |
| * Programs should be able to manipulate all data types without having to link |
| in the RPC stubs. |
| * We want to be able to introduce some features as experiments, or possibly |
| product-specific features, and later elevate them to be generally-available |
| features, without having to do a migration (e.g. to rename them or move them |
| to a new package). |
| |
| There are also some requirements we considered adhering to, but are explicitly |
| avoiding: |
| |
| #### (Google Internal) Every .proto file is its own build target |
| |
| This is considered best practice inside of Google, but is incompatible with the |
| way that Go (and other languages) actually interact with protos outside of |
| Google. If we followed this adage to the letter, it would mean that using N |
| messages from this repo would require N different Go package imports, and we |
| would also have to fight protoc-gen-go to make every .proto file in its own Go |
| package (my experiments show that even when you get all the imports and |
| `go_package` options right, the generated code will still result in a cycle due |
| to 'underscore' imports of parent packages). |
| |
| Our compromise here is to make it so that each logical group of definitions is |
| inside the same proto package, but those packages are always treated as an |
| atomic unit both inside and outside of Google. |
| |
| #### (Google Internal) Every .proto has exactly one top-level definition |
| |
| (and also avoid nested definitions at all costs) |
| |
| This is meant to be used in conjunction with the previous 'every .proto file is |
| its own build target' requirement. The theory here is that it helps alleviate |
| the build dependency graph when importing some small proto. If every definition |
| is a separate proto file, then you can target exactly what you need. If you have |
| larger protos, then you run the risk of "including a small message" resulting in |
| a dependency on a massive collection of protos. |
| |
| Our compromise here is to break things up by feature (and also make a special |
| case for graph IDs which tend to show up in many lightweight contexts which |
| don't need any of the graph node definitions). This still gives some granular |
| control over which options/results/stages a given program needs to worry about, |
| without decomposing all the way down to the single-definition level. |
| |
| #### aip.dev/191 |
| |
| Confusingly, aip.dev/191 recommends putting service and request/response |
| messages in the same file. We specifically don't want to do this to allow the |
| maximum amount of message manipulation without requiring linking to the rpc |
| apparatus. |
| |
| Our compromise here requires just the service definition on its own, and the |
| request/response messages in with the other regular protos. |
| |
| ## Data Annotations (TBD) |
| |
| Messages in the `turboci.{data,stages}.v1` namespaces have the following |
| annotations: |
| |
| * TBD: Option for maturity |
| * TBD: Option for audience |
| * TBD: Option for intended usage (check option (per kind), check result (per |
| kind), stage args, embedded) |
| |
| [depot_tools]: https://chromium.googlesource.com/chromium/tools/depot_tools.git |
| [backwards incompatible changes]: https://protobuf.dev/programming-guides/editions/#updating |
| |
| ## Bugs and Feature Requests |
| |
| Please file a go/turbo-ci-bug for any bugs or feature requests related to the |
| protobuf definitions in this repository. |
| |
| ## Turbo CI Data Model |
| |
| The Turbo CI data model utilizes a graph of nodes to describe the work done when |
| a workflow is run. |
| |
| ### Core Concepts |
| |
| * **Workflow:** The high-level definition of the actions required to build, |
| test, and validate software changes. A workflow functions as a "template" |
| from which individual workplans can be instantiated. Workflows are specific |
| to both a product (Chrome/Chromium, Android, etc.) and a role (presubmit, |
| postsubmit, etc.), such as `Android presubmit`. Today Turbo CI does not |
| store workflows so there is no Workflow proto definition, but that will be |
| added later in 2026. |
| * **Workplan:** A graph holding all nodes related to a single run of a |
| workflow. See |
| [graph/orchestrator/v1/workplan.proto](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/orchestrator/v1/workplan.proto). |
| * **Checks:** Nodes representing work the workplan intends to accomplish, and |
| the results of that work. Checks contain options (definition of the work to |
| do) and results. Checks form the public API of the workplan, allowing the |
| stages to be implementation details that typically don't need examination. |
| See |
| [graph/orchestrator/v1/check.proto](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/orchestrator/v1/check.proto). |
| * **Stages:** Executable nodes managed by the Orchestrator, which will have |
| their stage executor run them once all dependencies are satisfied. Whereas |
| checks represent what to do, stages are a directive to actually do the work. |
| * **Stage Attempts:** An attempt to run a stage, where a single stage may have |
| more than one attempt due to retrying failures. See |
| [graph/orchestrator/v1/stage.proto](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/orchestrator/v1/stage.proto). |
| * **Stage Executors:** Services registered to execute specific stage types, |
| based on the `type_url` of the stage's `args` field. Stage executors must |
| run in Google's internal infrastructure, but it's possible to use GCP-hosted |
| services via a Google-hosted proxy that invokes the GCP-hosted service. Such |
| a proxy exists for LUCI Buildbucket, and can be added for other GCP-hosted |
| services. See |
| [graph/executor/v1/turbo_ci_stage_executor_service.proto](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/executor/v1/turbo_ci_stage_executor_service.proto). |
| Googlers looking to maintain a stage executor can find more information at |
| [go/turboci-stage-maintainers](http://go/turboci-stage-maintainers). |
| |
| ### Values (`Any`) & Decoupled Architecture |
| |
| To cleanly separate the workplan's "skeleton" that the orchestrator needs to |
| operate on from workplan-specific data, Turbo CI encapsulates workplan-specific |
| payloads (Check Options, Check Results, Stage Args, and others) within protobuf |
| `Any` "Value" messages. Checks and Stages contain both non-Value content (graph |
| structure and other information the orchestrator directly uses) and Value |
| content (which is meaningful to the workplan, its stages, and its readers, but |
| can be opaque to the orchestrator). |
| |
| Values are represented via a set of related protos, where different protos are |
| used when sending data to the orchestrator vs. when retrieving data from it: |
| |
| * **ValueWrite**: A message used to hold a Value when sending data to the |
| orchestrator. See |
| [graph/orchestrator/v1/value_write.proto](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/orchestrator/v1/value_write.proto). |
| * **ValueRef**: Wraps/references a ValueData to embed it into a check, stage, |
| or other message in the data model. Used when retrieving data from the |
| orchestrator. ValueRefs retrieved from the orchestrator always store the |
| Value's digest to allow the ValueData be looked up from the `value_data` |
| map; the `inline` field is only used within the orchestrator. See |
| [graph/orchestrator/v1/value_ref.proto](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/orchestrator/v1/value_ref.proto). |
| * **ValueData**: A message used to hold a Value (or possibly a JSONPB-encoded |
| version of it) when retrieving data from the orchestrator. Returned in a |
| `value_data` map, keyed on the digest. Clients should typically use helper |
| functions such as `value.Lookup()` |
| ([Go](https://pkg.go.dev/go.chromium.org/luci/turboci/value#Lookup), |
| [Python](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/py/turboci/utils/value/decode.py#84)) |
| to access the Value content rather than interacting directly with ValueData |
| messages. See |
| [graph/orchestrator/v1/value_data.proto](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/orchestrator/v1/value_data.proto). |
| |
| Encapsulating payloads in Value messages not only decouples the core |
| Orchestrator messages from workplan-specific messages, but also allows clients |
| to request only the specific Values they care about (avoiding unnecessary proto |
| dependencies) and allows the orchestrator to cleanly omit Value data that a |
| client doesn't have permission to access if they can see some but not all of the |
| content in a given check. |
| |
| See [Repo layout](#repo-layout) for additional details. |
| |
| ### Turbo CI Lifecycles |
| |
| The Turbo CI Orchestrator manages explicit, auditable state machines for Checks, |
| Stages, and Stage Attempts. In most cases the state transition graphs are |
| acyclic so there is no way to move from a later state to an earlier one, though |
| Stage Attempts can cycle between PENDING and THROTTLED repeatedly if necessary. |
| |
| These are the three lifecycles in the Turbo CI data model: |
| |
| * [CheckState](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/orchestrator/v1/check_state.proto) |
| * [StageState](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/orchestrator/v1/stage_state.proto) |
| * [StageAttemptState](https://chromium.googlesource.com/infra/turboci/proto/+/refs/heads/main/turboci/graph/orchestrator/v1/stage_attempt_state.proto) |
| |
| Lifecycle states for workplans will be added in the near future. |