Sub-issue of #276. Hardened spec — do not re-derive decisions. Mirrors az pipelines build tag list from the Azure CLI and the Python implementation at azure-dev-ops/azext_devops/dev/pipelines/build.py#L210-L218.
Command Description
List the tags currently attached to an Azure Pipelines build identified by its numeric build ID. The command returns a *[]string (the list of tag name strings). The output is a single-column table Tags, one row per tag.
GET https://dev.azure.com/{organization}/{project}/_apis/build/builds/{buildId}/tags?api-version=7.1-preview.3
Locked Decisions (do not re-derive)
| # |
Decision |
Rationale |
| 1 |
Use the vendored SDK method build.Client.GetBuildTags. Mock already generated at internal/mocks/build_client_mock.go. |
Mandate: vendored API. |
| 2 |
The build is identified by a positional BUILD argument ([ORGANIZATION/]PROJECT/BUILD). The BUILD segment must parse as a positive integer (no name resolution). |
Mirrors cancel (#252) and queue (#253). |
| 3 |
Parse the positional using util.ParseProjectTargetWithDefaultOrganization from internal/cmd/util/scope.go:183. The function returns a *Target with Organization, Project, Target fields, accepting 2- or 3-segment inputs. |
Mirrors internal/cmd/pipelines/build/{cancel,queue}/ precedent. |
| 4 |
The user is not required to pass a project; the project segment in the positional is optional. When omitted, the project is resolved from the default config. |
Mirrors all other pipelines build leaves. |
| 5 |
No additional flags beyond --json / --jq / --template. The build ID is the only input. |
Mirrors az pipelines build tag list which is flagless aside from the build identifier. |
| 6 |
Default output is a table. JSON output via --json passes the raw SDK result *[]string to opts.exporter.Write. |
No view struct needed for a list of strings; mirrors the show-sibling convention. |
| 7 |
A build with no tags returns an empty *[]string; the table emits zero rows; JSON emits []. No error. |
Mirrors az pipelines build tag list behavior. |
| 8 |
No --max-items cap. Build tag lists are short (typically < 20 entries) and the SDK does not paginate. |
Mirrors az pipelines build tag list. |
| 9 |
No new SDK client, no new helper, no new package beyond internal/cmd/pipelines/build/tag/list. |
Mandate: minimal code. |
| 10 |
Mock for GetBuildTags is already generated. Do not regenerate. |
Verified at internal/mocks/build_client_mock.go. |
Command Signature
azdo pipelines build tag list [ORGANIZATION/]PROJECT/BUILD
[--json ...]
cobra.ExactArgs(1) — args[0] → target (via util.ParseProjectTargetWithDefaultOrganization).
- The
Target field is parsed as int; non-numeric / zero / negative values rejected with util.FlagErrorf.
Flags
| Flag |
Maps to |
Notes |
--json / --jq / --template |
util.AddJSONFlags |
JSON export |
JSON Output Contract
Pass the raw SDK result *[]string to opts.exporter.Write. The result is a list of tag name strings. No view struct is required.
Caveat: the SDK returns *[]string (a pointer to a slice of strings). The exporter handles both nil and empty-slice cases by emitting null and [] respectively.
Table Output Contract
Mirrors transform_build_tags_output from _format.py (single column Tags). One row per tag in the SDK response. The table header is Tags (uppercase, single column).
| Tags |
| release |
| nightly |
| v1.2.3 |
An empty SDK result produces a table with no rows (only the header).
Command Wiring
- Package path:
internal/cmd/pipelines/build/tag/list
- Files:
list.go — NewCmd(ctx util.CmdContext) *cobra.Command + listOptions + runList. Cobra Use: "list [ORGANIZATION/]PROJECT/BUILD". Aliases: []string{"ls", "l"} to match the project convention.
list_test.go — table-driven gomock tests
- Update
internal/cmd/pipelines/build/tag/tag.go to add list.NewCmd(ctx) to cmd.AddCommand(...). Update the Example block.
- Higher-level parents must already remain wired:
pipelines → build → tag → list.
API Surface
Reuse the already-vendored client. No new SDK clients or mocks required.
build.Client.GetBuildTags → Tags - Get (REST 7.1-preview.3)
build.GetBuildTagsArgs struct: {Project *string, BuildId *int}.
- The method returns
*[]string (the list of tag name strings).
Mock for GetBuildTags is already generated. No mock regeneration needed.
Reference Existing Patterns
References
Sub-issue of #276. Hardened spec — do not re-derive decisions. Mirrors
az pipelines build tag listfrom the Azure CLI and the Python implementation atazure-dev-ops/azext_devops/dev/pipelines/build.py#L210-L218.Command Description
List the tags currently attached to an Azure Pipelines build identified by its numeric build ID. The command returns a
*[]string(the list of tag name strings). The output is a single-column tableTags, one row per tag.Locked Decisions (do not re-derive)
build.Client.GetBuildTags. Mock already generated atinternal/mocks/build_client_mock.go.BUILDargument ([ORGANIZATION/]PROJECT/BUILD). TheBUILDsegment must parse as a positive integer (no name resolution).cancel(#252) andqueue(#253).util.ParseProjectTargetWithDefaultOrganizationfrominternal/cmd/util/scope.go:183. The function returns a*TargetwithOrganization,Project,Targetfields, accepting 2- or 3-segment inputs.internal/cmd/pipelines/build/{cancel,queue}/precedent.pipelines buildleaves.--json/--jq/--template. The build ID is the only input.az pipelines build tag listwhich is flagless aside from the build identifier.--jsonpasses the raw SDK result*[]stringtoopts.exporter.Write.*[]string; the table emits zero rows; JSON emits[]. No error.az pipelines build tag listbehavior.--max-itemscap. Build tag lists are short (typically < 20 entries) and the SDK does not paginate.az pipelines build tag list.internal/cmd/pipelines/build/tag/list.GetBuildTagsis already generated. Do not regenerate.internal/mocks/build_client_mock.go.Command Signature
cobra.ExactArgs(1)—args[0]→ target (viautil.ParseProjectTargetWithDefaultOrganization).Targetfield is parsed asint; non-numeric / zero / negative values rejected withutil.FlagErrorf.Flags
--json/--jq/--templateutil.AddJSONFlagsJSON Output Contract
Pass the raw SDK result
*[]stringtoopts.exporter.Write. The result is a list of tag name strings. No view struct is required.Table Output Contract
Mirrors
transform_build_tags_outputfrom_format.py(single columnTags). One row per tag in the SDK response. The table header isTags(uppercase, single column).An empty SDK result produces a table with no rows (only the header).
Command Wiring
internal/cmd/pipelines/build/tag/listlist.go—NewCmd(ctx util.CmdContext) *cobra.Command+listOptions+runList. CobraUse: "list [ORGANIZATION/]PROJECT/BUILD".Aliases: []string{"ls", "l"}to match the project convention.list_test.go— table-driven gomock testsinternal/cmd/pipelines/build/tag/tag.goto addlist.NewCmd(ctx)tocmd.AddCommand(...). Update theExampleblock.pipelines→build→tag→list.API Surface
Reuse the already-vendored client. No new SDK clients or mocks required.
build.Client.GetBuildTags→ Tags - Get (REST 7.1-preview.3)build.GetBuildTagsArgsstruct:{Project *string, BuildId *int}.*[]string(the list of tag name strings).Mock for
GetBuildTagsis already generated. No mock regeneration needed.Reference Existing Patterns
azure-dev-ops/azuredevops/azext_devops/dev/pipelines/build.py#L210-L218—get_build_tagsPython implementation.azure-dev-ops/azuredevops/azext_devops/dev/pipelines/commands.py—g.command('list', 'get_build_tags', table_transformer=transform_build_tags_output).azure-dev-ops/azuredevops/azext_devops/dev/pipelines/_format.py—transform_build_tags_output(single-columnTagstable).internal/cmd/pipelines/build/cancel/cancel.go(feat: Implementazdo pipelines build cancelcommand #252) — primary target-resolution precedent: usesUse: "cancel [ORGANIZATION/]PROJECT/BUILD",cobra.ExactArgs(1),util.ParseProjectTargetWithDefaultOrganization.listis read-only, so the destructive confirmation flow is dropped.internal/cmd/pipelines/build/queue/queue.go(feat: Implementazdo pipelines build queuecommand #253) — secondary target-resolution precedent: uses the same parser and the samecobra.ExactArgs(1)validation.internal/cmd/boards/workitem/list/list_test.go:765-844—setupFakeDeps/stub*fixture.internal/mocks/build_client_mock.go— mock forGetBuildTags(already generated).internal/azdo/factory.go:61—ClientFactory().Build(...)accessor (reuse).internal/cmd/util/scope.go:183—util.ParseProjectTargetWithDefaultOrganization(project-scoped parser).References
azext_devops/dev/pipelines/build.pyazext_devops/dev/pipelines/commands.pyazext_devops/dev/pipelines/_format.py