go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/starlark/stdlib/internal/luci/rules/notifier_template.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.notifier_template(...) rule."""
    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 _notifier_template(
    23          ctx,  # @unused
    24          *,
    25          name = None,
    26          body = None):
    27      r"""Defines a template to use for notifications from LUCI.
    28  
    29      Such template can be referenced by luci.notifier(...) and
    30      luci.tree_closer(...) rules.
    31  
    32      The main template body should have format `<subject>\n\n<body>` where
    33      subject is one line of [text/template] and body is an [html/template]. The
    34      body can either be specified inline right in the starlark script or loaded
    35      from an external file via io.read_file(...).
    36  
    37      [text/template]: https://godoc.org/text/template
    38      [html/template]: https://godoc.org/html/template
    39  
    40      #### Template input
    41  
    42      The input to both templates is a
    43      [TemplateInput](https://godoc.org/go.chromium.org/luci/luci_notify/api/config#TemplateInput)
    44      Go struct derived from
    45      [TemplateInput](https://cs.chromium.org/chromium/infra/go/src/go.chromium.org/luci/luci_notify/api/config/notify.proto?q=TemplateInput)
    46      proto message.
    47  
    48      #### Template functions
    49  
    50      The following functions are available to templates in addition to the
    51      [standard ones](https://godoc.org/text/template#hdr-Functions).
    52  
    53      * `time`: converts a
    54        [Timestamp](https://pkg.go.dev/google.golang.org/protobuf/types/known/timestamppb#Timestamp)
    55        to [time.Time](https://godoc.org/time).
    56        Example: `{{.Build.EndTime | time}}`
    57  
    58      #### Template example
    59  
    60      ```html
    61      A {{.Build.Builder.Builder}} build completed
    62  
    63      <a href="https://ci.chromium.org/b/{{.Build.Id}}">Build {{.Build.Number}}</a>
    64      has completed with status {{.Build.Status}}
    65      on `{{.Build.EndTime | time}}`
    66      ```
    67  
    68      #### Template sharing
    69  
    70      A template can "import" subtemplates defined in all other
    71      luci.notifier_template(...). When rendering, *all* templates defined in the
    72      project are merged into one. Example:
    73  
    74      ```python
    75      # The actual email template which uses subtemplates defined below. In the
    76      # real life it might be better to load such large template from an external
    77      # file using io.read_file.
    78      luci.notifier_template(
    79          name = 'default',
    80          body = '\n'.join([
    81              'A {{.Build.Builder.Builder}} completed',
    82              '',
    83              'A <a href="https://ci.chromium.org/b/{{.Build.Id}}">build</a> has completed.',
    84              '',
    85              'Steps: {{template "steps" .}}',
    86              '',
    87              '{{template "footer"}}',
    88          ]),
    89      )
    90  
    91      # This template renders only steps. It is "executed" by other templates.
    92      luci.notifier_template(
    93          name = 'steps',
    94          body = '{{range $step := .Build.Steps}}<li>{{$step.name}}</li>{{end}',
    95      )
    96  
    97      # This template defines subtemplates used by other templates.
    98      luci.notifier_template(
    99          name = 'common',
   100          body = '{{define "footer"}}Have a nice day!{{end}}',
   101      )
   102      ```
   103  
   104  
   105      #### Email preview
   106  
   107      [preview_email](http://godoc.org/go.chromium.org/luci/luci_notify/cmd/preview_email)
   108      command can render a template file to stdout.
   109  
   110      Example:
   111  
   112      ```shell
   113        bb get -json -A 8914184822697034512 | preview_email ./default.template
   114      ```
   115  
   116      This example uses bb tool, available in
   117      [depot_tools](https://chromium.googlesource.com/chromium/tools/depot_tools/).
   118  
   119      Command `preview_email` is available in
   120      [infra Go env](https://chromium.googlesource.com/infra/infra/+/main/go/README.md)
   121      and as a
   122      [CIPD package](https://chrome-infra-packages.appspot.com/p/infra/tools/preview_email).
   123  
   124      #### Error handling
   125  
   126      If a user-defined template fails to render, a built-in template is used to
   127      generate a very short email with a link to the build and details about the
   128      failure.
   129  
   130      Args:
   131        ctx: the implicit rule context, see lucicfg.rule(...).
   132        name: name of this template to reference it from luci.notifier(...) or
   133          luci.tree_closer(...) rules. Must match `^[a-z][a-z0-9\_]*$`. Required.
   134        body: string with the template body. Use io.read_file(...) to load it from
   135          an external file, if necessary. Required.
   136      """
   137      name = validate.string("name", name, regexp = r"^[a-z][a-z0-9\_]*$")
   138      key = keys.notifier_template(name)
   139      graph.add_node(key, idempotent = True, props = {
   140          "name": name,
   141          "body": validate.string("body", body),
   142      })
   143      graph.add_edge(keys.project(), key)
   144      return graph.keyset(key)
   145  
   146  notifier_template = lucicfg.rule(impl = _notifier_template)