mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
283 lines
6.9 KiB
Go
283 lines
6.9 KiB
Go
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License.AGPL.txt in the project root for license information.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/manifoldco/promptui"
|
|
"github.com/spf13/cobra"
|
|
yaml "gopkg.in/yaml.v2"
|
|
|
|
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/gitpod"
|
|
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/gitpodlib"
|
|
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/utils"
|
|
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
|
|
"github.com/gitpod-io/gitpod/supervisor/api"
|
|
)
|
|
|
|
var (
|
|
interactive = false
|
|
)
|
|
|
|
// initCmd initializes the workspace's .gitpod.yml file
|
|
var initCmd = &cobra.Command{
|
|
Use: "init",
|
|
Short: "Create a Gitpod configuration for this project.",
|
|
Long: `
|
|
Create a Gitpod configuration for this project.
|
|
`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
ctx := cmd.Context()
|
|
cfg := gitpodlib.GitpodFile{}
|
|
if interactive {
|
|
if err := askForDockerImage(ctx, &cfg); err != nil {
|
|
return err
|
|
}
|
|
if err := askForPorts(&cfg); err != nil {
|
|
return err
|
|
}
|
|
if err := askForTask(&cfg); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
cfg.AddPort(3000)
|
|
cfg.AddTask("echo 'start script'", "echo 'init script'")
|
|
}
|
|
|
|
d, err := yaml.Marshal(cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !interactive {
|
|
wsInfo, err := gitpod.GetWSInfo(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get workspace info: %w", err)
|
|
}
|
|
defaultImage, err := getDefaultWorkspaceImage(ctx, wsInfo)
|
|
if err != nil {
|
|
fmt.Printf("failed to get organization default workspace image: %v\n", err)
|
|
fmt.Println("fallback to gitpod default")
|
|
defaultImage = ""
|
|
}
|
|
yml := ""
|
|
if defaultImage != "" {
|
|
yml = yml + fmt.Sprintf("# Image of workspace. Learn more: https://www.gitpod.io/docs/configure/workspaces/workspace-image\nimage: %s\n\n", defaultImage)
|
|
}
|
|
yml = yml + `# List the start up tasks. Learn more: https://www.gitpod.io/docs/configure/workspaces/tasks
|
|
tasks:
|
|
- name: Script Task
|
|
init: echo 'init script' # runs during prebuild => https://www.gitpod.io/docs/configure/projects/prebuilds
|
|
command: echo 'start script'
|
|
|
|
# List the ports to expose. Learn more: https://www.gitpod.io/docs/configure/workspaces/ports
|
|
ports:
|
|
- name: Frontend
|
|
description: Port 3000 for the frontend
|
|
port: 3000
|
|
onOpen: open-preview
|
|
|
|
# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart
|
|
`
|
|
d = []byte(yml)
|
|
} else {
|
|
fmt.Printf("\n\n---\n%s", d)
|
|
}
|
|
|
|
if _, err = os.Stat(".gitpod.yml"); err == nil {
|
|
prompt := promptui.Prompt{
|
|
IsConfirm: true,
|
|
Label: ".gitpod.yml file already exists, overwrite?",
|
|
}
|
|
if _, err = prompt.Run(); err != nil {
|
|
fmt.Printf("Not overwriting .gitpod.yml file. Aborting.\n")
|
|
return GpError{Silence: true, Err: err, OutCome: utils.Outcome_Success}
|
|
}
|
|
}
|
|
|
|
if err = os.WriteFile(".gitpod.yml", d, 0644); err != nil {
|
|
return err
|
|
}
|
|
|
|
// open .gitpod.yml and Dockerfile
|
|
if v, ok := cfg.Image.(gitpodlib.GitpodImage); ok {
|
|
if _, err = os.Stat(v.File); os.IsNotExist(err) {
|
|
if err = os.WriteFile(v.File, []byte(`FROM gitpod/workspace-full
|
|
|
|
USER gitpod
|
|
|
|
# Install custom tools, runtime, etc. using apt-get
|
|
# For example, the command below would install "bastet" - a command line tetris clone:
|
|
#
|
|
# RUN sudo apt-get -q update && \
|
|
# sudo apt-get install -yq bastet && \
|
|
# sudo rm -rf /var/lib/apt/lists/*
|
|
#
|
|
# More information: https://www.gitpod.io/docs/config-docker/
|
|
`), 0644); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = openCmd.RunE(cmd, []string{v.File})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return openCmd.RunE(cmd, []string{".gitpod.yml"})
|
|
},
|
|
}
|
|
|
|
func getDefaultWorkspaceImage(ctx context.Context, wsInfo *api.WorkspaceInfoResponse) (string, error) {
|
|
client, err := gitpod.ConnectToServer(ctx, wsInfo, []string{
|
|
"function:getDefaultWorkspaceImage",
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer client.Close()
|
|
|
|
res, err := client.GetDefaultWorkspaceImage(ctx, &protocol.GetDefaultWorkspaceImageParams{
|
|
WorkspaceID: wsInfo.WorkspaceId,
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if res.Source == protocol.WorkspaceImageSourceInstallation {
|
|
return "", nil
|
|
}
|
|
return res.Image, nil
|
|
}
|
|
|
|
func isRequired(input string) error {
|
|
if input == "" {
|
|
return errors.New("Cannot be empty")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ask(lbl string, def string, validator promptui.ValidateFunc) (string, error) {
|
|
scslbl := strings.Trim(strings.Split(lbl, "(")[0], " ")
|
|
prompt := promptui.Prompt{
|
|
Label: lbl,
|
|
Validate: validator,
|
|
Default: def,
|
|
Templates: &promptui.PromptTemplates{
|
|
Success: fmt.Sprintf("%s: ", scslbl),
|
|
},
|
|
}
|
|
return prompt.Run()
|
|
}
|
|
|
|
func askForDockerImage(ctx context.Context, cfg *gitpodlib.GitpodFile) error {
|
|
prompt := promptui.Select{
|
|
Label: "Workspace Docker image",
|
|
Items: []string{"default", "custom image", "docker file"},
|
|
Templates: &promptui.SelectTemplates{
|
|
Selected: "Workspace Image: {{ . }}",
|
|
},
|
|
}
|
|
chce, _, err := prompt.Run()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if chce == 0 {
|
|
wsInfo, err := gitpod.GetWSInfo(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get workspace info: %w", err)
|
|
}
|
|
defaultImage, err := getDefaultWorkspaceImage(ctx, wsInfo)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get organization default workspace image: %w", err)
|
|
}
|
|
cfg.SetImageName(defaultImage)
|
|
return nil
|
|
}
|
|
if chce == 1 {
|
|
nme, err := ask("Image name", "", isRequired)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cfg.SetImageName(nme)
|
|
return nil
|
|
}
|
|
|
|
// configure docker file
|
|
dockerFile, err := ask("Dockerfile path", ".gitpod.Dockerfile", isRequired)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ctxtPath, err := ask("Docker context path (enter to skip)", "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cfg.SetImage(gitpodlib.GitpodImage{
|
|
File: dockerFile,
|
|
Context: ctxtPath,
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func parsePorts(input string) ([]int32, error) {
|
|
if input == "" {
|
|
return []int32{}, nil
|
|
}
|
|
prts := strings.Split(input, ",")
|
|
rst := make([]int32, 0)
|
|
for _, prt := range prts {
|
|
if pv, err := strconv.ParseUint(strings.TrimSpace(prt), 10, 16); err != nil {
|
|
return nil, err
|
|
} else {
|
|
rst = append(rst, int32(pv))
|
|
}
|
|
}
|
|
return rst, nil
|
|
}
|
|
|
|
func askForPorts(cfg *gitpodlib.GitpodFile) error {
|
|
input, err := ask("Expose Ports (comma separated)", "", func(input string) error {
|
|
if _, err := parsePorts(input); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
prts, err := parsePorts(input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, pv := range prts {
|
|
cfg.AddPort(pv)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func askForTask(cfg *gitpodlib.GitpodFile) error {
|
|
input, err := ask("Startup task (enter to skip)", "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if input != "" {
|
|
cfg.AddTask(input)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(initCmd)
|
|
initCmd.Flags().BoolVarP(&interactive, "interactive", "i", false, "walk me through an interactive setup.")
|
|
}
|