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