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.
-