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
Sub-issue of #240. Hardened spec — do not re-derive decisions. Mirrors internal/cmd/pr/view/view.go and uses Go text templates via internal/template.Template (the same engine used by azdo pr view).
Show commands have no table output. This command renders a single queue via Go text template (default) or --json (opt-in). Table output is reserved for list commands.
Command Description
Display the details of a single Azure DevOps agent queue by integer ID or name. The command resolves the target (positive integer is used directly; a string is resolved via GetAgentQueues), fetches the matching TaskAgentQueue via the Agent Queues REST 7.1 endpoint, and renders it as a Go text template. The queue's Pool (the agent pool it references) is rendered with its name and ID when present.
GET https://dev.azure.com/{organization}/{project}/_apis/distributedtask/queues/{queueId}?api-version=7.1
Locked Decisions (do not re-derive)
#
Decision
Rationale
1
Use the vendored SDK taskagent.Client.GetAgentQueue (not raw HTTP). Mock already generated at internal/mocks/taskagent_client_mock.go:456-471.
Consistent with the other pool/queue subgroup siblings; the SDK is what the umbrella wires in #240.
2
The queue is identified by a positional QUEUE argument ([ORGANIZATION/]PROJECT/QUEUE). The target segment is resolved via a new shared.ResolveQueue helper at internal/cmd/pipelines/queue/show/shared/resolve.go: if it parses as a positive integer, use it directly; otherwise call taskagent.Client.GetAgentQueues(queueName=...) and pick the first match. Folds the previous --id flag into the positional.
Mirrors the variablegroup/delete resolution pattern. Queues are project-scoped.
2.5
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.
Standard cobra pattern; the 1st positional is the full target.
4
Use the Go text template engine from internal/template/template.go with an //go:embed show.tpl file. Mirror internal/cmd/pr/view/view.go and view.tpl structure: bold, hyperlink, s, timeago, timefmt, markdown, pluck, join, truncate, stripprefix, tablerow, tablerender.
The user explicitly requested template rendering like pr/view.
5
Aliases: view, status. Primary name is show. cmd.Use: "show [ORGANIZATION/]PROJECT/QUEUE", cmd.Aliases: []string{"view", "status"}.
Mirrors the pr/view aliasing pattern but with show as the primary.
6
JSON output passes the raw SDK *taskagent.TaskAgentQueue to opts.exporter.Write. No view struct.
Symmetric with other show siblings.
7
No confirmation prompt. Show is read-only.
Show is non-destructive.
8
--raw flag dumps the full SDK queue with spew.Dump to stderr for debugging.
Mirrors pr/view --raw.
9
No new SDK client, no new helper beyond shared.ResolveQueue, no new package beyond internal/cmd/pipelines/queue/show. Reuse SDK call from the vendored taskagent package.
Mandate: minimal code.
10
Mock for GetAgentQueue is already generated at internal/mocks/taskagent_client_mock.go:456-471. Do not regenerate.
Verified.
11
taskagent.Client.GetAgentQueues is used for name resolution; mock also generated.
Mirrors the variablegroup/delete name-resolution pattern.
Command Signature
azdo pipelines queue show [ORGANIZATION/]PROJECT/QUEUE
[--raw]
[--json ...]
Pass the raw *taskagent.TaskAgentQueue returned by the SDK (Decision 6).
Template Output Contract (show.tpl)
The default template renders (see internal/cmd/pr/view/view.tpl for the established pattern):
url:
id:
name:
project id:
pool: () (id and name when present)
created on:
created by: () (if present)
last changed on: () (if present)
last changed by: () (if present)
Update internal/cmd/pipelines/queue/queue.go to add showcmd "github.com/tmeckel/azdo-cli/internal/cmd/pipelines/queue/show" and cmd.AddCommand(showcmd.NewCmd(ctx)). Update the Example block.
Phase 1 — RED (tests first). Mirror setupFakeDeps from internal/cmd/boards/workitem/list/list_test.go:765-844. Add show_test.go with the following table-driven / behaviour tests, all using t.Parallel() and gomock (require for preconditions, assert for verifications):
TestNewCmd_RegistersAsShowLeaf — asserts cmd.Name() == "show", cmd.Aliases contains view and status, cmd.Use starts with show [ORGANIZATION/]PROJECT/QUEUE.
TestRunShow_ClientFactoryError — stubs factory to return error; asserts wrapped error.
TestRunShow_SDKError — stubs SDK to return error; asserts wrapped error.
TestRunShow_OrganizationFromConfigDefault — when scopeArg is Fabrikam/7 (no org), asserts clientFact.TaskAgent(ctx, defaultOrg) is called with the configured default.
Phase 2 — GREEN (minimal implementation). Strict reuse rules:
No new helpers beyond shared.ResolveQueue(ctx, clientFact, raw) (int, error) (~30 lines): positive-int fast path + taskagent.Client.GetAgentQueues first-match lookup; error on zero/ambiguous matches.
Output split: JSON via opts.exporter.Write(ios, res) passing the raw SDK *TaskAgentQueue; template via template.New(...).ExecuteData(templateData{Queue: res}).
Debug log at the point of the SDK call: organization, project, queueId.
Target delta: show.go ≤ ~130 LOC, show.tpl ≤ ~50 LOC, shared/resolve.go ≤ ~30 LOC, show_test.go ≤ ~450 LOC (20 tests), parent queue.go +3 LOC, docs/pipelines_queue_show.md regenerated via make docs. No changes to other queue siblings (none yet).
Tooling and Verification Checklist
Run gofmt / gofumpt on touched files
go test ./internal/cmd/pipelines/queue/...
go test ./...
make lint
make docs
Reference Existing Patterns
internal/cmd/pr/view/view.go — primary template-engine reference (Primary: view.go:483-549; viewOptions struct at view.go:23-31; template embed at view.go:33-34).
internal/cmd/pr/view/view.tpl — primary template-file reference (45 lines; same field-bullet style).
internal/cmd/pipelines/pool/show/show.go (sibling under the pool show sub-issue filed in the same wave) — closest sibling; mirror the entire file structure. The only differences are: (a) Use: "show [ORGANIZATION/]PROJECT/QUEUE" vs Use: "show [ORGANIZATION/]POOL"; (b) util.ParseProjectTargetWithDefaultOrganization vs util.ParseTargetWithDefaultOrganization; (c) GetAgentQueue vs GetAgentPool.
Sub-issue of #240. Hardened spec — do not re-derive decisions. Mirrors
internal/cmd/pr/view/view.goand uses Go text templates viainternal/template.Template(the same engine used byazdo pr view).Command Description
Display the details of a single Azure DevOps agent queue by integer ID or name. The command resolves the target (positive integer is used directly; a string is resolved via
GetAgentQueues), fetches the matchingTaskAgentQueuevia the Agent Queues REST 7.1 endpoint, and renders it as a Go text template. The queue'sPool(the agent pool it references) is rendered with its name and ID when present.Locked Decisions (do not re-derive)
taskagent.Client.GetAgentQueue(not raw HTTP). Mock already generated atinternal/mocks/taskagent_client_mock.go:456-471.QUEUEargument ([ORGANIZATION/]PROJECT/QUEUE). The target segment is resolved via a newshared.ResolveQueuehelper atinternal/cmd/pipelines/queue/show/shared/resolve.go: if it parses as a positive integer, use it directly; otherwise calltaskagent.Client.GetAgentQueues(queueName=...)and pick the first match. Folds the previous--idflag into the positional.util.ParseProjectTargetWithDefaultOrganizationfrominternal/cmd/util/scope.go:183. The function returns a*TargetwithOrganization,Project,Targetfields, accepting 2- or 3-segment inputs.internal/cmd/pipelines/variablegroup/{create,delete,show,update}/precedent.util.ExactArgs(1, "queue target is required").internal/template/template.gowith an//go:embed show.tplfile. Mirrorinternal/cmd/pr/view/view.goandview.tplstructure:bold,hyperlink,s,timeago,timefmt,markdown,pluck,join,truncate,stripprefix,tablerow,tablerender.pr/view.view,status. Primary name isshow.cmd.Use: "show [ORGANIZATION/]PROJECT/QUEUE",cmd.Aliases: []string{"view", "status"}.pr/viewaliasing pattern but withshowas the primary.*taskagent.TaskAgentQueuetoopts.exporter.Write. No view struct.--rawflag dumps the full SDK queue withspew.Dumpto stderr for debugging.pr/view --raw.shared.ResolveQueue, no new package beyondinternal/cmd/pipelines/queue/show. Reuse SDK call from the vendoredtaskagentpackage.GetAgentQueueis already generated atinternal/mocks/taskagent_client_mock.go:456-471. Do not regenerate.taskagent.Client.GetAgentQueuesis used for name resolution; mock also generated.Command Signature
view,statusargs[0]→ target (viautil.ParseProjectTargetWithDefaultOrganization).Targetfield of the parsed*Targetis resolved viashared.ResolveQueue(ctx, clientFact, args[0]).Flags
--raw(bool)spew.Dumpof full SDK queue to stderr--json/--jq/--templateutil.AddJSONFlagsJSON Output Contract
Pass the raw
*taskagent.TaskAgentQueuereturned by the SDK (Decision 6).Template Output Contract (
show.tpl)The default template renders (see
internal/cmd/pr/view/view.tplfor the established pattern):Command Wiring
internal/cmd/pipelines/queue/showshow.go—NewCmd(ctx util.CmdContext) *cobra.Command+showOptions+runShowshow.tpl— Go text templateshared/resolve.go—ResolveQueue(ctx, clientFact, raw) (int, error)(positive-int fast path +GetAgentQueuesfirst-match lookup)show_test.go— table-driven gomock testsinternal/cmd/pipelines/queue/queue.goto addshowcmd "github.com/tmeckel/azdo-cli/internal/cmd/pipelines/queue/show"andcmd.AddCommand(showcmd.NewCmd(ctx)). Update theExampleblock.pipelines→queue→show.API Surface
Reuse the already-vendored client. No new SDK clients required.
taskagent.Client.GetAgentQueue→ Agent Queues - Get (REST 7.1)taskagent.GetAgentQueueArgsstruct:{Project *string, QueueId *int}.taskagent.Client.GetAgentQueues(andGetAgentQueuesArgswithProject *string, QueueName *string) — for name → ID resolution.internal/template.Templatewithbold,hyperlink,s,timeago,timefmt,markdown,pluck,join,truncate,stripprefix,tablerow,tablerender.Mock for
GetAgentQueueis already generated atinternal/mocks/taskagent_client_mock.go:456-471. No mock regeneration needed.Implementation Approach (TDD, reuse-first, minimal)
Phase 1 — RED (tests first). Mirror
setupFakeDepsfrominternal/cmd/boards/workitem/list/list_test.go:765-844. Addshow_test.gowith the following table-driven / behaviour tests, all usingt.Parallel()andgomock(requirefor preconditions,assertfor verifications):TestNewCmd_RegistersAsShowLeaf— assertscmd.Name() == "show",cmd.Aliasescontainsviewandstatus,cmd.Usestarts withshow [ORGANIZATION/]PROJECT/QUEUE.TestNewCmd_RequiresOneArg— runscmd.SetArgs([]string{})+cmd.Execute(); asserts cobraExactArgserror.TestRunShow_ResolveByPositiveInteger— setsFabrikam/7; assertsshared.ResolveQueuereturns7without callingGetAgentQueues.TestRunShow_ResolveByName— setsFabrikam/Default; stubstaskagent.EXPECT().GetAgentQueues(...); asserts*args.QueueId == 7(resolved ID).TestRunShow_BasicCall— setsFabrikam/7; stubstaskagent.EXPECT().GetAgentQueue(gomock.Any(), gomock.Any()); asserts*args.QueueId == 7,*args.Project == "Fabrikam".TestRunShow_TemplateOutput_BasicFields— mocks return*TaskAgentQueue{Id, Name, ProjectId, Url}; asserts rendered output contains all field labels and values.TestRunShow_TemplateOutput_Hyperlink— asserts theurl:line uses ANSI hyperlink escape sequence.TestRunShow_TemplateOutput_Pool_Nested— mocks returnPool: &TaskAgentPoolReference{Id, Name}; assertspool: 7 (Default)rendered (orpool: 7if name missing).TestRunShow_TemplateOutput_NoPool— mocks returnPool: nil; assertspool:line is omitted.TestRunShow_TemplateOutput_CreatedBy_Nested— mocks returnCreatedBy: &IdentityRef{DisplayName, UniqueName}; assertscreated by: Alice (alice@contoso.com)rendered.TestRunShow_TemplateOutput_NoCreatedBy— mocks returnCreatedBy: nil; assertscreated by:line is omitted.TestRunShow_TemplateOutput_LastChangedBy_Nested— mocks returnLastChangedBy: &IdentityRef{...}; assertslast changed by:rendered.TestRunShow_TemplateOutput_NoLastChangedBy— mocks returnLastChangedBy: nil; assertslast changed by:line is omitted.TestRunShow_JSONOutput— sets--json; mocks return queue; asserts JSON containsid,name,pool,projectId,url,createdBy,createdDate,lastChangedBy,lastChangedDate.TestRunShow_RawFlag— sets--raw; assertsspew.Dumpwas invoked.TestRunShow_ProjectScopeParsing— table-driven:myorg/Fabrikam/7,Fabrikam/7, invalid ("org/proj/extra/x"), empty.TestRunShow_InvalidProjectScope— assertsutil.FlagErrorWrapreturned.TestRunShow_ClientFactoryError— stubs factory to return error; asserts wrapped error.TestRunShow_SDKError— stubs SDK to return error; asserts wrapped error.TestRunShow_OrganizationFromConfigDefault— when scopeArg isFabrikam/7(no org), assertsclientFact.TaskAgent(ctx, defaultOrg)is called with the configured default.Phase 2 — GREEN (minimal implementation). Strict reuse rules:
shared.ResolveQueue(ctx, clientFact, raw) (int, error)(~30 lines): positive-int fast path +taskagent.Client.GetAgentQueuesfirst-match lookup; error on zero/ambiguous matches.util.ParseProjectTargetWithDefaultOrganization,util.AddJSONFlags,util.FlagErrorf/FlagErrorWrap,types.GetValue,types.ToPtr,ios.StartProgressIndicator/StopProgressIndicator,iostreams.Testas-is.internal/template.New(...).WithFuncs(...).Parse(show.tpl).ExecuteData(data)— exact same pattern asinternal/cmd/pr/view/view.go:483-549.ios.StartProgressIndicator()+defer ios.StopProgressIndicator(); callios.StopProgressIndicator()immediately before template execution.opts.exporter.Write(ios, res)passing the raw SDK*TaskAgentQueue; template viatemplate.New(...).ExecuteData(templateData{Queue: res}).Target delta:
show.go≤ ~130 LOC,show.tpl≤ ~50 LOC,shared/resolve.go≤ ~30 LOC,show_test.go≤ ~450 LOC (20 tests), parentqueue.go+3 LOC,docs/pipelines_queue_show.mdregenerated viamake docs. No changes to other queue siblings (none yet).Tooling and Verification Checklist
gofmt/gofumpton touched filesgo test ./internal/cmd/pipelines/queue/...go test ./...make lintmake docsReference Existing Patterns
internal/cmd/pr/view/view.go— primary template-engine reference (Primary:view.go:483-549;viewOptionsstruct atview.go:23-31; template embed atview.go:33-34).internal/cmd/pr/view/view.tpl— primary template-file reference (45 lines; same field-bullet style).internal/cmd/boards/workitem/show/show.go(sibling under feat: Implementazdo boards work-item showcommand #238) — copy structure for the show flow, raw-SDK JSON output, progress lifecycle.internal/cmd/pipelines/pool/show/show.go(sibling under thepool showsub-issue filed in the same wave) — closest sibling; mirror the entire file structure. The only differences are: (a)Use: "show [ORGANIZATION/]PROJECT/QUEUE"vsUse: "show [ORGANIZATION/]POOL"; (b)util.ParseProjectTargetWithDefaultOrganizationvsutil.ParseTargetWithDefaultOrganization; (c)GetAgentQueuevsGetAgentPool.internal/cmd/boards/workitem/list/list_test.go:765-844—setupFakeDeps/stub*fixture; copy structure.internal/cmd/pipelines/variablegroup/delete/delete.go— primary target-resolution precedent (Decision 2 / 2.5): usesUse: "delete [ORGANIZATION/]PROJECT/GROUP",util.ExactArgs(1, "..."),util.ParseProjectTargetWithDefaultOrganization, and ashared.ResolveVariableGrouphelper.internal/cmd/util/scope.go:183—util.ParseProjectTargetWithDefaultOrganization(project-scoped parser).internal/mocks/taskagent_client_mock.go:456-471— mock forGetAgentQueue(already generated, do not regenerate).internal/azdo/factory.go:133-139—ClientFactory().TaskAgent(...)accessor (reuse).internal/template/template.go— template engine + funcs (reuse, do not reimplement).References