关于 ado
使用 `az` CLI 和 REST API 操作 Azure DevOps 资源的编码技能。涵盖仓库、拉取请求、服务连接、Agent 池与 Agent 等流程,并说明如何解析远程地址和使用环境默认值。
主要功能
- 01管理 Azure DevOps 项目与仓库
- 02创建和更新拉取请求
- 03读取和发布 PR 评论
- 04管理服务连接
- 05查看 Agent 池与 Agent
适用场景
- 01列出项目中的仓库
- 02审查或完成拉取请求
- 03检查服务连接配置
- 04查找在线构建 Agent
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jimweller/ clanker-skills --skill ado安装后在 Claude Code 中输入/ado确认已加载。
详细介绍
STARTER_CHARACTER = 🔷
Azure DevOps 运维
使用 az CLI 和 ADO REST API 与 Azure DevOps 资源交互。
前置条件
身份验证:始终设置 $AZURE_DEVOPS_EXT_PAT。无需登录。
仓库操作需要 ADO 远程地址(dev.azure.com 或 visualstudio.com)。
默认组织和项目
Shell 环境通过 $ADO_DEFAULT_ORG 和 $ADO_DEFAULT_PROJECT 提供默认值。当无法从 git 远程地址解析组织或项目,或在 git 仓库之外操作时,将这些值用作回退值。
org="${org:-$ADO_DEFAULT_ORG}"
project="${project:-$ADO_DEFAULT_PROJECT}"
通过 --org "https://dev.azure.com/${org}" 和 --project "$project" 将它们传递给 az 命令。
URL 解析
Azure DevOps 远程地址有三种格式。解析远程 URL 中的组织、项目和仓库,以构造网页 URL 并将参数传递给 az 命令。
remote_url=$(git remote get-url origin 2>/dev/null)
格式 1:dev.azure.com HTTPS
模式:https://dev.azure.com/{org}/{project}/_git/{repo} 或 https://{user}@dev.azure.com/{org}/{project}/_git/{repo}
clean_url=$(echo "$remote_url" | sed 's|https://[^@]*@|https://|')
org=$(echo "$clean_url" | sed -n 's|https://dev\.azure\.com/\([^/]*\)/.*|\1|p')
project=$(echo "$clean_url" | sed -n 's|https://dev\.azure\.com/[^/]*/\([^/]*\)/_git/.*|\1|p')
repo=$(echo "$clean_url" | sed -n 's|.*/_git/\(.*\)|\1|p')
格式 2:visualstudio.com
模式:https://{org}.visualstudio.com/DefaultCollection/{project}/_git/{repo}
clean_url=$(echo "$remote_url" | sed 's|https://[^@]*@|https://|')
org=$(echo "$clean_url" | sed -n 's|^https://\([^.]*\)\..*|\1|p')
project=$(echo "$clean_url" | sed -n 's|.*/DefaultCollection/\([^/]*\)/_git/.*|\1|p')
repo=$(echo "$clean_url" | sed -n 's|.*/_git/\(.*\)|\1|p')
格式 3:SSH
模式:[email protected]:v3/{org}/{project}/{repo}
org=$(echo "$remote_url" | sed -n 's|git@ssh\.dev\.azure\.com:v3/\([^/]*\)/.*|\1|p')
project=$(echo "$remote_url" | sed -n 's|git@ssh\.dev\.azure\.com:v3/[^/]*/\([^/]*\)/.*|\1|p')
repo=$(echo "$remote_url" | sed -n 's|.*[^/]*/\([^/]*\)$|\1|p')
构造网页 URL
对项目名称中的空格进行 URL 编码(%20),然后构造:
base_url=https://dev.azure.com/{org}/{project}/_git/{repo}
pr_url={base_url}/pullrequest/{pr_id}
在 macOS 上,使用 open "$url" 打开 URL。
浏览
打开仓库
解析远程 URL,构造网页 URL 并打开。
open "https://dev.azure.com/${org}/${project}/_git/${repo}"
打开 PR
open "https://dev.azure.com/${org}/${project}/_git/${repo}/pullrequest/${pr_id}"
项目
列出项目
az devops project list --organization "https://dev.azure.com/${org}" --query "value[].{Name:name,ID:id,State:state}" -o table
显示项目详情
az devops project show --project "$project" --organization "https://dev.azure.com/${org}"
获取项目 ID
az devops project show --project "$project" --organization "https://dev.azure.com/${org}" --query id -o tsv
仓库
创建
az repos create \
--name "<repo-name>" \
--project "$project" \
--org "https://dev.azure.com/${org}" \
--output table \
--query "{Name:name,Project:project.name,URL:webUrl}"
删除
步骤 1:查找仓库 ID。
repo_id=$(az repos show \
--repository "<repo-name>" \
--project "$project" \
--query id --output tsv)
步骤 2:按 ID 删除。
az repos delete \
--id "$repo_id" \
--project "$project" \
--yes
列出
az repos list --organization "https://dev.azure.com/${org}" --project "$project" --query "[].{Name:name,ID:id,DefaultBranch:defaultBranch}" -o table
显示
az repos show --repository <REPO_NAME> --organization "https://dev.azure.com/${org}" --project "$project"
获取克隆 URL
az repos show --repository <REPO_NAME> --organization "https://dev.azure.com/${org}" --project "$project" --query remoteUrl -o tsv
拉取请求(CLI)
所有 PR 命令都使用 JMESPath 查询来输出结构化结果。base_url 变量由解析后的远程 URL 构造(参见上文的 URL 解析)。
创建
az repos pr create \
"$@" \
--output table \
--query "{PR:pullRequestId,Status:status,URL:join(\`\`,[\ \`${base_url}\`,\`/pullrequest/\`,to_string(pullRequestId)])}"
常用标志:--title、--description、--source-branch、--target-branch、--reviewers、--auto-complete、--draft
列出
az repos pr list \
--output table \
--query "[].{PR:pullRequestId,Title:title,Creator:createdBy.uniqueName,Created:creationDate,URL:join(\`\`,[\`${base_url}\`,\`/pullrequest/\`,to_string(pullRequestId)])}"
常用标志:--status、--creator、--reviewer、--source-branch、--target-branch
显示
az repos pr show \
--id <pr-id> \
--output table \
--query "{PR:pullRequestId,Title:title,Creator:createdBy.uniqueName,Created:creationDate,URL:join(\`\`,[\`${base_url}\`,\`/pullrequest/\`,to_string(pullRequestId)])}"
更新
az repos pr update \
--id <pr-id> \
--output table \
--query "{PR:pullRequestId,Status:status,URL:join(\`\`,[\`${base_url}\`,\`/pullrequest/\`,to_string(pullRequestId)])}"
常用标志:--title、--description、--status、--auto-complete、--draft
完成
通过将状态设为 completed 并绕过策略来完成(合并)拉取请求。
az repos pr update \
--id <pr-id> \
--status completed \
--bypass-policy true \
--output table \
--query "{PR:pullRequestId,Status:status,URL:join(\`\`,[\`${base_url}\`,\`/pullrequest/\`,to_string(pullRequestId)])}"
其他标志:--merge-commit-message
完成操作为异步操作。响应中的状态仍会显示为 active。随后运行 az repos pr show --id <pr-id>,确认 status: completed 且 mergeStatus: succeeded。
放弃
az repos pr update \
--id <pr-id> \
--status abandoned \
--output table \
--query "{PR:pullRequestId,Status:status,URL:join(\`\`,[\`${base_url}\`,\`/pullrequest/\`,to_string(pullRequestId)])}"
投票/批准
az repos pr set-vote \
--id <pr-id> \
--vote approve
投票后,显示 PR 详情:
az repos pr show \
--id <pr-id> \
--output table \
--query "{PR:pullRequestId,Title:title,Creator:createdBy.uniqueName,Created:creationDate,URL:join(\`\`,[\`${base_url}\`,\`/pullrequest/\`,to_string(pullRequestId)])}"
有效的投票值:approve、approve-with-suggestions、wait-for-author、reject、reset
PR 评论和讨论串(REST API)
发布评论
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pullRequests/<PR_ID>/threads?api-version=7.0" \
-H "Content-Type: application/json" \
-d '{
"comments": [{"parentCommentId": 0, "content": "Comment text here", "commentType": 1}],
"status": 1
}'
获取评论/讨论串
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pullRequests/<PR_ID>/threads?api-version=7.0" \
| jq '.value[] | {id: .id, status: .status, comments: [.comments[] | {author: .author.displayName, content: .content}]}'
搜索包含指定文本的评论
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pullRequests/<PR_ID>/threads?api-version=7.0" \
| jq '.value[].comments[] | select(.content | contains("SEARCH_TEXT")) | {author: .author.displayName, content: .content}'
服务连接
列出
az devops service-endpoint list --organization "https://dev.azure.com/${org}" --project "$project" --query "[].{Name:name,Type:type,ID:id,Scheme:authorization.scheme}" -o table
显示详情
az devops service-endpoint show --id <ENDPOINT_ID> --organization "https://dev.azure.com/${org}" --project "$project"
按名称获取
az devops service-endpoint list --organization "https://dev.azure.com/${org}" --project "$project" --query "[?name=='<NAME>']" -o json
删除
az devops service-endpoint delete --id <ENDPOINT_ID> --organization "https://dev.azure.com/${org}" --project "$project" --yes
获取 WIF 详情(REST API)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/serviceendpoint/endpoints?api-version=7.1" \
| jq ".value[] | select(.name == \"<NAME>\") | {issuer: .authorization.parameters.workloadIdentityFederationIssuer, subject: .authorization.parameters.workloadIdentityFederationSubject}"
授权所有管道(REST API)
SC_ID=$(az devops service-endpoint list --organization "https://dev.azure.com/${org}" --project "$project" --query "[?name=='<NAME>'].id" -o tsv)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X PATCH \
"https://dev.azure.com/${org}/${project}/_apis/pipelines/pipelinePermissions/endpoint/${SC_ID}?api-version=7.1-preview.1" \
-H "Content-Type: application/json" \
-d '{"allPipelines": {"authorized": true}}'
Agent 池
池属于组织级别,可在多个项目间共享。
列出所有
az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[].{Name:name,ID:id,PoolType:poolType,IsHosted:isHosted,Size:size}" -o table
按名称模式列出
az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?contains(name, '<PATTERN>')].{Name:name,ID:id,PoolType:poolType,Size:size}" -o table
按名称获取池 ID
az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?name=='<POOL_NAME>'].id" -o tsv
显示详情
az pipelines pool show --organization "https://dev.azure.com/${org}" --pool-id <POOL_ID>
创建(REST API)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/_apis/distributedtask/pools?api-version=7.1" \
-H "Content-Type: application/json" \
-d '{"name": "<POOL_NAME>", "poolType": "automation", "isHosted": false, "autoProvision": false}'
删除(REST API)
POOL_ID=$(az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?name=='<POOL_NAME>'].id" -o tsv)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X DELETE \
"https://dev.azure.com/${org}/_apis/distributedtask/pools/${POOL_ID}?api-version=7.1"
Agent
Agent 在池中运行。
列出池中的 Agent
POOL_ID=$(az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?name=='<POOL_NAME>'].id" -o tsv)
az pipelines agent list --organization "https://dev.azure.com/${org}" --pool-id $POOL_ID --query "[].{Name:name,ID:id,Status:status,Version:version}" -o table
列出在线 Agent
POOL_ID=$(az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?name=='<POOL_NAME>'].id" -o tsv)
az pipelines agent list --organization "https://dev.azure.com/${org}" --pool-id $POOL_ID --query "[?status=='online'].{Name:name,ID:id,Version:version}" -o table
显示 Agent 详情
az pipelines agent show --organization "https://dev.azure.com/${org}" --pool-id <POOL_ID> --agent-id <AGENT_ID>
统计在线 Agent 数量
POOL_ID=$(az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?name=='<POOL_NAME>'].id" -o tsv)
az pipelines agent list --organization "https://dev.azure.com/${org}" --pool-id $POOL_ID --query "[?status=='online']" -o tsv | wc -l
队列
队列是项目级别对组织级别池的引用。管道授权针对队列而非池。
列出所有
az pipelines queue list --organization "https://dev.azure.com/${org}" --project "$project" --query "[].{Name:name,QueueID:id,PoolID:pool.id}" -o table
按名称模式列出
az pipelines queue list --organization "https://dev.azure.com/${org}" --project "$project" --query "[?contains(name, '<PATTERN>')].{Name:name,QueueID:id,PoolID:pool.id}" -o table
获取池对应的队列 ID
POOL_ID=$(az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?name=='<POOL_NAME>'].id" -o tsv)
az pipelines queue list --organization "https://dev.azure.com/${org}" --project "$project" --query "[?pool.id==\`${POOL_ID}\`].id" -o tsv
为池创建队列(REST API)
POOL_ID=$(az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?name=='<POOL_NAME>'].id" -o tsv)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/${project}/_apis/distributedtask/queues?api-version=7.1" \
-H "Content-Type: application/json" \
-d '{"name": "<QUEUE_NAME>", "pool": {"id": '${POOL_ID}'}}'
为所有管道授权队列(REST API)
QUEUE_ID=$(az pipelines queue list --organization "https://dev.azure.com/${org}" --project "$project" --query "[?name=='<QUEUE_NAME>'].id" -o tsv)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X PATCH \
"https://dev.azure.com/${org}/${project}/_apis/pipelines/pipelinePermissions/queue/${QUEUE_ID}?api-version=7.1-preview.1" \
-H "Content-Type: application/json" \
-d '{"allPipelines": {"authorized": true}}'
为特定管道授权队列(REST API)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X PATCH \
"https://dev.azure.com/${org}/${project}/_apis/pipelines/pipelinePermissions/queue/<QUEUE_ID>?api-version=7.1-preview.1" \
-H "Content-Type: application/json" \
-d '{"pipelines": [{"id": <PIPELINE_ID>, "authorized": true}]}'
管道
列出全部
az pipelines list --organization "https://dev.azure.com/${org}" --project "$project" --query "[].{Name:name,ID:id,Folder:folder,QueueStatus:queueStatus}" -o table
按名称模式列出
az pipelines list --organization "https://dev.azure.com/${org}" --project "$project" --name "<PATTERN>*" --query "[].{Name:name,ID:id}" -o table
显示详情
az pipelines show --organization "https://dev.azure.com/${org}" --project "$project" --name <PIPELINE_NAME>
根据名称获取管道 ID
az pipelines list --organization "https://dev.azure.com/${org}" --project "$project" --query "[?name=='<PIPELINE_NAME>'].id" -o tsv
创建
az pipelines create \
--organization "https://dev.azure.com/${org}" \
--project "$project" \
--name <PIPELINE_NAME> \
--repository <REPO_NAME> \
--repository-type tfsgit \
--branch main \
--yml-path azure-pipelines.yml \
--skip-first-run
删除
PIPELINE_ID=$(az pipelines list --organization "https://dev.azure.com/${org}" --project "$project" --query "[?name=='<PIPELINE_NAME>'].id" -o tsv)
az pipelines delete --id $PIPELINE_ID --organization "https://dev.azure.com/${org}" --project "$project" --yes
管道运行(构建)
运行管道
az pipelines run --organization "https://dev.azure.com/${org}" --project "$project" --name <PIPELINE_NAME>
运行并获取运行 ID
RUN_ID=$(az pipelines run --organization "https://dev.azure.com/${org}" --project "$project" --name <PIPELINE_NAME> --query id -o tsv)
echo "Run ID: $RUN_ID"
使用变量运行
az pipelines run --organization "https://dev.azure.com/${org}" --project "$project" --name <PIPELINE_NAME> --variables "VAR1=value1" "VAR2=value2"
列出最近的运行
az pipelines runs list --organization "https://dev.azure.com/${org}" --project "$project" --top 10 --query "[].{ID:id,Pipeline:definition.name,Status:status,Result:result,StartTime:startTime}" -o table
列出特定管道的运行
PIPELINE_ID=$(az pipelines list --organization "https://dev.azure.com/${org}" --project "$project" --query "[?name=='<PIPELINE_NAME>'].id" -o tsv)
az pipelines runs list --organization "https://dev.azure.com/${org}" --project "$project" --pipeline-ids $PIPELINE_ID --query "[].{ID:id,Status:status,Result:result}" -o table
显示运行状态
az pipelines runs show --organization "https://dev.azure.com/${org}" --project "$project" --id <RUN_ID> --query "{Status:status,Result:result}" -o json
监视运行直至完成
RUN_ID=<RUN_ID>
while true; do
STATUS=$(az pipelines runs show --organization "https://dev.azure.com/${org}" --project "$project" --id $RUN_ID --query status -o tsv)
RESULT=$(az pipelines runs show --organization "https://dev.azure.com/${org}" --project "$project" --id $RUN_ID --query result -o tsv)
echo "Status: $STATUS, Result: ${RESULT:-pending}"
if [ "$STATUS" == "completed" ]; then break; fi
sleep 10
done
构建日志
获取构建时间线(阶段/作业)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/build/builds/<RUN_ID>/timeline?api-version=7.1" \
| jq '.records[] | {name: .name, type: .type, state: .state, result: .result}'
列出构建日志
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/build/builds/<RUN_ID>/logs?api-version=7.1" \
| jq '.value[] | {id: .id, type: .type, lineCount: .lineCount}'
下载指定日志
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/build/builds/<RUN_ID>/logs/<LOG_ID>?api-version=7.1"
获取失败步骤的日志
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/build/builds/<RUN_ID>/timeline?api-version=7.1" \
| jq '.records[] | select(.result == "failed") | {name: .name, log: .log.url}'
Git 操作(REST API)
无需本地克隆即可通过编程方式执行 Git 工作流(分支、提交、推送)。
按名称获取存储库
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>?api-version=7.0" \
| jq '{id: .id, name: .name, defaultBranch: .defaultBranch}'
获取分支引用
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/refs?filter=heads/main&api-version=7.0" \
| jq '.value[] | {name: .name, objectId: .objectId}'
获取主分支提交 SHA
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/refs?filter=heads/main&api-version=7.0" \
| jq -r '.value[0].objectId'
从主分支创建分支
MAIN_SHA=$(curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/refs?filter=heads/main&api-version=7.0" \
| jq -r '.value[0].objectId')
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/refs?api-version=7.0" \
-H "Content-Type: application/json" \
-d '[{"name": "refs/heads/<BRANCH_NAME>", "oldObjectId": "0000000000000000000000000000000000000000", "newObjectId": "'${MAIN_SHA}'"}]'
删除分支
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/refs?api-version=7.0" \
-H "Content-Type: application/json" \
-d '[{"name": "refs/heads/<BRANCH_NAME>", "oldObjectId": "<LAST_COMMIT_SHA>", "newObjectId": "0000000000000000000000000000000000000000"}]'
推送提交(添加/编辑文件)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pushes?api-version=7.0" \
-H "Content-Type: application/json" \
-d '{
"refUpdates": [{"name": "refs/heads/<BRANCH_NAME>", "oldObjectId": "<BASE_COMMIT_SHA>"}],
"commits": [{
"comment": "Commit message here",
"changes": [
{
"changeType": "edit",
"item": {"path": "/path/to/file"},
"newContent": {"content": "file content here", "contentType": "rawtext"}
}
]
}]
}'
推送初始提交(新存储库)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pushes?api-version=7.0" \
-H "Content-Type: application/json" \
-d '{
"refUpdates": [{"name": "refs/heads/main", "oldObjectId": "0000000000000000000000000000000000000000"}],
"commits": [{
"comment": "Initial commit",
"changes": [
{
"changeType": "add",
"item": {"path": "/README.md"},
"newContent": {"content": "# Repo Title", "contentType": "rawtext"}
}
]
}]
}'
推送 Base64 编码的内容
CONTENT_B64=$(echo -n "file contents here" | base64)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pushes?api-version=7.0" \
-H "Content-Type: application/json" \
-d '{
"refUpdates": [{"name": "refs/heads/<BRANCH>", "oldObjectId": "<SHA>"}],
"commits": [{
"comment": "Add file",
"changes": [{
"changeType": "add",
"item": {"path": "/file.txt"},
"newContent": {"content": "'${CONTENT_B64}'", "contentType": "base64encoded"}
}]
}]
}'
拉取请求(REST API)
需要更多控制时,可替代 az repos pr 命令。
创建
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pullrequests?api-version=7.0" \
-H "Content-Type: application/json" \
-d '{
"sourceRefName": "refs/heads/<SOURCE_BRANCH>",
"targetRefName": "refs/heads/main",
"title": "PR Title",
"description": "PR description"
}' | jq '{pullRequestId: .pullRequestId, status: .status}'
获取详情
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pullRequests/<PR_ID>?api-version=7.0" \
| jq '{id: .pullRequestId, title: .title, status: .status, sourceRefName: .sourceRefName}'
列出
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pullrequests?api-version=7.0" \
| jq '.value[] | {id: .pullRequestId, title: .title, status: .status}'
放弃
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X PATCH \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pullRequests/<PR_ID>?api-version=7.0" \
-H "Content-Type: application/json" \
-d '{"status": "abandoned"}'
完成(合并)
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X PATCH \
"https://dev.azure.com/${org}/${project}/_apis/git/repositories/<REPO_NAME>/pullRequests/<PR_ID>?api-version=7.0" \
-H "Content-Type: application/json" \
-d '{
"status": "completed",
"lastMergeSourceCommit": {"commitId": "<LAST_COMMIT_SHA>"},
"completionOptions": {"deleteSourceBranch": true, "mergeStrategy": "squash"}
}'
代码搜索(REST API)
跨存储库搜索代码。使用 almsearch.dev.azure.com 主机,而非 dev.azure.com。
在项目中搜索
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://almsearch.dev.azure.com/${org}/${project}/_apis/search/codesearchresults?api-version=7.1" \
-H "Content-Type: application/json" \
-d '{"searchText":"<TEXT>","$top":25}' \
| jq '{count: .count, results: [.results[] | {repo: .repository.name, path: .path}]}'
使用筛选条件跨组织搜索
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://almsearch.dev.azure.com/${org}/_apis/search/codesearchresults?api-version=7.1" \
-H "Content-Type: application/json" \
-d '{
"searchText":"<TEXT>",
"filters":{"Project":["<PROJECT>"],"Repository":["<REPO>"],"Branch":["main"],"Path":["/src"]},
"$top":25
}' \
| jq '.results[] | {project: .project.name, repo: .repository.name, path: .path}'
可用的筛选键:Project、Repository、Branch、Path、CodeElement(例如 ["def","class"])。
搜索运算符
searchText 字段接受 ADO 搜索语法:
ext:go quiver-bolt-- 文件扩展名筛选file:Makefile quiver-bolt-- 文件名筛选def:MyFunction-- 函数/方法定义"exact phrase"-- 精确匹配foo AND bar、foo OR bar、foo NOT bar-- 布尔运算
Webhook(REST API)
列出服务挂钩
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" \
"https://dev.azure.com/${org}/_apis/hooks/subscriptions?api-version=7.0" \
| jq '.value[] | {id: .id, eventType: .eventType, consumerActionId: .consumerActionId, publisherInputs: .publisherInputs}'
为 PR 事件创建 Webhook
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X POST \
"https://dev.azure.com/${org}/_apis/hooks/subscriptions?api-version=7.0" \
-H "Content-Type: application/json" \
-d '{
"publisherId": "tfs",
"eventType": "git.pullrequest.created",
"resourceVersion": "1.0",
"consumerId": "webHooks",
"consumerActionId": "httpRequest",
"publisherInputs": {
"projectId": "<PROJECT_ID>",
"repository": "<REPO_ID>"
},
"consumerInputs": {
"url": "<WEBHOOK_URL>"
}
}'
删除 Webhook
curl -s -u ":${AZURE_DEVOPS_EXT_PAT}" -X DELETE \
"https://dev.azure.com/${org}/_apis/hooks/subscriptions/<SUBSCRIPTION_ID>?api-version=7.0"
诊断
检查 ADO 基础设施运行状况
echo "=== Service Connections ==="
az devops service-endpoint list --organization "https://dev.azure.com/${org}" --project "$project" --query "[].{Name:name,Type:type}" -o table
echo ""
echo "=== Agent Pools ==="
az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[].{Name:name,Size:size,IsHosted:isHosted}" -o table
echo ""
echo "=== Queues ==="
az pipelines queue list --organization "https://dev.azure.com/${org}" --project "$project" --query "[].{Name:name,QueueID:id}" -o table
检查池中的 Agent
POOL_ID=$(az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?name=='<POOL_NAME>'].id" -o tsv)
if [ -n "$POOL_ID" ]; then
az pipelines agent list --organization "https://dev.azure.com/${org}" --pool-id $POOL_ID --query "[].{Name:name,Status:status}" -o table
fi
等待 Agent 上线
POOL_ID=$(az pipelines pool list --organization "https://dev.azure.com/${org}" --query "[?name=='<POOL_NAME>'].id" -o tsv)
MAX_WAIT=300
ELAPSED=0
while [ $ELAPSED -lt $MAX_WAIT ]; do
ONLINE=$(az pipelines agent list --organization "https://dev.azure.com/${org}" --pool-id $POOL_ID --query "[?status=='online']" -o tsv | wc -l)
if [ "$ONLINE" -gt 0 ]; then
echo "Found $ONLINE online agent(s)"
break
fi
echo "Waiting for agents... ($ELAPSED/${MAX_WAIT}s)"
sleep 10
ELAPSED=$((ELAPSED + 10))
done
关键概念
池与队列
- 池是组织级别的(在多个项目间共享)
- 队列是项目级别的池引用
- 管道授权针对的是队列,而非池
工作负载身份联合
服务连接使用 WIF(OIDC)。ADO 中不存储任何密钥。联合凭据在 App Registration 中管理。
REST API 与 CLI
某些操作需要使用 REST API(没有对应的 CLI 命令):
- 创建具有特定设置的池
- 为管道授权队列
- 查询服务连接中的 WIF 主体
- Git 推送操作(分支、提交)
- PR 评论线程
- Webhook