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}