blob: bb2ed9028a710965229a3f46fcf7208f3b63c547 [file] [log] [blame]
// Copyright 2018 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package cv.config;
option go_package = "go.chromium.org/luci/cv/api/config/v2;cfgpb";
import "google/protobuf/duration.proto";
import "go.chromium.org/luci/common/proto/options.proto";
option (luci.file_metadata) = {
doc_url: "https://luci-config.appspot.com/schemas/projects:commit-queue.cfg";
};
// This message describes a CQ configuration.
//
// The config file commit-queue.cfg should be stored in the config directory of
// your project, alongside cr-buildbucket.cfg.
//
// Pro-tip: a command line tool exists to validate a locally generated .cfg
// file and verify that it matches arbitrary given CLs as expected.
// See https://chromium.googlesource.com/infra/luci/luci-go/+/refs/heads/main/cv/#luci-cv-command-line-utils
message Config {
// Deprecated. Ignored. Do not use.
// See also https://crbug.com/1208569 to implement an alternative.
string draining_start_time = 1 [deprecated = true];
// Optional.
//
// Originally, this was used to specify the URL of the CQ status app where
// users have access to their Run details. Currently, with the migration
// from CQ to LUCI CV, this field no longer serves as the same meaning,
// but is used to decide the visibility of the Run details via LUCI CV UI at
// https://luci-change-verifier.appspot.com/ui/recents
//
// This field accepts the following values:
// * "chromium-cq-status.appspot.com": all Runs in this Project are public.
// * "internal-cq-status.appspot.com": all Runs in this Project are visible
// to Googler only.
//
// Note that the Run details page contains the name of the builders launched
// to verify CL(s). Therefore, if this Project launches internal builders for
// public repos and the builder names are confidential, please don't
// use public host.
string cq_status_host = 2 [deprecated = true];
// Optional options for how CLs should be submitted.
SubmitOptions submit_options = 3;
// At least 1 ConfigGroup is required.
repeated ConfigGroup config_groups = 4;
// Deprecated. Ignored. Do not use.
Toggle project_scoped_account = 5 [deprecated = true];
}
// ConfigGroup allows one to share single verifiers config across a set of
// Gerrit repositories, which may be in different Gerrit installations.
message ConfigGroup {
// Next field number: 10.
reserved 3; // allow_cq_depend.
// The human- and machine-readable name (unique within this project) of this
// config group. This is used in messages posted to users and in monitoring
// data. Must match regex "^[a-zA-Z][a-zA-Z0-9_-]*$".
string name = 6;
// Enumerates repositories on a Gerrit instance for which CQ should work.
message Gerrit {
// Gerrit URL, e.g., https://chromium-review.googlesource.com.
// No trailing slashes allowed.
string url = 1;
// Gerrit projects of this Gerrit instance to work with.
//
// At least 1 required.
repeated Project projects = 2;
message Project {
// Repository name inside Gerrit host. Required.
//
// No leading or trailing slashes allowed, no '.git' at the end.
// 'a/' prefix is also not allowed (it's used on *.googlesource.com for
// forcing authentication).
//
// Examples on https://chromium-review.googlesource.com:
// catapult
// chromium/src
// chromium/tools/depot_tools
string name = 1;
// Limit CLs in this repo to only these refs. Required.
//
// If not specified, defaults to "refs/heads/master".
//
// NOTE: your Gerrit admin must configure Gerrit ACLs such that CQ has
// read access to these refs, otherwise your users will be waiting for CQ
// to act on their CLs forever.
//
// Regular expression is validated by https://github.com/google/re2 library.
//
// NOTE: Git globs aren't supported. Convert them to a regular expression,
// e.g., Git glob "refs/heads/*" should be "refs/heads/[^/]+".
// However, users typically expect "refs/heads/.+", since expectation is
// that every typical Git branch to be CQ-able, including
// "refs/heads/experimental/foobar".
repeated string ref_regexp = 2;
// Exclude matching refs in this repo. Optional.
//
// If a ref is matches at least one of the ref_regexp_exclude,
// then it is ignored by this config group regardless of ref_regexp.
//
// The syntax is the same as for ref_regexp.
repeated string ref_regexp_exclude = 3;
}
}
// At least 1 Gerrit instance with repositories to work with is required.
repeated Gerrit gerrit = 1;
// Optional. If specified, CQ will consider sets of dependent CLs to test and
// submit at the same time.
//
// Typical use-case is testing & submitting changes to multiple repos at the
// same time, in which case all such repos must be declared up-front in
// `Gerrit` part of this config_group.
//
// Not allowed to be used together with
// submit_options.allow_submit_with_open_deps=true.
CombineCLs combine_cls = 4;
// Defines how to verify a CL before submitting it. Required.
Verifiers verifiers = 2;
// If set, this ConfigGroup will be used if no other ConfigGroup matches.
//
// At most 1 config_group can be YES.
//
// Example use is to define specific config_group for refs/heads/main,
// and fallback one for refs/heads/* which will pick up all CLs on
// non-main branches.
//
// TODO(crbug/966115): provide a better solution.
Toggle fallback = 5;
// Optional. Additional run modes supported besides standard DRY_RUN and
// FULL_RUN.
//
// If specified, the order matters. CQ will create the Run with the first
// mode for which triggering conditions are fulfilled. If there is no such
// mode, CQ will fallback to standard DRY_RUN or FULL_RUN.
repeated Mode additional_modes = 7;
// Per-user limits.
//
// At the time of a Run creation, CV will iterate `user_limits` and apply the
// first applicable limit to the Run. If none of the limit(s) are applicable,
// CV will apply `user_limits_default` to the run.
repeated UserLimit user_limits = 8;
// Default per-user limit.
//
// If CV couldn't find an applicable limit from `user_limits`, this limit
// will be applied.
UserLimit user_limit_default = 9;
}
// SubmitOptions control how CQ submits CLs.
message SubmitOptions {
// Optional. Maximum number of successful CQ attempts completed by submitting
// corresponding Gerrit CL(s) before waiting burst_delay.
//
// This feature today applies to all attempts processed by this CQ, across all
// config_groups.
//
// Must be >0 to take effect. Requires burst_delay to be set, too.
int32 max_burst = 1;
// Optional. Delay between bursts of submissions of CQ attempts.
// See max_burst for more info.
//
// Must be >0 to take effect. Requires max_burst to be set, too.
google.protobuf.Duration burst_delay = 2;
}
// Mode defines a CQ Run mode and how it can be triggered.
message Mode {
// Required. Name of this mode.
//
// The mode name will be passed verbatim to all Tryjobs that this CQ Run
// launches. Must match regex "^[a-zA-Z][a-zA-Z0-9_-]{0,39}$".
//
// As of March 2021, only `QUICK_DRY_RUN` is allowed.
string name = 1;
// Required. The value of Commit-Queue label that MUST be set to when
// triggering a CQ Run in this mode.
//
// Must be either 1 or 2.
int32 cq_label_value = 2;
// Required. The Gerrit label that MUST also be set in order to trigger a
// CQ Run in this mode.
//
// Both this label and the Commit-Queue label MUST be set at the same time.
string triggering_label = 3;
// Required. The value of the `triggering_label` that MUST be set to when
// triggering a CQ Run in this mode.
int32 triggering_value = 4;
}
// CombineCLs defines how CQ works with >1 CL per attempt.
//
// Dependencies between CLs are either implicit via Git child->parent
// relationship (e.g. stacked CLs in Gerrit) or explicit via "CQ-Depend:"
// footer in CL description (next to Change-Id:). "CQ-Depend" may span
// across repositories and even Gerrit hosts. For example, a CL on
// https://pdfium-review.googlesource.com may declare dependency on
// https://chromium-review.googlesource.com/1111111 by adding this footer:
//
// CQ-Depend: chromium:1111111
//
// The "chromium" part means that 1111111 is on the
// chromium-review.googlesource.com host. It can be omitted if dependency
// is on the same host as the CL depending on it.
//
// CQ-Depend alone or with Git dependencies may form cycles, which is useful
// to require CQ to test & submit all CLs in a cycle at the same time, never
// alone.
//
// A user must vote on CQ label on **each CL** individually. Since it can't be
// instantaneous, `stabilization_delay` controls how long CQ waits for all
// CQ+1/2 votes before computing maximal expanded set of CLs and starting the
// attempt.
//
// For any CL with CQ+1/2 vote, each of its dependency must have the same CQ
// vote and be configured for CQ **in the same config group**, else CQ would
// abort the attempt with appropriate error message.
//
// Each tryjob CQ triggers via Buildbucket will be associated with each CL of
// the attempt via `gerrit_changes` parameter of Buildbucket. These changes are
// then available to a build as it is being executed. If ran via recipes,
// the `ordered_gerrit_changes` property of
// https://chromium.googlesource.com/infra/luci/recipes-py/+/HEAD/README.recipes.md#class-cqapi_recipeapi
// can be used to CLs in the right order.
//
// WARNING: When submitting CLs, CQ can not do so atomically (all submitted or
// none submitted) because Gerrit doesn't support this even for the same repo.
message CombineCLs {
// Roughly, how long CQ waits for CQ to be triggered on each of the related
// CLs.
//
// Must be greater than 10s.
// 30s is recommended.
//
// Technically precise definition is time to wait since the latest CL among
// related ones receives CQ+1/2 vote before starting actual attempt.
//
// For example, during this delay, a CQ vote may be added on another CL
// which depends on previously CQ-ed CL in this not-yet-started attempt. Then,
// CQ would extend the attempt with additional CL and reset the waiting
// counter.
//
// Additional implication is that a standalone CL w/o any other relations to
// other CLs will need to wait this much time before CQ would start processing
// it (i.e., before it triggers first tryjob).
google.protobuf.Duration stabilization_delay = 1;
}
// Verifiers are various types of checks that a Commit Queue performs on a CL.
// All verifiers must pass in order for a CL to be submitted. Configuration file
// describes types of verifiers that should be applied to each CL and their
// parameters.
message Verifiers {
// Required. GerritCQAbility ensures that a user who triggered
// this CQ attempt actually has rights to do so based on 3 factors:
// * membership of the user in committers & dryrunners group,
// * the state of CL/patchset on which CQ is triggered,
// * relationship of the user to the CL.
GerritCQAbility gerrit_cq_ability = 1;
// This verifier is used to check tree status before committing a CL. If the
// tree is closed, then the verifier will wait until it is reopened.
TreeStatus tree_status = 2;
// This verifier triggers a set of builds through Buildbucket.
//
// CQ automatically retries failed tryjobs and only allows CL to land if each
// builder has succeeded in the latest retry.
// If a given tryjob result is too old (>1 day) it is ignored.
//
// Typically, builds from Buildbucket are executed on LUCI stack, however, CQ
// is agnostic to how and where builds are executed.
Tryjob tryjob = 3;
// CQLinter is for internal CQ use only. DO NOT USE IN YOUR cq.cfg.
CQLinter cqlinter = 4;
// Fake is for internal CQ use only. DO NOT USE IN YOUR cq.cfg.
Fake fake = 5;
// Next tag: 6.
message GerritCQAbility {
// Required. List of chrome-infra-auth groups, whose members are authorized
// to trigger full CQ runs.
//
// Typically, such groups are named "project-<name>-committers".
repeated string committer_list = 1;
// Optional, but strongly recommended. List of chrome-infra-auth groups,
// whose members are authorized to trigger CQ dry run on Gerrit CLs they own
// (not to be confused with OWNER files) even if CL hasn't been approved.
//
// Typically, such groups are named "project-<name>-tryjob-access".
repeated string dry_run_access_list = 2;
// Optional, but required to use auto-triggered runs on new patchset upload,
// it defines a list of chrome-infra-auth groups whose members are
// authorized to trigger a new patchset runs. That is, CV will check the
// owner of the CL when a new patchset is uploaded against these.
repeated string new_patchset_run_access_list = 5;
// Optional. allow_submit_with_open_deps controls how CQ full run behaves
// when current Gerrit CL has open dependencies (not yet submitted CLs on
// which *this* CL depends).
//
// If set to false (default), CQ will abort full run attempt immediately if
// open dependencies are detected.
//
// If set to true, then CQ will not abort full run and upon passing all
// other verifiers, CQ will attempt to submit the CL regardless of open
// dependencies and whether CQ verified those open dependencies.
// In turn, if Gerrit project config allows this, Gerrit will execute submit
// of all dependent CLs first and then this CL.
bool allow_submit_with_open_deps = 3;
// See `allow_owner_if_submittable` doc below.
enum CQAction {
UNSET = 0;
DRY_RUN = 1;
// COMMIT implies ability to trigger dry run as well.
COMMIT = 2;
}
// Optional. Allow CL owner to trigger CQ dry or full run on their own CL,
// even if not a member of `committer_list` or `dry_run_access_list`.
// Defaults to no such allowance.
//
// WARNING: using this option is not recommended if you have sticky
// Code-Review label because this allows a malicious developer to upload
// an good looking patchset at first, get code review approval,
// and then upload a bad patchset and CQ it right away.
//
// CL owner is Gerrit user owning a CL, i.e., its first patchset uploader.
// not to be confused with OWNERS files.
CQAction allow_owner_if_submittable = 4;
}
message TreeStatus {
// Required. URL of the project tree status app.
string url = 1;
}
// Tryjob configures builders which CQ may trigger and/or use to verify CL(s).
message Tryjob {
// Next field number: 4
// Builders on which tryjobs should be triggered.
//
// CQ won't allow adding any builder via `CQ-Include-Trybots:` in CL
// description except those in this list (including the equivalent
// builders).
repeated Builder builders = 1;
// Optional, defaulting to no retries whatsoever.
RetryConfig retry_config = 2;
// DEPRECATED. Use per-builder `cancel_stale` instead.
Toggle cancel_stale_tryjobs = 3 [deprecated = true];
message Builder {
// Next field number: 17
reserved 3; // triggered_by
// The buildbucket host name.
//
// Optional. If empty, default to "cr-buildbucket.appspot.com".
string host = 16;
// Required. Name of the builder as <project>/<bucket>/<builder>
//
// Examples:
// "chromium/try/linux-tester"
// "other-project/try/shared-try-builder"
string name = 1;
// If true, this builder will only be used if specified via
// `CQ-Include-Trybots:` on CL description.
//
// If false, the builder may still be included via `CQ-Include-Trybots:`
// on CL description.
//
// This is useful if you want individual CLs to opt-in to this builder,
// e.g.:
// builder {name: "win-release"} # required for all.
// builder {name: "win-debug" includable_only: true} # opt in only.
//
// Not combinable with:
// * location_filters
// * experiment_percentage
bool includable_only = 9;
// Determines how visible the results of a build for this builder are in
// Gerrit for this tryjob.
//
// This doesn't affect the buildbucket plugin (green/red chips).
CommentLevel result_visibility = 10;
// Optional. If true, a fresh build will be required for each CQ attempt.
//
// Default is false, meaning CQ may re-use a successful build
// triggered before current CQ attempt started.
//
// This option is typically used for builders which run depot_tools'
// PRESUBMIT scripts, which are supposed to be quick to run and provide
// additional OWNERS, lint, etc checks which are useful to run against
// the latest revision of the CL's target branch.
bool disable_reuse = 2;
// Optional. If YES (default), running or not-yet-started builds of this
// builder previously triggered by CQ will be cancelled as soon as
// a substantially different patchset is uploaded to a CL.
//
// The following paragraph is only applicable to LUCI Change Verifier (
// The successor of legacy CQ). As of 09/2020, the implementation is
// still WIP. TODO(crbug/1127991): Remove after migration is done.
//
// CV will snapshot the value of this option when a Run is created
// and use it throughout the lifecycle of this Run. Therefore,
// modification on this option will only take effect on newly-created
// Runs after config change is propagated to CV.
Toggle cancel_stale = 11;
// Optional. When this field is present, it marks given builder as
// experimental. It is only triggered on a given percentage of the CLs
// and the outcome does not affect the decision of whether a CL can land
// or not. This is typically used to test new builders and estimate their
// capacity requirements. May be combined with location_filters.
float experiment_percentage = 4;
// Optionally specified alternative builder for CQ to choose instead.
// If provided, CQ will choose only one of the equivalent builders as
// required based purely on given CL and CL's owner and **regardless** of
// the possibly already completed tryjobs.
//
// Note: The equivalent builders can be included using
// `CQ-Include-Trybots:` footer. In this case, CQ will always try to
// trigger the equivalent builders regardless of the equivalent
// percentage.
EquivalentBuilder equivalent_to = 5;
// Optional. Require this builder only if a file in the CL is included
// by location_filters.
//
// location_filters is an ordered list of LocationFilter messages, where
// each message includes regular expressions for the matching Gerrit
// host, Gerrit project and file path, as well as a boolean field to
// specify whether the filter is an exclude filter or not.
//
// Files "in the CL" means all files touched, i.e. added, modified or
// deleted.
//
// The Gerrit host, Gerrit project and file path are matched against the
// respective patterns in the LocationFilter. The last LocationFilter
// that match all patterns (host, project, and path) determines whether
// the the file is considered included, i.e. if the last matching
// LocationFilter has exclude set to false, then it's included; else
// included.
//
// If none of the LocationFilters match, then the file is considered
// included if the first rule is an exclude rule; else the file is
// excluded.
//
// If host, project or path are empty, then they will match anything
// (".*") by default. The comparison is a full match; the pattern is
// implicitly anchored with "^" and "$", so there is no need add them.
// The syntax for regular expressions is Google's re2 syntax for regexp,
// documented here: https://github.com/google/re2/wiki/Syntax.
//
// If location_filters is non-empty, and no file in a CL is included by
// location_filters, then this builder will not be triggered. If
// location_filters is empty or not specified, then the builder will be
// triggered by default, subject to other conditions, e.g.
// mode_allowlist.
//
// This option currently can not be combined with the
// `GerritCQAbility.allow_submit_with_open_deps` option.
// If you need to combine, please talk to LUCI CV owners.
// (https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/cv/OWNERS)
//
// Examples:
//
// location_filters: {path_regexp: "excluded/.+", exclude: true}
// location_filters: {path_regexp: "excluded/exception", exclude: false}
//
// Result: files outside of the "excluded" directory will be
// included, and so will "excluded/exception" will, but other files
// in the "excluded" directory will not match.
//
// location_filters: {path_regexp: ".*", exclude: false} // include all by default
// location_filters: {host_regexp: "example.com",
// project_regexp: "repo",
// exclude: true}
// location_filters: {host_regexp: "example.com",
// project_regexp: "repo",
// path_regexp: "all/one.txt",
// exclude: false}
//
// Result: files in the specified host and repo will not be included,
// except all/one.txt which will be included; files outside of the
// given host/repo will be included.
message LocationFilter {
// Pattern to match Gerrit host. Does not include scheme, which is
// always assumed to be "https://".
string gerrit_host_regexp = 1;
// Pattern to match Gerrit project, e.g. "infra/luci/luci-go".
string gerrit_project_regexp = 2;
// Pattern to match file path. This is a relative path from repo root
// (no starting slash).
string path_regexp = 3;
// If exclude is true, then if this filter matches a file, the file is
// considered not included. (If all files are not included, then the
// builder is not triggered.)
bool exclude = 4;
}
repeated LocationFilter location_filters = 15;
// If set, this builder will only be triggered if the CL owner (who first
// uploaded the CL) is a member of at least one of these groups.
repeated string owner_whitelist_group = 8;
reserved 12, 13; // mode_regexp[_exclude]
// If set, require this builder only if the Run mode matches
// one of the modes in this list. If unset, it implies that only FULL_RUN
// and DRY_RUN are allowed, and temporarily QUICK_DRY_RUN. Optional.
repeated string mode_allowlist = 14;
}
message EquivalentBuilder {
// Required. Name of this builder.
// Format is the same in the same format as Builder.name.
string name = 1;
// Percentage expressing probability of CQ triggering this builder instead
// of the builder to which this builder is equivalent to.
//
// A choice itself is made deterministically based on CL alone, hereby
// all CQ attempts on all patchsets of a given CL will trigger the same
// builder, assuming CQ config doesn't change in the mean time.
//
// Note that if `owner_whitelist_group` is also specified, the choice over
// which of the two builders to trigger will be made only for CLs owned by
// whitelisted group.
//
// If not specified, defaults to 0, meaning this builder is never
// triggered by CQ, but an existing build can be re-used by CQ.
//
// To illustrate, suppose percentage=10. Then,
// Without owner_whitelist_group,
// ~10% of all CQ attempts will trigger this builder.
// With owner_whitelist_group set and, suppose, 1/5 of CQ attempts are
// ran on CLs owned by this group, then only ~(1/10)*(1/5) or
// ~2% of all CQ attempts will trigger this builder.
float percentage = 2;
// If specified, limits the builder to CL owners in this group.
string owner_whitelist_group = 3;
}
// Builder which can be included via `CQ-Include-Trybots:` in CL
// description.
message IncludableBuilder {
// Required. Name of this builder.
// Format is the same in the same format as Builder.name.
string name = 1;
}
// Collection of parameters for deciding whether to retry a single build.
// If parameter is not specified, its value defaults to 0 (per proto3).
// Thus, omitting all parameters means no retries of any kind.
//
// CQ's retry logic is as follows:
// All builds triggered during the lifetime of a CQ attempt for a tryjob are
// weighted according to the failure type, as described below. The resulting
// weights are then added together. Call this number W. If W > single_quota,
// then no more builds are scheduled for that tryjob.
//
// W for every tryjob is then summed up. If that result is strictly greater
// than global_quota, then the CQ attempt fails, and no more builds are
// scheduled for the attempt.
message RetryConfig {
// Retry quota for a single tryjob.
int32 single_quota = 1;
// Retry quota for all tryjobs in a CL.
int32 global_quota = 2;
// The weight assigned to each tryjob failure. A failure is as a
// buildbucket build whose result == 'FAILURE'.
int32 failure_weight = 3;
// The weight assigned to each transient failure. A transient failure is a
// buildbucket build which has result == 'FAILURE' and 'failure_reason' in
// ('BUILDBUCKET_FAILURE', 'INFRA_FAILURE').
int32 transient_failure_weight = 4;
// The weight assigned to tryjob timeouts. A tryjob timeout is as a
// buildbucket build with result == 'CANCELED' and cancelation_reason ==
// 'TIMEOUT'.
int32 timeout_weight = 5;
}
}
// CQLinter is for internal use in CQ.
//
// Deprecated. Do not use.
// TODO(crbug/1127991): Remove after migration off CQDaemon is completed.
message CQLinter{}
// Fake is for internal use in CQ.
//
// Deprecated. Do not use.
// TODO(crbug/1127991): Remove after migration off CQDaemon is completed.
message Fake {
string name = 1;
string eventual_state = 2;
int32 delay = 3;
}
}
// Determines how visible the results of a build for this builder are in Gerrit
// comments.
//
// This doesn't affect the buildbucket plugin (green/red chips).
enum CommentLevel {
// Currently default to full visibility.
COMMENT_LEVEL_UNSET = 0;
// The CQ reports the summary markdown and a link to the buildbucket build id
// in Milo with the builder name in the URL in a Gerrit comment.
COMMENT_LEVEL_FULL = 1;
// The CQ reports a generic "Build failed: https://ci.chromium.org/b/1234"
// with no summary markdown.
COMMENT_LEVEL_RESTRICTED = 2;
}
// A boolean with an "unset" default value.
enum Toggle {
UNSET = 0;
YES = 1;
NO = 2;
}
// UserLimit specifies Run and Tryjob limits per user.
message UserLimit {
// Name of the limit. Must be unique across all the policies
// with the LUCI project.
//
// Required. Must match regex '^[0-9A-Za-z][0-9A-Za-z\.\-@_+]{0,511}$'
string name = 1;
// Principals to apply the limits to.
//
// Each entry can be either an identity string "user:<email>"
// or a LUCI group reference "group:<name>"
repeated string principals = 2;
message Limit {
// Required. value must be > 0, unless unlimited is set to True.
oneof limit {
int64 value = 1;
bool unlimited = 2;
}
}
message Run {
// Maximum number of active runs that the user can have at any moment.
//
// Required. Active Run is a Run that has started but not ended yet.
Limit max_active = 1;
}
message Tryjob {
// Maximum number of active tryjobs that the user can have at any
// moment.
//
// Active tryjob is a tryjob that CV has successfully launched,
// and has not ended yet from CV's point of view. Also, optional
// tryjobs, which are experimental tryjobs that were not explicitly
// requested via the git footer, are not counted as active tryjobs.
//
// Note that counting active tryjobs and launching tryjobs are not
// atomic operations. So, # of active tryjobs may not match # of
// the successfully launched tryjobs for a short period, but will
// match eventually.
//
// If not specified, an unlimited number of active Tryjobs are allowed.
Limit max_active = 1;
}
// Run limits. Required.
Run run = 5;
// Tryjob limits. Required
Tryjob tryjob = 6;
}