commands/exec.go (133 lines of code) (raw):
package commands
import (
"os"
"os/exec"
"strings"
"github.com/Sirupsen/logrus"
"github.com/urfave/cli"
"gitlab.com/ayufan/golang-cli-helpers"
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
"gitlab.com/gitlab-org/gitlab-ci-multi-runner/helpers/gitlab_ci_yaml_parser"
// Force to load all executors, executes init() on them
_ "gitlab.com/gitlab-org/gitlab-ci-multi-runner/executors/docker"
_ "gitlab.com/gitlab-org/gitlab-ci-multi-runner/executors/parallels"
_ "gitlab.com/gitlab-org/gitlab-ci-multi-runner/executors/shell"
_ "gitlab.com/gitlab-org/gitlab-ci-multi-runner/executors/ssh"
_ "gitlab.com/gitlab-org/gitlab-ci-multi-runner/executors/virtualbox"
)
type ExecCommand struct {
common.RunnerSettings
Job string
Timeout int `long:"timeout" description:"Job execution timeout (in seconds)"`
}
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
}
func (c *ExecCommand) createBuild(repoURL string, abortSignal chan os.Signal) (build *common.Build, err error) {
// Check if we have uncommitted changes
_, 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 {
return
}
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 {
return
}
build = &common.Build{
JobResponse: common.JobResponse{
ID: 1,
Token: "",
AllowGitFetch: false,
JobInfo: common.JobInfo{
Name: "",
Stage: "",
ProjectID: 1,
ProjectName: "",
},
GitInfo: common.GitInfo{
RepoURL: repoURL,
Ref: strings.TrimSpace(refName),
Sha: strings.TrimSpace(sha),
BeforeSha: strings.TrimSpace(beforeSha),
},
RunnerInfo: common.RunnerInfo{
Timeout: c.getTimeout(),
},
},
Runner: &common.RunnerConfig{
RunnerSettings: c.RunnerSettings,
},
SystemInterrupt: abortSignal,
}
return
}
func (c *ExecCommand) getTimeout() int {
if c.Timeout > 0 {
return c.Timeout
}
return common.DefaultExecTimeout
}
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)
go waitForInterrupts(nil, abortSignal, doneSignal)
// 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)
}
parser := gitlab_ci_yaml_parser.NewGitLabCiYamlParser(c.Job)
err = parser.ParseYaml(&build.JobResponse)
if err != nil {
logrus.Fatalln(err)
}
err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
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)
}