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

astronomer / astro-cli / 716c27ab-af54-4fbb-904c-9fa0a6da08dc

26 May 2026 02:32PM UTC coverage: 44.676% (+5.0%) from 39.653%
716c27ab-af54-4fbb-904c-9fa0a6da08dc

push

circleci

web-flow
Migrate CLI to v1 public API; retire v1beta1 and v1alpha1 (except IDE) (#2093)

1848 of 18362 new or added lines in 58 files covered. (10.06%)

925 existing lines in 15 files now uncovered.

24957 of 55862 relevant lines covered (44.68%)

7.74 hits per line

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

88.35
/cloud/workspace/workspace.go
1
package workspace
2

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

10
        "github.com/pkg/errors"
11

12
        "github.com/astronomer/astro-cli/astro-client-v1"
13
        "github.com/astronomer/astro-cli/config"
14
        "github.com/astronomer/astro-cli/context"
15
        "github.com/astronomer/astro-cli/pkg/ansi"
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

21
var (
22
        errInvalidWorkspaceKey = errors.New("invalid workspace selection")
23
        ErrInvalidName         = errors.New("no name provided for the workspace. Retry with a valid name")
24
        ErrInvalidTokenName    = errors.New("no name provided for the workspace token. Retry with a valid name")
25
        ErrWorkspaceNotFound   = errors.New("no workspace was found for the ID you provided")
26
        ErrNoWorkspaceExists   = errors.New("no workspace was found in your organization")
27
        ErrWrongEnforceInput   = errors.New("the input to the `--enforce-cicd` flag")
28
)
29

30
var workspaceTableConfig = output.BuildTableConfig(
31
        []output.Column[WorkspaceInfo]{
32
                {Header: "NAME", Value: func(w WorkspaceInfo) string { return w.Name }},
2✔
33
                {Header: "ID", Value: func(w WorkspaceInfo) string { return w.ID }},
2✔
34
        },
35
        func(d any) []WorkspaceInfo { return d.(*WorkspaceList).Workspaces },
2✔
36
        output.WithColorRow(func(w WorkspaceInfo) bool { return w.IsCurrent }, [2]string{"\033[1;32m", "\033[0m"}),
2✔
37
        output.WithPadding([]int{44, 50}),
38
)
39

40
// GetCurrentWorkspace gets the current workspace set in context config
41
// Returns a string representing the current workspace and an error if it doesn't exist
42
func GetCurrentWorkspace() (string, error) {
3✔
43
        c, err := config.GetCurrentContext()
3✔
44
        if err != nil {
4✔
45
                return "", err
1✔
46
        }
1✔
47

48
        if c.Workspace == "" {
3✔
49
                return "", errors.New("current workspace context not set, you can switch to a workspace with \n\tastro workspace switch WORKSPACEID")
1✔
50
        }
1✔
51

52
        return c.Workspace, nil
1✔
53
}
54

55
// ListData returns workspace list data for structured output
56
func ListData(client astrov1.APIClient) (*WorkspaceList, error) {
6✔
57
        c, err := config.GetCurrentContext()
6✔
58
        if err != nil {
6✔
59
                return nil, err
×
60
        }
×
61

62
        ws, err := GetWorkspaces(client)
6✔
63
        if err != nil {
8✔
64
                return nil, err
2✔
65
        }
2✔
66

67
        result := &WorkspaceList{
4✔
68
                Workspaces: make([]WorkspaceInfo, 0, len(ws)),
4✔
69
        }
4✔
70

4✔
71
        for i := range ws {
8✔
72
                isCurrent := c.Workspace == ws[i].Id
4✔
73
                result.Workspaces = append(result.Workspaces, WorkspaceInfo{
4✔
74
                        Name:      ws[i].Name,
4✔
75
                        ID:        ws[i].Id,
4✔
76
                        IsCurrent: isCurrent,
4✔
77
                })
4✔
78
        }
4✔
79

80
        return result, nil
4✔
81
}
82

83
// List all workspaces
84
func List(client astrov1.APIClient, out io.Writer) error {
2✔
85
        return ListWithFormat(client, output.FormatTable, "", out)
2✔
86
}
2✔
87

88
// ListWithFormat lists workspaces with the specified output format
89
func ListWithFormat(client astrov1.APIClient, format output.Format, tmpl string, out io.Writer) error {
4✔
90
        return output.PrintData(
4✔
91
                func() (*WorkspaceList, error) { return ListData(client) },
8✔
92
                workspaceTableConfig, format, tmpl, out,
93
        )
94
}
95

96
var GetWorkspaceSelection = func(client astrov1.APIClient, out io.Writer) (string, error) {
6✔
97
        tab := printutil.Table{
6✔
98
                Padding:        []int{5, 44, 50},
6✔
99
                DynamicPadding: true,
6✔
100
                Header:         []string{"#", "NAME", "ID"},
6✔
101
                ColorRowCode:   [2]string{"\033[1;32m", "\033[0m"},
6✔
102
        }
6✔
103

6✔
104
        var c config.Context
6✔
105
        c, err := config.GetCurrentContext()
6✔
106
        if err != nil {
7✔
107
                return "", err
1✔
108
        }
1✔
109

110
        ws, err := GetWorkspaces(client)
5✔
111
        if err != nil {
6✔
112
                return "", err
1✔
113
        }
1✔
114

115
        deployMap := map[string]astrov1.Workspace{}
4✔
116
        for i := range ws {
10✔
117
                index := i + 1
6✔
118

6✔
119
                color := c.Workspace == ws[i].Id
6✔
120
                tab.AddRow([]string{strconv.Itoa(index), ws[i].Name, ws[i].Id}, color)
6✔
121

6✔
122
                deployMap[strconv.Itoa(index)] = ws[i]
6✔
123
        }
6✔
124
        tab.Print(out)
4✔
125
        choice := input.Text("\n> ")
4✔
126
        selected, ok := deployMap[choice]
4✔
127
        if !ok {
6✔
128
                return "", errInvalidWorkspaceKey
2✔
129
        }
2✔
130

131
        return selected.Id, nil
2✔
132
}
133

134
func Switch(workspaceNameOrID string, client astrov1.APIClient, out io.Writer) error {
5✔
135
        var wsID string
5✔
136
        if workspaceNameOrID == "" {
7✔
137
                id, err := GetWorkspaceSelection(client, out)
2✔
138
                if err != nil {
3✔
139
                        return err
1✔
140
                }
1✔
141

142
                wsID = id
1✔
143
        } else {
3✔
144
                ws, err := GetWorkspaces(client)
3✔
145
                if err != nil {
5✔
146
                        return err
2✔
147
                }
2✔
148
                for i := range ws {
3✔
149
                        if ws[i].Name == workspaceNameOrID || ws[i].Id == workspaceNameOrID {
3✔
150
                                wsID = ws[i].Id
1✔
151
                        }
1✔
152
                }
153

154
                if wsID == "" {
1✔
155
                        return errors.Wrap(err, "workspace id/name could not be found")
×
156
                }
×
157
        }
158

159
        c, err := config.GetCurrentContext()
2✔
160
        if err != nil {
2✔
161
                return err
×
162
        }
×
163

164
        err = c.SetContextKey("workspace", wsID)
2✔
165
        if err != nil {
2✔
166
                return err
×
167
        }
×
168

169
        err = c.SetContextKey("last_used_workspace", wsID)
2✔
170
        if err != nil {
2✔
171
                return err
×
172
        }
×
173

174
        err = c.SetOrganizationContext(c.Organization, c.OrganizationProduct)
2✔
175
        if err != nil {
2✔
176
                return err
×
177
        }
×
178

179
        err = config.PrintCurrentCloudContext(out)
2✔
180
        if err != nil {
2✔
181
                return err
×
182
        }
×
183

184
        return nil
2✔
185
}
186

187
func validateEnforceCD(enforceCD string) (bool, error) {
6✔
188
        var enforce bool
6✔
189
        switch {
6✔
190
        case enforceCD == "OFF" || enforceCD == "":
×
191
                enforce = false
×
192
        case enforceCD == "ON":
5✔
193
                enforce = true
5✔
194
        default:
1✔
195
                return false, ErrWrongEnforceInput
1✔
196
        }
197
        return enforce, nil
5✔
198
}
199

200
func Create(name, description, enforceCD string, out io.Writer, client astrov1.APIClient) error {
5✔
201
        if name == "" {
5✔
202
                return ErrInvalidName
×
203
        }
×
204
        ctx, err := context.GetCurrentContext()
5✔
205
        if err != nil {
6✔
206
                return err
1✔
207
        }
1✔
208
        enforce, err := validateEnforceCD(enforceCD)
4✔
209
        if err != nil {
5✔
210
                return err
1✔
211
        }
1✔
212
        workspaceCreateRequest := astrov1.CreateWorkspaceJSONRequestBody{
3✔
213
                CicdEnforcedDefault: &enforce,
3✔
214
                Description:         &description,
3✔
215
                Name:                name,
3✔
216
        }
3✔
217
        resp, err := client.CreateWorkspaceWithResponse(httpContext.Background(), ctx.Organization, workspaceCreateRequest)
3✔
218
        if err != nil {
4✔
219
                return err
1✔
220
        }
1✔
221
        err = astrov1.NormalizeAPIError(resp.HTTPResponse, resp.Body)
2✔
222
        if err != nil {
3✔
223
                return err
1✔
224
        }
1✔
225
        fmt.Fprintf(out, "Astro Workspace %s was successfully created\n", name)
1✔
226
        return nil
1✔
227
}
228

229
func Update(id, name, description, enforceCD string, out io.Writer, client astrov1.APIClient) error {
7✔
230
        ctx, err := context.GetCurrentContext()
7✔
231
        if err != nil {
8✔
232
                return err
1✔
233
        }
1✔
234
        workspaces, err := GetWorkspaces(client)
6✔
235
        if err != nil {
6✔
236
                return err
×
237
        }
×
238
        var workspace astrov1.Workspace
6✔
239
        if id == "" {
8✔
240
                workspace, err = selectWorkspace(workspaces)
2✔
241
                if workspace.Id == "" {
3✔
242
                        return ErrNoWorkspaceExists
1✔
243
                }
1✔
244
                if err != nil {
1✔
245
                        return err
×
246
                }
×
247
        } else {
4✔
248
                for i := range workspaces {
12✔
249
                        if workspaces[i].Id == id {
12✔
250
                                workspace = workspaces[i]
4✔
251
                        }
4✔
252
                }
253
                if workspace.Id == "" {
4✔
254
                        return ErrWorkspaceNotFound
×
255
                }
×
256
        }
257
        workspaceID := workspace.Id
5✔
258

5✔
259
        workspaceUpdateRequest := astrov1.UpdateWorkspaceRequest{}
5✔
260

5✔
261
        if name == "" {
8✔
262
                workspaceUpdateRequest.Name = workspace.Name
3✔
263
        } else {
5✔
264
                workspaceUpdateRequest.Name = name
2✔
265
        }
2✔
266

267
        if description == "" {
8✔
268
                if workspace.Description != nil {
6✔
269
                        workspaceUpdateRequest.Description = *workspace.Description
3✔
270
                }
3✔
271
        } else {
2✔
272
                workspaceUpdateRequest.Description = description
2✔
273
        }
2✔
274
        if enforceCD == "" {
8✔
275
                workspaceUpdateRequest.CicdEnforcedDefault = workspace.CicdEnforcedDefault
3✔
276
        } else {
5✔
277
                enforce, err := validateEnforceCD(enforceCD)
2✔
278
                if err != nil {
2✔
279
                        return err
×
280
                }
×
281
                workspaceUpdateRequest.CicdEnforcedDefault = enforce
2✔
282
        }
283
        resp, err := client.UpdateWorkspaceWithResponse(httpContext.Background(), ctx.Organization, workspaceID, workspaceUpdateRequest)
5✔
284
        if err != nil {
6✔
285
                return err
1✔
286
        }
1✔
287
        err = astrov1.NormalizeAPIError(resp.HTTPResponse, resp.Body)
4✔
288
        if err != nil {
5✔
289
                return err
1✔
290
        }
1✔
291
        fmt.Fprintf(out, "Astro Workspace %s was successfully updated\n", workspace.Name)
3✔
292
        return nil
3✔
293
}
294

295
func Delete(id string, out io.Writer, client astrov1.APIClient) error {
6✔
296
        ctx, err := context.GetCurrentContext()
6✔
297
        if err != nil {
7✔
298
                return err
1✔
299
        }
1✔
300
        workspaces, err := GetWorkspaces(client)
5✔
301
        if err != nil {
5✔
302
                return err
×
303
        }
×
304
        var workspace astrov1.Workspace
5✔
305
        if id == "" {
7✔
306
                workspace, err = selectWorkspace(workspaces)
2✔
307
                if workspace.Id == "" {
3✔
308
                        return ErrNoWorkspaceExists
1✔
309
                }
1✔
310
                if err != nil {
1✔
311
                        return err
×
312
                }
×
313
        } else {
3✔
314
                for i := range workspaces {
6✔
315
                        if workspaces[i].Id == id {
6✔
316
                                workspace = workspaces[i]
3✔
317
                        }
3✔
318
                }
319
                if workspace.Id == "" {
3✔
320
                        return ErrWorkspaceNotFound
×
321
                }
×
322
        }
323
        workspaceID := workspace.Id
4✔
324
        resp, err := client.DeleteWorkspaceWithResponse(httpContext.Background(), ctx.Organization, workspaceID)
4✔
325
        if err != nil {
5✔
326
                return err
1✔
327
        }
1✔
328
        err = astrov1.NormalizeAPIError(resp.HTTPResponse, resp.Body)
3✔
329
        if err != nil {
4✔
330
                return err
1✔
331
        }
1✔
332
        fmt.Fprintf(out, "Astro Workspace %s was successfully deleted\n", workspace.Name)
2✔
333
        return nil
2✔
334
}
335

336
func selectWorkspace(workspaces []astrov1.Workspace) (astrov1.Workspace, error) {
4✔
337
        if len(workspaces) == 0 {
6✔
338
                return astrov1.Workspace{}, nil
2✔
339
        }
2✔
340

341
        if len(workspaces) == 1 {
3✔
342
                fmt.Println("Only one Workspace was found. Using the following Workspace by default: \n" +
1✔
343
                        fmt.Sprintf("\n Workspace Name: %s", ansi.Bold(workspaces[0].Name)) +
1✔
344
                        fmt.Sprintf("\n Workspace ID: %s\n", ansi.Bold(workspaces[0].Id)))
1✔
345

1✔
346
                return workspaces[0], nil
1✔
347
        }
1✔
348

349
        table := printutil.Table{
1✔
350
                Padding:        []int{30, 50, 10, 50, 10, 10, 10},
1✔
351
                DynamicPadding: true,
1✔
352
                Header:         []string{"#", "WORKSPACENAME", "ID", "CICD ENFORCEMENT"},
1✔
353
        }
1✔
354

1✔
355
        fmt.Println("\nPlease select the workspace you would like to update:")
1✔
356

1✔
357
        workspaceMap := map[string]astrov1.Workspace{}
1✔
358
        for i := range workspaces {
3✔
359
                index := i + 1
2✔
360
                table.AddRow([]string{
2✔
361
                        strconv.Itoa(index),
2✔
362
                        workspaces[i].Name,
2✔
363
                        workspaces[i].Id,
2✔
364
                        strconv.FormatBool(workspaces[i].CicdEnforcedDefault),
2✔
365
                }, false)
2✔
366
                workspaceMap[strconv.Itoa(index)] = workspaces[i]
2✔
367
        }
2✔
368

369
        table.Print(os.Stdout)
1✔
370
        choice := input.Text("\n> ")
1✔
371
        selected, ok := workspaceMap[choice]
1✔
372
        if !ok {
1✔
NEW
373
                return astrov1.Workspace{}, errInvalidWorkspaceKey
×
374
        }
×
375
        return selected, nil
1✔
376
}
377

378
func GetWorkspaces(client astrov1.APIClient) ([]astrov1.Workspace, error) {
25✔
379
        ctx, err := context.GetCurrentContext()
25✔
380
        if err != nil {
26✔
381
                return []astrov1.Workspace{}, err
1✔
382
        }
1✔
383

384
        sorts := []astrov1.ListWorkspacesParamsSorts{"name:asc"}
24✔
385
        limit := 1000
24✔
386
        workspaceListParams := &astrov1.ListWorkspacesParams{
24✔
387
                Limit: &limit,
24✔
388
                Sorts: &sorts,
24✔
389
        }
24✔
390

24✔
391
        resp, err := client.ListWorkspacesWithResponse(httpContext.Background(), ctx.Organization, workspaceListParams)
24✔
392
        if err != nil {
28✔
393
                return []astrov1.Workspace{}, err
4✔
394
        }
4✔
395
        err = astrov1.NormalizeAPIError(resp.HTTPResponse, resp.Body)
20✔
396
        if err != nil {
20✔
NEW
397
                return []astrov1.Workspace{}, err
×
398
        }
×
399

400
        workspaces := resp.JSON200.Workspaces
20✔
401

20✔
402
        return workspaces, nil
20✔
403
}
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