exec.go 3.79 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 61 62 63 64 65 66 67 68
	jobResponse := common.JobResponse{
		ID:            1,
		Token:         "",
		AllowGitFetch: false,
		JobInfo: common.JobInfo{
			Name:        "",
			Stage:       "",
			ProjectID:   1,
			ProjectName: "",
69
		},
70 71 72 73 74
		GitInfo: common.GitInfo{
			RepoURL:   repoURL,
			Ref:       strings.TrimSpace(refName),
			Sha:       strings.TrimSpace(sha),
			BeforeSha: strings.TrimSpace(beforeSha),
75
		},
76 77 78 79 80 81 82
		RunnerInfo: common.RunnerInfo{
			Timeout: c.getTimeout(),
		},
	}

	runner := &common.RunnerConfig{
		RunnerSettings: c.RunnerSettings,
83
	}
84 85 86

	build = common.NewBuild(jobResponse, runner, abortSignal, nil)

87 88 89
	return
}

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

	return common.DefaultExecTimeout
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
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)

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

	// 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)
	}
131

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

138
	err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
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 164
	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)
}