clone.go 4.7 KB

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