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  }