// applyPatch is used by the agent to copy patch data onto disk // and then call the necessary git commands to apply the patch file func (gapc *GitApplyPatchCommand) applyPatch(conf *model.TaskConfig, patch *patch.Patch, pluginLogger plugin.Logger) error { // patch sets and contain multiple patches, some of them for modules for _, patchPart := range patch.Patches { var dir string if patchPart.ModuleName == "" { // if patch is not part of a module, just apply patch against src root dir = gapc.Directory pluginLogger.LogExecution(slogger.INFO, "Applying patch with git...") } else { // if patch is part of a module, apply patch in module root module, err := conf.Project.GetModuleByName(patchPart.ModuleName) if err != nil { return fmt.Errorf("Error getting module: %v", err) } if module == nil { return fmt.Errorf("Module not found: %v", patchPart.ModuleName) } // skip the module if this build variant does not use it if !util.SliceContains(conf.BuildVariant.Modules, module.Name) { pluginLogger.LogExecution(slogger.INFO, "Skipping patch for"+ " module %v, since the current build variant does not"+ " use it", module.Name) continue } dir = filepath.Join(gapc.Directory, module.Prefix, module.Name) pluginLogger.LogExecution(slogger.INFO, "Applying module patch with git...") } // create a temporary folder and store patch files on disk, // for later use in shell script tempFile, err := ioutil.TempFile("", "mcipatch_") if err != nil { return err } defer tempFile.Close() _, err = io.WriteString(tempFile, patchPart.PatchSet.Patch) if err != nil { return err } tempAbsPath := tempFile.Name() // this applies the patch using the patch files in the temp directory patchCommandStrings := []string{ fmt.Sprintf("set -o verbose"), fmt.Sprintf("set -o errexit"), fmt.Sprintf("ls"), fmt.Sprintf("cd '%v'", dir), fmt.Sprintf("git checkout '%v'", patchPart.Githash), fmt.Sprintf("git apply --check --whitespace=fix '%v'", tempAbsPath), fmt.Sprintf("git apply --stat '%v'", tempAbsPath), fmt.Sprintf("git apply --whitespace=fix < '%v'", tempAbsPath), } cmdsJoined := strings.Join(patchCommandStrings, "\n") patchCmd := &command.LocalCommand{ CmdString: cmdsJoined, WorkingDirectory: conf.WorkDir, Stdout: pluginLogger.GetTaskLogWriter(slogger.INFO), Stderr: pluginLogger.GetTaskLogWriter(slogger.ERROR), ScriptMode: true, } err = patchCmd.Run() if err != nil { return err } pluginLogger.Flush() } return nil }
// Execute gets the source code required by the project func (ggpc *GitGetProjectCommand) Execute(pluginLogger plugin.Logger, pluginCom plugin.PluginCommunicator, conf *model.TaskConfig, stop chan bool) error { // expand the github parameters before running the task if err := plugin.ExpandValues(&ggpc.Revisions, conf.Expansions); err != nil { return err } location, err := conf.ProjectRef.Location() if err != nil { return err } gitCommands := []string{ fmt.Sprintf("set -o errexit"), fmt.Sprintf("set -o verbose"), fmt.Sprintf("rm -rf %v", ggpc.Directory), fmt.Sprintf("git clone %v '%v'", location, ggpc.Directory), fmt.Sprintf("cd %v; git checkout %v", ggpc.Directory, conf.Task.Revision), } cmdsJoined := strings.Join(gitCommands, "\n") fetchSourceCmd := &command.LocalCommand{ CmdString: cmdsJoined, WorkingDirectory: conf.WorkDir, Stdout: pluginLogger.GetTaskLogWriter(slogger.INFO), Stderr: pluginLogger.GetTaskLogWriter(slogger.ERROR), ScriptMode: true, } errChan := make(chan error) go func() { pluginLogger.LogExecution(slogger.INFO, "Fetching source from git...") errChan <- fetchSourceCmd.Run() pluginLogger.Flush() }() // wait until the command finishes or the stop channel is tripped select { case err := <-errChan: if err != nil { return err } case <-stop: pluginLogger.LogExecution(slogger.INFO, "Got kill signal") if fetchSourceCmd.Cmd != nil { pluginLogger.LogExecution(slogger.INFO, "Stopping process: %v", fetchSourceCmd.Cmd.Process.Pid) if err := fetchSourceCmd.Stop(); err != nil { pluginLogger.LogExecution(slogger.ERROR, "Error occurred stopping process: %v", err) } } return fmt.Errorf("Fetch command interrupted.") } // Fetch source for the modules for _, moduleName := range conf.BuildVariant.Modules { pluginLogger.LogExecution(slogger.INFO, "Fetching module: %v", moduleName) module, err := conf.Project.GetModuleByName(moduleName) if err != nil { pluginLogger.LogExecution(slogger.ERROR, "Couldn't get module %v: %v", moduleName, err) continue } if module == nil { pluginLogger.LogExecution(slogger.ERROR, "No module found for %v", moduleName) continue } moduleBase := filepath.Join(module.Prefix, module.Name) moduleDir := filepath.Join(conf.WorkDir, moduleBase, "/_") err = os.MkdirAll(moduleDir, 0755) if err != nil { return err } // clear the destination err = os.RemoveAll(moduleDir) if err != nil { return err } revision := ggpc.Revisions[moduleName] // if there is no revision, then use the branch name if revision == "" { revision = module.Branch } moduleCmds := []string{ fmt.Sprintf("set -o errexit"), fmt.Sprintf("set -o verbose"), fmt.Sprintf("git clone %v '%v'", module.Repo, filepath.ToSlash(moduleBase)), fmt.Sprintf("cd %v; git checkout '%v'", filepath.ToSlash(moduleBase), revision), } moduleFetchCmd := &command.LocalCommand{ CmdString: strings.Join(moduleCmds, "\n"), WorkingDirectory: filepath.ToSlash(filepath.Join(conf.WorkDir, ggpc.Directory)), Stdout: pluginLogger.GetTaskLogWriter(slogger.INFO), Stderr: pluginLogger.GetTaskLogWriter(slogger.ERROR), ScriptMode: true, } go func() { errChan <- moduleFetchCmd.Run() pluginLogger.Flush() }() // wait until the command finishes or the stop channel is tripped select { case err := <-errChan: if err != nil { return err } case <-stop: pluginLogger.LogExecution(slogger.INFO, "Got kill signal") if moduleFetchCmd.Cmd != nil { pluginLogger.LogExecution(slogger.INFO, "Stopping process: %v", moduleFetchCmd.Cmd.Process.Pid) if err := moduleFetchCmd.Stop(); err != nil { pluginLogger.LogExecution(slogger.ERROR, "Error occurred stopping process: %v", err) } } return fmt.Errorf("Fetch module command interrupted.") } } return nil }
// Execute gets the source code required by the project func (self *GitGetProjectCommand) Execute(pluginLogger plugin.Logger, pluginCom plugin.PluginCommunicator, conf *model.TaskConfig, stop chan bool) error { location, err := conf.ProjectRef.Location() if err != nil { return err } gitCommands := []string{ fmt.Sprintf("set -o errexit"), fmt.Sprintf("set -o verbose"), fmt.Sprintf("rm -rf %v", self.Directory), fmt.Sprintf("git clone %v '%v'", location, self.Directory), fmt.Sprintf("cd %v; git checkout %v", self.Directory, conf.Task.Revision), } cmdsJoined := strings.Join(gitCommands, "\n") fetchSourceCmd := &command.LocalCommand{ CmdString: cmdsJoined, WorkingDirectory: conf.WorkDir, Stdout: pluginLogger.GetTaskLogWriter(slogger.INFO), Stderr: pluginLogger.GetTaskLogWriter(slogger.ERROR), ScriptMode: true, } pluginLogger.LogExecution(slogger.INFO, "Fetching source from git...") if err := fetchSourceCmd.Run(); err != nil { return err } pluginLogger.Flush() // Fetch source for the modules for _, moduleName := range conf.BuildVariant.Modules { pluginLogger.LogExecution(slogger.INFO, "Fetching module: %v", moduleName) module, err := conf.Project.GetModuleByName(moduleName) if err != nil { pluginLogger.LogExecution(slogger.ERROR, "Couldn't get module %v: %v", moduleName, err) continue } if module == nil { pluginLogger.LogExecution(slogger.ERROR, "No module found for %v", moduleName) continue } moduleBase := filepath.Join(module.Prefix, module.Name) moduleDir := filepath.Join(conf.WorkDir, moduleBase, "/_") err = os.MkdirAll(moduleDir, 0755) if err != nil { return err } // clear the destination err = os.RemoveAll(moduleDir) if err != nil { return err } moduleCmds := []string{ fmt.Sprintf("set -o errexit"), fmt.Sprintf("set -o verbose"), fmt.Sprintf("git clone %v '%v'", module.Repo, filepath.ToSlash(moduleBase)), fmt.Sprintf("cd %v; git checkout '%v'", filepath.ToSlash(moduleBase), module.Branch), } moduleFetchCmd := &command.LocalCommand{ CmdString: strings.Join(moduleCmds, "\n"), WorkingDirectory: filepath.ToSlash(filepath.Join(conf.WorkDir, self.Directory)), Stdout: pluginLogger.GetTaskLogWriter(slogger.INFO), Stderr: pluginLogger.GetTaskLogWriter(slogger.ERROR), ScriptMode: true, } err = moduleFetchCmd.Run() if err != nil { return err } pluginLogger.Flush() } return nil }
// Execute starts the shell with its given parameters. func (self *ShellExecCommand) Execute(pluginLogger plugin.Logger, pluginCom plugin.PluginCommunicator, conf *model.TaskConfig, stop chan bool) error { pluginLogger.LogExecution(slogger.DEBUG, "Preparing script...") logWriterInfo := pluginLogger.GetTaskLogWriter(slogger.INFO) logWriterErr := pluginLogger.GetTaskLogWriter(slogger.ERROR) outBufferWriter := util.NewLineBufferingWriter(logWriterInfo) errorBufferWriter := util.NewLineBufferingWriter(logWriterErr) defer outBufferWriter.Flush() defer errorBufferWriter.Flush() localCmd := &command.LocalCommand{ CmdString: self.Script, Stdout: outBufferWriter, Stderr: errorBufferWriter, ScriptMode: true, } if self.WorkingDir != "" { localCmd.WorkingDirectory = filepath.Join(conf.WorkDir, self.WorkingDir) } else { localCmd.WorkingDirectory = conf.WorkDir } err := localCmd.PrepToRun(conf.Expansions) if err != nil { return fmt.Errorf("Failed to apply expansions: %v", err) } if self.Silent { pluginLogger.LogExecution(slogger.INFO, "Executing script (source hidden)...") } else { pluginLogger.LogExecution(slogger.INFO, "Executing script: %v", localCmd.CmdString) } doneStatus := make(chan error) go func() { var err error env := os.Environ() env = append(env, fmt.Sprintf("EVR_TASK_ID=%v", conf.Task.Id)) env = append(env, fmt.Sprintf("EVR_AGENT_PID=%v", os.Getpid())) localCmd.Environment = env err = localCmd.Start() if err == nil { pluginLogger.LogSystem(slogger.DEBUG, "spawned shell process with pid %v", localCmd.Cmd.Process.Pid) // Call the platform's process-tracking function. On some OSes this will be a noop, // on others this may need to do some additional work to track the process so that // it can be cleaned up later. if trackedTask != "" && trackedTask == conf.Task.Id { trackProcess(conf.Task.Id, localCmd.Cmd.Process.Pid, pluginLogger) } if !self.Background { err = localCmd.Cmd.Wait() } } else { pluginLogger.LogSystem(slogger.DEBUG, "error spawning shell process: %v", err) } doneStatus <- err }() defer pluginLogger.Flush() select { case err = <-doneStatus: if err != nil { if self.ContinueOnError { pluginLogger.LogExecution(slogger.INFO, "(ignoring) Script finished with error: %v", err) return nil } else { pluginLogger.LogExecution(slogger.INFO, "Script finished with error: %v", err) return err } } else { pluginLogger.LogExecution(slogger.INFO, "Script execution complete.") } case <-stop: pluginLogger.LogExecution(slogger.INFO, "Got kill signal") // need to check command has started if localCmd.Cmd != nil { pluginLogger.LogExecution(slogger.INFO, "Stopping process: %v", localCmd.Cmd.Process.Pid) // try and stop the process if err := localCmd.Stop(); err != nil { pluginLogger.LogExecution(slogger.ERROR, "Error occurred stopping process: %v", err) } } return fmt.Errorf("Shell command interrupted.") } return nil }