Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/local-app/cmd/workspace-create.go
2497 views
1
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package cmd
6
7
import (
8
"fmt"
9
"log/slog"
10
"strings"
11
12
"github.com/bufbuild/connect-go"
13
v1 "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1"
14
"github.com/gitpod-io/local-app/pkg/config"
15
"github.com/gitpod-io/local-app/pkg/helper"
16
"github.com/gitpod-io/local-app/pkg/prettyprint"
17
"github.com/spf13/cobra"
18
)
19
20
// workspaceCreateCmd creates a new workspace
21
var workspaceCreateCmd = &cobra.Command{
22
Use: "create <repo-url>",
23
Short: "Creates a new workspace based on a given context",
24
Args: cobra.ExactArgs(1),
25
RunE: func(cmd *cobra.Command, args []string) error {
26
cmd.SilenceUsage = true
27
repoURL := args[0]
28
29
cfg := config.FromContext(cmd.Context())
30
gpctx, err := cfg.GetActiveContext()
31
if err != nil {
32
return err
33
}
34
gitpod, err := getGitpodClient(cmd.Context())
35
if err != nil {
36
return err
37
}
38
39
if workspaceCreateOpts.WorkspaceClass != "" {
40
resp, err := gitpod.Workspaces.ListWorkspaceClasses(cmd.Context(), connect.NewRequest(&v1.ListWorkspaceClassesRequest{}))
41
if err != nil {
42
return prettyprint.MarkExceptional(prettyprint.AddResolution(fmt.Errorf("cannot list workspace classes: %w", err),
43
"don't pass an explicit workspace class, i.e. omit the --class flag",
44
))
45
}
46
var (
47
classes []string
48
found bool
49
)
50
for _, cls := range resp.Msg.GetResult() {
51
classes = append(classes, cls.Id)
52
if cls.Id == workspaceCreateOpts.WorkspaceClass {
53
found = true
54
}
55
}
56
if !found {
57
return prettyprint.AddResolution(fmt.Errorf("workspace class %s not found", workspaceCreateOpts.WorkspaceClass),
58
fmt.Sprintf("use one of the available workspace classes: %s", strings.Join(classes, ", ")),
59
)
60
}
61
}
62
63
if workspaceCreateOpts.Editor != "" {
64
resp, err := gitpod.Editors.ListEditorOptions(cmd.Context(), connect.NewRequest(&v1.ListEditorOptionsRequest{}))
65
if err != nil {
66
return prettyprint.MarkExceptional(prettyprint.AddResolution(fmt.Errorf("cannot list editor options: %w", err),
67
"don't pass an explicit editor, i.e. omit the --editor flag",
68
))
69
}
70
var (
71
editors []string
72
found bool
73
)
74
for _, editor := range resp.Msg.GetResult() {
75
editors = append(editors, editor.Id)
76
if editor.Id == workspaceCreateOpts.Editor {
77
found = true
78
}
79
}
80
if !found {
81
return prettyprint.AddResolution(fmt.Errorf("editor %s not found", workspaceCreateOpts.Editor),
82
fmt.Sprintf("use one of the available editor options: %s", strings.Join(editors, ", ")),
83
)
84
}
85
}
86
87
var (
88
orgId = gpctx.OrganizationID
89
ctx = cmd.Context()
90
)
91
92
slog.Debug("Attempting to create workspace...", "org", orgId, "repo", repoURL)
93
newWorkspace, err := gitpod.Workspaces.CreateAndStartWorkspace(ctx, connect.NewRequest(
94
&v1.CreateAndStartWorkspaceRequest{
95
Source: &v1.CreateAndStartWorkspaceRequest_ContextUrl{ContextUrl: repoURL},
96
OrganizationId: orgId,
97
StartSpec: &v1.StartWorkspaceSpec{
98
IdeSettings: &v1.IDESettings{
99
DefaultIde: workspaceCreateOpts.Editor,
100
UseLatestVersion: false,
101
},
102
WorkspaceClass: workspaceCreateOpts.WorkspaceClass,
103
},
104
// Without this flag we might not create a new workspace because there's already one running on the same commit.
105
IgnoreRunningWorkspaceOnSameCommit: true,
106
// Note(cw): the CLI cannot handle running prebuilds yet, so we ignore them for now.
107
IgnoreRunningPrebuild: true,
108
AllowUsingPreviousPrebuilds: true,
109
},
110
))
111
if err != nil {
112
return err
113
}
114
115
workspaceID := newWorkspace.Msg.WorkspaceId
116
if len(workspaceID) == 0 {
117
return prettyprint.MarkExceptional(prettyprint.AddResolution(fmt.Errorf("workspace was not created"),
118
"try to create the workspace again",
119
))
120
}
121
122
if workspaceCreateOpts.StartOpts.DontWait {
123
// There is no more information to print other than the workspace ID. No need to faff with tabular pretty printing.
124
fmt.Println(workspaceID)
125
return nil
126
}
127
128
_, err = helper.ObserveWorkspaceUntilStarted(ctx, gitpod, workspaceID)
129
if err != nil {
130
return err
131
}
132
133
if workspaceCreateOpts.StartOpts.OpenSSH {
134
return helper.SSHConnectToWorkspace(ctx, gitpod, workspaceID, false)
135
}
136
if workspaceCreateOpts.StartOpts.OpenEditor {
137
return helper.OpenWorkspaceInPreferredEditor(ctx, gitpod, workspaceID)
138
}
139
140
return nil
141
},
142
}
143
144
var workspaceCreateOpts struct {
145
StartOpts workspaceStartOptions
146
147
WorkspaceClass string
148
Editor string
149
}
150
151
func classCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
152
ctx := cmd.Context()
153
gitpod, err := getGitpodClient(ctx)
154
if err != nil {
155
return nil, cobra.ShellCompDirectiveError
156
}
157
resp, err := gitpod.Workspaces.ListWorkspaceClasses(ctx, connect.NewRequest(&v1.ListWorkspaceClassesRequest{}))
158
if err != nil {
159
return nil, cobra.ShellCompDirectiveError
160
}
161
items := resp.Msg.GetResult()
162
completionStr := []string{}
163
for _, cls := range items {
164
defaultDesc := ""
165
if cls.IsDefault {
166
defaultDesc = "(default)"
167
}
168
completionStr = append(completionStr, fmt.Sprintf("%s\t%s%s - %s", cls.Id, cls.DisplayName, defaultDesc, cls.Description))
169
}
170
return completionStr, cobra.ShellCompDirectiveNoFileComp
171
}
172
173
func editorCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
174
ctx := cmd.Context()
175
gitpod, err := getGitpodClient(ctx)
176
if err != nil {
177
return nil, cobra.ShellCompDirectiveError
178
}
179
resp, err := gitpod.Editors.ListEditorOptions(ctx, connect.NewRequest(&v1.ListEditorOptionsRequest{}))
180
if err != nil {
181
return nil, cobra.ShellCompDirectiveError
182
}
183
items := resp.Msg.GetResult()
184
completionStr := []string{}
185
for _, editor := range items {
186
completionStr = append(completionStr, fmt.Sprintf("%s\t%s", editor.Id, editor.Title))
187
}
188
return completionStr, cobra.ShellCompDirectiveNoFileComp
189
}
190
191
func init() {
192
workspaceCmd.AddCommand(workspaceCreateCmd)
193
addWorkspaceStartOptions(workspaceCreateCmd, &workspaceCreateOpts.StartOpts)
194
195
workspaceCreateCmd.Flags().StringVar(&workspaceCreateOpts.WorkspaceClass, "class", "", "the workspace class")
196
workspaceCreateCmd.Flags().StringVar(&workspaceCreateOpts.Editor, "editor", "code", "the editor to use")
197
198
_ = workspaceCreateCmd.RegisterFlagCompletionFunc("class", classCompletionFunc)
199
_ = workspaceCreateCmd.RegisterFlagCompletionFunc("editor", editorCompletionFunc)
200
}
201
202