go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/starlark/stdlib/internal/luci/rules/executable.star (about) 1 # Copyright 2019 The LUCI Authors. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 """Defines luci.executable(...) and luci.recipe(...) rules.""" 16 17 load("@stdlib//internal/graph.star", "graph") 18 load("@stdlib//internal/lucicfg.star", "lucicfg") 19 load("@stdlib//internal/validate.star", "validate") 20 load("@stdlib//internal/luci/common.star", "keys") 21 22 def _executable_props( 23 ctx, 24 cipd_package = None, 25 cipd_version = None, 26 recipe = None, 27 recipes_py3 = None, 28 cmd = None, 29 wrapper = None): 30 """Defines an executable's properties. See luci.executable(...). 31 32 Args: 33 ctx: the implicit rule context, see lucicfg.rule(...). 34 cipd_package: a cipd package name with the executable. Supports the 35 module-scoped default. 36 cipd_version: a version of the executable package to fetch, default 37 is `refs/heads/main`. Supports the module-scoped default. 38 recipe: name of a recipe. Required if the executable is a recipe bundle. 39 recipes_py3: True iff this is a recipe and should run under python3. 40 cmd: a list of strings to use as the executable command. If None 41 (or empty), Buildbucket will fill this in on the server side to either 42 `['luciexe']` or `['recipes']`, depending on its global configuration. 43 wrapper: an optional list of strings which are a command and its arguments 44 to wrap around `cmd`. If set, the builder will run `<wrapper> -- <cmd>`. 45 The 0th argument of the wrapper may be an absolute path. It is up to the 46 owner of the builder to ensure that the wrapper executable is distributed 47 to whatever machines this executable may run on. 48 """ 49 return { 50 "cipd_package": validate.string( 51 "cipd_package", 52 cipd_package, 53 default = ctx.defaults.cipd_package.get(), 54 required = ctx.defaults.cipd_package.get() == None, 55 ), 56 "cipd_version": validate.string( 57 "cipd_version", 58 cipd_version, 59 default = ctx.defaults.cipd_version.get() or "refs/heads/main", 60 required = False, 61 ), 62 "recipe": validate.string( 63 "recipe", 64 recipe, 65 required = False, 66 ), 67 "recipes_py3": validate.bool( 68 "recipes_py3", 69 recipes_py3, 70 required = False, 71 ), 72 "cmd": validate.str_list( 73 "cmd", 74 cmd, 75 required = False, 76 ), 77 "wrapper": validate.str_list( 78 "wrapper", 79 wrapper, 80 required = False, 81 ), 82 } 83 84 def _executable( 85 ctx, 86 name = None, 87 cipd_package = None, 88 cipd_version = None, 89 cmd = None, 90 wrapper = None): 91 """Defines an executable. 92 93 Builders refer to such executables in their `executable` field, see 94 luci.builder(...). Multiple builders can execute the same executable 95 (perhaps passing different properties to it). 96 97 Executables must be available as cipd packages. 98 99 The cipd version to fetch is usually a lower-cased git ref (like 100 `refs/heads/main`), or it can be a cipd tag (like `git_revision:abc...`). 101 102 A luci.executable(...) with some particular name can be redeclared many 103 times as long as all fields in all declaration are identical. This is 104 helpful when luci.executable(...) is used inside a helper function that at 105 once declares a builder and an executable needed for this builder. 106 107 Args: 108 ctx: the implicit rule context, see lucicfg.rule(...). 109 name: name of this executable entity, to refer to it from builders. 110 Required. 111 cipd_package: a cipd package name with the executable. Supports the 112 module-scoped default. 113 cipd_version: a version of the executable package to fetch, default 114 is `refs/heads/main`. Supports the module-scoped default. 115 cmd: a list of strings which are the command line to use for this 116 executable. If omitted, either `('recipes',)` or `('luciexe',)` will be 117 used by Buildbucket, according to its global configuration. The special 118 value of `('recipes',)` indicates that this executable should be run 119 under the legacy kitchen runtime. All other values will be executed 120 under the go.chromium.org/luci/luciexe protocol. 121 wrapper: an optional list of strings which are a command and its arguments 122 to wrap around `cmd`. If set, the builder will run `<wrapper> -- <cmd>`. 123 The 0th argument of the wrapper may be an absolute path. It is up to the 124 owner of the builder to ensure that the wrapper executable is distributed 125 to whatever machines this executable may run on. 126 """ 127 name = validate.string("name", name) 128 key = keys.executable(name) 129 props = _executable_props( 130 ctx, 131 cipd_package = cipd_package, 132 cipd_version = cipd_version, 133 cmd = cmd, 134 wrapper = wrapper, 135 ) 136 graph.add_node(key, idempotent = True, props = props) 137 return graph.keyset(key) 138 139 def _recipe( 140 ctx, 141 *, 142 name = None, 143 cipd_package = None, 144 cipd_version = None, 145 recipe = None, 146 use_bbagent = None, # transitional for crbug.com/1015181 147 use_python3 = None, 148 wrapper = None): 149 """Defines an executable that runs a particular recipe. 150 151 Recipes are python-based DSL for defining what a builder should do, see 152 [recipes-py](https://chromium.googlesource.com/infra/luci/recipes-py/). 153 154 Builders refer to such executable recipes in their `executable` field, see 155 luci.builder(...). Multiple builders can execute the same recipe (perhaps 156 passing different properties to it). 157 158 Recipes are located inside cipd packages called "recipe bundles". Typically 159 the cipd package name with the recipe bundle will look like: 160 161 infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build 162 163 Recipes bundled from internal repositories are typically under 164 165 infra_internal/recipe_bundles/... 166 167 But if you're building your own recipe bundles, they could be located 168 elsewhere. 169 170 The cipd version to fetch is usually a lower-cased git ref (like 171 `refs/heads/main`), or it can be a cipd tag (like `git_revision:abc...`). 172 173 A luci.recipe(...) with some particular name can be redeclared many times as 174 long as all fields in all declaration are identical. This is helpful when 175 luci.recipe(...) is used inside a helper function that at once declares 176 a builder and a recipe needed for this builder. 177 178 Args: 179 ctx: the implicit rule context, see lucicfg.rule(...). 180 name: name of this recipe entity, to refer to it from builders. If 181 `recipe` is None, also specifies the recipe name within the bundle. 182 Required. 183 cipd_package: a cipd package name with the recipe bundle. Supports the 184 module-scoped default. 185 cipd_version: a version of the recipe bundle package to fetch, default 186 is `refs/heads/main`. Supports the module-scoped default. 187 recipe: name of a recipe inside the recipe bundle if it differs from 188 `name`. Useful if recipe names clash between different recipe bundles. 189 When this happens, `name` can be used as a non-ambiguous alias, and 190 `recipe` can provide the actual recipe name. Defaults to `name`. 191 use_bbagent: a boolean to override Buildbucket's global configuration. If 192 True, then builders with this recipe will always use bbagent. If False, 193 then builders with this recipe will temporarily stop using bbagent (note 194 that all builders are expected to use bbagent by ~2020Q3). Defaults to 195 unspecified, which will cause Buildbucket to pick according to it's own 196 global configuration. See [this bug](crbug.com/1015181) for the global 197 bbagent rollout. Supports the module-scoped default. 198 use_python3: a boolean to use python3 to run the recipes. If set, also 199 implies use_bbagent=True. This is equivalent to setting the 200 'luci.recipes.use_python3' experiment on the builder to 100%. 201 Supports the module-scoped default. 202 wrapper: an optional list of strings which are a command and its arguments 203 to wrap around recipe execution. If set, the builder will run `<wrapper> -- <luciexe>`. 204 The 0th argument of the wrapper may be an absolute path. It is up to the 205 owner of the builder to ensure that the wrapper executable is distributed 206 to whatever machines this executable may run on. 207 """ 208 name = validate.string("name", name) 209 use_bbagent = validate.bool( 210 "use_bbagent", 211 use_bbagent, 212 required = False, 213 default = ctx.defaults.use_bbagent.get(), 214 ) 215 use_python3 = validate.bool( 216 "use_python3", 217 use_python3, 218 required = False, 219 default = ctx.defaults.use_python3.get(), 220 ) 221 key = keys.executable(name) 222 223 if use_python3: 224 use_bbagent = True 225 226 cmd = None 227 if use_bbagent != None: 228 if use_bbagent: 229 cmd = ["luciexe"] 230 else: 231 cmd = ["recipes"] 232 233 props = _executable_props( 234 ctx, 235 cipd_package = cipd_package, 236 cipd_version = cipd_version, 237 recipe = recipe or name, 238 recipes_py3 = use_python3, 239 cmd = cmd, 240 wrapper = wrapper, 241 ) 242 graph.add_node(key, idempotent = True, props = props) 243 return graph.keyset(key) 244 245 executable = lucicfg.rule( 246 impl = _executable, 247 defaults = validate.vars_with_validators({ 248 "cipd_package": validate.string, 249 "cipd_version": validate.string, 250 }), 251 ) 252 253 recipe = lucicfg.rule( 254 impl = _recipe, 255 defaults = validate.vars_with_validators({ 256 "cipd_package": validate.string, 257 "cipd_version": validate.string, 258 "use_bbagent": validate.bool, 259 "use_python3": validate.bool, 260 }), 261 )