Pipeline decorator expression context

Azure DevOps Services

Pipeline decorators have access to context about the pipeline in which they run. As a pipeline decorator author, you can use this context to make decisions about the decorator's behavior. The information available in context is different for pipelines and for release. Also, decorators run after task names are resolved to task globally unique identifiers (GUIDs). When your decorator wants to reference a task, it should use the GUID rather than the name or keyword.

Tip

Check out our newest documentation on extension development using the Azure DevOps Extension SDK.

Note

Pipeline decorators are used when building web extensions. These examples are not designed to work in YAML pipelines.

Resources

Pipeline resources are available on the resources object.

Repositories

Currently, there's only one key: repositories. repositories is a map from repo ID to information about the repository.

In a designer build, the primary repo alias is __designer_repo. In a YAML pipeline, the primary repo is called self. In a release pipeline, repositories aren't available. Release artifact variables are available.

For example, to print the name of the self repo in a YAML pipeline:

steps:
- script: echo ${{ resources.repositories['self'].name }}

Repositories contain these properties:

resources['repositories']['self'] =
{
	"alias": "self",
	"id": "<repo guid>",
	"type": "Git",
	"version": "<commit hash>",
	"name": "<repo name>",
	"project": "<project guid>",
	"defaultBranch": "<default ref of repo, like 'refs/heads/main'>",
	"ref": "<current pipeline ref, like 'refs/heads/topic'>",
	"versionInfo": {
		"author": "<author of tip commit>",
		"message": "<commit message of tip commit>"
	},
	"checkoutOptions": {}
}

Job

Job details are available on the job object.

The data looks similar to:

job = 
{
	"steps": [
		{
			"environment": null,
			"inputs": {
				"script": "echo hi"
			},
			"type": "Task",
			"task": {
				"id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9",
				"name": "CmdLine",
				"version": "2.146.1"
			},
			"condition": null,
			"continueOnError": false,
			"timeoutInMinutes": 0,
			"id": "5c09f0b5-9bc3-401f-8cfb-09c716403f48",
			"name": "CmdLine",
			"displayName": "CmdLine",
			"enabled": true
		}
	]
}

For instance, to conditionally add a task only if it doesn't already exist:

- ${{ if not(containsValue(job.steps.*.task.id, 'f3ab91e7-bed6-436a-b651-399a66fe6c2a')) }}:
  - script: echo conditionally inserted

Variables

Pipeline variables are also available.

For instance, if the pipeline had a variable called myVar, its value would be available to the decorator as variables['myVar'].

For example, to give a decorator an opt-out, we could look for a variable. Pipeline authors who wish to opt out of the decorator can set this variable, and the decorator isn't injected. If the variable isn't present, then the decorator is injected as usual.

my-decorator.yml

- ${{ if ne(variables['skipInjecting'], 'true') }}:
  - script: echo Injected the decorator

Then, in a pipeline in the organization, the author can request the decorator not to inject itself.

pipeline-with-opt-out.yml

variables:
  skipInjecting: true
steps:
- script: echo This is the only step. No decorator is added.

Task names and GUIDs

Decorators run after tasks already turned into GUIDs. Consider the following YAML:

steps:
- checkout: self
- bash: echo This is the Bash task
- task: PowerShell@2
  inputs:
    targetType: inline
    script: Write-Host This is the PowerShell task

Each of those steps maps to a task. Each task has a unique GUID. Task names and keywords map to task GUIDs before decorators run. If a decorator wants to check for the existence of another task, it must search by task GUID rather than by name or keyword.

For normal tasks (which you specify with the task keyword), you can look at the task's task.json to determine its GUID. For special keywords like checkout and bash in the previous example, you can use the following GUIDs:

Keyword GUID Task Name
checkout 6D15AF64-176C-496D-B583-FD2AE21D4DF4 n/a, see note
bash 6C731C3C-3C68-459A-A5C9-BDE6E6595B5B Bash
script D9BAFED4-0B18-4F58-968D-86655B4D2CE9 CmdLine
powershell E213FF0F-5D5C-4791-802D-52EA3E7BE1F1 PowerShell
pwsh E213FF0F-5D5C-4791-802D-52EA3E7BE1F1 PowerShell
publish ECDC45F6-832D-4AD9-B52B-EE49E94659BE PublishPipelineArtifact
download 30f35852-3f7e-4c0c-9a88-e127b4f97211 DownloadPipelineArtifact

After task names and keywords resolve, the previous YAML becomes:

steps:
- task: 6D15AF64-176C-496D-B583-FD2AE21D4DF4@1
  inputs:
    repository: self
- task: 6C731C3C-3C68-459A-A5C9-BDE6E6595B5B@3
  inputs:
    targetType: inline
    script: echo This is the Bash task
- task: E213FF0F-5D5C-4791-802D-52EA3E7BE1F1@2
  inputs:
    targetType: inline
    script: Write-Host This is the PowerShell task

Tip

You can find each of these GUIDs in the task.json for the corresponding in-box task. The only exception is checkout, which is a native capability of the agent. Its GUID is built into the Azure Pipelines service and agent.