Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/gitpod-cli/cmd/init.go
2498 views
1
// Copyright (c) 2020 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
"context"
9
"errors"
10
"fmt"
11
"os"
12
"strconv"
13
"strings"
14
15
"github.com/manifoldco/promptui"
16
"github.com/spf13/cobra"
17
yaml "gopkg.in/yaml.v2"
18
19
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/gitpod"
20
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/gitpodlib"
21
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/utils"
22
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
23
"github.com/gitpod-io/gitpod/supervisor/api"
24
)
25
26
var (
27
interactive = false
28
)
29
30
// initCmd initializes the workspace's .gitpod.yml file
31
var initCmd = &cobra.Command{
32
Use: "init",
33
Short: "Create a Gitpod configuration for this project.",
34
Long: `
35
Create a Gitpod configuration for this project.
36
`,
37
RunE: func(cmd *cobra.Command, args []string) error {
38
ctx := cmd.Context()
39
cfg := gitpodlib.GitpodFile{}
40
if interactive {
41
if err := askForDockerImage(ctx, &cfg); err != nil {
42
return err
43
}
44
if err := askForPorts(&cfg); err != nil {
45
return err
46
}
47
if err := askForTask(&cfg); err != nil {
48
return err
49
}
50
} else {
51
cfg.AddPort(3000)
52
cfg.AddTask("echo 'start script'", "echo 'init script'")
53
}
54
55
d, err := yaml.Marshal(cfg)
56
if err != nil {
57
return err
58
}
59
if !interactive {
60
wsInfo, err := gitpod.GetWSInfo(ctx)
61
if err != nil {
62
return fmt.Errorf("failed to get workspace info: %w", err)
63
}
64
defaultImage, err := getDefaultWorkspaceImage(ctx, wsInfo)
65
if err != nil {
66
fmt.Printf("failed to get organization default workspace image: %v\n", err)
67
fmt.Println("fallback to gitpod default")
68
defaultImage = ""
69
}
70
yml := ""
71
if defaultImage != "" {
72
yml = yml + fmt.Sprintf("# Image of workspace. Learn more: https://www.gitpod.io/docs/configure/workspaces/workspace-image\nimage: %s\n\n", defaultImage)
73
}
74
yml = yml + `# List the start up tasks. Learn more: https://www.gitpod.io/docs/configure/workspaces/tasks
75
tasks:
76
- name: Script Task
77
init: echo 'init script' # runs during prebuild => https://www.gitpod.io/docs/configure/projects/prebuilds
78
command: echo 'start script'
79
80
# List the ports to expose. Learn more: https://www.gitpod.io/docs/configure/workspaces/ports
81
ports:
82
- name: Frontend
83
description: Port 3000 for the frontend
84
port: 3000
85
onOpen: open-preview
86
87
# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart
88
`
89
d = []byte(yml)
90
} else {
91
fmt.Printf("\n\n---\n%s", d)
92
}
93
94
if _, err = os.Stat(".gitpod.yml"); err == nil {
95
prompt := promptui.Prompt{
96
IsConfirm: true,
97
Label: ".gitpod.yml file already exists, overwrite?",
98
}
99
if _, err = prompt.Run(); err != nil {
100
fmt.Printf("Not overwriting .gitpod.yml file. Aborting.\n")
101
return GpError{Silence: true, Err: err, OutCome: utils.Outcome_Success}
102
}
103
}
104
105
if err = os.WriteFile(".gitpod.yml", d, 0644); err != nil {
106
return err
107
}
108
109
// open .gitpod.yml and Dockerfile
110
if v, ok := cfg.Image.(gitpodlib.GitpodImage); ok {
111
if _, err = os.Stat(v.File); os.IsNotExist(err) {
112
if err = os.WriteFile(v.File, []byte(`FROM gitpod/workspace-full
113
114
USER gitpod
115
116
# Install custom tools, runtime, etc. using apt-get
117
# For example, the command below would install "bastet" - a command line tetris clone:
118
#
119
# RUN sudo apt-get -q update && \
120
# sudo apt-get install -yq bastet && \
121
# sudo rm -rf /var/lib/apt/lists/*
122
#
123
# More information: https://www.gitpod.io/docs/config-docker/
124
`), 0644); err != nil {
125
return err
126
}
127
}
128
129
err = openCmd.RunE(cmd, []string{v.File})
130
if err != nil {
131
return err
132
}
133
}
134
return openCmd.RunE(cmd, []string{".gitpod.yml"})
135
},
136
}
137
138
func getDefaultWorkspaceImage(ctx context.Context, wsInfo *api.WorkspaceInfoResponse) (string, error) {
139
client, err := gitpod.ConnectToServer(ctx, wsInfo, []string{
140
"function:getDefaultWorkspaceImage",
141
})
142
if err != nil {
143
return "", err
144
}
145
defer client.Close()
146
147
res, err := client.GetDefaultWorkspaceImage(ctx, &protocol.GetDefaultWorkspaceImageParams{
148
WorkspaceID: wsInfo.WorkspaceId,
149
})
150
if err != nil {
151
return "", err
152
}
153
if res.Source == protocol.WorkspaceImageSourceInstallation {
154
return "", nil
155
}
156
return res.Image, nil
157
}
158
159
func isRequired(input string) error {
160
if input == "" {
161
return errors.New("Cannot be empty")
162
}
163
return nil
164
}
165
166
func ask(lbl string, def string, validator promptui.ValidateFunc) (string, error) {
167
scslbl := strings.Trim(strings.Split(lbl, "(")[0], " ")
168
prompt := promptui.Prompt{
169
Label: lbl,
170
Validate: validator,
171
Default: def,
172
Templates: &promptui.PromptTemplates{
173
Success: fmt.Sprintf("%s: ", scslbl),
174
},
175
}
176
return prompt.Run()
177
}
178
179
func askForDockerImage(ctx context.Context, cfg *gitpodlib.GitpodFile) error {
180
prompt := promptui.Select{
181
Label: "Workspace Docker image",
182
Items: []string{"default", "custom image", "docker file"},
183
Templates: &promptui.SelectTemplates{
184
Selected: "Workspace Image: {{ . }}",
185
},
186
}
187
chce, _, err := prompt.Run()
188
if err != nil {
189
return err
190
}
191
192
if chce == 0 {
193
wsInfo, err := gitpod.GetWSInfo(ctx)
194
if err != nil {
195
return fmt.Errorf("failed to get workspace info: %w", err)
196
}
197
defaultImage, err := getDefaultWorkspaceImage(ctx, wsInfo)
198
if err != nil {
199
return fmt.Errorf("failed to get organization default workspace image: %w", err)
200
}
201
cfg.SetImageName(defaultImage)
202
return nil
203
}
204
if chce == 1 {
205
nme, err := ask("Image name", "", isRequired)
206
if err != nil {
207
return err
208
}
209
cfg.SetImageName(nme)
210
return nil
211
}
212
213
// configure docker file
214
dockerFile, err := ask("Dockerfile path", ".gitpod.Dockerfile", isRequired)
215
if err != nil {
216
return err
217
}
218
ctxtPath, err := ask("Docker context path (enter to skip)", "", nil)
219
if err != nil {
220
return err
221
}
222
cfg.SetImage(gitpodlib.GitpodImage{
223
File: dockerFile,
224
Context: ctxtPath,
225
})
226
return nil
227
}
228
229
func parsePorts(input string) ([]int32, error) {
230
if input == "" {
231
return []int32{}, nil
232
}
233
prts := strings.Split(input, ",")
234
rst := make([]int32, 0)
235
for _, prt := range prts {
236
if pv, err := strconv.ParseUint(strings.TrimSpace(prt), 10, 16); err != nil {
237
return nil, err
238
} else {
239
rst = append(rst, int32(pv))
240
}
241
}
242
return rst, nil
243
}
244
245
func askForPorts(cfg *gitpodlib.GitpodFile) error {
246
input, err := ask("Expose Ports (comma separated)", "", func(input string) error {
247
if _, err := parsePorts(input); err != nil {
248
return err
249
}
250
return nil
251
})
252
if err != nil {
253
return err
254
}
255
256
prts, err := parsePorts(input)
257
if err != nil {
258
return err
259
}
260
261
for _, pv := range prts {
262
cfg.AddPort(pv)
263
}
264
return nil
265
}
266
267
func askForTask(cfg *gitpodlib.GitpodFile) error {
268
input, err := ask("Startup task (enter to skip)", "", nil)
269
if err != nil {
270
return err
271
}
272
if input != "" {
273
cfg.AddTask(input)
274
}
275
276
return nil
277
}
278
279
func init() {
280
rootCmd.AddCommand(initCmd)
281
initCmd.Flags().BoolVarP(&interactive, "interactive", "i", false, "walk me through an interactive setup.")
282
}
283
284