1
2
3
4
5
6 package github
7
8 import (
9 "bytes"
10 "context"
11 "errors"
12 "fmt"
13 "strings"
14 "time"
15
16 "golang.org/x/crypto/openpgp"
17 )
18
19
20 type SignatureVerification struct {
21 Verified *bool `json:"verified,omitempty"`
22 Reason *string `json:"reason,omitempty"`
23 Signature *string `json:"signature,omitempty"`
24 Payload *string `json:"payload,omitempty"`
25 }
26
27
28 type Commit struct {
29 SHA *string `json:"sha,omitempty"`
30 Author *CommitAuthor `json:"author,omitempty"`
31 Committer *CommitAuthor `json:"committer,omitempty"`
32 Message *string `json:"message,omitempty"`
33 Tree *Tree `json:"tree,omitempty"`
34 Parents []*Commit `json:"parents,omitempty"`
35 Stats *CommitStats `json:"stats,omitempty"`
36 HTMLURL *string `json:"html_url,omitempty"`
37 URL *string `json:"url,omitempty"`
38 Verification *SignatureVerification `json:"verification,omitempty"`
39 NodeID *string `json:"node_id,omitempty"`
40
41
42
43
44 CommentCount *int `json:"comment_count,omitempty"`
45
46
47
48
49 SigningKey *openpgp.Entity `json:"-"`
50 }
51
52 func (c Commit) String() string {
53 return Stringify(c)
54 }
55
56
57
58 type CommitAuthor struct {
59 Date *time.Time `json:"date,omitempty"`
60 Name *string `json:"name,omitempty"`
61 Email *string `json:"email,omitempty"`
62
63
64 Login *string `json:"username,omitempty"`
65 }
66
67 func (c CommitAuthor) String() string {
68 return Stringify(c)
69 }
70
71
72
73
74 func (s *GitService) GetCommit(ctx context.Context, owner string, repo string, sha string) (*Commit, *Response, error) {
75 u := fmt.Sprintf("repos/%v/%v/git/commits/%v", owner, repo, sha)
76 req, err := s.client.NewRequest("GET", u, nil)
77 if err != nil {
78 return nil, nil, err
79 }
80
81 c := new(Commit)
82 resp, err := s.client.Do(ctx, req, c)
83 if err != nil {
84 return nil, resp, err
85 }
86
87 return c, resp, nil
88 }
89
90
91 type createCommit struct {
92 Author *CommitAuthor `json:"author,omitempty"`
93 Committer *CommitAuthor `json:"committer,omitempty"`
94 Message *string `json:"message,omitempty"`
95 Tree *string `json:"tree,omitempty"`
96 Parents []string `json:"parents,omitempty"`
97 Signature *string `json:"signature,omitempty"`
98 }
99
100
101
102
103
104
105
106
107
108 func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string, commit *Commit) (*Commit, *Response, error) {
109 if commit == nil {
110 return nil, nil, fmt.Errorf("commit must be provided")
111 }
112
113 u := fmt.Sprintf("repos/%v/%v/git/commits", owner, repo)
114
115 parents := make([]string, len(commit.Parents))
116 for i, parent := range commit.Parents {
117 parents[i] = *parent.SHA
118 }
119
120 body := &createCommit{
121 Author: commit.Author,
122 Committer: commit.Committer,
123 Message: commit.Message,
124 Parents: parents,
125 }
126 if commit.Tree != nil {
127 body.Tree = commit.Tree.SHA
128 }
129 if commit.SigningKey != nil {
130 signature, err := createSignature(commit.SigningKey, body)
131 if err != nil {
132 return nil, nil, err
133 }
134 body.Signature = &signature
135 }
136 if commit.Verification != nil {
137 body.Signature = commit.Verification.Signature
138 }
139
140 req, err := s.client.NewRequest("POST", u, body)
141 if err != nil {
142 return nil, nil, err
143 }
144
145 c := new(Commit)
146 resp, err := s.client.Do(ctx, req, c)
147 if err != nil {
148 return nil, resp, err
149 }
150
151 return c, resp, nil
152 }
153
154 func createSignature(signingKey *openpgp.Entity, commit *createCommit) (string, error) {
155 if signingKey == nil || commit == nil {
156 return "", errors.New("createSignature: invalid parameters")
157 }
158
159 message, err := createSignatureMessage(commit)
160 if err != nil {
161 return "", err
162 }
163
164 writer := new(bytes.Buffer)
165 reader := bytes.NewReader([]byte(message))
166 if err := openpgp.ArmoredDetachSign(writer, signingKey, reader, nil); err != nil {
167 return "", err
168 }
169
170 return writer.String(), nil
171 }
172
173 func createSignatureMessage(commit *createCommit) (string, error) {
174 if commit == nil || commit.Message == nil || *commit.Message == "" || commit.Author == nil {
175 return "", errors.New("createSignatureMessage: invalid parameters")
176 }
177
178 var message []string
179
180 if commit.Tree != nil {
181 message = append(message, fmt.Sprintf("tree %s", *commit.Tree))
182 }
183
184 for _, parent := range commit.Parents {
185 message = append(message, fmt.Sprintf("parent %s", parent))
186 }
187
188 message = append(message, fmt.Sprintf("author %s <%s> %d %s", commit.Author.GetName(), commit.Author.GetEmail(), commit.Author.GetDate().Unix(), commit.Author.GetDate().Format("-0700")))
189
190 committer := commit.Committer
191 if committer == nil {
192 committer = commit.Author
193 }
194
195
196 message = append(message, fmt.Sprintf("committer %s <%s> %d %s\n", committer.GetName(), committer.GetEmail(), committer.GetDate().Unix(), committer.GetDate().Format("-0700")))
197 message = append(message, *commit.Message)
198
199 return strings.Join(message, "\n"), nil
200 }
201
View as plain text