github.com/abayer/test-infra@v0.0.5/prow/pod-utils/clone/clone.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package clone
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"os"
    23  	"os/exec"
    24  	"strings"
    25  
    26  	"github.com/sirupsen/logrus"
    27  	"k8s.io/test-infra/prow/kube"
    28  )
    29  
    30  // Run clones the refs under the prescribed directory and optionally
    31  // configures the git username and email in the repository as well.
    32  func Run(refs *kube.Refs, dir, gitUserName, gitUserEmail string, env []string) Record {
    33  	logrus.WithFields(logrus.Fields{"refs": refs}).Info("Cloning refs")
    34  	record := Record{Refs: refs}
    35  	repositoryURI := fmt.Sprintf("https://github.com/%s/%s.git", refs.Org, refs.Repo)
    36  	if refs.CloneURI != "" {
    37  		repositoryURI = refs.CloneURI
    38  	}
    39  	cloneDir := PathForRefs(dir, refs)
    40  
    41  	commands := []cloneCommand{
    42  		func() (string, string, error) {
    43  			return fmt.Sprintf("os.MkdirAll(%s, 0755)", cloneDir), "", os.MkdirAll(cloneDir, 0755)
    44  		},
    45  	}
    46  
    47  	commands = append(commands, shellCloneCommand(cloneDir, env, "git", "init"))
    48  	if gitUserName != "" {
    49  		commands = append(commands, shellCloneCommand(cloneDir, env, "git", "config", "user.name", gitUserName))
    50  	}
    51  	if gitUserEmail != "" {
    52  		commands = append(commands, shellCloneCommand(cloneDir, env, "git", "config", "user.email", gitUserEmail))
    53  	}
    54  	commands = append(commands, shellCloneCommand(cloneDir, env, "git", "fetch", repositoryURI, "--tags", "--prune"))
    55  	commands = append(commands, shellCloneCommand(cloneDir, env, "git", "fetch", repositoryURI, refs.BaseRef))
    56  
    57  	var target string
    58  	if refs.BaseSHA != "" {
    59  		target = refs.BaseSHA
    60  	} else {
    61  		target = "FETCH_HEAD"
    62  	}
    63  	// we need to be "on" the target branch after the sync
    64  	// so we need to set the branch to point to the base ref,
    65  	// but we cannot update a branch we are on, so in case we
    66  	// are on the branch we are syncing, we check out the SHA
    67  	// first and reset the branch second, then check out the
    68  	// branch we just reset to be in the correct final state
    69  	commands = append(commands, shellCloneCommand(cloneDir, env, "git", "checkout", target))
    70  	commands = append(commands, shellCloneCommand(cloneDir, env, "git", "branch", "--force", refs.BaseRef, target))
    71  	commands = append(commands, shellCloneCommand(cloneDir, env, "git", "checkout", refs.BaseRef))
    72  
    73  	for _, prRef := range refs.Pulls {
    74  		ref := fmt.Sprintf("pull/%d/head", prRef.Number)
    75  		if prRef.Ref != "" {
    76  			ref = prRef.Ref
    77  		}
    78  		commands = append(commands, shellCloneCommand(cloneDir, env, "git", "fetch", repositoryURI, ref))
    79  		var prCheckout string
    80  		if prRef.SHA != "" {
    81  			prCheckout = prRef.SHA
    82  		} else {
    83  			prCheckout = "FETCH_HEAD"
    84  		}
    85  		commands = append(commands, shellCloneCommand(cloneDir, env, "git", "merge", prCheckout))
    86  	}
    87  
    88  	for _, command := range commands {
    89  		formattedCommand, output, err := command()
    90  		logrus.WithFields(logrus.Fields{"command": formattedCommand, "output": output, "error": err}).Info("Ran command")
    91  		message := ""
    92  		if err != nil {
    93  			message = err.Error()
    94  			record.Failed = true
    95  		}
    96  		record.Commands = append(record.Commands, Command{Command: formattedCommand, Output: output, Error: message})
    97  		if err != nil {
    98  			break
    99  		}
   100  	}
   101  
   102  	return record
   103  }
   104  
   105  // PathForRefs determines the full path to where
   106  // refs should be cloned
   107  func PathForRefs(baseDir string, refs *kube.Refs) string {
   108  	var clonePath string
   109  	if refs.PathAlias != "" {
   110  		clonePath = refs.PathAlias
   111  	} else {
   112  		clonePath = fmt.Sprintf("github.com/%s/%s", refs.Org, refs.Repo)
   113  	}
   114  	return fmt.Sprintf("%s/src/%s", baseDir, clonePath)
   115  }
   116  
   117  type cloneCommand func() (string, string, error)
   118  
   119  func shellCloneCommand(dir string, env []string, command string, args ...string) cloneCommand {
   120  	output := bytes.Buffer{}
   121  	cmd := exec.Command(command, args...)
   122  	cmd.Dir = dir
   123  	cmd.Env = append(cmd.Env, env...)
   124  	cmd.Stdout = &output
   125  	cmd.Stderr = &output
   126  
   127  	return func() (string, string, error) {
   128  		err := cmd.Run()
   129  		return strings.Join(append([]string{command}, args...), " "), output.String(), err
   130  	}
   131  }