1*03ce13f7SAndroid Build Coastguard Worker// Copyright 2019 The SwiftShader Authors. All Rights Reserved. 2*03ce13f7SAndroid Build Coastguard Worker// 3*03ce13f7SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*03ce13f7SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*03ce13f7SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*03ce13f7SAndroid Build Coastguard Worker// 7*03ce13f7SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*03ce13f7SAndroid Build Coastguard Worker// 9*03ce13f7SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*03ce13f7SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*03ce13f7SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*03ce13f7SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*03ce13f7SAndroid Build Coastguard Worker// limitations under the License. 14*03ce13f7SAndroid Build Coastguard Worker 15*03ce13f7SAndroid Build Coastguard Worker// Package git provides functions for interacting with Git. 16*03ce13f7SAndroid Build Coastguard Workerpackage git 17*03ce13f7SAndroid Build Coastguard Worker 18*03ce13f7SAndroid Build Coastguard Workerimport ( 19*03ce13f7SAndroid Build Coastguard Worker "encoding/hex" 20*03ce13f7SAndroid Build Coastguard Worker "fmt" 21*03ce13f7SAndroid Build Coastguard Worker "io/ioutil" 22*03ce13f7SAndroid Build Coastguard Worker "net/url" 23*03ce13f7SAndroid Build Coastguard Worker "os" 24*03ce13f7SAndroid Build Coastguard Worker "os/exec" 25*03ce13f7SAndroid Build Coastguard Worker "strings" 26*03ce13f7SAndroid Build Coastguard Worker "time" 27*03ce13f7SAndroid Build Coastguard Worker 28*03ce13f7SAndroid Build Coastguard Worker "swiftshader.googlesource.com/SwiftShader/tests/regres/shell" 29*03ce13f7SAndroid Build Coastguard Worker) 30*03ce13f7SAndroid Build Coastguard Worker 31*03ce13f7SAndroid Build Coastguard Workerconst ( 32*03ce13f7SAndroid Build Coastguard Worker gitTimeout = time.Minute * 15 // timeout for a git operation 33*03ce13f7SAndroid Build Coastguard Worker) 34*03ce13f7SAndroid Build Coastguard Worker 35*03ce13f7SAndroid Build Coastguard Workervar exe string 36*03ce13f7SAndroid Build Coastguard Worker 37*03ce13f7SAndroid Build Coastguard Workerfunc init() { 38*03ce13f7SAndroid Build Coastguard Worker path, err := exec.LookPath("git") 39*03ce13f7SAndroid Build Coastguard Worker if err != nil { 40*03ce13f7SAndroid Build Coastguard Worker panic(fmt.Errorf("failed to find path to git executable: %w", err)) 41*03ce13f7SAndroid Build Coastguard Worker } 42*03ce13f7SAndroid Build Coastguard Worker exe = path 43*03ce13f7SAndroid Build Coastguard Worker} 44*03ce13f7SAndroid Build Coastguard Worker 45*03ce13f7SAndroid Build Coastguard Worker// Hash is a 20 byte, git object hash. 46*03ce13f7SAndroid Build Coastguard Workertype Hash [20]byte 47*03ce13f7SAndroid Build Coastguard Worker 48*03ce13f7SAndroid Build Coastguard Workerfunc (h Hash) String() string { return hex.EncodeToString(h[:]) } 49*03ce13f7SAndroid Build Coastguard Worker 50*03ce13f7SAndroid Build Coastguard Worker// ParseHash returns a Hash from a hexadecimal string. 51*03ce13f7SAndroid Build Coastguard Workerfunc ParseHash(s string) Hash { 52*03ce13f7SAndroid Build Coastguard Worker b, _ := hex.DecodeString(s) 53*03ce13f7SAndroid Build Coastguard Worker h := Hash{} 54*03ce13f7SAndroid Build Coastguard Worker copy(h[:], b) 55*03ce13f7SAndroid Build Coastguard Worker return h 56*03ce13f7SAndroid Build Coastguard Worker} 57*03ce13f7SAndroid Build Coastguard Worker 58*03ce13f7SAndroid Build Coastguard Worker// Add calls 'git add <file>'. 59*03ce13f7SAndroid Build Coastguard Workerfunc Add(wd, file string) error { 60*03ce13f7SAndroid Build Coastguard Worker if err := shell.Shell(gitTimeout, exe, wd, "add", file); err != nil { 61*03ce13f7SAndroid Build Coastguard Worker return fmt.Errorf("`git add %v` in working directory %v failed: %w", file, wd, err) 62*03ce13f7SAndroid Build Coastguard Worker } 63*03ce13f7SAndroid Build Coastguard Worker return nil 64*03ce13f7SAndroid Build Coastguard Worker} 65*03ce13f7SAndroid Build Coastguard Worker 66*03ce13f7SAndroid Build Coastguard Worker// CommitFlags advanced flags for Commit 67*03ce13f7SAndroid Build Coastguard Workertype CommitFlags struct { 68*03ce13f7SAndroid Build Coastguard Worker Name string // Used for author and committer 69*03ce13f7SAndroid Build Coastguard Worker Email string // Used for author and committer 70*03ce13f7SAndroid Build Coastguard Worker} 71*03ce13f7SAndroid Build Coastguard Worker 72*03ce13f7SAndroid Build Coastguard Worker// Commit calls 'git commit -m <msg> --author <author>'. 73*03ce13f7SAndroid Build Coastguard Workerfunc Commit(wd, msg string, flags CommitFlags) error { 74*03ce13f7SAndroid Build Coastguard Worker args := []string{} 75*03ce13f7SAndroid Build Coastguard Worker if flags.Name != "" { 76*03ce13f7SAndroid Build Coastguard Worker args = append(args, "-c", "user.name="+flags.Name) 77*03ce13f7SAndroid Build Coastguard Worker } 78*03ce13f7SAndroid Build Coastguard Worker if flags.Email != "" { 79*03ce13f7SAndroid Build Coastguard Worker args = append(args, "-c", "user.email="+flags.Email) 80*03ce13f7SAndroid Build Coastguard Worker } 81*03ce13f7SAndroid Build Coastguard Worker args = append(args, "commit", "-m", msg) 82*03ce13f7SAndroid Build Coastguard Worker return shell.Shell(gitTimeout, exe, wd, args...) 83*03ce13f7SAndroid Build Coastguard Worker} 84*03ce13f7SAndroid Build Coastguard Worker 85*03ce13f7SAndroid Build Coastguard Worker// PushFlags advanced flags for Commit 86*03ce13f7SAndroid Build Coastguard Workertype PushFlags struct { 87*03ce13f7SAndroid Build Coastguard Worker Username string // Used for authentication when uploading 88*03ce13f7SAndroid Build Coastguard Worker Password string // Used for authentication when uploading 89*03ce13f7SAndroid Build Coastguard Worker} 90*03ce13f7SAndroid Build Coastguard Worker 91*03ce13f7SAndroid Build Coastguard Worker// Push pushes the local branch to remote. 92*03ce13f7SAndroid Build Coastguard Workerfunc Push(wd, remote, localBranch, remoteBranch string, flags PushFlags) error { 93*03ce13f7SAndroid Build Coastguard Worker args := []string{} 94*03ce13f7SAndroid Build Coastguard Worker if flags.Username != "" { 95*03ce13f7SAndroid Build Coastguard Worker f, err := ioutil.TempFile("", "regres-cookies.txt") 96*03ce13f7SAndroid Build Coastguard Worker if err != nil { 97*03ce13f7SAndroid Build Coastguard Worker return fmt.Errorf("failed to create cookie file: %w", err) 98*03ce13f7SAndroid Build Coastguard Worker } 99*03ce13f7SAndroid Build Coastguard Worker defer f.Close() 100*03ce13f7SAndroid Build Coastguard Worker defer os.Remove(f.Name()) 101*03ce13f7SAndroid Build Coastguard Worker u, err := url.Parse(remote) 102*03ce13f7SAndroid Build Coastguard Worker if err != nil { 103*03ce13f7SAndroid Build Coastguard Worker return fmt.Errorf("failed to parse url '%v': %w", remote, err) 104*03ce13f7SAndroid Build Coastguard Worker } 105*03ce13f7SAndroid Build Coastguard Worker f.WriteString(fmt.Sprintf("%v FALSE / TRUE 2147483647 o %v=%v\n", u.Host, flags.Username, flags.Password)) 106*03ce13f7SAndroid Build Coastguard Worker f.Close() 107*03ce13f7SAndroid Build Coastguard Worker args = append(args, "-c", "http.cookiefile="+f.Name()) 108*03ce13f7SAndroid Build Coastguard Worker } 109*03ce13f7SAndroid Build Coastguard Worker args = append(args, "push", remote, localBranch+":"+remoteBranch) 110*03ce13f7SAndroid Build Coastguard Worker return shell.Shell(gitTimeout, exe, wd, args...) 111*03ce13f7SAndroid Build Coastguard Worker} 112*03ce13f7SAndroid Build Coastguard Worker 113*03ce13f7SAndroid Build Coastguard Worker// CheckoutRemoteBranch performs a git fetch and checkout of the given branch into path. 114*03ce13f7SAndroid Build Coastguard Workerfunc CheckoutRemoteBranch(path, url string, branch string) error { 115*03ce13f7SAndroid Build Coastguard Worker if err := os.MkdirAll(path, 0777); err != nil { 116*03ce13f7SAndroid Build Coastguard Worker return fmt.Errorf("mkdir '"+path+"' failed: %w", err) 117*03ce13f7SAndroid Build Coastguard Worker } 118*03ce13f7SAndroid Build Coastguard Worker 119*03ce13f7SAndroid Build Coastguard Worker for _, cmds := range [][]string{ 120*03ce13f7SAndroid Build Coastguard Worker {"init"}, 121*03ce13f7SAndroid Build Coastguard Worker {"remote", "add", "origin", url}, 122*03ce13f7SAndroid Build Coastguard Worker {"fetch", "origin", "--depth=1", branch}, 123*03ce13f7SAndroid Build Coastguard Worker {"checkout", branch}, 124*03ce13f7SAndroid Build Coastguard Worker } { 125*03ce13f7SAndroid Build Coastguard Worker if err := shell.Shell(gitTimeout, exe, path, cmds...); err != nil { 126*03ce13f7SAndroid Build Coastguard Worker os.RemoveAll(path) 127*03ce13f7SAndroid Build Coastguard Worker return err 128*03ce13f7SAndroid Build Coastguard Worker } 129*03ce13f7SAndroid Build Coastguard Worker } 130*03ce13f7SAndroid Build Coastguard Worker 131*03ce13f7SAndroid Build Coastguard Worker return nil 132*03ce13f7SAndroid Build Coastguard Worker} 133*03ce13f7SAndroid Build Coastguard Worker 134*03ce13f7SAndroid Build Coastguard Worker// CheckoutRemoteCommit performs a git fetch and checkout of the given commit into path. 135*03ce13f7SAndroid Build Coastguard Workerfunc CheckoutRemoteCommit(path, url string, commit Hash) error { 136*03ce13f7SAndroid Build Coastguard Worker if err := os.MkdirAll(path, 0777); err != nil { 137*03ce13f7SAndroid Build Coastguard Worker return fmt.Errorf("mkdir '"+path+"' failed: %w", err) 138*03ce13f7SAndroid Build Coastguard Worker } 139*03ce13f7SAndroid Build Coastguard Worker 140*03ce13f7SAndroid Build Coastguard Worker for _, cmds := range [][]string{ 141*03ce13f7SAndroid Build Coastguard Worker {"init"}, 142*03ce13f7SAndroid Build Coastguard Worker {"remote", "add", "origin", url}, 143*03ce13f7SAndroid Build Coastguard Worker {"fetch", "origin", "--depth=1", commit.String()}, 144*03ce13f7SAndroid Build Coastguard Worker {"checkout", commit.String()}, 145*03ce13f7SAndroid Build Coastguard Worker } { 146*03ce13f7SAndroid Build Coastguard Worker if err := shell.Shell(gitTimeout, exe, path, cmds...); err != nil { 147*03ce13f7SAndroid Build Coastguard Worker os.RemoveAll(path) 148*03ce13f7SAndroid Build Coastguard Worker return err 149*03ce13f7SAndroid Build Coastguard Worker } 150*03ce13f7SAndroid Build Coastguard Worker } 151*03ce13f7SAndroid Build Coastguard Worker 152*03ce13f7SAndroid Build Coastguard Worker return nil 153*03ce13f7SAndroid Build Coastguard Worker} 154*03ce13f7SAndroid Build Coastguard Worker 155*03ce13f7SAndroid Build Coastguard Worker// CheckoutCommit performs a git checkout of the given commit. 156*03ce13f7SAndroid Build Coastguard Workerfunc CheckoutCommit(path string, commit Hash) error { 157*03ce13f7SAndroid Build Coastguard Worker return shell.Shell(gitTimeout, exe, path, "checkout", commit.String()) 158*03ce13f7SAndroid Build Coastguard Worker} 159*03ce13f7SAndroid Build Coastguard Worker 160*03ce13f7SAndroid Build Coastguard Worker// Apply applys the patch file to the git repo at dir. 161*03ce13f7SAndroid Build Coastguard Workerfunc Apply(dir, patch string) error { 162*03ce13f7SAndroid Build Coastguard Worker return shell.Shell(gitTimeout, exe, dir, "apply", patch) 163*03ce13f7SAndroid Build Coastguard Worker} 164*03ce13f7SAndroid Build Coastguard Worker 165*03ce13f7SAndroid Build Coastguard Worker// FetchRefHash returns the git hash of the given ref. 166*03ce13f7SAndroid Build Coastguard Workerfunc FetchRefHash(ref, url string) (Hash, error) { 167*03ce13f7SAndroid Build Coastguard Worker out, err := shell.Exec(gitTimeout, exe, "", nil, "", "ls-remote", url, ref) 168*03ce13f7SAndroid Build Coastguard Worker if err != nil { 169*03ce13f7SAndroid Build Coastguard Worker return Hash{}, err 170*03ce13f7SAndroid Build Coastguard Worker } 171*03ce13f7SAndroid Build Coastguard Worker return ParseHash(string(out)), nil 172*03ce13f7SAndroid Build Coastguard Worker} 173*03ce13f7SAndroid Build Coastguard Worker 174*03ce13f7SAndroid Build Coastguard Workertype ChangeList struct { 175*03ce13f7SAndroid Build Coastguard Worker Hash Hash 176*03ce13f7SAndroid Build Coastguard Worker Date time.Time 177*03ce13f7SAndroid Build Coastguard Worker Author string 178*03ce13f7SAndroid Build Coastguard Worker Subject string 179*03ce13f7SAndroid Build Coastguard Worker Description string 180*03ce13f7SAndroid Build Coastguard Worker} 181*03ce13f7SAndroid Build Coastguard Worker 182*03ce13f7SAndroid Build Coastguard Worker// Log returns the top count ChangeLists at HEAD. 183*03ce13f7SAndroid Build Coastguard Workerfunc Log(path string, count int) ([]ChangeList, error) { 184*03ce13f7SAndroid Build Coastguard Worker return LogFrom(path, "HEAD", count) 185*03ce13f7SAndroid Build Coastguard Worker} 186*03ce13f7SAndroid Build Coastguard Worker 187*03ce13f7SAndroid Build Coastguard Worker// LogFrom returns the top count ChangeList starting from at. 188*03ce13f7SAndroid Build Coastguard Workerfunc LogFrom(path, at string, count int) ([]ChangeList, error) { 189*03ce13f7SAndroid Build Coastguard Worker if at == "" { 190*03ce13f7SAndroid Build Coastguard Worker at = "HEAD" 191*03ce13f7SAndroid Build Coastguard Worker } 192*03ce13f7SAndroid Build Coastguard Worker out, err := shell.Exec(gitTimeout, exe, "", nil, "", "log", at, "--pretty=format:"+prettyFormat, fmt.Sprintf("-%d", count), path) 193*03ce13f7SAndroid Build Coastguard Worker if err != nil { 194*03ce13f7SAndroid Build Coastguard Worker return nil, err 195*03ce13f7SAndroid Build Coastguard Worker } 196*03ce13f7SAndroid Build Coastguard Worker return parseLog(string(out)), nil 197*03ce13f7SAndroid Build Coastguard Worker} 198*03ce13f7SAndroid Build Coastguard Worker 199*03ce13f7SAndroid Build Coastguard Worker// Parent returns the parent ChangeList for cl. 200*03ce13f7SAndroid Build Coastguard Workerfunc Parent(cl ChangeList) (ChangeList, error) { 201*03ce13f7SAndroid Build Coastguard Worker out, err := shell.Exec(gitTimeout, exe, "", nil, "", "log", "--pretty=format:"+prettyFormat, fmt.Sprintf("%v^", cl.Hash)) 202*03ce13f7SAndroid Build Coastguard Worker if err != nil { 203*03ce13f7SAndroid Build Coastguard Worker return ChangeList{}, err 204*03ce13f7SAndroid Build Coastguard Worker } 205*03ce13f7SAndroid Build Coastguard Worker cls := parseLog(string(out)) 206*03ce13f7SAndroid Build Coastguard Worker if len(cls) == 0 { 207*03ce13f7SAndroid Build Coastguard Worker return ChangeList{}, fmt.Errorf("Unexpected output") 208*03ce13f7SAndroid Build Coastguard Worker } 209*03ce13f7SAndroid Build Coastguard Worker return cls[0], nil 210*03ce13f7SAndroid Build Coastguard Worker} 211*03ce13f7SAndroid Build Coastguard Worker 212*03ce13f7SAndroid Build Coastguard Worker// HeadCL returns the HEAD ChangeList at the given commit/tag/branch. 213*03ce13f7SAndroid Build Coastguard Workerfunc HeadCL(path string) (ChangeList, error) { 214*03ce13f7SAndroid Build Coastguard Worker cls, err := LogFrom(path, "HEAD", 1) 215*03ce13f7SAndroid Build Coastguard Worker if err != nil { 216*03ce13f7SAndroid Build Coastguard Worker return ChangeList{}, err 217*03ce13f7SAndroid Build Coastguard Worker } 218*03ce13f7SAndroid Build Coastguard Worker if len(cls) == 0 { 219*03ce13f7SAndroid Build Coastguard Worker return ChangeList{}, fmt.Errorf("No commits found") 220*03ce13f7SAndroid Build Coastguard Worker } 221*03ce13f7SAndroid Build Coastguard Worker return cls[0], nil 222*03ce13f7SAndroid Build Coastguard Worker} 223*03ce13f7SAndroid Build Coastguard Worker 224*03ce13f7SAndroid Build Coastguard Worker// Show content of the file at path for the given commit/tag/branch. 225*03ce13f7SAndroid Build Coastguard Workerfunc Show(path, at string) ([]byte, error) { 226*03ce13f7SAndroid Build Coastguard Worker return shell.Exec(gitTimeout, exe, "", nil, "", "show", at+":"+path) 227*03ce13f7SAndroid Build Coastguard Worker} 228*03ce13f7SAndroid Build Coastguard Worker 229*03ce13f7SAndroid Build Coastguard Workerconst prettyFormat = "ǁ%Hǀ%cIǀ%an <%ae>ǀ%sǀ%b" 230*03ce13f7SAndroid Build Coastguard Worker 231*03ce13f7SAndroid Build Coastguard Workerfunc parseLog(str string) []ChangeList { 232*03ce13f7SAndroid Build Coastguard Worker msgs := strings.Split(str, "ǁ") 233*03ce13f7SAndroid Build Coastguard Worker cls := make([]ChangeList, 0, len(msgs)) 234*03ce13f7SAndroid Build Coastguard Worker for _, s := range msgs { 235*03ce13f7SAndroid Build Coastguard Worker if parts := strings.Split(s, "ǀ"); len(parts) == 5 { 236*03ce13f7SAndroid Build Coastguard Worker cl := ChangeList{ 237*03ce13f7SAndroid Build Coastguard Worker Hash: ParseHash(parts[0]), 238*03ce13f7SAndroid Build Coastguard Worker Author: strings.TrimSpace(parts[2]), 239*03ce13f7SAndroid Build Coastguard Worker Subject: strings.TrimSpace(parts[3]), 240*03ce13f7SAndroid Build Coastguard Worker Description: strings.TrimSpace(parts[4]), 241*03ce13f7SAndroid Build Coastguard Worker } 242*03ce13f7SAndroid Build Coastguard Worker date, err := time.Parse(time.RFC3339, parts[1]) 243*03ce13f7SAndroid Build Coastguard Worker if err != nil { 244*03ce13f7SAndroid Build Coastguard Worker panic(err) 245*03ce13f7SAndroid Build Coastguard Worker } 246*03ce13f7SAndroid Build Coastguard Worker cl.Date = date 247*03ce13f7SAndroid Build Coastguard Worker 248*03ce13f7SAndroid Build Coastguard Worker cls = append(cls, cl) 249*03ce13f7SAndroid Build Coastguard Worker } 250*03ce13f7SAndroid Build Coastguard Worker } 251*03ce13f7SAndroid Build Coastguard Worker return cls 252*03ce13f7SAndroid Build Coastguard Worker} 253