/
types.go
1455 lines (1288 loc) · 54.7 KB
/
types.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package github
import (
"encoding/json"
"fmt"
"strings"
"time"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
)
const (
// EventGUID is sent by GitHub in a header of every webhook request.
// Used as a log field across prow.
EventGUID = "event-GUID"
// PrLogField is the number of a PR.
// Used as a log field across prow.
PrLogField = "pr"
// OrgLogField is the organization of a PR.
// Used as a log field across prow.
OrgLogField = "org"
// RepoLogField is the repository of a PR.
// Used as a log field across prow.
RepoLogField = "repo"
// SearchTimeFormat is a time.Time format string for ISO8601 which is the
// format that GitHub requires for times specified as part of a search query.
SearchTimeFormat = "2006-01-02T15:04:05Z"
// DefaultAPIEndpoint is the default GitHub API endpoint.
DefaultAPIEndpoint = "https://api.github.com"
// DefaultHost is the default GitHub base endpoint.
DefaultHost = "github.com"
// DefaultGraphQLEndpoint is the default GitHub GraphQL API endpoint.
DefaultGraphQLEndpoint = "https://api.github.com/graphql"
)
var (
// FoundingYear is the year GitHub was founded. This is just used so that
// we can lower bound dates related to PRs and issues.
FoundingYear, _ = time.Parse(SearchTimeFormat, "2007-01-01T00:00:00Z")
)
// These are possible State entries for a Status.
const (
StatusPending = "pending"
StatusSuccess = "success"
StatusError = "error"
StatusFailure = "failure"
)
// Possible contents for reactions.
const (
ReactionThumbsUp = "+1"
ReactionThumbsDown = "-1"
ReactionLaugh = "laugh"
ReactionConfused = "confused"
ReactionHeart = "heart"
ReactionHooray = "hooray"
stateCannotBeChangedMessagePrefix = "state cannot be changed."
)
// PullRequestMergeType enumerates the types of merges the GitHub API can
// perform
// https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-button
type PullRequestMergeType string
// Possible types of merges for the GitHub merge API
const (
MergeMerge PullRequestMergeType = "merge"
MergeRebase PullRequestMergeType = "rebase"
MergeSquash PullRequestMergeType = "squash"
)
func unmarshalClientError(b []byte) error {
var errors []error
clientError := ClientError{}
err := json.Unmarshal(b, &clientError)
if err == nil {
return clientError
}
errors = append(errors, err)
alternativeClientError := AlternativeClientError{}
err = json.Unmarshal(b, &alternativeClientError)
if err == nil {
return alternativeClientError
}
errors = append(errors, err)
return utilerrors.NewAggregate(errors)
}
// ClientError represents https://developer.github.com/v3/#client-errors
type ClientError struct {
Message string `json:"message"`
Errors []clientErrorSubError `json:"errors,omitempty"`
}
type clientErrorSubError struct {
Resource string `json:"resource"`
Field string `json:"field"`
Code string `json:"code"`
Message string `json:"message,omitempty"`
}
func (r ClientError) Error() string {
return r.Message
}
// AlternativeClientError represents an alternative format for https://developer.github.com/v3/#client-errors
// This is probably a GitHub bug, as documentation_url should appear only in custom errors
type AlternativeClientError struct {
Message string `json:"message"`
Errors []string `json:"errors,omitempty"`
DocumentationURL string `json:"documentation_url,omitempty"`
}
func (r AlternativeClientError) Error() string {
return r.Message
}
// Reaction holds the type of emotional reaction.
type Reaction struct {
Content string `json:"content"`
}
// Status is used to set a commit status line.
type Status struct {
State string `json:"state"`
TargetURL string `json:"target_url,omitempty"`
Description string `json:"description,omitempty"`
Context string `json:"context,omitempty"`
}
// CombinedStatus is the latest statuses for a ref.
type CombinedStatus struct {
SHA string `json:"sha"`
Statuses []Status `json:"statuses"`
State string `json:"state"`
}
// User is a GitHub user account.
type User struct {
Login string `json:"login"`
Name string `json:"name"`
Email string `json:"email"`
ID int `json:"id"`
HTMLURL string `json:"html_url"`
Permissions RepoPermissions `json:"permissions"`
Type string `json:"type"`
}
const (
// UserTypeUser identifies an actual user account in the User.Type field
UserTypeUser = "User"
// UserTypeBot identifies a github app bot user in the User.Type field
UserTypeBot = "Bot"
)
// NormLogin normalizes GitHub login strings
func NormLogin(login string) string {
return strings.TrimPrefix(strings.ToLower(login), "@")
}
// PullRequestEventAction enumerates the triggers for this
// webhook payload type. See also:
// https://developer.github.com/v3/activity/events/types/#pullrequestevent
type PullRequestEventAction string
const (
// PullRequestActionAssigned means assignees were added.
PullRequestActionAssigned PullRequestEventAction = "assigned"
// PullRequestActionUnassigned means assignees were removed.
PullRequestActionUnassigned PullRequestEventAction = "unassigned"
// PullRequestActionReviewRequested means review requests were added.
PullRequestActionReviewRequested PullRequestEventAction = "review_requested"
// PullRequestActionReviewRequestRemoved means review requests were removed.
PullRequestActionReviewRequestRemoved PullRequestEventAction = "review_request_removed"
// PullRequestActionLabeled means labels were added.
PullRequestActionLabeled PullRequestEventAction = "labeled"
// PullRequestActionUnlabeled means labels were removed
PullRequestActionUnlabeled PullRequestEventAction = "unlabeled"
// PullRequestActionOpened means the PR was created
PullRequestActionOpened PullRequestEventAction = "opened"
// PullRequestActionEdited means the PR body changed.
PullRequestActionEdited PullRequestEventAction = "edited"
// PullRequestActionClosed means the PR was closed (or was merged).
PullRequestActionClosed PullRequestEventAction = "closed"
// PullRequestActionReopened means the PR was reopened.
PullRequestActionReopened PullRequestEventAction = "reopened"
// PullRequestActionSynchronize means the git state changed.
PullRequestActionSynchronize PullRequestEventAction = "synchronize"
// PullRequestActionReadyForReview means the PR is no longer a draft PR.
PullRequestActionReadyForReview PullRequestEventAction = "ready_for_review"
// PullRequestActionConvertedToDraft means the PR is now a draft PR.
PullRequestActionConvertedToDraft PullRequestEventAction = "converted_to_draft"
// PullRequestActionLocked means labels were added.
PullRequestActionLocked PullRequestEventAction = "locked"
// PullRequestActionUnlocked means labels were removed
PullRequestActionUnlocked PullRequestEventAction = "unlocked"
// PullRequestActionAutoMergeEnabled means auto merge was enabled
PullRequestActionAutoMergeEnabled PullRequestEventAction = "auto_merge_enabled"
// PullRequestActionAutoMergeDisabled means auto merge was disabled
PullRequestActionAutoMergeDisabled PullRequestEventAction = "auto_merge_disabled"
)
// GenericEvent is a lightweight struct containing just Sender, Organization and Repo as
// they are allWebhook payload object common properties:
// https://developer.github.com/webhooks/event-payloads/#webhook-payload-object-common-properties
type GenericEvent struct {
Sender User `json:"sender"`
Org Organization `json:"organization"`
Repo Repo `json:"repository"`
}
// PullRequestEvent is what GitHub sends us when a PR is changed.
type PullRequestEvent struct {
Action PullRequestEventAction `json:"action"`
Number int `json:"number"`
PullRequest PullRequest `json:"pull_request"`
Repo Repo `json:"repository"`
Label Label `json:"label"`
Sender User `json:"sender"`
// Changes holds raw change data, which we must inspect
// and deserialize later as this is a polymorphic field
Changes json.RawMessage `json:"changes"`
// GUID is included in the header of the request received by GitHub.
GUID string
}
const (
PullRequestStateOpen = "open"
PullRequestStateClosed = "closed"
)
// PullRequest contains information about a PullRequest.
type PullRequest struct {
ID int `json:"id"`
NodeID string `json:"node_id"`
Number int `json:"number"`
HTMLURL string `json:"html_url"`
User User `json:"user"`
Labels []Label `json:"labels"`
Base PullRequestBranch `json:"base"`
Head PullRequestBranch `json:"head"`
Title string `json:"title"`
Body string `json:"body"`
RequestedReviewers []User `json:"requested_reviewers"`
Assignees []User `json:"assignees"`
State string `json:"state"`
Draft bool `json:"draft"`
Merged bool `json:"merged"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
// ref https://developer.github.com/v3/pulls/#get-a-single-pull-request
// If Merged is true, MergeSHA is the SHA of the merge commit, or squashed commit
// If Merged is false, MergeSHA is a commit SHA that github created to test if
// the PR can be merged automatically.
MergeSHA *string `json:"merge_commit_sha"`
// ref https://developer.github.com/v3/pulls/#response-1
// The value of the mergeable attribute can be true, false, or null. If the value
// is null, this means that the mergeability hasn't been computed yet, and a
// background job was started to compute it. When the job is complete, the response
// will include a non-null value for the mergeable attribute.
Mergable *bool `json:"mergeable,omitempty"`
// If the PR doesn't have any milestone, `milestone` is null and is unmarshaled to nil.
Milestone *Milestone `json:"milestone,omitempty"`
Commits int `json:"commits"`
AuthorAssociation string `json:"author_association,omitempty"`
}
// PullRequestBranch contains information about a particular branch in a PR.
type PullRequestBranch struct {
Ref string `json:"ref"`
SHA string `json:"sha"`
Repo Repo `json:"repo"`
}
// Label describes a GitHub label.
type Label struct {
URL string `json:"url"`
Name string `json:"name"`
Description string `json:"description"`
Color string `json:"color"`
}
// PullRequestFileStatus enumerates the statuses for this webhook payload type.
type PullRequestFileStatus string
const (
// PullRequestFileModified means a file changed.
PullRequestFileModified PullRequestFileStatus = "modified"
// PullRequestFileAdded means a file was added.
PullRequestFileAdded = "added"
// PullRequestFileRemoved means a file was deleted.
PullRequestFileRemoved = "removed"
// PullRequestFileRenamed means a file moved.
PullRequestFileRenamed = "renamed"
)
// PullRequestChange contains information about what a PR changed.
type PullRequestChange struct {
SHA string `json:"sha"`
Filename string `json:"filename"`
Status string `json:"status"`
Additions int `json:"additions"`
Deletions int `json:"deletions"`
Changes int `json:"changes"`
Patch string `json:"patch"`
BlobURL string `json:"blob_url"`
PreviousFilename string `json:"previous_filename"`
}
// Repo contains general repository information: it includes fields available
// in repo records returned by GH "List" methods but not those returned by GH
// "Get" method.
// See also https://developer.github.com/v3/repos/#list-organization-repositories
type Repo struct {
Owner User `json:"owner"`
Name string `json:"name"`
FullName string `json:"full_name"`
HTMLURL string `json:"html_url"`
Fork bool `json:"fork"`
DefaultBranch string `json:"default_branch"`
Archived bool `json:"archived"`
Private bool `json:"private"`
Description string `json:"description"`
Homepage string `json:"homepage"`
HasIssues bool `json:"has_issues"`
HasProjects bool `json:"has_projects"`
HasWiki bool `json:"has_wiki"`
NodeID string `json:"node_id"`
// Permissions reflect the permission level for the requester, so
// on a repository GET call this will be for the user whose token
// is being used, if listing a team's repos this will be for the
// team's privilege level in the repo
Permissions RepoPermissions `json:"permissions"`
Parent ParentRepo `json:"parent"`
}
// ParentRepo contains a small subsection of general repository information: it
// just includes the information needed to confirm that a parent repo exists
// and what the name of that repo is.
type ParentRepo struct {
Owner User `json:"owner"`
Name string `json:"name"`
FullName string `json:"full_name"`
HTMLURL string `json:"html_url"`
}
// Repo contains detailed repository information, including items
// that are not available in repo records returned by GH "List" methods
// but are in those returned by GH "Get" method.
// See https://developer.github.com/v3/repos/#list-organization-repositories
// See https://developer.github.com/v3/repos/#get
type FullRepo struct {
Repo
AllowSquashMerge bool `json:"allow_squash_merge,omitempty"`
AllowMergeCommit bool `json:"allow_merge_commit,omitempty"`
AllowRebaseMerge bool `json:"allow_rebase_merge,omitempty"`
}
// RepoRequest contains metadata used in requests to create or update a Repo.
// Compared to `Repo`, its members are pointers to allow the "not set/use default
// semantics.
// See also:
// - https://developer.github.com/v3/repos/#create
// - https://developer.github.com/v3/repos/#edit
type RepoRequest struct {
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
Homepage *string `json:"homepage,omitempty"`
Private *bool `json:"private,omitempty"`
HasIssues *bool `json:"has_issues,omitempty"`
HasProjects *bool `json:"has_projects,omitempty"`
HasWiki *bool `json:"has_wiki,omitempty"`
AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"`
AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"`
AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"`
}
// RepoCreateRequest contains metadata used in requests to create a repo.
// See also: https://developer.github.com/v3/repos/#create
type RepoCreateRequest struct {
RepoRequest `json:",omitempty"`
AutoInit *bool `json:"auto_init,omitempty"`
GitignoreTemplate *string `json:"gitignore_template,omitempty"`
LicenseTemplate *string `json:"license_template,omitempty"`
}
func (r RepoRequest) ToRepo() *FullRepo {
setString := func(dest, src *string) {
if src != nil {
*dest = *src
}
}
setBool := func(dest, src *bool) {
if src != nil {
*dest = *src
}
}
var repo FullRepo
setString(&repo.Name, r.Name)
setString(&repo.Description, r.Description)
setString(&repo.Homepage, r.Homepage)
setBool(&repo.Private, r.Private)
setBool(&repo.HasIssues, r.HasIssues)
setBool(&repo.HasProjects, r.HasProjects)
setBool(&repo.HasWiki, r.HasWiki)
setBool(&repo.AllowSquashMerge, r.AllowSquashMerge)
setBool(&repo.AllowMergeCommit, r.AllowMergeCommit)
setBool(&repo.AllowRebaseMerge, r.AllowRebaseMerge)
return &repo
}
// Defined returns true if at least one of the pointer fields are not nil
func (r RepoRequest) Defined() bool {
return r.Name != nil || r.Description != nil || r.Homepage != nil || r.Private != nil ||
r.HasIssues != nil || r.HasProjects != nil || r.HasWiki != nil || r.AllowSquashMerge != nil ||
r.AllowMergeCommit != nil || r.AllowRebaseMerge != nil
}
// RepoUpdateRequest contains metadata used for updating a repository
// See also: https://developer.github.com/v3/repos/#edit
type RepoUpdateRequest struct {
RepoRequest `json:",omitempty"`
DefaultBranch *string `json:"default_branch,omitempty"`
Archived *bool `json:"archived,omitempty"`
}
func (r RepoUpdateRequest) ToRepo() *FullRepo {
repo := r.RepoRequest.ToRepo()
if r.DefaultBranch != nil {
repo.DefaultBranch = *r.DefaultBranch
}
if r.Archived != nil {
repo.Archived = *r.Archived
}
return repo
}
func (r RepoUpdateRequest) Defined() bool {
return r.RepoRequest.Defined() || r.DefaultBranch != nil || r.Archived != nil
}
// RepoPermissions describes which permission level an entity has in a
// repo. At most one of the booleans here should be true.
type RepoPermissions struct {
// Pull is equivalent to "Read" permissions in the web UI
Pull bool `json:"pull"`
Triage bool `json:"triage"`
// Push is equivalent to "Edit" permissions in the web UI
Push bool `json:"push"`
Maintain bool `json:"maintain"`
Admin bool `json:"admin"`
}
// RepoPermissionLevel is admin, write, read or none.
//
// See https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level
type RepoPermissionLevel string
// For more information on access levels, see:
// https://docs.github.com/en/github/setting-up-and-managing-organizations-and-teams/repository-permission-levels-for-an-organization
const (
// Read allows pull but not push
Read RepoPermissionLevel = "read"
// Triage allows Read and managing issues
// pull requests but not push
Triage RepoPermissionLevel = "triage"
// Write allows Read plus push
Write RepoPermissionLevel = "write"
// Maintain allows Write along with managing
// repository without access to sensitive or
// destructive instructions.
Maintain RepoPermissionLevel = "maintain"
// Admin allows Write plus change others' rights.
Admin RepoPermissionLevel = "admin"
// None disallows everything
None RepoPermissionLevel = "none"
)
var repoPermissionLevels = map[RepoPermissionLevel]bool{
Read: true,
Triage: true,
Write: true,
Maintain: true,
Admin: true,
None: true,
}
// MarshalText returns the byte representation of the permission
func (l RepoPermissionLevel) MarshalText() ([]byte, error) {
return []byte(l), nil
}
// UnmarshalText validates the text is a valid string
func (l *RepoPermissionLevel) UnmarshalText(text []byte) error {
v := RepoPermissionLevel(text)
if _, ok := repoPermissionLevels[v]; !ok {
return fmt.Errorf("bad repo permission: %s not in %v", v, repoPermissionLevels)
}
*l = v
return nil
}
type TeamPermission string
const (
RepoPull TeamPermission = "pull"
RepoTriage TeamPermission = "triage"
RepoMaintain TeamPermission = "maintain"
RepoPush TeamPermission = "push"
RepoAdmin TeamPermission = "admin"
)
// Branch contains general branch information.
type Branch struct {
Name string `json:"name"`
Protected bool `json:"protected"` // only included for ?protection=true requests
// TODO(fejta): consider including undocumented protection key
}
// BranchProtection represents protections
// currently in place for a branch
// See also: https://developer.github.com/v3/repos/branches/#get-branch-protection
type BranchProtection struct {
RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"`
EnforceAdmins EnforceAdmins `json:"enforce_admins"`
RequiredPullRequestReviews *RequiredPullRequestReviews `json:"required_pull_request_reviews"`
Restrictions *Restrictions `json:"restrictions"`
}
// EnforceAdmins specifies whether to enforce the
// configured branch restrictions for administrators.
type EnforceAdmins struct {
Enabled bool `json:"enabled"`
}
// RequiredPullRequestReviews exposes the state of review rights.
type RequiredPullRequestReviews struct {
DismissalRestrictions *Restrictions `json:"dismissal_restrictions"`
DismissStaleReviews bool `json:"dismiss_stale_reviews"`
RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"`
RequiredApprovingReviewCount int `json:"required_approving_review_count"`
}
// Restrictions exposes restrictions in github for an activity to people/teams.
type Restrictions struct {
Users []User `json:"users,omitempty"`
Teams []Team `json:"teams,omitempty"`
}
// BranchProtectionRequest represents
// protections to put in place for a branch.
// See also: https://developer.github.com/v3/repos/branches/#update-branch-protection
type BranchProtectionRequest struct {
RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"`
EnforceAdmins *bool `json:"enforce_admins"`
RequiredPullRequestReviews *RequiredPullRequestReviewsRequest `json:"required_pull_request_reviews"`
Restrictions *RestrictionsRequest `json:"restrictions"`
RequiredLinearHistory bool `json:"required_linear_history"`
AllowForcePushes bool `json:"allow_force_pushes"`
AllowDeletions bool `json:"allow_deletions"`
}
func (r BranchProtectionRequest) String() string {
bytes, err := json.Marshal(&r)
if err != nil {
return fmt.Sprintf("%#v", r)
}
return string(bytes)
}
// RequiredStatusChecks specifies which contexts must pass to merge.
type RequiredStatusChecks struct {
Strict bool `json:"strict"` // PR must be up to date (include latest base branch commit).
Contexts []string `json:"contexts"`
}
// RequiredPullRequestReviewsRequest controls a request for review rights.
type RequiredPullRequestReviewsRequest struct {
DismissalRestrictions RestrictionsRequest `json:"dismissal_restrictions"`
DismissStaleReviews bool `json:"dismiss_stale_reviews"`
RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"`
RequiredApprovingReviewCount int `json:"required_approving_review_count"`
}
// RestrictionsRequest tells github to restrict an activity to people/teams.
//
// Use *[]string in order to distinguish unset and empty list.
// This is needed by dismissal_restrictions to distinguish
// do not restrict (empty object) and restrict everyone (nil user/teams list)
type RestrictionsRequest struct {
// Users is a list of user logins
Users *[]string `json:"users,omitempty"`
// Teams is a list of team slugs
Teams *[]string `json:"teams,omitempty"`
}
// HookConfig holds the endpoint and its secret.
type HookConfig struct {
URL string `json:"url"`
ContentType *string `json:"content_type,omitempty"`
Secret *string `json:"secret,omitempty"`
}
// Hook holds info about the webhook configuration.
type Hook struct {
ID int `json:"id"`
Name string `json:"name"`
Events []string `json:"events"`
Active bool `json:"active"`
Config HookConfig `json:"config"`
}
// HookRequest can create and/or edit a webhook.
//
// AddEvents and RemoveEvents are only valid during an edit, and only for a repo
type HookRequest struct {
Name string `json:"name,omitempty"` // must be web or "", only create
Active *bool `json:"active,omitempty"`
AddEvents []string `json:"add_events,omitempty"` // only repo edit
Config *HookConfig `json:"config,omitempty"`
Events []string `json:"events,omitempty"`
RemoveEvents []string `json:"remove_events,omitempty"` // only repo edit
}
// AllHookEvents causes github to send all events.
// https://developer.github.com/v3/activity/events/types/
var AllHookEvents = []string{"*"}
// IssueEventAction enumerates the triggers for this
// webhook payload type. See also:
// https://developer.github.com/v3/activity/events/types/#issuesevent
type IssueEventAction string
const (
// IssueActionAssigned means assignees were added.
IssueActionAssigned IssueEventAction = "assigned"
// IssueActionUnassigned means assignees were added.
IssueActionUnassigned IssueEventAction = "unassigned"
// IssueActionLabeled means labels were added.
IssueActionLabeled IssueEventAction = "labeled"
// IssueActionUnlabeled means labels were removed.
IssueActionUnlabeled IssueEventAction = "unlabeled"
// IssueActionOpened means issue was opened/created.
IssueActionOpened IssueEventAction = "opened"
// IssueActionEdited means issue body was edited.
IssueActionEdited IssueEventAction = "edited"
// IssueActionDeleted means the issue was deleted.
IssueActionDeleted IssueEventAction = "deleted"
// IssueActionMilestoned means the milestone was added/changed.
IssueActionMilestoned IssueEventAction = "milestoned"
// IssueActionDemilestoned means a milestone was removed.
IssueActionDemilestoned IssueEventAction = "demilestoned"
// IssueActionClosed means issue was closed.
IssueActionClosed IssueEventAction = "closed"
// IssueActionReopened means issue was reopened.
IssueActionReopened IssueEventAction = "reopened"
// IssueActionPinned means the issue was pinned.
IssueActionPinned IssueEventAction = "pinned"
// IssueActionUnpinned means the issue was unpinned.
IssueActionUnpinned IssueEventAction = "unpinned"
// IssueActionTransferred means the issue was transferred to another repo.
IssueActionTransferred IssueEventAction = "transferred"
// IssueActionLocked means the issue was locked.
IssueActionLocked IssueEventAction = "locked"
// IssueActionUnlocked means the issue was unlocked.
IssueActionUnlocked IssueEventAction = "unlocked"
)
// IssueEvent represents an issue event from a webhook payload (not from the events API).
type IssueEvent struct {
Action IssueEventAction `json:"action"`
Issue Issue `json:"issue"`
Repo Repo `json:"repository"`
// Label is specified for IssueActionLabeled and IssueActionUnlabeled events.
Label Label `json:"label"`
Sender User `json:"sender"`
// GUID is included in the header of the request received by GitHub.
GUID string
}
// ListedIssueEvent represents an issue event from the events API (not from a webhook payload).
// https://developer.github.com/v3/issues/events/
type ListedIssueEvent struct {
Event IssueEventAction `json:"event"` // This is the same as IssueEvent.Action.
Actor User `json:"actor"`
Label Label `json:"label"`
CreatedAt time.Time `json:"created_at"`
}
// IssueCommentEventAction enumerates the triggers for this
// webhook payload type. See also:
// https://developer.github.com/v3/activity/events/types/#issuecommentevent
type IssueCommentEventAction string
const (
// IssueCommentActionCreated means the comment was created.
IssueCommentActionCreated IssueCommentEventAction = "created"
// IssueCommentActionEdited means the comment was edited.
IssueCommentActionEdited IssueCommentEventAction = "edited"
// IssueCommentActionDeleted means the comment was deleted.
IssueCommentActionDeleted IssueCommentEventAction = "deleted"
)
// IssueCommentEvent is what GitHub sends us when an issue comment is changed.
type IssueCommentEvent struct {
Action IssueCommentEventAction `json:"action"`
Issue Issue `json:"issue"`
Comment IssueComment `json:"comment"`
Repo Repo `json:"repository"`
// GUID is included in the header of the request received by GitHub.
GUID string
}
// Issue represents general info about an issue.
type Issue struct {
ID int `json:"id"`
NodeID string `json:"node_id"`
User User `json:"user"`
Number int `json:"number"`
Title string `json:"title"`
State string `json:"state"`
HTMLURL string `json:"html_url"`
Labels []Label `json:"labels"`
Assignees []User `json:"assignees"`
Body string `json:"body"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Milestone Milestone `json:"milestone"`
// This will be non-nil if it is a pull request.
PullRequest *struct{} `json:"pull_request,omitempty"`
}
// IsAssignee checks if a user is assigned to the issue.
func (i Issue) IsAssignee(login string) bool {
for _, assignee := range i.Assignees {
if NormLogin(login) == NormLogin(assignee.Login) {
return true
}
}
return false
}
// IsAuthor checks if a user is the author of the issue.
func (i Issue) IsAuthor(login string) bool {
return NormLogin(i.User.Login) == NormLogin(login)
}
// IsPullRequest checks if an issue is a pull request.
func (i Issue) IsPullRequest() bool {
return i.PullRequest != nil
}
// HasLabel checks if an issue has a given label.
func (i Issue) HasLabel(labelToFind string) bool {
for _, label := range i.Labels {
if strings.EqualFold(label.Name, labelToFind) {
return true
}
}
return false
}
// IssueComment represents general info about an issue comment.
type IssueComment struct {
ID int `json:"id,omitempty"`
Body string `json:"body"`
User User `json:"user,omitempty"`
HTMLURL string `json:"html_url,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
}
// StatusEvent fires whenever a git commit changes.
//
// See https://developer.github.com/v3/activity/events/types/#statusevent
type StatusEvent struct {
SHA string `json:"sha,omitempty"`
State string `json:"state,omitempty"`
Description string `json:"description,omitempty"`
TargetURL string `json:"target_url,omitempty"`
ID int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Context string `json:"context,omitempty"`
Sender User `json:"sender,omitempty"`
Repo Repo `json:"repository,omitempty"`
// GUID is included in the header of the request received by GitHub.
GUID string
}
// IssuesSearchResult represents the result of an issues search.
type IssuesSearchResult struct {
Total int `json:"total_count,omitempty"`
Issues []Issue `json:"items,omitempty"`
}
// PushEvent is what GitHub sends us when a user pushes to a repo.
type PushEvent struct {
Ref string `json:"ref"`
Before string `json:"before"`
After string `json:"after"`
Created bool `json:"created"`
Deleted bool `json:"deleted"`
Forced bool `json:"forced"`
Compare string `json:"compare"`
Commits []Commit `json:"commits"`
// Pusher is the user that pushed the commit, valid in a webhook event.
Pusher User `json:"pusher"`
// Sender contains more information that Pusher about the user.
Sender User `json:"sender"`
Repo Repo `json:"repository"`
// GUID is included in the header of the request received by GitHub.
GUID string
}
// Branch returns the name of the branch to which the user pushed.
func (pe PushEvent) Branch() string {
ref := strings.TrimPrefix(pe.Ref, "refs/heads/") // if Ref is a branch
ref = strings.TrimPrefix(ref, "refs/tags/") // if Ref is a tag
return ref
}
// Commit represents general info about a commit.
type Commit struct {
ID string `json:"id"`
Message string `json:"message"`
Added []string `json:"added"`
Removed []string `json:"removed"`
Modified []string `json:"modified"`
}
// Tree represents a GitHub tree.
type Tree struct {
SHA string `json:"sha,omitempty"`
}
// ReviewEventAction enumerates the triggers for this
// webhook payload type. See also:
// https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent
type ReviewEventAction string
const (
// ReviewActionSubmitted means the review was submitted.
ReviewActionSubmitted ReviewEventAction = "submitted"
// ReviewActionEdited means the review was edited.
ReviewActionEdited ReviewEventAction = "edited"
// ReviewActionDismissed means the review was dismissed.
ReviewActionDismissed ReviewEventAction = "dismissed"
)
// ReviewEvent is what GitHub sends us when a PR review is changed.
type ReviewEvent struct {
Action ReviewEventAction `json:"action"`
PullRequest PullRequest `json:"pull_request"`
Repo Repo `json:"repository"`
Review Review `json:"review"`
// GUID is included in the header of the request received by GitHub.
GUID string
}
// ReviewState is the state a review can be in.
type ReviewState string
// Possible review states.
const (
ReviewStateApproved ReviewState = "APPROVED"
ReviewStateChangesRequested = "CHANGES_REQUESTED"
ReviewStateCommented = "COMMENTED"
ReviewStateDismissed = "DISMISSED"
ReviewStatePending = "PENDING"
)
// Review describes a Pull Request review.
type Review struct {
ID int `json:"id"`
NodeID string `json:"node_id"`
User User `json:"user"`
Body string `json:"body"`
State ReviewState `json:"state"`
HTMLURL string `json:"html_url"`
SubmittedAt time.Time `json:"submitted_at"`
}
// ReviewCommentEventAction enumerates the triggers for this
// webhook payload type. See also:
// https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent
type ReviewCommentEventAction string
const (
// ReviewCommentActionCreated means the comment was created.
ReviewCommentActionCreated ReviewCommentEventAction = "created"
// ReviewCommentActionEdited means the comment was edited.
ReviewCommentActionEdited ReviewCommentEventAction = "edited"
// ReviewCommentActionDeleted means the comment was deleted.
ReviewCommentActionDeleted ReviewCommentEventAction = "deleted"
)
// ReviewCommentEvent is what GitHub sends us when a PR review comment is changed.
type ReviewCommentEvent struct {
Action ReviewCommentEventAction `json:"action"`
PullRequest PullRequest `json:"pull_request"`
Repo Repo `json:"repository"`
Comment ReviewComment `json:"comment"`
// GUID is included in the header of the request received by GitHub.
GUID string
}
// DiffSide enumerates the sides of the diff that the PR's changes appear on.
// See also: https://docs.github.com/en/rest/reference/pulls#create-a-review-comment-for-a-pull-request
type DiffSide string
const (
// DiffSideLeft means left side of the diff.
DiffSideLeft = "LEFT"
// DiffSideRight means right side of the diff.
DiffSideRight = "RIGHT"
)
// ReviewComment describes a Pull Request review.
type ReviewComment struct {
ID int `json:"id"`
NodeID string `json:"node_id"`
ReviewID int `json:"pull_request_review_id"`
User User `json:"user"`
Body string `json:"body"`
Path string `json:"path"`
HTMLURL string `json:"html_url"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Position will be nil if the code has changed such that the comment is no
// longer relevant.
Position *int `json:"position,omitempty"`
Side DiffSide `json:"side,omitempty"`
StartSide DiffSide `json:"start_side,omitempty"`
Line int `json:"line,omitempty"`
StartLine int `json:"start_line,omitempty"`
}
// ReviewAction is the action that a review can be made with.
type ReviewAction string
// Possible review actions. Leave Action blank for a pending review.
const (
Approve ReviewAction = "APPROVE"
RequestChanges = "REQUEST_CHANGES"
Comment = "COMMENT"
)
// DraftReview is what we give GitHub when we want to make a PR Review. This is
// different than what we receive when we ask for a Review.
type DraftReview struct {
// If unspecified, defaults to the most recent commit in the PR.
CommitSHA string `json:"commit_id,omitempty"`
Body string `json:"body"`
// If unspecified, defaults to PENDING.
Action ReviewAction `json:"event,omitempty"`
Comments []DraftReviewComment `json:"comments,omitempty"`
}
// DraftReviewComment is a comment in a draft review.
type DraftReviewComment struct {
Path string `json:"path"`
// Position in the patch, not the line number in the file.
Position int `json:"position"`
Body string `json:"body"`