• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

astronomer / astro-cli / 7d6dfa49-17c3-4f98-9f61-6d6efddfe894

25 Mar 2026 09:09PM UTC coverage: 36.216% (+0.1%) from 36.08%
7d6dfa49-17c3-4f98-9f61-6d6efddfe894

Pull #2043

circleci

jlaneve
feat(ai-84): add --json, --output, and --template flags to list commands

Add structured output support (JSON, Go templates) to all list commands:
- astro dev ps
- astro deployment list
- astro workspace list
- astro organization list
- astro deployment/workspace/organization user list
- astro deployment/workspace/organization team list

New pkg/output package provides a unified Printer that handles table,
JSON (with terminal colorization), and Go template output formats.
Flags (--json, --output/-o, --template) are registered consistently
via output.Flags and marked mutually exclusive.

Key design decisions:
- PS() returns structured *PSStatus; table rendering moved to cmd layer
- JSON output uses empty strings + omitempty instead of "N/A" placeholders
- Table output applies orNA() for human-readable display
- PSStatus.Running is *bool, omitted in Docker mode (per-container
  state is in Containers array), present in standalone mode
- BuildTableConfig generics provide type-safe table column definitions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pull Request #2043: [AI-84] --json output and --format consistency pass

419 of 680 new or added lines in 13 files covered. (61.62%)

18 existing lines in 2 files now uncovered.

24773 of 68404 relevant lines covered (36.22%)

8.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.35
/cmd/cloud/workspace.go
1
package cloud
2

3
import (
4
        "fmt"
5
        "io"
6
        "os"
7
        "strconv"
8
        "strings"
9

10
        astrocore "github.com/astronomer/astro-cli/astro-client-core"
11
        "github.com/astronomer/astro-cli/cloud/organization"
12
        "github.com/astronomer/astro-cli/cloud/team"
13
        "github.com/astronomer/astro-cli/cloud/user"
14
        "github.com/astronomer/astro-cli/cloud/workspace"
15
        workspacetoken "github.com/astronomer/astro-cli/cloud/workspace-token"
16
        "github.com/astronomer/astro-cli/pkg/input"
17
        "github.com/astronomer/astro-cli/pkg/output"
18
        "github.com/astronomer/astro-cli/pkg/printutil"
19

20
        "github.com/pkg/errors"
21
        "github.com/spf13/cobra"
22
)
23

24
var (
25
        errInvalidWorkspaceRoleKey   = errors.New("invalid workspace role selection")
26
        workspaceID                  string
27
        addWorkspaceRole             string
28
        updateWorkspaceRole          string
29
        workspaceName                string
30
        workspaceDescription         string
31
        enforceCD                    string
32
        tokenName                    string
33
        tokenDescription             string
34
        tokenRole                    string
35
        orgTokenName                 string
36
        tokenID                      string
37
        orgTokenID                   string
38
        workspaceTokenID             string
39
        cleanTokenOutput             bool
40
        forceRotate                  bool
41
        tokenExpiration              int
42
        validWorkspaceRoles          []string
43
        workspaceListOutputFlags     output.Flags
44
        workspaceUserListOutputFlags output.Flags
45
        workspaceTeamListOutputFlags output.Flags
46
)
47

48
const (
49
        allowedWorkspaceRoleNames      = "WORKSPACE_MEMBER, WORKSPACE_AUTHOR, WORKSPACE_OPERATOR, WORKSPACE_OWNER"
50
        allowedWorkspaceRoleNamesProse = "WORKSPACE_MEMBER, WORKSPACE_AUTHOR, WORKSPACE_OPERATOR, and WORKSPACE_OWNER"
51
)
52

53
func init() {
1✔
54
        validWorkspaceRoles = []string{"WORKSPACE_MEMBER", "WORKSPACE_AUTHOR", "WORKSPACE_OPERATOR", "WORKSPACE_OWNER"}
1✔
55
}
1✔
56

57
func newWorkspaceCmd(out io.Writer) *cobra.Command {
107✔
58
        cmd := &cobra.Command{
107✔
59
                Use:     "workspace",
107✔
60
                Aliases: []string{"wo"},
107✔
61
                Short:   "Manage Astro Workspaces",
107✔
62
                Long:    "Create and manage Workspaces on Astro. Workspaces can contain multiple Deployments and can be shared across users.",
107✔
63
        }
107✔
64
        cmd.AddCommand(
107✔
65
                newWorkspaceListCmd(out),
107✔
66
                newWorkspaceSwitchCmd(out),
107✔
67
                newWorkspaceCreateCmd(out),
107✔
68
                newWorkspaceUpdateCmd(out),
107✔
69
                newWorkspaceDeleteCmd(out),
107✔
70
                newWorkspaceUserRootCmd(out),
107✔
71
                newWorkspaceTokenRootCmd(out),
107✔
72
                newWorkspaceTeamRootCmd(out),
107✔
73
        )
107✔
74
        return cmd
107✔
75
}
107✔
76

77
func newWorkspaceListCmd(out io.Writer) *cobra.Command {
107✔
78
        cmd := &cobra.Command{
107✔
79
                Use:     "list",
107✔
80
                Aliases: []string{"ls"},
107✔
81
                Short:   "List all Astro Workspaces in your organization",
107✔
82
                Long:    "List all Astro Workspaces in your organization.",
107✔
83
                RunE: func(cmd *cobra.Command, args []string) error {
109✔
84
                        return workspaceList(cmd, out)
2✔
85
                },
2✔
86
        }
87
        workspaceListOutputFlags.AddFlags(cmd)
107✔
88
        return cmd
107✔
89
}
90

91
func newWorkspaceSwitchCmd(out io.Writer) *cobra.Command {
107✔
92
        cmd := &cobra.Command{
107✔
93
                Use:     "switch [workspace name/id]",
107✔
94
                Aliases: []string{"sw"},
107✔
95
                Short:   "Switch to a different Astro Workspace",
107✔
96
                Long:    "Switch to a different Astro Workspace",
107✔
97
                Args:    cobra.MaximumNArgs(1),
107✔
98
                RunE: func(cmd *cobra.Command, args []string) error {
108✔
99
                        return workspaceSwitch(cmd, out, args)
1✔
100
                },
1✔
101
        }
102
        return cmd
107✔
103
}
104

105
func newWorkspaceCreateCmd(out io.Writer) *cobra.Command {
107✔
106
        cmd := &cobra.Command{
107✔
107
                Use:     "create",
107✔
108
                Aliases: []string{"cr"},
107✔
109
                Short:   "Create an Astro Workspace",
107✔
110
                Long:    "Create an Astro Workspace",
107✔
111
                RunE: func(cmd *cobra.Command, args []string) error {
111✔
112
                        return workspaceCreate(cmd, out)
4✔
113
                },
4✔
114
        }
115
        cmd.Flags().StringVarP(&workspaceName, "name", "n", "", "The Workspace's name. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
116
        cmd.Flags().StringVarP(&workspaceDescription, "description", "d", "", "Description of the Workspace. If the description contains a space, specify the entire description in quotes \"\"")
107✔
117
        cmd.Flags().StringVarP(&enforceCD, "enforce-cicd", "e", "OFF", "Provide this flag either ON/OFF. ON means deploys to deployments must use an API Key or Token. This essentially forces Deploys to happen through CI/CD")
107✔
118
        return cmd
107✔
119
}
120

121
func newWorkspaceUpdateCmd(out io.Writer) *cobra.Command {
107✔
122
        cmd := &cobra.Command{
107✔
123
                Use:     "update [workspace_id]",
107✔
124
                Aliases: []string{"up"},
107✔
125
                Short:   "Update an Astro Workspace",
107✔
126
                Long:    "Update an Astro Workspace",
107✔
127
                Args:    cobra.MaximumNArgs(1),
107✔
128
                RunE: func(cmd *cobra.Command, args []string) error {
112✔
129
                        return workspaceUpdate(cmd, out, args)
5✔
130
                },
5✔
131
        }
132
        cmd.Flags().StringVarP(&workspaceName, "name", "n", "", "The Workspace's name. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
133
        cmd.Flags().StringVarP(&workspaceDescription, "description", "d", "", "Description of the Workspace. If the description contains a space, specify the entire description in quotes \"\"")
107✔
134
        cmd.Flags().StringVarP(&enforceCD, "enforce-cicd", "e", "OFF", "Provide this flag either ON/OFF. ON means deploys to deployments must use an API Key or Token. This essentially forces Deploys to happen through CI/CD")
107✔
135
        return cmd
107✔
136
}
137

138
func newWorkspaceDeleteCmd(out io.Writer) *cobra.Command {
107✔
139
        cmd := &cobra.Command{
107✔
140
                Use:     "delete [workspace_id]",
107✔
141
                Aliases: []string{"de"},
107✔
142
                Short:   "Delete an Astro Workspace",
107✔
143
                Long:    "Delete an Astro Workspace",
107✔
144
                Args:    cobra.MaximumNArgs(1),
107✔
145
                RunE: func(cmd *cobra.Command, args []string) error {
112✔
146
                        return workspaceDelete(cmd, out, args)
5✔
147
                },
5✔
148
        }
149
        return cmd
107✔
150
}
151

152
func newWorkspaceUserRootCmd(out io.Writer) *cobra.Command {
107✔
153
        cmd := &cobra.Command{
107✔
154
                Use:     "user",
107✔
155
                Aliases: []string{"us", "users"},
107✔
156
                Short:   "Manage users in your Astro Workspace",
107✔
157
                Long:    "Manage users in your Astro Workspace.",
107✔
158
        }
107✔
159
        cmd.SetOut(out)
107✔
160
        cmd.AddCommand(
107✔
161
                newWorkspaceUserListCmd(out),
107✔
162
                newWorkspaceUserUpdateCmd(out),
107✔
163
                newWorkspaceUserRemoveCmd(out),
107✔
164
                newWorkspaceUserAddCmd(out),
107✔
165
        )
107✔
166
        cmd.PersistentFlags().StringVar(&workspaceID, "workspace-id", "", "workspace where you'd like to manage users")
107✔
167

107✔
168
        return cmd
107✔
169
}
107✔
170

171
func newWorkspaceUserAddCmd(out io.Writer) *cobra.Command {
107✔
172
        cmd := &cobra.Command{
107✔
173
                Use:   "add [email]",
107✔
174
                Short: "Add a user to an Astro Workspace with a specific role",
107✔
175
                Long:  "Add a user to an Astro Workspace with a specific role\n$astro workspace user add [email] --role [" + allowedWorkspaceRoleNames + "].",
107✔
176
                RunE: func(cmd *cobra.Command, args []string) error {
112✔
177
                        return addWorkspaceUser(cmd, args, out)
5✔
178
                },
5✔
179
        }
180
        cmd.Flags().StringVarP(&addWorkspaceRole, "role", "r", "WORKSPACE_MEMBER", "The role for the "+
107✔
181
                "new user. Possible values are "+allowedWorkspaceRoleNamesProse)
107✔
182
        return cmd
107✔
183
}
184

185
func newWorkspaceUserListCmd(out io.Writer) *cobra.Command {
107✔
186
        cmd := &cobra.Command{
107✔
187
                Use:     "list",
107✔
188
                Aliases: []string{"ls"},
107✔
189
                Short:   "List all the users in an Astro Workspace",
107✔
190
                Long:    "List all the users in an Astro Workspace",
107✔
191
                RunE: func(cmd *cobra.Command, args []string) error {
109✔
192
                        return listWorkspaceUser(cmd, out)
2✔
193
                },
2✔
194
        }
195
        workspaceUserListOutputFlags.AddFlags(cmd)
107✔
196
        return cmd
107✔
197
}
198

199
func newWorkspaceUserUpdateCmd(out io.Writer) *cobra.Command {
107✔
200
        cmd := &cobra.Command{
107✔
201
                Use:     "update [email]",
107✔
202
                Aliases: []string{"up"},
107✔
203
                Short:   "Update a the role of a user in an Astro Workspace",
107✔
204
                Long:    "Update the role of a user in an Astro Workspace\n$astro workspace user update [email] --role [" + allowedWorkspaceRoleNames + "].",
107✔
205
                RunE: func(cmd *cobra.Command, args []string) error {
112✔
206
                        return updateWorkspaceUser(cmd, args, out)
5✔
207
                },
5✔
208
        }
209
        cmd.Flags().StringVarP(&updateWorkspaceRole, "role", "r", "", "The new role for the "+
107✔
210
                "user. Possible values are "+allowedWorkspaceRoleNamesProse)
107✔
211
        return cmd
107✔
212
}
213

214
func newWorkspaceUserRemoveCmd(out io.Writer) *cobra.Command {
107✔
215
        cmd := &cobra.Command{
107✔
216
                Use:     "remove",
107✔
217
                Aliases: []string{"rm"},
107✔
218
                Short:   "Remove a user from an Astro Workspace",
107✔
219
                Long:    "Remove a user from an Astro Workspace",
107✔
220
                RunE: func(cmd *cobra.Command, args []string) error {
111✔
221
                        return removeWorkspaceUser(cmd, args, out)
4✔
222
                },
4✔
223
        }
224
        return cmd
107✔
225
}
226

227
//nolint:dupl
228
func newWorkspaceTokenRootCmd(out io.Writer) *cobra.Command {
107✔
229
        cmd := &cobra.Command{
107✔
230
                Use:     "token",
107✔
231
                Aliases: []string{"to"},
107✔
232
                Short:   "Manage tokens in your Astro Workspace",
107✔
233
                Long:    "Manage tokens in your Astro Workspace.",
107✔
234
        }
107✔
235
        cmd.SetOut(out)
107✔
236
        cmd.AddCommand(
107✔
237
                newWorkspaceTokenListCmd(out),
107✔
238
                newWorkspaceTokenCreateCmd(out),
107✔
239
                newWorkspaceTokenUpdateCmd(out),
107✔
240
                newWorkspaceTokenRotateCmd(out),
107✔
241
                newWorkspaceTokenDeleteCmd(out),
107✔
242
                newWorkspaceTokenAddOrgTokenCmd(out),
107✔
243
                newWorkspaceOrgTokenManageCmd(out),
107✔
244
        )
107✔
245
        cmd.PersistentFlags().StringVar(&workspaceID, "workspace-id", "", "workspace where you would like to manage tokens")
107✔
246
        return cmd
107✔
247
}
107✔
248

249
//nolint:dupl
250
func newWorkspaceTokenListCmd(out io.Writer) *cobra.Command {
107✔
251
        cmd := &cobra.Command{
107✔
252
                Use:     "list",
107✔
253
                Aliases: []string{"ls"},
107✔
254
                Short:   "List all the API tokens in an Astro Workspace",
107✔
255
                Long:    "List all the API tokens in an Astro Workspace",
107✔
256
                RunE: func(cmd *cobra.Command, args []string) error {
111✔
257
                        return listWorkspaceToken(cmd, out)
4✔
258
                },
4✔
259
        }
260
        return cmd
107✔
261
}
262

263
//nolint:dupl
264
func newWorkspaceTeamRootCmd(out io.Writer) *cobra.Command {
107✔
265
        cmd := &cobra.Command{
107✔
266
                Use:     "team",
107✔
267
                Aliases: []string{"te", "teams"},
107✔
268
                Short:   "Manage teams in your Astro Workspace",
107✔
269
                Long:    "Manage teams in your Astro Workspace.",
107✔
270
        }
107✔
271
        cmd.SetOut(out)
107✔
272
        cmd.AddCommand(
107✔
273
                newWorkspaceTeamListCmd(out),
107✔
274
                newWorkspaceTeamUpdateCmd(out),
107✔
275
                newWorkspaceTeamRemoveCmd(out),
107✔
276
                newWorkspaceTeamAddCmd(out),
107✔
277
        )
107✔
278
        return cmd
107✔
279
}
107✔
280

281
func newWorkspaceTeamListCmd(out io.Writer) *cobra.Command {
107✔
282
        cmd := &cobra.Command{
107✔
283
                Use:     "list",
107✔
284
                Aliases: []string{"ls"},
107✔
285
                Short:   "List all the teams in an Astro Workspace",
107✔
286
                Long:    "List all the teams in an Astro Workspace",
107✔
287
                RunE: func(cmd *cobra.Command, args []string) error {
109✔
288
                        return listWorkspaceTeam(cmd, out)
2✔
289
                },
2✔
290
        }
291
        workspaceTeamListOutputFlags.AddFlags(cmd)
107✔
292
        return cmd
107✔
293
}
294

295
//nolint:dupl
296
func newWorkspaceTokenCreateCmd(out io.Writer) *cobra.Command {
107✔
297
        cmd := &cobra.Command{
107✔
298
                Use:     "create",
107✔
299
                Aliases: []string{"cr"},
107✔
300
                Short:   "Create an API token in an Astro Workspace",
107✔
301
                Long:    "Create an API token in an Astro Workspace\n$astro workspace token create --name [token name] --role [" + allowedWorkspaceRoleNames + "].",
107✔
302
                RunE: func(cmd *cobra.Command, args []string) error {
111✔
303
                        return createWorkspaceToken(cmd, out)
4✔
304
                },
4✔
305
        }
306
        cmd.Flags().StringVarP(&tokenName, "name", "n", "", "The token's name. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
307
        cmd.Flags().BoolVarP(&cleanTokenOutput, "clean-output", "c", false, "Print only the token as output. For use of the command in scripts")
107✔
308
        cmd.Flags().StringVarP(&tokenDescription, "description", "d", "", "Description of the token. If the description contains a space, specify the entire description within quotes \"\"")
107✔
309
        cmd.Flags().StringVarP(&tokenRole, "role", "r", "", "The role for the "+
107✔
310
                "token. Possible values are "+allowedWorkspaceRoleNamesProse)
107✔
311
        cmd.Flags().IntVarP(&tokenExpiration, "expiration", "e", 0, "Expiration of the token in days. If the flag isn't used the token won't have an expiration. Must be between 1 and 3650 days. ")
107✔
312
        return cmd
107✔
313
}
314

315
//nolint:dupl
316
func newWorkspaceTokenUpdateCmd(out io.Writer) *cobra.Command {
107✔
317
        cmd := &cobra.Command{
107✔
318
                Use:     "update [TOKEN_ID]",
107✔
319
                Aliases: []string{"up"},
107✔
320
                Short:   "Update a Workspace or Organaization API token",
107✔
321
                Long:    "Update a Workspace or Organaization API token that has a role in an Astro Workspace\n$astro workspace token update [TOKEN_ID] --name [new token name] --role [" + allowedWorkspaceRoleNames + "].",
107✔
322
                RunE: func(cmd *cobra.Command, args []string) error {
111✔
323
                        return updateWorkspaceToken(cmd, args, out)
4✔
324
                },
4✔
325
        }
326
        cmd.Flags().StringVarP(&name, "name", "t", "", "The current name of the token. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
327
        cmd.Flags().StringVarP(&tokenName, "new-name", "n", "", "The token's new name. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
328
        cmd.Flags().StringVarP(&tokenDescription, "description", "d", "", "updated description of the token. If the description contains a space, specify the entire description in quotes \"\"")
107✔
329
        cmd.Flags().StringVarP(&tokenRole, "role", "r", "", "The new role for the "+
107✔
330
                "token. Possible values are "+allowedWorkspaceRoleNamesProse)
107✔
331
        return cmd
107✔
332
}
333

334
//nolint:dupl
335
func newWorkspaceTokenRotateCmd(out io.Writer) *cobra.Command {
107✔
336
        cmd := &cobra.Command{
107✔
337
                Use:     "rotate [TOKEN_ID]",
107✔
338
                Aliases: []string{"ro"},
107✔
339
                Short:   "Rotate a Workspace API token",
107✔
340
                Long:    "Rotate a Workspace API token. You can only rotate Workspace API tokens. You cannot rotate Organization API tokens with this command",
107✔
341
                RunE: func(cmd *cobra.Command, args []string) error {
112✔
342
                        return rotateWorkspaceToken(cmd, args, out)
5✔
343
                },
5✔
344
        }
345
        cmd.Flags().BoolVarP(&cleanTokenOutput, "clean-output", "c", false, "Print only the token as output. For use of the command in scripts")
107✔
346
        cmd.Flags().StringVarP(&name, "name", "t", "", "The name of the token to be rotated. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
347
        cmd.Flags().BoolVarP(&forceRotate, "force", "f", false, "Rotate the Workspace API token without showing a warning")
107✔
348

107✔
349
        return cmd
107✔
350
}
351

352
//nolint:dupl
353
func newWorkspaceTokenDeleteCmd(out io.Writer) *cobra.Command {
107✔
354
        cmd := &cobra.Command{
107✔
355
                Use:     "delete [TOKEN_ID]",
107✔
356
                Aliases: []string{"de"},
107✔
357
                Short:   "Delete a Workspace API token or remove an Organization API token from a Workspace",
107✔
358
                Long:    "Delete a Workspace API token or remove an Organization API token from a Workspace",
107✔
359
                RunE: func(cmd *cobra.Command, args []string) error {
112✔
360
                        return deleteWorkspaceToken(cmd, args, out)
5✔
361
                },
5✔
362
        }
363
        cmd.Flags().StringVarP(&name, "name", "t", "", "The name of the token to be deleted. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
364
        cmd.Flags().BoolVarP(&forceDelete, "force", "f", false, "Delete or remove the API token without showing a warning")
107✔
365

107✔
366
        return cmd
107✔
367
}
368

369
//nolint:dupl
370
func newWorkspaceTokenAddOrgTokenCmd(out io.Writer) *cobra.Command {
107✔
371
        cmd := &cobra.Command{
107✔
372
                Use:   "add [ORG_TOKEN_ID]",
107✔
373
                Short: "Add an Organization API token to an Astro Workspace",
107✔
374
                Long:  "Add an Organization API token to an Astro Workspace\n$astro workspace token add [ORG_TOKEN_ID] --org-token-name [token name] --role [" + allowedWorkspaceRoleNames + "].",
107✔
375
                RunE: func(cmd *cobra.Command, args []string) error {
112✔
376
                        return addOrgTokenToWorkspace(cmd, args, out)
5✔
377
                },
5✔
378
        }
379
        cmd.Flags().StringVarP(&orgTokenName, "org-token-name", "n", "", "The name of the Organization API token you want to add to a Workspace. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
380
        cmd.Flags().StringVarP(&tokenRole, "role", "r", "", "The Workspace role to grant to the "+
107✔
381
                "Organization API token. Possible values are "+allowedWorkspaceRoleNamesProse)
107✔
382
        return cmd
107✔
383
}
384

385
//nolint:dupl
386
func newWorkspaceOrgTokenManageCmd(out io.Writer) *cobra.Command {
107✔
387
        cmd := &cobra.Command{
107✔
388
                Use:   "organization-token",
107✔
389
                Short: "Manage organization tokens in a workspace",
107✔
390
                Long:  "Manage organization tokens in a workspace",
107✔
391
        }
107✔
392
        cmd.SetOut(out)
107✔
393
        cmd.AddCommand(
107✔
394
                newAddOrganizationTokenWorkspaceRole(out),
107✔
395
                newUpdateOrganizationTokenWorkspaceRole(out),
107✔
396
                newRemoveOrganizationTokenWorkspaceRole(out),
107✔
397
                newListOrganizationTokensInWorkspace(out),
107✔
398
        )
107✔
399
        return cmd
107✔
400
}
107✔
401

402
func newAddOrganizationTokenWorkspaceRole(out io.Writer) *cobra.Command {
107✔
403
        cmd := &cobra.Command{
107✔
404
                Use:   "add [ORG_TOKEN_ID]",
107✔
405
                Short: "Add an Organization API token to a Workspace",
107✔
406
                Long:  "Add an Organization API token to a Workspace\n$astro workspace token organization-token add [ORG_TOKEN_ID] --org-token-name [token name] --role [" + allowedWorkspaceRoleNames + "].",
107✔
407
                RunE: func(cmd *cobra.Command, args []string) error {
109✔
408
                        return addOrgTokenWorkspaceRole(cmd, args, out)
2✔
409
                },
2✔
410
        }
411
        cmd.Flags().StringVarP(&orgTokenName, "org-token-name", "n", "", "The name of the Organization API token you want to add to a Workspace. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
412
        cmd.Flags().StringVarP(&tokenRole, "role", "r", "", "The Workspace role to grant to the "+
107✔
413
                "Organization API token. Possible values are"+allowedWorkspaceRoleNamesProse)
107✔
414
        return cmd
107✔
415
}
416

417
func newUpdateOrganizationTokenWorkspaceRole(out io.Writer) *cobra.Command {
107✔
418
        cmd := &cobra.Command{
107✔
419
                Use:   "update [ORG_TOKEN_ID]",
107✔
420
                Short: "Update an Organization API token's Workspace Role",
107✔
421
                Long:  "Update an Organization API token's Workspace Role\n$astro workspace token organization-token update [ORG_TOKEN_ID] --org-token-name [token name] --role [" + allowedWorkspaceRoleNames + "].",
107✔
422
                RunE: func(cmd *cobra.Command, args []string) error {
109✔
423
                        return updateOrgTokenWorkspaceRole(cmd, args, out)
2✔
424
                },
2✔
425
        }
426
        cmd.Flags().StringVarP(&orgTokenName, "org-token-name", "n", "", "The name of the Organization API token you want to update in a Workspace. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
427
        cmd.Flags().StringVarP(&tokenRole, "role", "r", "", "The Workspace role to update the "+
107✔
428
                "Organization API token. Possible values are"+allowedWorkspaceRoleNamesProse)
107✔
429
        return cmd
107✔
430
}
431

432
func addOrgTokenWorkspaceRole(cmd *cobra.Command, args []string, out io.Writer) error {
2✔
433
        // if an id was provided in the args we use it
2✔
434
        if len(args) > 0 {
3✔
435
                // make sure the id is lowercase
1✔
436
                orgTokenID = strings.ToLower(args[0])
1✔
437
        }
1✔
438
        if tokenRole == "" {
3✔
439
                // no role was provided so ask the user for it
1✔
440
                tokenRole = input.Text("Enter a role for the API token. Possible values are " + allowedWorkspaceRoleNamesProse + ": ")
1✔
441
        }
1✔
442
        cmd.SilenceUsage = true
2✔
443

2✔
444
        return workspacetoken.UpsertOrgTokenWorkspaceRole(orgTokenID, orgTokenName, tokenRole, workspaceID, "create", out, astroCoreClient, astroCoreIamClient)
2✔
445
}
446

447
func updateOrgTokenWorkspaceRole(cmd *cobra.Command, args []string, out io.Writer) error {
2✔
448
        // if an id was provided in the args we use it
2✔
449
        if len(args) > 0 {
3✔
450
                // make sure the id is lowercase
1✔
451
                orgTokenID = strings.ToLower(args[0])
1✔
452
        }
1✔
453
        if tokenRole == "" {
3✔
454
                // no role was provided so ask the user for it
1✔
455
                tokenRole = input.Text("Enter a role for the new Workspace API token. Possible values are " + allowedWorkspaceRoleNamesProse + ": ")
1✔
456
        }
1✔
457
        cmd.SilenceUsage = true
2✔
458

2✔
459
        return workspacetoken.UpsertOrgTokenWorkspaceRole(orgTokenID, orgTokenName, tokenRole, workspaceID, "update", out, astroCoreClient, astroCoreIamClient)
2✔
460
}
461

462
func newRemoveOrganizationTokenWorkspaceRole(out io.Writer) *cobra.Command {
107✔
463
        cmd := &cobra.Command{
107✔
464
                Use:   "remove [ORG_TOKEN_ID]",
107✔
465
                Short: "Remove an Organization API token's Workspace Role",
107✔
466
                Long:  "Remove an Organization API token's Workspace Role\n$astro workspace token organization-token remove [ORG_TOKEN_ID] --org-token-name [token name].",
107✔
467
                RunE: func(cmd *cobra.Command, args []string) error {
109✔
468
                        return removeOrganizationTokenWorkspaceRole(cmd, args, out)
2✔
469
                },
2✔
470
        }
471
        cmd.Flags().StringVarP(&orgTokenName, "org-token-name", "n", "", "The name of the Workspace API token you want to remove from a Deployment. If the name contains a space, specify the entire name within quotes \"\" ")
107✔
472
        return cmd
107✔
473
}
474

475
func removeOrganizationTokenWorkspaceRole(cmd *cobra.Command, args []string, out io.Writer) error {
2✔
476
        // if an id was provided in the args we use it
2✔
477
        if len(args) > 0 {
3✔
478
                // make sure the id is lowercase
1✔
479
                orgTokenID = strings.ToLower(args[0])
1✔
480
        }
1✔
481

482
        cmd.SilenceUsage = true
2✔
483
        return workspacetoken.RemoveOrgTokenWorkspaceRole(orgTokenID, orgTokenName, workspaceID, out, astroCoreClient, astroCoreIamClient)
2✔
484
}
485

486
func newListOrganizationTokensInWorkspace(out io.Writer) *cobra.Command {
107✔
487
        cmd := &cobra.Command{
107✔
488
                Use:   "list",
107✔
489
                Short: "List all Organization API tokens in a workspace",
107✔
490
                Long:  "List all Organization API tokens in a workspace\n$astro workspace token organization-token list",
107✔
491
                RunE: func(cmd *cobra.Command, args []string) error {
108✔
492
                        return listOrganizationTokensInWorkspace(cmd, out)
1✔
493
                },
1✔
494
        }
495
        return cmd
107✔
496
}
497

498
func listOrganizationTokensInWorkspace(cmd *cobra.Command, out io.Writer) error {
1✔
499
        cmd.SilenceUsage = true
1✔
500
        tokenTypes := []astrocore.ListWorkspaceApiTokensParamsTokenTypes{
1✔
501
                "ORGANIZATION",
1✔
502
        }
1✔
503
        return workspacetoken.ListTokens(astroCoreClient, deploymentID, &tokenTypes, out)
1✔
504
}
1✔
505

506
func newWorkspaceTeamRemoveCmd(out io.Writer) *cobra.Command {
107✔
507
        cmd := &cobra.Command{
107✔
508
                Use:     "remove",
107✔
509
                Aliases: []string{"rm"},
107✔
510
                Short:   "Remove a team from an Astro Workspace",
107✔
511
                Long:    "Remove a team from an Astro Workspace",
107✔
512
                RunE: func(cmd *cobra.Command, args []string) error {
111✔
513
                        return removeWorkspaceTeam(cmd, args, out)
4✔
514
                },
4✔
515
        }
516
        return cmd
107✔
517
}
518

519
func listWorkspaceTeam(cmd *cobra.Command, out io.Writer) error {
2✔
520
        format, err := workspaceTeamListOutputFlags.Resolve()
2✔
521
        if err != nil {
2✔
NEW
522
                return err
×
NEW
523
        }
×
524

525
        cmd.SilenceUsage = true
2✔
526
        return team.ListWorkspaceTeamsWithFormat(astroCoreClient, "", format, workspaceTeamListOutputFlags.Template, out)
2✔
527
}
528

529
func removeWorkspaceTeam(cmd *cobra.Command, args []string, out io.Writer) error {
4✔
530
        var id string
4✔
531

4✔
532
        // if an id was provided in the args we use it
4✔
533
        if len(args) > 0 {
7✔
534
                // make sure the email is lowercase
3✔
535
                id = args[0]
3✔
536
        }
3✔
537
        cmd.SilenceUsage = true
4✔
538
        return team.RemoveWorkspaceTeam(id, "", out, astroCoreClient)
4✔
539
}
540

541
func newWorkspaceTeamAddCmd(out io.Writer) *cobra.Command {
107✔
542
        cmd := &cobra.Command{
107✔
543
                Use:   "add [id]",
107✔
544
                Short: "Add a team to an Astro Workspace with a specific role",
107✔
545
                Long:  "Add a team to an Astro Workspace with a specific role\n$astro workspace team add [id] --role [" + allowedWorkspaceRoleNames + "].",
107✔
546
                RunE: func(cmd *cobra.Command, args []string) error {
113✔
547
                        return addWorkspaceTeam(cmd, args, out)
6✔
548
                },
6✔
549
        }
550
        cmd.Flags().StringVarP(&workspaceID, "workspace-id", "w", "", "The Workspace's unique identifier")
107✔
551
        cmd.Flags().StringVarP(&addWorkspaceRole, "role", "r", "WORKSPACE_MEMBER", "The role for the "+
107✔
552
                "new team. Possible values are "+allowedWorkspaceRoleNamesProse)
107✔
553
        return cmd
107✔
554
}
555

556
func addWorkspaceTeam(cmd *cobra.Command, args []string, out io.Writer) error {
6✔
557
        var id string
6✔
558

6✔
559
        // if an email was provided in the args we use it
6✔
560
        if len(args) > 0 {
11✔
561
                // make sure the email is lowercase
5✔
562
                id = args[0]
5✔
563
        }
5✔
564
        cmd.SilenceUsage = true
6✔
565
        return team.AddWorkspaceTeam(id, addWorkspaceRole, workspaceID, out, astroCoreClient)
6✔
566
}
567

568
func newWorkspaceTeamUpdateCmd(out io.Writer) *cobra.Command {
107✔
569
        cmd := &cobra.Command{
107✔
570
                Use:     "update [id]",
107✔
571
                Aliases: []string{"up"},
107✔
572
                Short:   "Update a the role of a team in an Astro Workspace",
107✔
573
                Long:    "Update the role of a team in an Astro Workspace\n$astro workspace team update [id] --role [" + allowedWorkspaceRoleNames + "].",
107✔
574
                RunE: func(cmd *cobra.Command, args []string) error {
112✔
575
                        return updateWorkspaceTeam(cmd, args, out)
5✔
576
                },
5✔
577
        }
578
        cmd.Flags().StringVarP(&updateWorkspaceRole, "role", "r", "", "The new role for the "+
107✔
579
                "team. Possible values are "+allowedWorkspaceRoleNamesProse)
107✔
580
        return cmd
107✔
581
}
582

583
func updateWorkspaceTeam(cmd *cobra.Command, args []string, out io.Writer) error {
5✔
584
        var id string
5✔
585

5✔
586
        // if an id was provided in the args we use it
5✔
587
        if len(args) > 0 {
9✔
588
                id = args[0]
4✔
589
        }
4✔
590
        var err error
5✔
591
        if updateWorkspaceRole == "" {
5✔
592
                // no role was provided so ask the user for it
×
593
                updateWorkspaceRole, err = selectWorkspaceRole()
×
594
                if err != nil {
×
595
                        return err
×
596
                }
×
597
        }
598

599
        cmd.SilenceUsage = true
5✔
600
        return team.UpdateWorkspaceTeamRole(id, updateWorkspaceRole, "", out, astroCoreClient)
5✔
601
}
602

603
func workspaceList(cmd *cobra.Command, out io.Writer) error {
2✔
604
        format, err := workspaceListOutputFlags.Resolve()
2✔
605
        if err != nil {
2✔
NEW
606
                return err
×
NEW
607
        }
×
608

609
        // Silence Usage as we have now validated command input
610
        cmd.SilenceUsage = true
2✔
611
        return workspace.ListWithFormat(astroCoreClient, format, workspaceListOutputFlags.Template, out)
2✔
612
}
613

614
func workspaceSwitch(cmd *cobra.Command, out io.Writer, args []string) error {
1✔
615
        // Silence Usage as we have now validated command input
1✔
616

1✔
617
        workspaceNameOrID := ""
1✔
618

1✔
619
        if len(args) == 1 {
1✔
620
                workspaceNameOrID = args[0]
×
621
        }
×
622
        cmd.SilenceUsage = true
1✔
623
        return workspace.Switch(workspaceNameOrID, astroCoreClient, out)
1✔
624
}
625

626
func workspaceCreate(cmd *cobra.Command, out io.Writer) error {
4✔
627
        cmd.SilenceUsage = true
4✔
628
        return workspace.Create(workspaceName, workspaceDescription, enforceCD, out, astroCoreClient)
4✔
629
}
4✔
630

631
func workspaceUpdate(cmd *cobra.Command, out io.Writer, args []string) error {
5✔
632
        id := ""
5✔
633

5✔
634
        if len(args) == 1 {
9✔
635
                id = args[0]
4✔
636
        }
4✔
637
        cmd.SilenceUsage = true
5✔
638
        return workspace.Update(id, workspaceName, workspaceDescription, enforceCD, out, astroCoreClient)
5✔
639
}
640

641
func workspaceDelete(cmd *cobra.Command, out io.Writer, args []string) error {
5✔
642
        id := ""
5✔
643

5✔
644
        if len(args) == 1 {
9✔
645
                id = args[0]
4✔
646
        }
4✔
647
        cmd.SilenceUsage = true
5✔
648
        return workspace.Delete(id, out, astroCoreClient)
5✔
649
}
650

651
func addWorkspaceUser(cmd *cobra.Command, args []string, out io.Writer) error {
5✔
652
        var email string
5✔
653

5✔
654
        // if an email was provided in the args we use it
5✔
655
        if len(args) > 0 {
9✔
656
                // make sure the email is lowercase
4✔
657
                email = strings.ToLower(args[0])
4✔
658
        }
4✔
659

660
        cmd.SilenceUsage = true
5✔
661
        return user.AddWorkspaceUser(email, addWorkspaceRole, workspaceID, out, astroCoreClient)
5✔
662
}
663

664
func listWorkspaceUser(cmd *cobra.Command, out io.Writer) error {
2✔
665
        format, err := workspaceUserListOutputFlags.Resolve()
2✔
666
        if err != nil {
2✔
NEW
667
                return err
×
NEW
668
        }
×
669

670
        cmd.SilenceUsage = true
2✔
671
        return user.ListWorkspaceUsersWithFormat(astroCoreClient, workspaceID, format, workspaceUserListOutputFlags.Template, out)
2✔
672
}
673

674
func updateWorkspaceUser(cmd *cobra.Command, args []string, out io.Writer) error {
5✔
675
        var email string
5✔
676

5✔
677
        // if an email was provided in the args we use it
5✔
678
        if len(args) > 0 {
9✔
679
                // make sure the email is lowercase
4✔
680
                email = strings.ToLower(args[0])
4✔
681
        }
4✔
682

683
        if updateWorkspaceRole == "" {
5✔
684
                // no role was provided so ask the user for it
×
685
                updateWorkspaceRole = input.Text("Enter a user Workspace role(" + allowedWorkspaceRoleNamesProse + ") to update user: ")
×
686
        }
×
687

688
        cmd.SilenceUsage = true
5✔
689
        return user.UpdateWorkspaceUserRole(email, updateWorkspaceRole, workspaceID, out, astroCoreClient)
5✔
690
}
691

692
func removeWorkspaceUser(cmd *cobra.Command, args []string, out io.Writer) error {
4✔
693
        var email string
4✔
694

4✔
695
        // if an email was provided in the args we use it
4✔
696
        if len(args) > 0 {
7✔
697
                // make sure the email is lowercase
3✔
698
                email = strings.ToLower(args[0])
3✔
699
        }
3✔
700

701
        cmd.SilenceUsage = true
4✔
702
        return user.RemoveWorkspaceUser(email, workspaceID, out, astroCoreClient)
4✔
703
}
704

705
func listWorkspaceToken(cmd *cobra.Command, out io.Writer) error {
4✔
706
        cmd.SilenceUsage = true
4✔
707
        return workspacetoken.ListTokens(astroCoreClient, workspaceID, nil, out)
4✔
708
}
4✔
709

710
func createWorkspaceToken(cmd *cobra.Command, out io.Writer) error {
4✔
711
        if tokenName == "" {
5✔
712
                // no role was provided so ask the user for it
1✔
713
                tokenName = input.Text("Enter a name for the new Workspace API token: ")
1✔
714
        }
1✔
715
        if tokenRole == "" {
5✔
716
                fmt.Println("select a Workspace Role for the new API token:")
1✔
717
                // no role was provided so ask the user for it
1✔
718
                var err error
1✔
719
                tokenRole, err = selectWorkspaceRole()
1✔
720
                if err != nil {
1✔
721
                        return err
×
722
                }
×
723
        }
724
        cmd.SilenceUsage = true
4✔
725

4✔
726
        return workspacetoken.CreateToken(tokenName, tokenDescription, tokenRole, workspaceID, tokenExpiration, cleanTokenOutput, out, astroCoreClient)
4✔
727
}
728

729
func updateWorkspaceToken(cmd *cobra.Command, args []string, out io.Writer) error {
4✔
730
        // if an id was provided in the args we use it
4✔
731
        if len(args) > 0 {
4✔
732
                // make sure the id is lowercase
×
733
                tokenID = strings.ToLower(args[0])
×
734
        }
×
735

736
        cmd.SilenceUsage = true
4✔
737
        return workspacetoken.UpdateToken(tokenID, name, tokenName, tokenDescription, tokenRole, workspaceID, out, astroCoreClient, astroCoreIamClient)
4✔
738
}
739

740
func rotateWorkspaceToken(cmd *cobra.Command, args []string, out io.Writer) error {
5✔
741
        // if an id was provided in the args we use it
5✔
742
        if len(args) > 0 {
5✔
743
                // make sure the id is lowercase
×
744
                tokenID = strings.ToLower(args[0])
×
745
        }
×
746
        cmd.SilenceUsage = true
5✔
747
        return workspacetoken.RotateToken(tokenID, name, workspaceID, cleanTokenOutput, forceRotate, out, astroCoreClient, astroCoreIamClient)
5✔
748
}
749

750
func deleteWorkspaceToken(cmd *cobra.Command, args []string, out io.Writer) error {
5✔
751
        // if an id was provided in the args we use it
5✔
752
        if len(args) > 0 {
5✔
753
                // make sure the id is lowercase
×
754
                tokenID = strings.ToLower(args[0])
×
755
        }
×
756

757
        cmd.SilenceUsage = true
5✔
758
        return workspacetoken.DeleteToken(tokenID, name, workspaceID, forceDelete, out, astroCoreClient, astroCoreIamClient)
5✔
759
}
760

761
func addOrgTokenToWorkspace(cmd *cobra.Command, args []string, out io.Writer) error {
5✔
762
        // if an id was provided in the args we use it
5✔
763
        if len(args) > 0 {
5✔
764
                // make sure the id is lowercase
×
765
                orgTokenID = strings.ToLower(args[0])
×
766
        }
×
767
        if tokenRole == "" {
6✔
768
                fmt.Println("select a Workspace Role for the Organization Token:")
1✔
769
                // no role was provided so ask the user for it
1✔
770
                var err error
1✔
771
                tokenRole, err = selectWorkspaceRole()
1✔
772
                if err != nil {
1✔
773
                        return err
×
774
                }
×
775
        }
776
        cmd.SilenceUsage = true
5✔
777
        return organization.AddOrgTokenToWorkspace(orgTokenID, orgTokenName, tokenRole, workspaceID, out, astroCoreClient, astroCoreIamClient)
5✔
778
}
779

780
func coalesceWorkspace() (string, error) {
138✔
781
        wsFlag := workspaceID
138✔
782
        wsCfg, err := workspace.GetCurrentWorkspace()
138✔
783
        if err != nil {
157✔
784
                return "", errors.Wrap(err, "failed to get current Workspace")
19✔
785
        }
19✔
786

787
        if wsFlag != "" {
146✔
788
                return wsFlag, nil
27✔
789
        }
27✔
790

791
        if wsCfg != "" {
184✔
792
                return wsCfg, nil
92✔
793
        }
92✔
794

795
        return "", errors.New("no valid Workspace source found")
×
796
}
797

798
func selectWorkspaceRole() (string, error) {
2✔
799
        tokenRolesMap := map[string]string{}
2✔
800
        tab := &printutil.Table{
2✔
801
                Padding:        []int{44, 50},
2✔
802
                DynamicPadding: true,
2✔
803
                Header:         []string{"#", "ROLE"},
2✔
804
        }
2✔
805
        for i := range validWorkspaceRoles {
10✔
806
                index := i + 1
8✔
807
                tab.AddRow([]string{
8✔
808
                        strconv.Itoa(index),
8✔
809
                        validWorkspaceRoles[i],
8✔
810
                }, false)
8✔
811
                tokenRolesMap[strconv.Itoa(index)] = validWorkspaceRoles[i]
8✔
812
        }
8✔
813

814
        tab.Print(os.Stdout)
2✔
815
        choice := input.Text("\n> ")
2✔
816
        selected, ok := tokenRolesMap[choice]
2✔
817
        if !ok {
2✔
818
                return "", errInvalidWorkspaceRoleKey
×
819
        }
×
820
        return selected, nil
2✔
821
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc