You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Remove a single tag from an Azure Pipelines build identified by its numeric build ID. The command requires exactly one --tag value (the underlying SDK only supports single-tag removal). The returned *[]string contains the remaining tags on the build after deletion.
Note: the SDK method DeleteBuildTag is singular-only. For multiple tags with special characters, Azure DevOps also supports a PATCH method (in API 6.0+), but the Python extension does not implement it and the az CLI does not expose it; we mirror Python's singular behavior. Special characters in the tag value are the user's responsibility (URL-encode if needed).
Locked Decisions (do not re-derive)
#
Decision
Rationale
1
Use the vendored SDK method build.Client.DeleteBuildTag (singular only). Mock already generated at internal/mocks/build_client_mock.go:210.
Mandate: vendored API. The SDK exposes no bulk delete.
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).
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.
The --tag flag accepts a single string value (not a slice). cobra.ExactArgs(0) for --tag; cobra validates with cobra.MinimumNArgs(0) and a Go-side check that exactly one --tag was provided. Repeatable --tag is rejected: an additional occurrence triggers a util.FlagErrorf error.
The SDK is singular-only; we do not introduce a multi-tag loop (a single call with one tag is the contract).
5
--tag is required. Cobra validation errors with util.FlagErrorf if --tag is empty or absent.
Mirrors az pipelines build tag delete which requires --tag.
6
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.
7
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.
8
The command issues a destructive mutation; require a confirmation prompt unless --yes is supplied. The prompt text is: "Are you sure you want to delete the tag <TAG> from build <BUILD_ID>?". On cancel, return util.ErrCancel.
AGENTS.md: Confirmation for Destructive Operations.
9
No new SDK client, no new helper, no new package beyond internal/cmd/pipelines/build/tag/delete.
Mandate: minimal code.
10
Mock for DeleteBuildTag is already generated. Do not regenerate.
Verified at internal/mocks/build_client_mock.go:210.
Command Signature
azdo pipelines build tag delete [ORGANIZATION/]PROJECT/BUILD
--tag TAG (string, required, single value)
[--yes]
[--json ...]
The Target field is parsed as int; non-numeric / zero / negative values rejected with util.FlagErrorf.
--tag is required and must be a single value. --tag TAG1 --tag TAG2 is rejected with util.FlagErrorf("only one --tag value is allowed").
Flags
Flag
Maps to
Notes
--tag (str, required, single)
DeleteBuildTagArgs.Tag
First occurrence also bound to -t; multiple occurrences rejected
--yes (bool)
confirmation skip
Suppresses the destructive-operation prompt
--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 the remaining tag name strings on the build after deletion. No view struct is required.
Table Output Contract
Mirrors transform_build_tags_output from _format.py (single column Tags). The output is the remaining tags on the build (not the deleted tag).
delete.go — NewCmd(ctx util.CmdContext) *cobra.Command + deleteOptions + runDelete. Note: package import path is delete, not del, to match the leaf directory. The cobra Use: "delete ..." matches the leaf name.
delete_test.go — table-driven gomock tests
Update internal/cmd/pipelines/build/tag/tag.go to add delete.NewCmd(ctx) to cmd.AddCommand(...). Update the Example block.
Higher-level parents must already remain wired: pipelines → build → tag → delete.
API Surface
Reuse the already-vendored client. No new SDK clients or mocks required.
internal/cmd/pipelines/build/cancel/cancel.go (feat: Implement azdo pipelines build cancel command #252) — primary target-resolution precedent: uses Use: "cancel [ORGANIZATION/]PROJECT/BUILD", cobra.ExactArgs(1), util.ParseProjectTargetWithDefaultOrganization. delete follows the same target parsing plus the destructive confirmation pattern.
internal/cmd/pipelines/variablegroup/delete/delete.go — primary destructive precedent: confirmation prompt unless --yes is supplied. delete uses the same pattern with the build-target message above.
internal/cmd/pipelines/build/list/list.go (feat: Implement azdo pipelines build list command #211) — primary list reference for the modern list pattern, JSON view struct, table printer, and --max-items cap. delete is a mutation so it does not use the list pattern; only the JSON/table output convention is shared.
Sub-issue of #276. Hardened spec — do not re-derive decisions. Mirrors
az pipelines build tag deletefrom the Azure CLI and the Python implementation atazure-devops/azext_devops/dev/pipelines/build.py#L198-L208.Command Description
Remove a single tag from an Azure Pipelines build identified by its numeric build ID. The command requires exactly one
--tagvalue (the underlying SDK only supports single-tag removal). The returned*[]stringcontains the remaining tags on the build after deletion.Locked Decisions (do not re-derive)
build.Client.DeleteBuildTag(singular only). Mock already generated atinternal/mocks/build_client_mock.go:210.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.--tagflag accepts a single string value (not a slice).cobra.ExactArgs(0)for--tag; cobra validates withcobra.MinimumNArgs(0)and a Go-side check that exactly one--tagwas provided. Repeatable--tagis rejected: an additional occurrence triggers autil.FlagErrorferror.--tagis required. Cobra validation errors withutil.FlagErrorfif--tagis empty or absent.az pipelines build tag deletewhich requires--tag.pipelines buildleaves.--jsonpasses the raw SDK result*[]stringtoopts.exporter.Write.--yesis supplied. The prompt text is:"Are you sure you want to delete the tag <TAG> from build <BUILD_ID>?". On cancel, returnutil.ErrCancel.internal/cmd/pipelines/build/tag/delete.DeleteBuildTagis already generated. Do not regenerate.internal/mocks/build_client_mock.go:210.Command Signature
cobra.ExactArgs(1)—args[0]→ target (viautil.ParseProjectTargetWithDefaultOrganization).Targetfield is parsed asint; non-numeric / zero / negative values rejected withutil.FlagErrorf.--tagis required and must be a single value.--tag TAG1 --tag TAG2is rejected withutil.FlagErrorf("only one --tag value is allowed").Flags
--tag(str, required, single)DeleteBuildTagArgs.Tag-t; multiple occurrences rejected--yes(bool)--json/--jq/--templateutil.AddJSONFlagsJSON Output Contract
Pass the raw SDK result
*[]stringtoopts.exporter.Write. The result is a list of the remaining tag name strings on the build after deletion. No view struct is required.Table Output Contract
Mirrors
transform_build_tags_outputfrom_format.py(single columnTags). The output is the remaining tags on the build (not the deleted tag).Command Wiring
internal/cmd/pipelines/build/tag/deletedelete.go—NewCmd(ctx util.CmdContext) *cobra.Command+deleteOptions+runDelete. Note: package import path isdelete, notdel, to match the leaf directory. The cobraUse: "delete ..."matches the leaf name.delete_test.go— table-driven gomock testsinternal/cmd/pipelines/build/tag/tag.goto adddelete.NewCmd(ctx)tocmd.AddCommand(...). Update theExampleblock.pipelines→build→tag→delete.API Surface
Reuse the already-vendored client. No new SDK clients or mocks required.
build.Client.DeleteBuildTag→ Tags - Delete (REST 7.1-preview.3, DELETE single tag)build.DeleteBuildTagArgsstruct:{Project *string, BuildId *int, Tag *string}.*[]string(the remaining tags on the build after deletion).Mock for
DeleteBuildTag(:210) is already generated. No mock regeneration needed.Reference Existing Patterns
azure-devops/azext_devops/dev/pipelines/build.py#L198-L208—delete_build_tagPython implementation.azure-devops/azext_devops/dev/pipelines/commands.py—g.command('delete', 'delete_build_tag', table_transformer=transform_build_tags_output).azure-dev-ops-cli-extension/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.deletefollows the same target parsing plus the destructive confirmation pattern.internal/cmd/pipelines/variablegroup/delete/delete.go— primary destructive precedent: confirmation prompt unless--yesis supplied.deleteuses the same pattern with the build-target message above.internal/cmd/pipelines/build/list/list.go(feat: Implementazdo pipelines build listcommand #211) — primary list reference for the modern list pattern, JSON view struct, table printer, and--max-itemscap.deleteis a mutation so it does not use the list pattern; only the JSON/table output convention is shared.internal/cmd/boards/workitem/list/list_test.go:765-844—setupFakeDeps/stub*fixture.internal/mocks/build_client_mock.go:210— mock forDeleteBuildTag(already generated).internal/azdo/factory.go:61—ClientFactory().Build(...)accessor (reuse).internal/cmd/util/scope.go:183—util.ParseProjectTargetWithDefaultOrganization(project-scoped parser).internal/cmd/util/prompter.go— confirmation prompt +util.ErrCancel(used by this leaf).References
azext_devops/dev/pipelines/build.pyazext_devops/dev/pipelines/commands.pyazext_devops/dev/pipelines/_format.py