fork of go-git with some jj specific features
at main 3.1 kB view raw
1package main 2 3import ( 4 "fmt" 5 "log" 6 "os" 7 8 "github.com/go-git/go-git/v5" 9 . "github.com/go-git/go-git/v5/_examples" 10 "github.com/go-git/go-git/v5/config" 11 "github.com/go-git/go-git/v5/plumbing/object" 12 "github.com/go-git/go-git/v5/plumbing/transport/ssh" 13) 14 15// Example of how create a tag and push it to a remote. 16func main() { 17 CheckArgs("<ssh-url>", "<directory>", "<tag>", "<name>", "<email>", "<public-key>") 18 url := os.Args[1] 19 directory := os.Args[2] 20 tag := os.Args[3] 21 key := os.Args[6] 22 23 r, err := cloneRepo(url, directory, key) 24 25 if err != nil { 26 log.Printf("clone repo error: %s", err) 27 return 28 } 29 30 created, err := setTag(r, tag) 31 if err != nil { 32 log.Printf("create tag error: %s", err) 33 return 34 } 35 36 if created { 37 err = pushTags(r, key) 38 if err != nil { 39 log.Printf("push tag error: %s", err) 40 return 41 } 42 } 43} 44 45func cloneRepo(url, dir, publicKeyPath string) (*git.Repository, error) { 46 log.Printf("cloning %s into %s", url, dir) 47 auth, keyErr := publicKey(publicKeyPath) 48 if keyErr != nil { 49 return nil, keyErr 50 } 51 52 Info("git clone %s", url) 53 r, err := git.PlainClone(dir, false, &git.CloneOptions{ 54 Progress: os.Stdout, 55 URL: url, 56 Auth: auth, 57 }) 58 59 if err != nil { 60 log.Printf("clone git repo error: %s", err) 61 return nil, err 62 } 63 64 return r, nil 65} 66 67func publicKey(filePath string) (*ssh.PublicKeys, error) { 68 var publicKey *ssh.PublicKeys 69 sshKey, _ := os.ReadFile(filePath) 70 publicKey, err := ssh.NewPublicKeys("git", []byte(sshKey), "") 71 if err != nil { 72 return nil, err 73 } 74 return publicKey, err 75} 76 77func tagExists(tag string, r *git.Repository) bool { 78 tagFoundErr := "tag was found" 79 Info("git show-ref --tag") 80 tags, err := r.TagObjects() 81 if err != nil { 82 log.Printf("get tags error: %s", err) 83 return false 84 } 85 res := false 86 err = tags.ForEach(func(t *object.Tag) error { 87 if t.Name == tag { 88 res = true 89 return fmt.Errorf(tagFoundErr) 90 } 91 return nil 92 }) 93 if err != nil && err.Error() != tagFoundErr { 94 log.Printf("iterate tags error: %s", err) 95 return false 96 } 97 return res 98} 99 100func setTag(r *git.Repository, tag string) (bool, error) { 101 if tagExists(tag, r) { 102 log.Printf("tag %s already exists", tag) 103 return false, nil 104 } 105 log.Printf("Set tag %s", tag) 106 h, err := r.Head() 107 if err != nil { 108 log.Printf("get HEAD error: %s", err) 109 return false, err 110 } 111 Info("git tag -a %s %s -m \"%s\"", tag, h.Hash(), tag) 112 _, err = r.CreateTag(tag, h.Hash(), &git.CreateTagOptions{ 113 Message: tag, 114 }) 115 116 if err != nil { 117 log.Printf("create tag error: %s", err) 118 return false, err 119 } 120 121 return true, nil 122} 123 124func pushTags(r *git.Repository, publicKeyPath string) error { 125 126 auth, _ := publicKey(publicKeyPath) 127 128 po := &git.PushOptions{ 129 RemoteName: "origin", 130 Progress: os.Stdout, 131 RefSpecs: []config.RefSpec{config.RefSpec("refs/tags/*:refs/tags/*")}, 132 Auth: auth, 133 } 134 Info("git push --tags") 135 err := r.Push(po) 136 137 if err != nil { 138 if err == git.NoErrAlreadyUpToDate { 139 log.Print("origin remote was up to date, no push done") 140 return nil 141 } 142 log.Printf("push to remote origin error: %s", err) 143 return err 144 } 145 146 return nil 147}