github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/git/localgit/localgit.go (about) 1 /* 2 Copyright 2017 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 localgit creates a local git repo that can be used for testing code 18 // that uses a git.Client. 19 package localgit 20 21 import ( 22 "fmt" 23 "os" 24 "os/exec" 25 "path/filepath" 26 "strings" 27 28 v2 "sigs.k8s.io/prow/pkg/git/v2" 29 ) 30 31 type Clients func() (*LocalGit, v2.ClientFactory, error) 32 33 func DefaultBranch(dir string) string { 34 cmd := exec.Command("git", "config", "init.defaultBranch") 35 cmd.Dir = dir 36 out, err := cmd.Output() 37 if err != nil { 38 return "master" 39 } 40 return strings.TrimSpace(string(out)) 41 } 42 43 // LocalGit stores the repos in a temp dir. Create with New and delete with 44 // Clean. 45 type LocalGit struct { 46 // Dir is the path to the base temp dir. Repos are at Dir/org/repo. 47 Dir string 48 // Git is the location of the git binary. 49 Git string 50 // InitialBranch is sent to git init 51 InitialBranch string 52 } 53 54 // Clean deletes the local git dir. 55 func (lg *LocalGit) Clean() error { 56 return os.RemoveAll(lg.Dir) 57 } 58 59 func runCmd(cmd, dir string, arg ...string) error { 60 c := exec.Command(cmd, arg...) 61 c.Dir = dir 62 if b, err := c.CombinedOutput(); err != nil { 63 return fmt.Errorf("%s %v: %v, %s", cmd, arg, err, string(b)) 64 } 65 return nil 66 } 67 68 func runCmdOutput(cmd, dir string, arg ...string) (string, error) { 69 c := exec.Command(cmd, arg...) 70 c.Dir = dir 71 b, err := c.CombinedOutput() 72 if err != nil { 73 return "", fmt.Errorf("%s %v: %v, %s", cmd, arg, err, string(b)) 74 } 75 return strings.TrimSpace(string(b)), nil 76 } 77 78 // MakeFakeRepo creates the given repo and makes an initial commit. 79 func (lg *LocalGit) MakeFakeRepo(org, repo string) error { 80 rdir := filepath.Join(lg.Dir, org, repo) 81 if err := os.MkdirAll(rdir, os.ModePerm); err != nil { 82 return err 83 } 84 85 initArgs := []string{"init"} 86 if lg.InitialBranch != "" { 87 initArgs = append(initArgs, "--initial-branch", lg.InitialBranch) 88 } 89 if err := runCmd(lg.Git, rdir, initArgs...); err != nil { 90 return err 91 } 92 if err := runCmd(lg.Git, rdir, "config", "user.email", "test@test.test"); err != nil { 93 return err 94 } 95 if err := runCmd(lg.Git, rdir, "config", "user.name", "test test"); err != nil { 96 return err 97 } 98 if err := runCmd(lg.Git, rdir, "config", "commit.gpgsign", "false"); err != nil { 99 return err 100 } 101 if err := lg.AddCommit(org, repo, map[string][]byte{"initial": {}}); err != nil { 102 return err 103 } 104 105 return nil 106 } 107 108 // AddCommit adds the files to a new commit in the repo. 109 func (lg *LocalGit) AddCommit(org, repo string, files map[string][]byte) error { 110 rdir := filepath.Join(lg.Dir, org, repo) 111 for f, b := range files { 112 path := filepath.Join(rdir, f) 113 if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { 114 return err 115 } 116 if err := os.WriteFile(path, b, os.ModePerm); err != nil { 117 return err 118 } 119 if err := runCmd(lg.Git, rdir, "add", f); err != nil { 120 return err 121 } 122 } 123 return runCmd(lg.Git, rdir, "commit", "-m", "wow") 124 } 125 126 // RmCommit adds a commit that removes some files from the repo 127 func (lg *LocalGit) RmCommit(org, repo string, files []string) error { 128 rdir := filepath.Join(lg.Dir, org, repo) 129 for _, f := range files { 130 if err := runCmd(lg.Git, rdir, "rm", f); err != nil { 131 return err 132 } 133 } 134 return runCmd(lg.Git, rdir, "commit", "-m", "remove some files") 135 } 136 137 // CheckoutNewBranch does git checkout -b. 138 func (lg *LocalGit) CheckoutNewBranch(org, repo, branch string) error { 139 rdir := filepath.Join(lg.Dir, org, repo) 140 return runCmd(lg.Git, rdir, "checkout", "-b", branch) 141 } 142 143 // Checkout does git checkout. 144 func (lg *LocalGit) Checkout(org, repo, commitlike string) error { 145 rdir := filepath.Join(lg.Dir, org, repo) 146 return runCmd(lg.Git, rdir, "checkout", commitlike) 147 } 148 149 // RevParse does git rev-parse. 150 func (lg *LocalGit) RevParse(org, repo, commitlike string) (string, error) { 151 rdir := filepath.Join(lg.Dir, org, repo) 152 return runCmdOutput(lg.Git, rdir, "rev-parse", commitlike) 153 } 154 155 // Merge does git merge. 156 func (lg *LocalGit) Merge(org, repo, commitlike string) (string, error) { 157 rdir := filepath.Join(lg.Dir, org, repo) 158 return runCmdOutput(lg.Git, rdir, "merge", "--no-ff", "--no-stat", "-m merge", commitlike) 159 } 160 161 // Rebase does git rebase. 162 func (lg *LocalGit) Rebase(org, repo, commitlike string) (string, error) { 163 rdir := filepath.Join(lg.Dir, org, repo) 164 return runCmdOutput(lg.Git, rdir, "rebase", commitlike) 165 } 166 167 // NewV2 creates a LocalGit and a v2 client factory pointing at it. 168 func NewV2() (*LocalGit, v2.ClientFactory, error) { 169 g, err := exec.LookPath("git") 170 if err != nil { 171 return nil, nil, err 172 } 173 t, err := os.MkdirTemp("", "localgit") 174 if err != nil { 175 return nil, nil, err 176 } 177 c, err := v2.NewLocalClientFactory(t, 178 func() (name, email string, err error) { return "robot", "robot@beep.boop", nil }, 179 func(content []byte) []byte { return content }) 180 if err != nil { 181 return nil, nil, err 182 } 183 return &LocalGit{ 184 Dir: t, 185 Git: g, 186 }, c, nil 187 }