exec.go 3.8 KB
Newer Older
1 2 3
package commands

import (
4 5 6 7
	"os"
	"os/exec"
	"strings"

8
	"github.com/sirupsen/logrus"
9
	"github.com/urfave/cli"
10
	"gitlab.com/ayufan/golang-cli-helpers"
11 12
	"gitlab.com/gitlab-org/gitlab-runner/common"
	"gitlab.com/gitlab-org/gitlab-runner/helpers/gitlab_ci_yaml_parser"
13 14

	// Force to load all executors, executes init() on them
15 16 17 18 19
	_ "gitlab.com/gitlab-org/gitlab-runner/executors/docker"
	_ "gitlab.com/gitlab-org/gitlab-runner/executors/parallels"
	_ "gitlab.com/gitlab-org/gitlab-runner/executors/shell"
	_ "gitlab.com/gitlab-org/gitlab-runner/executors/ssh"
	_ "gitlab.com/gitlab-org/gitlab-runner/executors/virtualbox"
20 21 22 23
)

type ExecCommand struct {
	common.RunnerSettings
24
	Job     string
25
	Timeout int `long:"timeout" description:"Job execution timeout (in seconds)"`
26 27 28 29 30 31 32 33 34 35
}

func (c *ExecCommand) runCommand(name string, arg ...string) (string, error) {
	cmd := exec.Command(name, arg...)
	cmd.Env = os.Environ()
	cmd.Stderr = os.Stderr
	result, err := cmd.Output()
	return string(result), err
}

36 37
func (c *ExecCommand) createBuild(repoURL string, abortSignal chan os.Signal) (build *common.Build, err error) {
	// Check if we have uncommitted changes
38 39 40 41 42 43 44 45 46
	_, err = c.runCommand("git", "diff", "--quiet", "HEAD")
	if err != nil {
		logrus.Warningln("You most probably have uncommitted changes.")
		logrus.Warningln("These changes will not be tested.")
	}

	// Parse Git settings
	sha, err := c.runCommand("git", "rev-parse", "HEAD")
	if err != nil {
47
		return
48 49 50 51 52 53 54 55 56
	}

	beforeSha, err := c.runCommand("git", "rev-parse", "HEAD~1")
	if err != nil {
		beforeSha = "0000000000000000000000000000000000000000"
	}

	refName, err := c.runCommand("git", "rev-parse", "--abbrev-ref", "HEAD")
	if err != nil {
57
		return
58 59
	}

60
	build = &common.Build{
61
		JobResponse: common.JobResponse{
62 63
			ID:            1,
			Token:         "",
64
			AllowGitFetch: false,
65
			JobInfo: common.JobInfo{
66 67 68 69 70
				Name:        "",
				Stage:       "",
				ProjectID:   1,
				ProjectName: "",
			},
71
			GitInfo: common.GitInfo{
72 73 74 75 76
				RepoURL:   repoURL,
				Ref:       strings.TrimSpace(refName),
				Sha:       strings.TrimSpace(sha),
				BeforeSha: strings.TrimSpace(beforeSha),
			},
77
			RunnerInfo: common.RunnerInfo{
78 79
				Timeout: c.getTimeout(),
			},
80 81 82 83
		},
		Runner: &common.RunnerConfig{
			RunnerSettings: c.RunnerSettings,
		},
84
		SystemInterrupt: abortSignal,
85
	}
86 87 88
	return
}

89 90 91 92 93 94 95 96
func (c *ExecCommand) getTimeout() int {
	if c.Timeout > 0 {
		return c.Timeout
	}

	return common.DefaultExecTimeout
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
func (c *ExecCommand) Execute(context *cli.Context) {
	wd, err := os.Getwd()
	if err != nil {
		logrus.Fatalln(err)
	}

	switch len(context.Args()) {
	case 1:
		c.Job = context.Args().Get(0)
	default:
		cli.ShowSubcommandHelp(context)
		os.Exit(1)
		return
	}

	c.Executor = context.Command.Name

	abortSignal := make(chan os.Signal)
	doneSignal := make(chan int, 1)

117
	go waitForInterrupts(nil, abortSignal, doneSignal, nil)
118 119 120 121 122 123 124 125 126 127 128 129

	// Add self-volume to docker
	if c.RunnerSettings.Docker == nil {
		c.RunnerSettings.Docker = &common.DockerConfig{}
	}
	c.RunnerSettings.Docker.Volumes = append(c.RunnerSettings.Docker.Volumes, wd+":"+wd+":ro")

	// Create build
	build, err := c.createBuild(wd, abortSignal)
	if err != nil {
		logrus.Fatalln(err)
	}
130

131 132
	parser := gitlab_ci_yaml_parser.NewGitLabCiYamlParser(c.Job)
	err = parser.ParseYaml(&build.JobResponse)
133 134 135 136
	if err != nil {
		logrus.Fatalln(err)
	}

137
	err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	if err != nil {
		logrus.Fatalln(err)
	}
}

func init() {
	cmd := &ExecCommand{}

	flags := clihelpers.GetFlagsFromStruct(cmd)
	cliCmd := cli.Command{
		Name:  "exec",
		Usage: "execute a build locally",
	}

	for _, executor := range common.GetExecutors() {
		subCmd := cli.Command{
			Name:   executor,
			Usage:  "use " + executor + " executor",
			Action: cmd.Execute,
			Flags:  flags,
		}
		cliCmd.Subcommands = append(cliCmd.Subcommands, subCmd)
	}

	common.RegisterCommand(cliCmd)
}