Pipedash
Providers

Azure DevOps

Monitoring of Azure Pipelines (YAML and classic) in Pipedash on dev.azure.com or Azure DevOps Server, with branch and variable triggers, run cancel and live job logs.

The Azure DevOps plugin (azure-devops) monitors the build pipelines of the selected Azure DevOps projects. It shows the latest build of each pipeline, loads run history, queues builds, cancels running builds and shows job logs inside Pipedash. It lists YAML and classic pipelines.

Requirements

The plugin authenticates with a personal access token (PAT). It has no sign-in flow and no other login method. Pipedash sends the token over HTTP Basic authentication with an empty user name.

The token needs these scopes:

ScopeRequiredWhat it unlocks
Build (Read & execute)YesPipelines, builds, timelines, logs, queueing a build and canceling a build
Project and Team (Read)YesListing the projects of the organization

Pipedash confirms the token with a request that lists one project. It does not test the scopes one by one.

A 401 answer is an authentication error and a 403 answer is a permission error. Azure DevOps also answers an invalid or expired token with a sign-in page, a redirect or a 203 status. Pipedash reports these as an authentication error that ends with "Check that the personal access token is valid and has not expired".

Configuration

Add provider dialog with Azure DevOps selected, showing the Organization URL and Personal Access Token fields. Desktop and mobile.
Azure DevOps asks for the organization URL and a personal access token.
KeyTypeRequiredDefaultDescription
server_urlTextYesNoneURL of the organization, for example https://dev.azure.com/my-org, or of an Azure DevOps Server collection, for example https://tfs.example.com/DefaultCollection. The form labels it "Organization URL". It must match ^https?://[^\s?#]+$, and the form says "Must be a valid HTTP or HTTPS URL" otherwise.
tokenPasswordYesNoneAzure DevOps personal access token with the scopes Build (Read & execute) and Project and Team (Read). The form labels it "Personal Access Token".
status_page_urlTextNoNoneStatuspage address for the instance. See the status page section. The form labels it "Status page URL (optional)".

The config table of the TOML file also takes selected_items, a comma-separated list of project names, for example Web App,Platform. Spaces around a comma are ignored, and spaces inside a name stay. The picker writes these values.

Base URL

There is no base_url key. server_url is the base URL and has no default. A missing or blank value fails with "Missing Azure DevOps server URL". A value that does not parse as a URL fails with "Invalid Azure DevOps server URL" and the parse reason. A URL with another scheme than http or https, or without a host, fails with "Azure DevOps server URL must start with http:// or https://".

Pipedash trims the value and removes a query and a fragment. It ignores a trailing slash. It builds every request from this URL:

RequestAddress
Organization level<server_url>/_apis/...
Project level<server_url>/<project>/_apis/...

Every request carries api-version=7.0. The plugin has no setting for another version. It has no option to skip certificate checks either.

The organization name in the picker and in the repository column is the last non-empty path segment of server_url, or the host name when the URL has no path. https://dev.azure.com/my-org gives my-org. https://tfs.example.com/tfs/DefaultCollection/ gives DefaultCollection.

Pipeline discovery

Discovery asks for an organization first. The list holds one entry, the organization name from server_url. The picker then lists the projects of the organization, sorted by name without regard to case. Each entry shows the project description when it has one. Search matches the project name and the description and ignores case.

The selection holds projects, not single pipelines. selected_items is the list of project names. Pipedash shows every build definition of each selected project, one pipeline per definition, named like the definition. The repository column shows <organization>/<project>.

For each pipeline, Pipedash shows the latest build of the definition. The status, the branch and the last run time come from that build. The last run time is the start time of the build, or its queue time when the build has not started. A definition without a build shows pending.

Pipedash fetches the selected projects at the same time. A failure on one project fails the whole refresh. An empty selected_items shows the error No projects configured.

Azure DevOps splits long lists into pages. Pipedash reads pages of 100 items and follows the continuation token of each answer for up to 100 pages. A list with more items than that fails with the error Azure DevOps list <path> still has more items after 100 pages.

Saving a provider in the app or through the REST API drops selected projects that have no build definition. A project whose check fails stays. If every selected project has no definition, the save fails with "None of the selected repositories has a CI file or a pipeline run yet". Providers that come from the TOML file or an import keep their selected_items as written.

Run history

Pipedash lists the builds of one definition, newest queue time first. It reads pages of up to 100 builds. It stops at the number of runs the history view needs, or after 100 pages.

Run history of the payments-api Azure DevOps pipeline with the Run and Build columns, statuses and branches. Desktop and mobile.
An Azure DevOps run shows the build id as Run and the build number as Build.

The run number is the build ID. The build number, such as 20261028.1, shows in the "Build" column. Each run has these values:

  • The branch is the source branch without the refs/heads/ prefix. Other refs, such as refs/pull/17/merge, stay whole.
  • The commit is the source version of the build. Azure DevOps builds carry no commit message in Pipedash.
  • The actor is the display name of the user the build was requested for.
  • The start time is the start time of the build, or its queue time when the build has not started. The duration needs both a start and a finish time.
  • The run link opens the build results page in Azure DevOps.
  • The inputs hold the branch as source-branch and the variables the build was queued with.

Run details load one build by its ID. When the build belongs to another definition, Pipedash returns the error Run #<id> does not belong to <pipeline ID>.

Trigger parameters

Pipedash reads the definition and offers one field for the branch and one field for each pipeline variable that a run may override.

NameTypeRequiredDefaultDescription
source-branchStringNoDefault branch of the repository of the definitionBranch to build. The form labels it "Branch". An empty value uses the default branch of the definition.
One per variableStringNoValue of the variableA pipeline variable that allows override at queue time and is not secret. The form labels it with the variable name. Variables appear in alphabetical order after the branch.

Pipedash queues the build with POST _apis/build/builds and a body that holds the definition ID. It adds two more entries when they apply:

  • sourceBranch is the branch with refs/heads/ in front. A value that already starts with refs/ goes as given.
  • parameters is a JSON object, sent as a string, with the variables to override.

Pipedash drops an input that is empty after trimming. It sends a boolean or a number as text. It skips other value types. Pipedash sends only the inputs whose name matches an overridable, non-secret variable of the definition, ignoring case. It leaves out all other inputs, so the REST API cannot set a variable the definition does not allow to override.

The trigger answer reads Queued build <id> on <branch>. When no branch was given, it reads Queued build <id> on the default branch.

Cancel

Pipedash cancels a run with PATCH _apis/build/builds/{id} and the body {"status": "cancelling"}. This needs the Build (Read & execute) scope. Before it sends the request, Pipedash loads the build. When the build belongs to another definition, it returns the error Run #<id> does not belong to <pipeline ID> and sends no cancel request.

Azure DevOps reports a build in cancellation as cancelling. Pipedash shows it as running until the build completes. The button is unavailable for a pending run.

Rerun

The plugin has no rerun call. It has no "Re-run failed jobs" action either. A rerun queues a new build through the same request as a trigger, with the inputs of the old run: the branch and the variables the build was queued with.

In the run history, Rerun opens the trigger form with these values filled in. In the run details, Rerun asks for confirmation and then queues the build. Pipedash sends only the variables that the definition still allows to override.

Run logs

The Logs tab of the run details shows the timeline of the build as a tree:

  • A stage is a top-level node. A build without stages lists its jobs at the top.
  • A job sits below its stage. It carries the log. Pipedash shows its state and result with the same mapping as the status mapping section below.
  • A step sits below its job. It has no log of its own. The log of the job holds the output of all its steps.

Pipedash sorts the nodes by the order that Azure DevOps reports and puts nodes without an order last. It leaves out timeline records of other types.

Pipedash reads the timeline with GET _apis/build/builds/{id}/timeline. For a job it finds the log ID and the attempt in the timeline. It then downloads the log as plain text with GET _apis/build/builds/{id}/logs/{logId} and streams it to disk. A download stops after 15 minutes.

A job shows its log as follows:

Job stateLogs tab
Not started"Logs appear when this job finishes" with its steps. The log opens by itself once Azure DevOps lists a log for the job.
Running, no log yetThe same message.
Running, with a logThe log so far. It updates while the job runs.
Finished, with a logThe whole log.
Finished, without a logNo log. A job that was skipped or canceled while it waited is an example.

While a run has not finished and the Logs tab is open, Pipedash refreshes the tree and the run status every 5 seconds. It reads the log of an open running job again about every 2 seconds and appends only the new lines. For this it keeps the number of lines it has seen, a hash of the last line and the attempt. It asks Azure DevOps for the log from the last line it has seen with startLine. It checks that the first line it gets back is that last line. When the last line it has seen had no line break, the new text starts on a fresh line. Pipedash downloads the whole log again and replaces the shown log when it cannot verify the position. This happens when:

  • The cursor belongs to another build, log or attempt.
  • The last line is longer than 1 MiB.
  • Azure DevOps returns a line that does not match.

After a failed log read, Pipedash waits 5 minutes before it tries again. When the tree refresh fails, it waits longer each time, up to 60 seconds.

Errors:

  • When Azure DevOps answers 404 or 410, the error reads Log <logId> of build <buildId> is no longer available.
  • When the timeline lists no log for the job, the error reads Run #<id> has no job log <node ID>.

The log follows the Azure Pipelines line format. Each line can start with a timestamp, and Pipedash reads it as the time of the line. Pipedash reads these markers:

  • ##[section]Starting: <task> starts a step and a collapsed section titled with the text after the marker.
  • ##[section]Finishing: <task> stays a plain line inside its section.
  • Any other ##[section] line, for example ##[section]Async Command Start: <name>, starts a collapsed section but not a step.
  • ##[group] and ##[endgroup] open and close nested sections.
  • ##[error], ##[warning], ##[command] and ##[debug] lines get their own style. A task output line that starts with [command] and no hashes counts as a command.

Only ##[error] lines count as errors. Pipedash does not scan plain output for error words. A ##[section] marker in the middle of a line is plain output. The Run logs page covers the viewer.

Status page

Azure DevOps has no default status page. status_page_url points Pipedash at the Statuspage of the instance. It follows the rules of the GitHub Actions status page. An invalid value makes the provider fail to initialize. See also Provider health.

TOML example

[providers.azure-work]
name = "Azure DevOps Work"
type = "azure-devops"
token = "${AZURE_DEVOPS_TOKEN}"
refresh_interval = 30

[providers.azure-work.config]
server_url = "https://dev.azure.com/my-org"
selected_items = "Web App,Platform"

For Azure DevOps Server, set server_url to the collection URL, for example https://tfs.example.com/DefaultCollection.

Status mapping

Pipedash maps the build status and, for completed builds, the result. Timeline records of stages and jobs use the same mapping with their state and result.

Pipedash statusAzure DevOps state
successcompleted with result succeeded
failedcompleted with result failed, partiallySucceeded or succeededWithIssues, or with any other or missing result
runninginProgress or cancelling
pendingnotStarted, postponed, none, all, a missing status and any other status. A pipeline with no build.
cancelledcompleted with result canceled or abandoned
skippedcompleted with result skipped

This plugin does not produce the degraded status.