project-scoped feeds 能否像 organization-scoped feeds 一样访问?
可以访问。project-scoped feed 同样支持 REST API 和 NuGet 客户端,但请求地址需要包含 feed 所属项目,调用身份也必须拥有该项目和 feed 的访问权限。
你给出的代码对所有 feed 都使用了 organization-scoped URL,需要先修正。Azure Artifacts 中的 promote 是将某个版本加入同一 feed 的 view,例如 @Release。跨 feed 发布则需要下载后再推送,或通过 upstream source 保存包。
1. 为 project-scoped feed 补上项目路径
列出 package 的请求地址分别为:
Organization-scoped feed:
https://feeds.dev.azure.com/{organization}/_apis/packaging/feeds/{feedId}/packages?api-version=7.1
Project-scoped feed:
https://feeds.dev.azure.com/{organization}/{project}/_apis/packaging/feeds/{feedId}/packages?api-version=7.1
{project} 是 feed 所属项目,可以使用项目 ID 或名称。即使使用 feed ID,project-scoped feed 的请求仍需包含项目参数。Get Packages API 文档
在现有 foreach ($feed in $bs_obj.value) 循环中,将构造 URL 的部分替换为:
$orgSegment = [Uri]::EscapeDataString($ORG)
$feedSegment = [Uri]::EscapeDataString([string]$feed.id)
$baseUrl = "https://feeds.dev.azure.com/$orgSegment"
if ($feed.project) {
$projectSegment = [Uri]::EscapeDataString([string]$feed.project.id)
$baseUrl += "/$projectSegment"
}
$url = "$baseUrl/_apis/packaging/feeds/$feedSegment/packages" +
"?includeAllVersions=true&api-version=7.1"
$header = @{ Authorization = "Bearer $TOKEN" }
$param = @{
Uri = $url
Headers = $header
ErrorAction = "Stop"
}
pipeline 遍历其他项目的 feed 时,应使用各个 $feed.project.id,不能统一使用 $(System.TeamProject)。
第一步保存 feed 列表时,也要提高 JSON 序列化深度,保留后续需要的嵌套属性:
$json = $result | ConvertTo-Json -Depth 20 -Compress
2. 检查实际使用的 Build Service 身份
能列出 feed,不代表后续 package 请求的路径和权限都正确。修正 URL 后若仍然失败,再检查调用身份的授权。
System.AccessToken 的权限取决于 job authorization scope 和对应 Build Service 账户的权限。常见身份有:
[Pipeline 项目名] Build Service ([Organization 名])
Project Collection Build Service ([Organization 名])
需要给实际使用的 Build Service 账户授权,只给自己的用户账户授权还不够。汇总表中的字符串 "Build Service" 也不能证明已确认实际调用身份。Job access token 文档
跨项目访问时,可以按以下顺序配置:
- 在 feed 所属项目的
Project settings > Permissions中,将 pipeline 所属项目的 Build Service 加入Readers。 - 在
Artifacts > Feed settings > Permissions中,为相关构建身份分配所需角色。 - 按官方跨项目连接说明,同时检查项目级 Build Service 和
Project Collection Build Service的 feed 权限。Feed scope 与跨项目访问说明
根据操作选择角色:
| 操作 | Feed 角色 |
|---|---|
| 列出、下载 feed 中已有的包 | Feed Reader |
| 从 upstream source 首次安装并保存包 | Feed and Upstream Reader(Collaborator) |
| 发布包、将版本 promote 到 view | Feed Publisher(Contributor) |
源 feed 的读取权限和目标 feed 的发布权限需要分别配置。Feed 权限说明
3. 保留错误信息,修正汇总代码
当前 catch 将所有异常都记为 Visible = "NO",无法区分路径错误、认证失败和服务异常。至少应在原有 catch 中加入:
$statusCode = $null
if ($_.Exception.Response) {
$statusCode = [int]$_.Exception.Response.StatusCode
}
Write-Warning (
"Feed={0}; HTTP={1}; URL={2}; Error={3}" -f
$feed.name, $statusCode, $url, $_.Exception.Message
)
if ($_.ErrorDetails.Message) {
Write-Warning $_.ErrorDetails.Message
}
排查时,遇到 401 优先检查 token,403 优先检查授权,404 则要同时检查项目路径、feed 标识和资源可见性。现有输出没有保留这些信息,还不能确定问题只出在权限上。
第三段脚本还引用了所给代码中未定义的 $pat_obj。如果只是输出 Build Service 查询结果,可以直接遍历 $bs_obj:
foreach ($entry in @($bs_obj)) {
Write-Host (
"{0} | {1} | {2} | {3} | {4} | {5}" -f
$entry.FeedName,
$entry.Scope,
$entry.Package,
$entry.Version,
$entry.Identity,
$entry.Visible
)
}
请求成功但返回空列表,与请求失败应分别记录。如果请求成功但缺少版本,再检查分页和筛选条件。includeAllVersions=true 仍会受到 NuGet 的 isRelease 等参数影响;package 较多时,还需要处理 $top、$skip。Get Packages 参数说明
4. 将稳定版本发布到 organization feed
如果采用“项目 feed 开发、组织 feed 统一发布”的布局,可以按以下流程发布:
- 确定经过验证的稳定版本。
- 从项目 feed 下载对应
.nupkg。 - 将该文件推送到 organization feed。
- 让各开发 feed 将 organization feed 配置为 upstream source。
NuGet 包的直接下载接口是:
GET https://pkgs.dev.azure.com/{organization}/{project}/_apis/packaging/feeds/{feedId}/nuget/packages/{packageName}/versions/{packageVersion}/content?api-version=7.1-preview.1
这个接口下载包文件,前面使用的 Get Packages 返回包的元数据。NuGet Download Package 文档
以下示例假设源 feed 已有经过验证的稳定版本 1.0.0,agent 已安装兼容的 .NET SDK,读取和发布权限也已配置。请将 SOURCE_PROJECT、SOURCE_FEED、TARGET_FEED 替换为实际值:
- task: NuGetAuthenticate@1
- pwsh: |
$ErrorActionPreference = "Stop"
$org = [Uri]::EscapeDataString($env:ORG)
$project = [Uri]::EscapeDataString($env:SOURCE_PROJECT)
$sourceFeed = [Uri]::EscapeDataString($env:SOURCE_FEED)
$targetFeed = [Uri]::EscapeDataString($env:TARGET_FEED)
$packageName = "my-package-name"
$packageVersion = "1.0.0"
$file = Join-Path $env:AGENT_TEMPDIRECTORY `
"$packageName.$packageVersion.nupkg"
$downloadUrl = "https://pkgs.dev.azure.com/$org/$project" +
"/_apis/packaging/feeds/$sourceFeed/nuget/packages/$packageName" +
"/versions/$packageVersion/content?api-version=7.1-preview.1"
Invoke-WebRequest `
-Uri $downloadUrl `
-Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } `
-OutFile $file
$targetUrl = "https://pkgs.dev.azure.com/$org" +
"/_packaging/$targetFeed/nuget/v3/index.json"
dotnet nuget push "$file" --source "$targetUrl" --api-key AzureArtifacts
if ($LASTEXITCODE -ne 0) {
throw "Package push failed: exit code $LASTEXITCODE"
}
displayName: "Publish stable package to organization feed"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
ORG: $(ORG)
SOURCE_PROJECT: $(SOURCE_PROJECT)
SOURCE_FEED: $(SOURCE_FEED)
TARGET_FEED: $(TARGET_FEED)
NuGetAuthenticate@1 用于配置 NuGet 客户端认证。--api-key AzureArtifacts 不是实际的访问 token,也不能替代 feed 授权。NuGetAuthenticate 使用说明
5. 只共享已验证版本,也可以使用 view
如果主要目的是控制下游可用的版本,可以在项目 feed 中将通过验证的版本 promote 到 @Release,再让 organization feed 将该项目 feed 的 @Release 配置为 upstream source。
添加 Azure Artifacts upstream 时,可以选择具体的 feed 和 view。配置完成后不会自动复制全部包;安装上游包时,才由 Collaborator 或更高角色触发保存副本。Upstream 配置说明
这种配置与“organization feed 作为开发 feed 的 upstream”方向不同,应根据实际依赖关系选择,避免形成循环。
发布审批状态和版本号也需要分开看:
- promote 到
@Release只改变版本所属的 view,不会把包移到其他 feed,也不会修改版本号。 0.1.0-ci.117即使进入@Release,仍是 NuGet 预发布版本。需要正式版本时,应通过打包流程生成对应版本的包。- Azure Artifacts 的 promote 不支持 demote;目标 feed 中已发布的版本也不能通过覆盖或删除后重发来替换。Promote 与 view 说明、包版本不可变性说明
organization feed 中的 Microsoft.Extensions、System.Text.Json 等包,可能是从公共 upstream 安装后保存的依赖,仅凭包名无法确认来源。如果希望组织 feed 只保存内部发布包,还需要检查它配置的公共 upstream。
备注:内容仅供参考。
