You are reading the docs for v0.0.1-beta-1. View the latest docs.
Skip to content

Azure DevOps ​

How kinhin talks to Azure Pipelines agent pools, and how that compares with Microsoft's current REST API docs.

← Home · Forge reference index · Setup guide

Planned. Azure DevOps support is not part of the beta. This page records the implementation for the work ahead; see the roadmap.

Overview ​

Forge idazuredevops
HostingAzure DevOps Services (https://dev.azure.com/{org}) or Azure DevOps Server (self-hosted). instance is required and is the organization (or collection) URL.
ScopeAn agent pool name.
SourceSources/KinhinKit/Forges/AzureDevOps/AzureDevOpsClient.swift, AzureDevOpsForge.swift, AzureDevOpsModels.swift; agent start-up in Sources/KinhinKit/Images/RunnerBootstrap/RunnerBootstrap+AzureDevOps.swift
StatusPlanned, not supported yet. Implemented and unit-tested; not yet run against the real service.

Auth ​

HTTP Basic with an empty user name and the PAT as the password: Authorization: Basic base64(":" + PAT) (AzureDevOpsClient.swift:136). A rejected PAT can come back as a 203, an HTML sign-in page, or a redirect to the sign-in host (which the session refuses to follow, so the 3xx itself arrives); the client treats all three as unauthorized (AzureDevOpsClient.rejectSignInPage). TokenRequirements.azureDevOps lists the permission (Agent Pools read and manage).

Microsoft's OAuth scopes line up: vso.agentpools for reading pools, agents and job state, vso.agentpools_manage for deleting agents.

API version and base URL ​

We sendDocumented (verified 2026-10-01)
Versionapi-version=7.0 on every URL (AzureDevOpsClient.swift:15,112)The version must be sent on every request.
Currentn/a7.1 is the stable reference version in the docs; 7.2 reference pages also exist.

Which products accept which version (Microsoft's REST API versioning page):

ProductHighest REST version
Azure DevOps Services7.0+ (docs also cover 7.1 and 7.2)
Azure DevOps Server 20227.0
Azure DevOps Server 20206.0
Azure DevOps Server 20195.0

Per-operation reference pages for Azure DevOps Server exist for 6.0, 7.0 and 7.1 (azure-devops-server-rest-7.1), so a newer Server release than 2022 ships API 7.1. Which Server release that is was not stated on the pages fetched.

Endpoints used ​

MethodPathPurposeCode
GET/_apis/distributedtask/pools?poolName=X&actionFilter=manageResolve the pool and check manage rights; falls back to no actionFilter to tell "not found" from "cannot manage"AzureDevOpsClient.swift:33,40
GET/_apis/distributedtask/pools?actionFilter=managePool pickerAzureDevOpsClient.swift:51
GET/_apis/distributedtask/pools/{id}/agents?includeAssignedRequest=trueList agentsAzureDevOpsClient.swift:62
DELETE/_apis/distributedtask/pools/{id}/agents/{aid}Remove an agent (a 404 is treated as already gone)AzureDevOpsClient.swift:70
GET/_apis/distributedtask/pools/{id}/jobrequests?completedRequestCount=0Demand and running jobsAzureDevOpsClient.swift:82

Pipeline view ​

The running job request names its build (owner.id), project (scopeId) and timeline job (jobId). The view reads GET /{project}/_apis/build/builds/{id}/timeline (Job records are the jobs, their Task children the steps) and /builds/{id} for the title, branch and link. This request-to-build link is undocumented (like the job requests endpoint itself); a request without it shows no detail.

Output is GET /{project}/_apis/build/builds/{id}/logs/{logId}, with the log id taken from the timeline record of the task (or job).

Registration flow ​

  • Registration mode sharedLongLived, exit semantics deregisters (AzureDevOpsForge.swift:28-29).
  • The registration credential is the PAT itself: mintRegistrationToken returns credentials.token. The agent is configured in the foreground with config.sh --unattended --url --auth pat --pool --agent, the token passed through VSTS_AGENT_INPUT_TOKEN for that command only, so a failed registration fails the script with the tail of agent.log. Registration is sequential, one runner after another. The registered agent then runs in the background (run.sh --once when the instance is ephemeral, plain run.sh when vm.ephemeral is false) and finally config.sh remove. The PAT is no longer written to a .pat file: it reaches the background shell through a pipe, in an unexported variable. It is still copied into every runner VM, which TokenRequirements already warns about.

Demand and label matching ​

  • demands(_:include:) requires a request's demands to include every label. A bare name matches any demand with that name; name=value matches name -equals value, case-insensitive.
  • Labels are exported to the agent as capabilities (name=true or name=value). The pool's labels join the account's (the account wins on a name clash), and every name is made a valid environment variable name, so kinhin-docker becomes kinhin_docker=true. A job on a docker pool must therefore demand both kinhin and kinhin_docker. A kinhin_docker demand is translated back to the kinhin-docker marker for routing.
  • Queued means assignTime == nil with no result and no reserved agent: a request already reserved for an agent is not demand for another. A request demanding Agent.OS -equals something other than Darwin or Linux (Windows) is not demand either, since no guest could take it.
  • Running means assignTime != nil with no result and a reserved agent.
  • An agent seen online that stays offline for 60 s is reported gone and its record deleted, so a failed config.sh remove in the guest no longer keeps the instance's exit watch waiting.

Pagination, rate limits, retries ​

No pagination: every list is a single {value: [...]} read. withTransientRetry on network errors, 429 and 5xx. No rate-limit header handling.

Self-hosted notes and minimum versions ​

  • For Azure DevOps Server, instance is the organization or collection URL the user supplies; the exact URL shape for Server was not verified here.
  • api-version=7.0 works on Services and Server 2022 only. Server 2020 and earlier reject it.

Version sensitivity ​

ItemDetail
api-version7.0 fits Services and Server 2022. Server 2020 tops out at 6.0; Server 2019 at 5.0. Newer Server releases offer 7.1.
Endpoints usedThe pool and agent operations exist in the 5.1, 6.0, 6.1, 7.0 and 7.1 references, so lowering api-version for older Server is plausible. Not tested.
Preview versionsAfter a release, its -preview form is deprecated and can be removed after 12 weeks. kinhin uses only stable versions.

Before support: to verify against a live service ​

  • Highest-impact risk: an empty pool plus a demands: job may fail fast rather than queue. Azure may reject a request whose demands no agent in the pool satisfies instead of leaving it waiting, so kinhin would never see demand to scale up from. If so, the pool needs a placeholder offline agent that advertises the capabilities, or min_runners >= 1. Not verified.
  • That an exported kinhin_docker=true environment variable is picked up as a capability by the agent, and that the demand arrives as kinhin_docker.
  • That installdependencies.sh under sudo succeeds on the Tart Linux image and that Agent.Listener --version runs there.
  • That the 60 s offline grace never reaps an agent that is merely between jobs.
  • That a request carries reservedAgent before assignTime, as the queued rule assumes.
  • That a redirect really is how an expired PAT presents on Azure DevOps Services and Server.
  • That sequential registration of several runners in one VM is fast enough.

Verified ​

2026-10-01, against: