Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ extend-exclude = [
# extend-ignore-re = [
# "https?://[^`\\s]+",
# ]
extend-ignore-re = [
"UpdatePipelinePermisionsForResource"
]
35 changes: 29 additions & 6 deletions docs/azdo_help_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,29 @@ Aliases
view, status
```

### `azdo pipelines queue`

Manage Azure DevOps agent queues

#### `azdo pipelines queue list [ORGANIZATION/]PROJECT [flags]`

List agent queues

```
--action-filter string Filter queues by caller permissions: {manage|none|use}
-q, --jq expression Filter JSON output using a jq expression
--json fields[=*] Output JSON with the specified fields. Prefix a field with '-' to exclude it.
--max-items int Optional client-side cap on results
--name string Filter queues by name
-t, --template string Format JSON output using a Go template; see "azdo help formatting"
```

Aliases

```
ls, l
```

### `azdo pipelines run [ORGANIZATION/]PROJECT/PIPELINE [flags]`

Queue a pipeline run
Expand All @@ -395,16 +418,16 @@ Manage pipeline runs
List runs of pipelines in a project.

```
--branch strings Filter by source branch (repeatable; first value is honored by the SDK). Bare names get refs/heads/ prepended.
--branch string Filter by source branch. Bare names get refs/heads/ prepended.
-q, --jq expression Filter JSON output using a jq expression
--json fields[=*] Output JSON with the specified fields. Prefix a field with '-' to exclude it.
--max-items int Maximum number of runs to return client-side (0 = unlimited).
--pipeline-id ints Limit to runs for these pipeline IDs (repeatable; first value is honored by the SDK).
--query-order string Order the results: finishTimeAscending, finishTimeDescending, queueTimeAscending, queueTimeDescending, startTimeAscending, startTimeDescending.
--reason strings Filter by reason (repeatable; first value is honored). Valid: manual, individualCI, batchedCI, schedule, scheduleForced, userCreated, pullRequest, etc.
--pipeline-id ints Filter by pipeline IDs (repeatable).
--query-order string Order the results: {finishtimeascending|finishtimedescending|queuetimeascending|queuetimedescending|starttimeascending|starttimedescending}
--reason string Filter by reason: {all|batchedci|buildcompletion|checkinshelveset|individualci|manual|none|pullrequest|resourcetrigger|schedule|scheduleforced|triggered|usercreated|validateshelveset}
--requested-for string Filter by the user who queued the run. Accepts @me to mean the authenticated user.
--result strings Filter by result (repeatable; first value is honored). Valid: none, succeeded, partiallySucceeded, failed, canceled.
--status strings Filter by status (repeatable; first value is honored). Valid: none, inProgress, completed, cancelling, postponed, notStarted, all.
--result string Filter by result: {canceled|failed|none|partiallysucceeded|succeeded}
--status string Filter by status: {all|cancelling|completed|inprogress|none|notstarted|postponed}
--tag strings Filter by tags (all supplied tags must match).
-t, --template string Format JSON output using a Go template; see "azdo help formatting"
--top int Maximum number of runs to request per server page (0 = server default).
Expand Down
1 change: 1 addition & 0 deletions docs/azdo_pipelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Manage Azure DevOps pipelines
* [azdo pipelines delete](./azdo_pipelines_delete.md)
* [azdo pipelines list](./azdo_pipelines_list.md)
* [azdo pipelines pool](./azdo_pipelines_pool.md)
* [azdo pipelines queue](./azdo_pipelines_queue.md)
* [azdo pipelines run](./azdo_pipelines_run.md)
* [azdo pipelines runs](./azdo_pipelines_runs.md)
* [azdo pipelines show](./azdo_pipelines_show.md)
Expand Down
20 changes: 20 additions & 0 deletions docs/azdo_pipelines_queue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## Command `azdo pipelines queue`

Manage Azure DevOps agent queues. Queues are project-scoped
and connect a project to an agent pool.


### Available commands

* [azdo pipelines queue list](./azdo_pipelines_queue_list.md)

### Examples

```bash
# List queues in a project
azdo pipelines queue list Fabrikam
```

### See also

* [azdo pipelines](./azdo_pipelines.md)
65 changes: 65 additions & 0 deletions docs/azdo_pipelines_queue_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
## Command `azdo pipelines queue list`

```
azdo pipelines queue list [ORGANIZATION/]PROJECT [flags]
```

List agent queues in an Azure DevOps project.


### Options


* `--action-filter` `string`

Filter queues by caller permissions: {manage|none|use}

* `-q`, `--jq` `expression`

Filter JSON output using a jq expression

* `--json` `fields`

Output JSON with the specified fields. Prefix a field with '-' to exclude it.

* `--max-items` `int` (default `0`)

Optional client-side cap on results

* `--name` `string`

Filter queues by name

* `-t`, `--template` `string`

Format JSON output using a Go template; see "azdo help formatting"


### ALIASES

- `ls`
- `l`

### JSON Fields

`id`, `name`, `pool`, `projectId`

### Examples

```bash
# List all queues in a project
azdo pipelines queue list Fabrikam

# List queues in a specific organization
azdo pipelines queue list myorg/Fabrikam

# List queues filtered by name
azdo pipelines queue list myorg/Fabrikam --name Default

# Output as JSON
azdo pipelines queue list Fabrikam --json
```

### See also

* [azdo pipelines queue](./azdo_pipelines_queue.md)
20 changes: 10 additions & 10 deletions docs/azdo_pipelines_runs_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ and tags. The full result set is paginated server-side; use
### Options


* `--branch` `strings`
* `--branch` `string`

Filter by source branch (repeatable; first value is honored by the SDK). Bare names get refs/heads/ prepended.
Filter by source branch. Bare names get refs/heads/ prepended.

* `-q`, `--jq` `expression`

Expand All @@ -33,27 +33,27 @@ and tags. The full result set is paginated server-side; use

* `--pipeline-id` `ints`

Limit to runs for these pipeline IDs (repeatable; first value is honored by the SDK).
Filter by pipeline IDs (repeatable).

* `--query-order` `string`

Order the results: finishTimeAscending, finishTimeDescending, queueTimeAscending, queueTimeDescending, startTimeAscending, startTimeDescending.
Order the results: {finishtimeascending|finishtimedescending|queuetimeascending|queuetimedescending|starttimeascending|starttimedescending}

* `--reason` `strings`
* `--reason` `string`

Filter by reason (repeatable; first value is honored). Valid: manual, individualCI, batchedCI, schedule, scheduleForced, userCreated, pullRequest, etc.
Filter by reason: {all|batchedci|buildcompletion|checkinshelveset|individualci|manual|none|pullrequest|resourcetrigger|schedule|scheduleforced|triggered|usercreated|validateshelveset}

* `--requested-for` `string`

Filter by the user who queued the run. Accepts @me to mean the authenticated user.

* `--result` `strings`
* `--result` `string`

Filter by result (repeatable; first value is honored). Valid: none, succeeded, partiallySucceeded, failed, canceled.
Filter by result: {canceled|failed|none|partiallysucceeded|succeeded}

* `--status` `strings`
* `--status` `string`

Filter by status (repeatable; first value is honored). Valid: none, inProgress, completed, cancelling, postponed, notStarted, all.
Filter by status: {all|cancelling|completed|inprogress|none|notstarted|postponed}

* `--tag` `strings`

Expand Down
2 changes: 2 additions & 0 deletions internal/cmd/pipelines/pipelines.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/tmeckel/azdo-cli/internal/cmd/pipelines/delete"
"github.com/tmeckel/azdo-cli/internal/cmd/pipelines/list"
"github.com/tmeckel/azdo-cli/internal/cmd/pipelines/pool"
"github.com/tmeckel/azdo-cli/internal/cmd/pipelines/queue"
"github.com/tmeckel/azdo-cli/internal/cmd/pipelines/run"
"github.com/tmeckel/azdo-cli/internal/cmd/pipelines/runs"
"github.com/tmeckel/azdo-cli/internal/cmd/pipelines/show"
Expand All @@ -33,5 +34,6 @@ func NewCmd(ctx util.CmdContext) *cobra.Command {
cmd.AddCommand(variablegroup.NewCmd(ctx))
cmd.AddCommand(agent.NewCmd(ctx))
cmd.AddCommand(pool.NewCmd(ctx))
cmd.AddCommand(queue.NewCmd(ctx))
return cmd
}
173 changes: 173 additions & 0 deletions internal/cmd/pipelines/queue/list/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package list

import (
"fmt"
"strings"

"github.com/MakeNowJust/heredoc/v2"
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/taskagent"
"github.com/spf13/cobra"
"go.uber.org/zap"

"github.com/tmeckel/azdo-cli/internal/cmd/util"
"github.com/tmeckel/azdo-cli/internal/types"
)

type opts struct {
scope string
name string
actionFilter *string
maxItems int
exporter util.Exporter
}

func NewCmd(ctx util.CmdContext) *cobra.Command {
opts := &opts{}

cmd := &cobra.Command{
Use: "list [ORGANIZATION/]PROJECT",
Short: "List agent queues",
Long: heredoc.Doc(`
List agent queues in an Azure DevOps project.
`),
Example: heredoc.Doc(`
# List all queues in a project
azdo pipelines queue list Fabrikam

# List queues in a specific organization
azdo pipelines queue list myorg/Fabrikam

# List queues filtered by name
azdo pipelines queue list myorg/Fabrikam --name Default

# Output as JSON
azdo pipelines queue list Fabrikam --json
`),
Aliases: []string{
"ls",
"l",
},
Args: util.ExactArgs(1, "project argument required"),
RunE: func(cmd *cobra.Command, args []string) error {
opts.scope = args[0]
return run(ctx, opts)
},
}

cmd.Flags().StringVar(&opts.name, "name", "", "Filter queues by name")
util.NilStringEnumFlag(cmd, &opts.actionFilter, "action-filter", "", actionFilterMap.Keys(), "Filter queues by caller permissions")
cmd.Flags().IntVar(&opts.maxItems, "max-items", 0, "Optional client-side cap on results")
util.AddJSONFlags(cmd, &opts.exporter, []string{
"id",
"name",
"pool",
"projectId",
})

return cmd
}

var actionFilterMap = types.EnumLookup[taskagent.TaskAgentQueueActionFilter]{
"none": taskagent.TaskAgentQueueActionFilterValues.None,
"manage": taskagent.TaskAgentQueueActionFilterValues.Manage,
"use": taskagent.TaskAgentQueueActionFilterValues.Use,
}

func run(cmdCtx util.CmdContext, opts *opts) error {
ios, err := cmdCtx.IOStreams()
if err != nil {
return err
}
ios.StartProgressIndicator()
defer ios.StopProgressIndicator()

if opts.maxItems < 0 {
return util.FlagErrorf("invalid --max-items value %d; must be >= 0", opts.maxItems)
}

scopeArg := strings.TrimSpace(opts.scope)
if strings.Count(scopeArg, "/") > 1 {
return util.FlagErrorf("invalid project argument: expected [ORGANIZATION/]PROJECT")
}

scope, err := util.ParseProjectScope(cmdCtx, scopeArg)
if err != nil {
return util.FlagErrorf("invalid project argument: %w", err)
}
if len(scope.Targets) != 0 {
return util.FlagErrorf("invalid project argument: expected [ORGANIZATION/]PROJECT")
}

taskClient, err := cmdCtx.ClientFactory().TaskAgent(cmdCtx.Context(), scope.Organization)
if err != nil {
return fmt.Errorf("failed to create task agent client: %w", err)
}

args := taskagent.GetAgentQueuesArgs{
Project: types.ToPtr(scope.Project),
QueueName: types.NotZeroPtrOrNil(strings.TrimSpace(opts.name)),
}

actionFilter, ok := actionFilterMap.GetValuePtr(opts.actionFilter)
if !ok {
return util.FlagErrorf("invalid action filter %q; expected none, manage, or use", *opts.actionFilter)
}
if actionFilter != nil {
args.ActionFilter = actionFilter
}

zap.L().Debug(
"listing agent queues",
zap.String("organization", scope.Organization),
zap.String("project", scope.Project),
zap.String("queueName", types.GetValue(args.QueueName, "")),
)

resp, err := taskClient.GetAgentQueues(cmdCtx.Context(), args)
if err != nil {
return fmt.Errorf("failed to list queues: %w", err)
}

var queues []taskagent.TaskAgentQueue
if resp != nil {
queues = *resp
}

if opts.maxItems > 0 && len(queues) > opts.maxItems {
zap.L().Debug("truncating result set to max-items", zap.Int("maxItems", opts.maxItems))
queues = queues[:opts.maxItems]
}

ios.StopProgressIndicator()

if opts.exporter != nil {
return opts.exporter.Write(ios, queues)
}

tp, err := cmdCtx.Printer("list")
if err != nil {
return err
}

tp.AddColumns("ID", "NAME", "POOL", "PROJECT")

for _, q := range queues {
poolName := ""
if q.Pool != nil {
poolName = types.GetValue(q.Pool.Name, "")
}

projectID := ""
if q.ProjectId != nil {
projectID = q.ProjectId.String()
}

tp.AddField(fmt.Sprintf("%d", types.GetValue(q.Id, 0)))
tp.AddField(types.GetValue(q.Name, ""))
tp.AddField(poolName)
tp.AddField(projectID)
tp.EndRow()
}

return tp.Render()
}
Loading
Loading