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

astronomer / astro-cli / 3594e26d-8682-4297-8627-84e8d776b975

27 Oct 2025 08:29PM UTC coverage: 38.52% (+0.01%) from 38.51%
3594e26d-8682-4297-8627-84e8d776b975

Pull #1968

circleci

ronidelacruz-astro
Merge branch 'fix-verbosity-issue' of github.com:astronomer/astro-cli into fix-verbosity-issue
Pull Request #1968: Verbosity flag provides no extra information (#1501)

15 of 20 new or added lines in 3 files covered. (75.0%)

46 existing lines in 2 files now uncovered.

24148 of 62689 relevant lines covered (38.52%)

10.75 hits per line

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

87.62
/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
        astrocore "github.com/astronomer/astro-cli/astro-client-core"
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/logger"
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
func newTableOut() *printutil.Table {
1✔
31
        return &printutil.Table{
1✔
32
                Padding:        []int{44, 50},
1✔
33
                DynamicPadding: true,
1✔
34
                Header:         []string{"NAME", "ID"},
1✔
35
                ColorRowCode:   [2]string{"\033[1;32m", "\033[0m"},
1✔
36
        }
1✔
37
}
1✔
38

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

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

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

54
// List all workspaces
55
func List(client astrocore.CoreClient, out io.Writer) error {
2✔
56
        c, err := config.GetCurrentContext()
2✔
57
        if err != nil {
2✔
NEW
58
                logger.Debugf("Failed to get current context: %v", err)
×
59
                return err
×
60
        }
×
61
        logger.Debugf("Current context - Organization: %s, Workspace: %s", c.Organization, c.Workspace)
2✔
62

2✔
63
        ws, err := GetWorkspaces(client)
2✔
64
        if err != nil {
3✔
65
                logger.Debugf("Failed to fetch workspaces: %v", err)
1✔
66
                return err
1✔
67
        }
1✔
68
        logger.Debugf("Successfully retrieved %d workspace(s)", len(ws))
1✔
69

1✔
70
        tab := newTableOut()
1✔
71
        for i := range ws {
2✔
72
                name := ws[i].Name
1✔
73
                workspace := ws[i].Id
1✔
74

1✔
75
                var color bool
1✔
76

1✔
77
                if c.Workspace == ws[i].Id {
1✔
78
                        color = true
×
NEW
79
                        logger.Debugf("Workspace '%s' (ID: %s) is the current workspace", name, workspace)
×
80
                } else {
1✔
81
                        color = false
1✔
82
                }
1✔
83
                tab.AddRow([]string{name, workspace}, color)
1✔
84
        }
85

86
        tab.Print(out)
1✔
87

1✔
88
        return nil
1✔
89
}
90

91
var GetWorkspaceSelection = func(client astrocore.CoreClient, out io.Writer) (string, error) {
6✔
92
        tab := printutil.Table{
6✔
93
                Padding:        []int{5, 44, 50},
6✔
94
                DynamicPadding: true,
6✔
95
                Header:         []string{"#", "NAME", "ID"},
6✔
96
                ColorRowCode:   [2]string{"\033[1;32m", "\033[0m"},
6✔
97
        }
6✔
98

6✔
99
        var c config.Context
6✔
100
        c, err := config.GetCurrentContext()
6✔
101
        if err != nil {
7✔
102
                return "", err
1✔
103
        }
1✔
104

105
        ws, err := GetWorkspaces(client)
5✔
106
        if err != nil {
6✔
107
                return "", err
1✔
108
        }
1✔
109

110
        deployMap := map[string]astrocore.Workspace{}
4✔
111
        for i := range ws {
10✔
112
                index := i + 1
6✔
113

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

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

126
        return selected.Id, nil
2✔
127
}
128

129
func Switch(workspaceNameOrID string, client astrocore.CoreClient, out io.Writer) error {
5✔
130
        var wsID string
5✔
131
        if workspaceNameOrID == "" {
7✔
132
                id, err := GetWorkspaceSelection(client, out)
2✔
133
                if err != nil {
3✔
134
                        return err
1✔
135
                }
1✔
136

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

149
                if wsID == "" {
1✔
150
                        return errors.Wrap(err, "workspace id/name could not be found")
×
151
                }
×
152
        }
153

154
        c, err := config.GetCurrentContext()
2✔
155
        if err != nil {
2✔
156
                return err
×
157
        }
×
158

159
        err = c.SetContextKey("workspace", wsID)
2✔
160
        if err != nil {
2✔
161
                return err
×
162
        }
×
163

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

169
        err = c.SetOrganizationContext(c.Organization, c.OrganizationProduct)
2✔
170
        if err != nil {
2✔
171
                return err
×
172
        }
×
173

174
        err = config.PrintCurrentCloudContext(out)
2✔
175
        if err != nil {
2✔
176
                return err
×
177
        }
×
178

179
        return nil
2✔
180
}
181

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

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

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

5✔
254
        workspaceUpdateRequest := astrocore.UpdateWorkspaceRequest{}
5✔
255

5✔
256
        if name == "" {
8✔
257
                workspaceUpdateRequest.Name = workspace.Name
3✔
258
        } else {
5✔
259
                workspaceUpdateRequest.Name = name
2✔
260
        }
2✔
261

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

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

329
func selectWorkspace(workspaces []astrocore.Workspace) (astrocore.Workspace, error) {
4✔
330
        if len(workspaces) == 0 {
6✔
331
                return astrocore.Workspace{}, nil
2✔
332
        }
2✔
333

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

1✔
339
                return workspaces[0], nil
1✔
340
        }
1✔
341

342
        table := printutil.Table{
1✔
343
                Padding:        []int{30, 50, 10, 50, 10, 10, 10},
1✔
344
                DynamicPadding: true,
1✔
345
                Header:         []string{"#", "WORKSPACENAME", "ID", "CICD ENFORCEMENT"},
1✔
346
        }
1✔
347

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

1✔
350
        workspaceMap := map[string]astrocore.Workspace{}
1✔
351
        for i := range workspaces {
3✔
352
                index := i + 1
2✔
353
                table.AddRow([]string{
2✔
354
                        strconv.Itoa(index),
2✔
355
                        workspaces[i].Name,
2✔
356
                        workspaces[i].Id,
2✔
357
                        strconv.FormatBool(workspaces[i].ApiKeyOnlyDeploymentsDefault),
2✔
358
                }, false)
2✔
359
                workspaceMap[strconv.Itoa(index)] = workspaces[i]
2✔
360
        }
2✔
361

362
        table.Print(os.Stdout)
1✔
363
        choice := input.Text("\n> ")
1✔
364
        selected, ok := workspaceMap[choice]
1✔
365
        if !ok {
1✔
366
                return astrocore.Workspace{}, errInvalidWorkspaceKey
×
367
        }
×
368
        return selected, nil
1✔
369
}
370

371
func GetWorkspaces(client astrocore.CoreClient) ([]astrocore.Workspace, error) {
21✔
372
        ctx, err := context.GetCurrentContext()
21✔
373
        if err != nil {
22✔
374
                return []astrocore.Workspace{}, err
1✔
375
        }
1✔
376

377
        sorts := []astrocore.ListWorkspacesParamsSorts{"name:asc"}
20✔
378
        limit := 1000
20✔
379
        workspaceListParams := &astrocore.ListWorkspacesParams{
20✔
380
                Limit: &limit,
20✔
381
                Sorts: &sorts,
20✔
382
        }
20✔
383

20✔
384
        resp, err := client.ListWorkspacesWithResponse(httpContext.Background(), ctx.Organization, workspaceListParams)
20✔
385
        if err != nil {
23✔
386
                return []astrocore.Workspace{}, err
3✔
387
        }
3✔
388
        err = astrocore.NormalizeAPIError(resp.HTTPResponse, resp.Body)
17✔
389
        if err != nil {
17✔
390
                return []astrocore.Workspace{}, err
×
391
        }
×
392

393
        workspaces := resp.JSON200.Workspaces
17✔
394

17✔
395
        return workspaces, nil
17✔
396
}
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

© 2025 Coveralls, Inc