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  )