From 749f6dee32155ea3c15e5c425331281a971ab03b Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Tue, 19 Jan 2021 10:30:16 +0100 Subject: [PATCH] Document dynamic changesetTemplates and steps.outputs (#17286) * Document dynamic changesetTemplates and outputs * Fix broken links * Rework templating docs to use tables and better examples * Update doc/campaigns/references/campaign_spec_yaml_reference.md Co-authored-by: Erik Seliger * Apply suggestions from code review Co-authored-by: Adam Harvey Co-authored-by: Erik Seliger Co-authored-by: Adam Harvey --- doc/_resources/assets/docsite.css | 12 ++ doc/campaigns/index.md | 2 +- .../references/campaign_spec_templating.md | 141 ++++++++++++++---- .../campaign_spec_yaml_reference.md | 108 +++++++++++++- doc/campaigns/references/index.md | 2 +- 5 files changed, 231 insertions(+), 34 deletions(-) diff --git a/doc/_resources/assets/docsite.css b/doc/_resources/assets/docsite.css index 7d88d0f4823e2..3f0d6ce27e811 100644 --- a/doc/_resources/assets/docsite.css +++ b/doc/_resources/assets/docsite.css @@ -28,6 +28,7 @@ The document content can set class names and IDs on elements (for example, Markd --warning-badge-color: #f59f00; --critical-badge-color: #f03e3e; --experimental-color: #b200f8; + --feature-color: #38757F; --table-row-bg-1: var(--body-bg); } @@ -432,6 +433,16 @@ body > #page > main > #content { background-color: var(--experimental-color); } +.badge-feature { + color: var(--feature-color); + background-color: var(--note-color); +} + +.badge-note { + color: #000000; + background-color: var(--note-color); +} + .badge-warning { color: #ffffff; background-color: var(--warning-badge-color); @@ -564,6 +575,7 @@ body > #page > main > #content { .markdown-body aside.experimental { border-color: var(--experimental-color); } + .markdown-body table { display: block; width: 100%; diff --git a/doc/campaigns/index.md b/doc/campaigns/index.md index aedaf4ed1c86a..6c0f877244143 100644 --- a/doc/campaigns/index.md +++ b/doc/campaigns/index.md @@ -99,5 +99,5 @@ Create a campaign by specifying a search query to get a list of repositories and - [Requirements](references/requirements.md) - [Campaign spec YAML reference](references/campaign_spec_yaml_reference.md) -- Experimental [Campaign spec templating](references/campaign_spec_templating.md) +- [Campaign spec templating](references/campaign_spec_templating.md) - [Troubleshooting](references/troubleshooting.md) diff --git a/doc/campaigns/references/campaign_spec_templating.md b/doc/campaigns/references/campaign_spec_templating.md index fc08ec28a586f..56934566fe31a 100644 --- a/doc/campaigns/references/campaign_spec_templating.md +++ b/doc/campaigns/references/campaign_spec_templating.md @@ -5,17 +5,15 @@ .markdown-body pre.chroma { font-size: 0.75em; } - - ## Overview -[Certain fields](#fields-with-template-support) in a [campaign spec YAML](campaign_spec_yaml_reference.md) can include template variables to create even more powerful and performant campaigns. +[Certain fields](#fields-with-template-support) in a [campaign spec YAML](campaign_spec_yaml_reference.md) support templating to create even more powerful and performant campaigns. + +Templating in a campaign spec uses the delimiters `${{` and `}}`. Inside the delimiters, [template variables](#template-variables) and [template helper functions](#template-helpers-functions) may be used to produce a text value. -Template variables in a campaign spec all have this form: `${{ }}`. They are evaluated before the execution of each entry in `steps` and allow accessing not only data from search results but also from previous steps. +### Example campaign spec -Here is an example excerpt of a campaign spec that uses the template variables: +Here is an excerpt of a campaign spec that uses templating: ```yaml on: @@ -23,12 +21,16 @@ on: steps: - run: comby -in-place 'fmt.Sprintf("%d", :[v])' 'strconv.Itoa(:[v])' ${{ join repository.search_result_paths " " }} + # ^ templating starts here container: comby/comby - run: goimports -w ${{ join previous_step.modified_files " " }} + # ^ templating starts here container: unibeautify/goimports ``` -In this case, `${{ repository.search_result_paths }}` will be replaced with the relative-to-root-dir file paths of each search resulted yielded by `repositoriesMatchingQuery`. By using the [template helper function](#template-helper-functions) `join`, an argument list of whitespace-separated values is constructed. Before the step is executed the final `run` value would look close to this: +Before executing the first `run` command, `repository.search_result_paths` will be replaced with the relative-to-root-dir file paths of each search result yielded by `repositoriesMatchingQuery`. By using the [template helper function](#template-helper-functions) `join`, an argument list of whitespace-separated values is constructed. + +The final `run` value, that will be executed, will look similar to this: ```yaml run: comby -in-place 'fmt.Sprintf("%d", :[v])' 'strconv.Itoa(:[v])' cmd/src/main.go internal/fmt/fmt.go @@ -36,51 +38,85 @@ run: comby -in-place 'fmt.Sprintf("%d", :[v])' 'strconv.Itoa(:[v])' cmd/src/main The result is that `comby` only search and replaces in those files, instead of having to search through the complete repository. -The `${{ previous_step.modified_files }}` in the second step will be replaced by the list of files that the previous `comby` step modified. The final `run` value will look like this, if `comby` modified both of these files: +Before the second step is executed `previous_step.modified_files` will be replaced with the list of files that the previous `comby` step modified. It will look similar to this: ```yaml run: goimports -w cmd/src/main.go internal/fmt/fmt.go ``` +See "[Examples](#examples)" for more examples of how to use and leverage templating in campaign specs. + ## Fields with template support -Template variables are supported in the following fields: +Templating is supported in the following fields: - [`steps.run`](campaign_spec_yaml_reference.md#steps-run) - [`steps.env`](campaign_spec_yaml_reference.md#steps-run) values - [`steps.files`](campaign_spec_yaml_reference.md#steps-run) values +- [`steps.outputs..value`](campaign_spec_yaml_reference.md#steps-outputs) + +Additionally, with Sourcegraph 3.24 and [Sourcegraph CLI](../../cli/index.md) 3.24 or later: + +- [`changesetTemplate.title`](campaign_spec_yaml_reference.md#changesettemplate-title) +- [`changesetTemplate.body`](campaign_spec_yaml_reference.md#changesettemplate-body) +- [`changesetTemplate.branch`](campaign_spec_yaml_reference.md#changesettemplate-branch) +- [`changesetTemplate.commit.message`](campaign_spec_yaml_reference.md#changesettemplate-commit-message) +- [`changesetTemplate.commit.author.name`](campaign_spec_yaml_reference.md#changesettemplate-commit-author) +- [`changesetTemplate.commit.author.email`](campaign_spec_yaml_reference.md#changesettemplate-commit-author) ## Template variables -The following template variables are available: +Template variables are the names that are defined and accessible when using templating syntax in a given context. -- `${{ repository.search_result_paths }}` +Depending on the context in which templating is used, different variables are available. - Unique list of file paths relative to the repository root directory in which the search results of the `repositoriesMatchingQuery`s have been found. -- `${{ repository.name }}` +For example: in the context of `steps` the template variable `previous_step` is available, but not in the context of `changesetTemplate`. - Full name of the repository in which the step is being executed. -- `${{ previous_step.modified_files }}` +### `steps` context - List of files that have been modified by the previous step in `steps`. Empty if no files have been modified. -- `${{ previous_step.added_files }}` +The following template variables are available in the fields under `steps`. - List of files that have been added by the previous step in `steps`. Empty if no files have been added. -- `${{ previous_step.deleted_files }}` +They are evaluated before the execution of each entry in `steps`, except for the `step.*` variables, which only contain values _after_ the step has executed. - List of files that have been deleted by the previous step in `steps`. Empty if no files have been deleted. -- `${{ previous_step.stdout }}` +| Template variable | Description | +| --- | --- | +| `repository.search_result_paths` | Unique list of file paths relative to the repository root directory in which the search results of the `repositoriesMatchingQuery`s have been found. | +| `repository.name` | Full name of the repository in which the step is being executed. | +| `previous_step.modified_files` | List of files that have been modified by the previous step in `steps`. Empty list if no files have been modified. | +| `previous_step.added_files` | List of files that have been added by the previous step in `steps`. Empty list if no files have been added. | +| `previous_step.deleted_files` | List of files that have been deleted by the previous step in `steps`. Empty list if no files have been deleted. | +| `previous_step.stdout` | The complete output of the previous step on standard output. | +| `previous_step.stderr` | The complete output of the previous step on standard error. | +| `step.modified_files` | Only in `steps.outputs`: List of files that have been modified by the just-executed step. Empty list if no files have been modified.
Requires Sourcegraph 3.24 and [Sourcegraph CLI](../../cli/index.md) 3.24 or later. | +| `step.added_files` | Only in `steps.outputs`: List of files that have been added by the just-executed step. Empty list if no files have been added.
Requires Sourcegraph 3.24 and [Sourcegraph CLI](../../cli/index.md) 3.24 or later. | +| `step.deleted_files` | Only in `steps.outputs`: List of files that have been deleted by the just-executed step. Empty list if no files have been deleted.
Requires Sourcegraph 3.24 and [Sourcegraph CLI](../../cli/index.md) 3.24 or later. | +| `step.stdout` | Only in `steps.outputs`: The complete output of the just-executed step on standard output.
Requires Sourcegraph 3.24 and [Sourcegraph CLI](../../cli/index.md) 3.24 or later. | +| `step.stderr` | Only in `steps.outputs`: The complete output of the just-executed step on standard error.
Requires Sourcegraph 3.24 and [Sourcegraph CLI](../../cli/index.md) 3.24 or later. | - The complete output of the previous step on standard output. -- `${{ previous_step.stderr }}` +### `changesetTemplate` context - The complete output of the previous step on standard error. +> NOTE: Templating in `changsetTemplate` is only supported in Sourcegraph 3.24 and [Sourcegraph CLI](../../cli/index.md) 3.24 or later. + +The following template variables are available in the fields under `changesetTemplate`. + +They are evaluated after the execution of all entries in `steps`. + +| Template variable | Description | +| --- | --- | +| `repository.search_result_paths` | Unique list of file paths relative to the repository root directory in which the search results of the `repositoriesMatchingQuery`s have been found. | +| `repository.name` | Full name of the repository in which the step is being executed. | +| `steps.modified_files` | List of files that have been modified by the `steps`. Empty list if no files have been modified. | +| `steps.added_files` | List of files that have been added by the `steps`. Empty list if no files have been added. | +| `steps.deleted_files` | List of files that have been deleted by the `steps`. Empty list if no files have been deleted. | +| `outputs.` | Value of an [`output`](campaign_spec_yaml_reference.md#steps-outputs) set by `steps`. If the [`outputs..format`](campaign_spec_yaml_reference.md#steps-outputs-format) is `yaml` or `json` and the `value` a data structure (i.e. array, object, ...), then subfields can be accessed too. See "[Examples](#examples)" below. | ## Template helper functions - `${{ join repository.search_result_paths "\n" }}` - `${{ split repository.name "/" }}` +The features of Go's [`text/template`](https://golang.org/pkg/text/template/) package are also available, including conditionals and loops, since it is the underlying templating engine. + ## Examples Pass the exact list of search result file paths to a command: @@ -135,7 +171,6 @@ steps: If you need to escape the `${{` and `}}` delimiters you can simply render them as string literals: - ```yaml steps: - run: cp /tmp/escaped.txt . @@ -143,3 +178,57 @@ steps: files: /tmp/escaped.txt: ${{ "${{" }} ${{ "}}" }} ``` + +Accessing the `outputs` set by `steps` in subsequent `steps` and the `changesetTemplate`: + +```yaml +steps: + - run: echo "Hello there!" + container: alpine:3 + outputs: + myFriendlyMessage: + value: "${{ step.stdout }}" + - run: echo "We have access to the output here: ${{ outputs.myFriendlyMessage }}" + container: alpine:3 + outputs: + stepTwoOutput: + otherMessage: "here too: ${{ outputs.myFriendlyMessage }}" + +changesetTemplate: + # [...] + body: | + The first step left us the following message: ${{ outputs.myFriendlyMessage }} + The second step left this one: ${{ outputs.otherMessage }} +``` + +Using the [`steps.outputs..format`](campaign_spec_yaml_reference.md#steps-outputs-name-format) field, it's possible to parse the value of an output as JSON or YAML and access it as a data structure instead of just text: + +```yaml +steps: + - run: cat .goreleaser.yml + container: alpine:3 + outputs: + goreleaserConfig: + value: "${{ step.stdout }}" + # The step's output is parsed as YAML, making it accessible as a YAML + # object in the other templating fields. + format: yaml + goreleaserConfigExists: + # We can use the power of Go's text/template engine to dynamically produce complex values + value: "exists: ${{ gt (len step.stderr) 0 }}" + format: yaml + +changesetTemplate: + # [...] + + # Since templating fields use Go's `text/template` and `goreleaserConfig` was + # parsed as YAML we can iterate over every field: + body: | + This repository has a `gorelaserConfig`: ${{ outputs.goreleaserConfigExists.exists }}. + + The `goreleaser.yml` defines the following `before.hooks`: + + ${{ range $index, $hook := outputs.goreleaserConfig.before.hooks }} + - `${{ $hook }}` + ${{ end }} +``` diff --git a/doc/campaigns/references/campaign_spec_yaml_reference.md b/doc/campaigns/references/campaign_spec_yaml_reference.md index 3c64f4e3e317c..4e132ee251a9c 100644 --- a/doc/campaigns/references/campaign_spec_yaml_reference.md +++ b/doc/campaigns/references/campaign_spec_yaml_reference.md @@ -157,8 +157,8 @@ steps: The shell command to run in the container. It can also be a multi-line shell script. The working directory is the root directory of the repository checkout. -