github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/plugin/builtin/git/git_get_project.go (about) 1 package git 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/evergreen-ci/evergreen" 10 "github.com/evergreen-ci/evergreen/command" 11 "github.com/evergreen-ci/evergreen/model" 12 "github.com/evergreen-ci/evergreen/plugin" 13 "github.com/mitchellh/mapstructure" 14 "github.com/mongodb/grip/slogger" 15 "github.com/pkg/errors" 16 ) 17 18 // GitGetProjectCommand is a command that fetches source code from git for the project 19 // associated with the current task 20 type GitGetProjectCommand struct { 21 // The root directory (locally) that the code should be checked out into. 22 // Must be a valid non-blank directory name. 23 Directory string `plugin:"expand"` 24 25 // Revisions are the optional revisions associated with the modules of a project. 26 // Note: If a module does not have a revision it will use the module's branch to get the project. 27 Revisions map[string]string `plugin:"expand"` 28 } 29 30 func (ggpc *GitGetProjectCommand) Name() string { 31 return GetProjectCmdName 32 } 33 34 func (ggpc *GitGetProjectCommand) Plugin() string { 35 return GitPluginName 36 } 37 38 // ParseParams parses the command's configuration. 39 // Fulfills the Command interface. 40 func (ggpc *GitGetProjectCommand) ParseParams(params map[string]interface{}) error { 41 err := mapstructure.Decode(params, ggpc) 42 if err != nil { 43 return err 44 } 45 46 if ggpc.Directory == "" { 47 return errors.Errorf("error parsing '%v' params: value for directory "+ 48 "must not be blank", ggpc.Name()) 49 } 50 return nil 51 } 52 53 // Execute gets the source code required by the project 54 func (ggpc *GitGetProjectCommand) Execute(pluginLogger plugin.Logger, 55 pluginCom plugin.PluginCommunicator, 56 conf *model.TaskConfig, 57 stop chan bool) error { 58 59 // expand the github parameters before running the task 60 if err := plugin.ExpandValues(ggpc, conf.Expansions); err != nil { 61 return err 62 } 63 64 location, err := conf.ProjectRef.Location() 65 66 if err != nil { 67 return err 68 } 69 70 gitCommands := []string{ 71 fmt.Sprintf("set -o errexit"), 72 fmt.Sprintf("set -o verbose"), 73 fmt.Sprintf("rm -rf %s", ggpc.Directory), 74 } 75 76 cloneCmd := fmt.Sprintf("git clone '%s' '%s'", location, ggpc.Directory) 77 if conf.ProjectRef.Branch != "" { 78 cloneCmd = fmt.Sprintf("%s --branch '%s'", cloneCmd, conf.ProjectRef.Branch) 79 } 80 81 gitCommands = append(gitCommands, 82 cloneCmd, 83 fmt.Sprintf("cd %v; git reset --hard %s", ggpc.Directory, conf.Task.Revision)) 84 85 cmdsJoined := strings.Join(gitCommands, "\n") 86 87 fetchSourceCmd := &command.LocalCommand{ 88 CmdString: cmdsJoined, 89 WorkingDirectory: conf.WorkDir, 90 Stdout: pluginLogger.GetTaskLogWriter(slogger.INFO), 91 Stderr: pluginLogger.GetTaskLogWriter(slogger.ERROR), 92 ScriptMode: true, 93 } 94 95 errChan := make(chan error) 96 go func() { 97 pluginLogger.LogExecution(slogger.INFO, "Fetching source from git...") 98 errChan <- fetchSourceCmd.Run() 99 pluginLogger.Flush() 100 }() 101 102 // wait until the command finishes or the stop channel is tripped 103 select { 104 case err := <-errChan: 105 if err != nil { 106 return errors.WithStack(err) 107 } 108 case <-stop: 109 pluginLogger.LogExecution(slogger.INFO, "Got kill signal") 110 if fetchSourceCmd.Cmd != nil { 111 pluginLogger.LogExecution(slogger.INFO, "Stopping process: %v", fetchSourceCmd.Cmd.Process.Pid) 112 if err := fetchSourceCmd.Stop(); err != nil { 113 pluginLogger.LogExecution(slogger.ERROR, "Error occurred stopping process: %v", err) 114 } 115 } 116 return errors.New("Fetch command interrupted") 117 } 118 119 // Fetch source for the modules 120 for _, moduleName := range conf.BuildVariant.Modules { 121 pluginLogger.LogExecution(slogger.INFO, "Fetching module: %v", moduleName) 122 module, err := conf.Project.GetModuleByName(moduleName) 123 if err != nil { 124 pluginLogger.LogExecution(slogger.ERROR, "Couldn't get module %v: %v", moduleName, err) 125 continue 126 } 127 if module == nil { 128 pluginLogger.LogExecution(slogger.ERROR, "No module found for %v", moduleName) 129 continue 130 } 131 132 moduleBase := filepath.Join(module.Prefix, module.Name) 133 moduleDir := filepath.Join(conf.WorkDir, moduleBase, "/_") 134 135 err = os.MkdirAll(moduleDir, 0755) 136 if err != nil { 137 return errors.WithStack(err) 138 } 139 // clear the destination 140 err = os.RemoveAll(moduleDir) 141 if err != nil { 142 return errors.WithStack(err) 143 } 144 145 revision := ggpc.Revisions[moduleName] 146 147 // if there is no revision, then use the branch name 148 if revision == "" { 149 revision = module.Branch 150 } 151 152 moduleCmds := []string{ 153 fmt.Sprintf("set -o errexit"), 154 fmt.Sprintf("set -o verbose"), 155 fmt.Sprintf("git clone %v '%v'", module.Repo, filepath.ToSlash(moduleBase)), 156 fmt.Sprintf("cd %v; git checkout '%v'", filepath.ToSlash(moduleBase), revision), 157 } 158 159 moduleFetchCmd := &command.LocalCommand{ 160 CmdString: strings.Join(moduleCmds, "\n"), 161 WorkingDirectory: filepath.ToSlash(filepath.Join(conf.WorkDir, ggpc.Directory)), 162 Stdout: pluginLogger.GetTaskLogWriter(slogger.INFO), 163 Stderr: pluginLogger.GetTaskLogWriter(slogger.ERROR), 164 ScriptMode: true, 165 } 166 167 go func() { 168 errChan <- moduleFetchCmd.Run() 169 pluginLogger.Flush() 170 }() 171 172 // wait until the command finishes or the stop channel is tripped 173 select { 174 case err := <-errChan: 175 if err != nil { 176 return err 177 } 178 case <-stop: 179 pluginLogger.LogExecution(slogger.INFO, "Got kill signal") 180 if moduleFetchCmd.Cmd != nil { 181 pluginLogger.LogExecution(slogger.INFO, "Stopping process: %v", moduleFetchCmd.Cmd.Process.Pid) 182 if err := moduleFetchCmd.Stop(); err != nil { 183 pluginLogger.LogExecution(slogger.ERROR, "Error occurred stopping process: %v", err) 184 } 185 } 186 return errors.New("Fetch module command interrupted.") 187 } 188 189 } 190 191 //Apply patches if necessary 192 if conf.Task.Requester != evergreen.PatchVersionRequester { 193 return nil 194 } 195 go func() { 196 pluginLogger.LogExecution(slogger.INFO, "Fetching patch.") 197 patch, err := ggpc.GetPatch(pluginCom, pluginLogger) 198 if err != nil { 199 pluginLogger.LogExecution(slogger.ERROR, "Failed to get patch: %v", err) 200 errChan <- errors.Wrap(err, "Failed to get patch") 201 } 202 err = ggpc.getPatchContents(pluginCom, pluginLogger, patch) 203 if err != nil { 204 pluginLogger.LogExecution(slogger.ERROR, "Failed to get patch contents: %v", err) 205 errChan <- errors.Wrap(err, "Failed to get patch contents") 206 } 207 err = ggpc.applyPatch(conf, patch, pluginLogger) 208 if err != nil { 209 pluginLogger.LogExecution(slogger.INFO, "Failed to apply patch: %v", err) 210 errChan <- errors.Wrap(err, "Failed to apply patch") 211 } 212 errChan <- nil 213 }() 214 215 select { 216 case err := <-errChan: 217 return err 218 case <-stop: 219 return errors.New("Patch command interrupted") 220 } 221 222 }