feat(clbot): Create Gerrit watcher and basic clbot binary.
gerrit.Watcher is a class which watches the Gerrit stream-events SSH connection and produces events. There's a basic CLBot binary as well, to demonstrate driving it to produce messages on the logging output. It doesn't really do anything else. Change-Id: I274fe0a77c8329f79456425405e2fbdc3ca2edf0 Reviewed-on: https://cl.tvl.fyi/c/depot/+/245 Reviewed-by: tazjin <mail@tazj.in>
This commit is contained in:
parent
f6c7c85d94
commit
c05803ff14
14 changed files with 1235 additions and 0 deletions
10
fun/clbot/gerrit/gerritevents/default.nix
Normal file
10
fun/clbot/gerrit/gerritevents/default.nix
Normal file
|
@ -0,0 +1,10 @@
|
|||
{ depot, ... }:
|
||||
|
||||
depot.nix.buildGo.package {
|
||||
name = "code.tvl.fyi/fun/clbot/gerrit/gerritevents";
|
||||
srcs = [
|
||||
./time.go
|
||||
./types.go
|
||||
./events.go
|
||||
];
|
||||
}
|
321
fun/clbot/gerrit/gerritevents/events.go
Normal file
321
fun/clbot/gerrit/gerritevents/events.go
Normal file
|
@ -0,0 +1,321 @@
|
|||
package gerritevents
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var events = map[string]func() Event{}
|
||||
|
||||
func registerEvent(e func() Event) {
|
||||
t := e().EventType()
|
||||
if _, ok := events[t]; ok {
|
||||
panic(fmt.Sprintf("%s already registered", t))
|
||||
}
|
||||
events[t] = e
|
||||
}
|
||||
|
||||
// These events are taken from https://cl.tvl.fyi/Documentation/cmd-stream-events.html.
|
||||
|
||||
// Event is implemented by Gerrit event structs.
|
||||
type Event interface {
|
||||
EventType() string
|
||||
}
|
||||
|
||||
type simpleEvent struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// Parse parses a Gerrit event from JSON.
|
||||
func Parse(bs []byte) (Event, error) {
|
||||
var s simpleEvent
|
||||
if err := json.Unmarshal(bs, &s); err != nil {
|
||||
return nil, fmt.Errorf("unmarshalling %q as Gerrit Event: %v", string(bs), err)
|
||||
}
|
||||
ef, ok := events[s.Type]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown event type %q", s.Type)
|
||||
}
|
||||
e := ef()
|
||||
if err := json.Unmarshal(bs, e); err != nil {
|
||||
return nil, fmt.Errorf("unmarshalling %q as Gerrit Event %q: %v", string(bs), e.EventType(), err)
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// AssigneeChanged indicates that a change's assignee has been changed.
|
||||
type AssigneeChanged struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
Changer Account `json:"changer"`
|
||||
OldAssignee Account `json:"oldAssignee"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (AssigneeChanged) EventType() string { return "assignee-changed" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &AssigneeChanged{} })
|
||||
}
|
||||
|
||||
// ChangeAbandoned indicates that a change has been abandoned.
|
||||
type ChangeAbandoned struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Abandoner Account `json:"abandoner"`
|
||||
Reason string `json:"reason"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (ChangeAbandoned) EventType() string { return "change-abandoned" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &ChangeAbandoned{} })
|
||||
}
|
||||
|
||||
// ChangeDeleted indicates that a change has been deleted.
|
||||
type ChangeDeleted struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
Deleter Account `json:"deleter"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (ChangeDeleted) EventType() string { return "change-deleted" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &ChangeDeleted{} })
|
||||
}
|
||||
|
||||
// ChangeMerged indicates that a change has been merged into the target branch.
|
||||
type ChangeMerged struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Submitter Account `json:"submitter"`
|
||||
NewRev string `json:"newRev"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (ChangeMerged) EventType() string { return "change-merged" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &ChangeMerged{} })
|
||||
}
|
||||
|
||||
// ChangeRestored indicates a change has been restored (i.e. un-abandoned).
|
||||
type ChangeRestored struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Restorer Account `json:"restorer"`
|
||||
Reason string `json:"reason"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (ChangeRestored) EventType() string { return "change-restored" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &ChangeRestored{} })
|
||||
}
|
||||
|
||||
// CommentAdded indicates someone has commented on a patchset.
|
||||
type CommentAdded struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Author Account `json:"author"`
|
||||
Approvals []Approval `json:"approvals"`
|
||||
Comment string `json:"comment"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (CommentAdded) EventType() string { return "comment-added" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &CommentAdded{} })
|
||||
}
|
||||
|
||||
// DroppedOutput indicates that some events may be missing from the stream.
|
||||
type DroppedOutput struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (DroppedOutput) EventType() string { return "dropped-output" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &DroppedOutput{} })
|
||||
}
|
||||
|
||||
// HashtagsChanged indicates that someone has added or removed hashtags from a change.
|
||||
type HashtagsChanged struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
Editor Account `json:"editor"`
|
||||
Added []string `json:"added"`
|
||||
Removed []string `json:"removed"`
|
||||
Hashtags []string `json:"hashtags"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (HashtagsChanged) EventType() string { return "hashtags-changed" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &HashtagsChanged{} })
|
||||
}
|
||||
|
||||
// ProjectCreated indicates that a new project has been created.
|
||||
type ProjectCreated struct {
|
||||
Type string `json:"type"`
|
||||
ProjectName string `json:"projectName"`
|
||||
ProjectHead string `json:"projectHead"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (ProjectCreated) EventType() string { return "project-created" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &ProjectCreated{} })
|
||||
}
|
||||
|
||||
// PatchSetCreated indicates that a new patchset has been added to a change.
|
||||
type PatchSetCreated struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Uploader Account `json:"uploader"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (PatchSetCreated) EventType() string { return "patchset-created" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &PatchSetCreated{} })
|
||||
}
|
||||
|
||||
// RefUpdated indicates that a ref has been updated.
|
||||
type RefUpdated struct {
|
||||
Type string `json:"type"`
|
||||
Submitter Account `json:"submitter"`
|
||||
RefUpdate RefUpdate `json:"refUpdate"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (RefUpdated) EventType() string { return "ref-updated" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &RefUpdated{} })
|
||||
}
|
||||
|
||||
// ReviewerAdded indicates that a reviewer has been added to a change.
|
||||
type ReviewerAdded struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Reviewer Account `json:"reviewer"`
|
||||
Adder Account `json:"adder"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (ReviewerAdded) EventType() string { return "reviewer-added" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &ReviewerAdded{} })
|
||||
}
|
||||
|
||||
// ReviewerDeleted indicates that a reviewer has been removed from a change, possibly removing one or more approvals.
|
||||
type ReviewerDeleted struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Reviewer Account `json:"reviewer"`
|
||||
Remover Account `json:"remover"`
|
||||
Approvals []Approval `json:"approvals"`
|
||||
Comment string `json:"comment"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (ReviewerDeleted) EventType() string { return "reviewer-deleted" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &ReviewerDeleted{} })
|
||||
}
|
||||
|
||||
// TopicChanged indicates that the topic attached to a change has been changed.
|
||||
type TopicChanged struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
Changer Account `json:"changer"`
|
||||
OldTopic string `json:"oldTopic"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (TopicChanged) EventType() string { return "topic-changed" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &TopicChanged{} })
|
||||
}
|
||||
|
||||
// WIPStateChanged indicates that the work-in-progress state of a change has changed.
|
||||
type WIPStateChanged struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Changer Account `json:"changer"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (WIPStateChanged) EventType() string { return "wip-state-changed" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &WIPStateChanged{} })
|
||||
}
|
||||
|
||||
// PrivateStateChanged indicates that the private state of a change has changed.
|
||||
type PrivateStateChanged struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Changer Account `json:"changer"`
|
||||
EventCreatedOn Time `json:"eventCreatedOn"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (PrivateStateChanged) EventType() string { return "private-state-changed" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &PrivateStateChanged{} })
|
||||
}
|
||||
|
||||
// VoteDeleted indicates that an approval vote has been deleted from a change.
|
||||
type VoteDeleted struct {
|
||||
Type string `json:"type"`
|
||||
Change Change `json:"change"`
|
||||
PatchSet PatchSet `json:"patchSet"`
|
||||
Reviewer Account `json:"reviewer"`
|
||||
Remover Account `json:"remover"`
|
||||
Approvals []Approval `json:"approvals"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
// EventType implements Event.
|
||||
func (VoteDeleted) EventType() string { return "vote-deleted" }
|
||||
|
||||
func init() {
|
||||
registerEvent(func() Event { return &VoteDeleted{} })
|
||||
}
|
38
fun/clbot/gerrit/gerritevents/time.go
Normal file
38
fun/clbot/gerrit/gerritevents/time.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package gerritevents
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Time is a time.Time that is formatted as a Unix timestamp in JSON.
|
||||
type Time struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals a Unix timestamp into a Time.
|
||||
func (t *Time) UnmarshalJSON(bs []byte) error {
|
||||
if string(bs) == "null" {
|
||||
return nil
|
||||
}
|
||||
u, err := strconv.ParseInt(string(bs), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Time = time.Unix(u, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON marshals a Time into a Unix timestamp.
|
||||
func (t *Time) MarshalJSON() ([]byte, error) {
|
||||
if t.IsZero() {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return []byte(fmt.Sprintf("%d", t.Unix())), nil
|
||||
}
|
||||
|
||||
// IsSet returns true if the time.Time is non-zero.
|
||||
func (t *Time) IsSet() bool {
|
||||
return !t.IsZero()
|
||||
}
|
221
fun/clbot/gerrit/gerritevents/types.go
Normal file
221
fun/clbot/gerrit/gerritevents/types.go
Normal file
|
@ -0,0 +1,221 @@
|
|||
package gerritevents
|
||||
|
||||
// These types are taken from https://cl.tvl.fyi/Documentation/json.html.
|
||||
|
||||
// Account is a Gerrit account (or just a Git name+email pair).
|
||||
type Account struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// ChangeStatus represents the states a change can be in.
|
||||
type ChangeStatus string
|
||||
|
||||
const (
|
||||
// ChangeStatusNew is the state a change is in during review.
|
||||
ChangeStatusNew ChangeStatus = "NEW"
|
||||
|
||||
// ChangeStatusMerged indicates a change was merged to the target branch.
|
||||
ChangeStatusMerged ChangeStatus = "MERGED"
|
||||
|
||||
// ChangeStatusAbandoned indicates a change was marked as abandoned.
|
||||
ChangeStatusAbandoned ChangeStatus = "ABANDONED"
|
||||
)
|
||||
|
||||
// Message is a message left by a reviewer.
|
||||
type Message struct {
|
||||
Timestamp Time `json:"timestamp"`
|
||||
Reviewer Account `json:"reviewer"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// TrackingID allows storing identifiers from external systems, i.e. bug trackers.
|
||||
type TrackingID struct {
|
||||
System string `json:"system"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// ChangeKind indicates the different changes that can be made to a change.
|
||||
type ChangeKind string
|
||||
|
||||
const (
|
||||
// ChangeKindRework indicates a non-trivial content change.
|
||||
ChangeKindRework ChangeKind = "REWORK"
|
||||
|
||||
// ChangeKindTrivialRebase indicates a conflict-free merge between the new parent and the prior patch set.
|
||||
ChangeKindTrivialRebase ChangeKind = "TRIVIAL_REBASE"
|
||||
|
||||
// ChangeKindMergeFirstParentUpdate indicates a conflict-free change of the first parent of a merge commit.
|
||||
ChangeKindMergeFirstParentUpdate ChangeKind = "MERGE_FIRST_PARENT_UPDATE"
|
||||
|
||||
// ChangeKindNoCodeChange indicates no code change (the tree and parent trees are unchanged) - commit message probably changed.
|
||||
ChangeKindNoCodeChange ChangeKind = "NO_CODE_CHANGE"
|
||||
|
||||
// ChangeKindNoChange indicates nothing changes: the commit message, tree, and parent tree are unchanged.
|
||||
ChangeKindNoChange ChangeKind = "NO_CHANGE"
|
||||
)
|
||||
|
||||
// Approval represents the current and past state of an approval label.
|
||||
type Approval struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Value string `json:"value"`
|
||||
OldValue *string `json:"oldValue"`
|
||||
GrantedOn *Time `json:"grantedOn"`
|
||||
By *Account `json:"by"`
|
||||
}
|
||||
|
||||
// PatchSetComment is a single comment left on a patchset.
|
||||
type PatchSetComment struct {
|
||||
File string `json:"file"`
|
||||
Line int `json:"line"`
|
||||
Reviewer Account `json:"reviewer"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// FilePatchType represents the different modifications that can be made to a file by a patchset.
|
||||
type FilePatchType string
|
||||
|
||||
const (
|
||||
// FilePatchTypeAdded indicates the file did not exist, and this patchset adds it to the tree.
|
||||
FilePatchTypeAdded FilePatchType = "ADDED"
|
||||
|
||||
// FilePatchTypeModified indicates the file exists before and after this patchset.
|
||||
FilePatchTypeModified FilePatchType = "MODIFIED"
|
||||
|
||||
// FilePatchTypeDeleted indicates the file is removed by this patchset.
|
||||
FilePatchTypeDeleted FilePatchType = "DELETED"
|
||||
|
||||
// FilePatchTypeRenamed indicates the file has a different name before this patchset than after.
|
||||
FilePatchTypeRenamed FilePatchType = "RENAMED"
|
||||
|
||||
// FilePatchTypeCopied indicates the file was copied from a different file.
|
||||
FilePatchTypeCopied FilePatchType = "COPIED"
|
||||
|
||||
// FilePatchTypeRewrite indicates the file had a significant quantity of content changed.
|
||||
FilePatchTypeRewrite FilePatchType = "REWRITE"
|
||||
)
|
||||
|
||||
// File represents a file in a patchset as well as how it is being modified.
|
||||
type File struct {
|
||||
File string `json:"file"`
|
||||
FileOld string `json:"fileOld"`
|
||||
Type FilePatchType `json:"type"`
|
||||
}
|
||||
|
||||
// PatchSet represents a single patchset within a change.
|
||||
type PatchSet struct {
|
||||
Number int `json:"number"`
|
||||
Revision string `json:"revision"`
|
||||
Parents []string `json:"parents"`
|
||||
Ref string `json:"ref"`
|
||||
Uploader Account `json:"uploader"`
|
||||
Author Account `json:"author"`
|
||||
CreatedOn Time `json:"createdOn"`
|
||||
Kind ChangeKind `json:"kind"`
|
||||
Approvals []Approval `json:"approvals"`
|
||||
Comments []PatchSetComment `json:"comments"`
|
||||
Files []File `json:"file"`
|
||||
SizeInsertions int `json:"sizeInsertions"`
|
||||
SizeDeletions int `json:"sizeDeletions"`
|
||||
}
|
||||
|
||||
// Dependency represents a change on which this change is dependent.
|
||||
type Dependency struct {
|
||||
ID string `json:"id"`
|
||||
Number int `json:"number"`
|
||||
Revision string `json:"revision"`
|
||||
Ref string `json:"ref"`
|
||||
IsCurrentPatchSet bool `json:"isCurrentPatchSet"`
|
||||
}
|
||||
|
||||
// SubmitStatus indicates whether this change has met the submit conditions and is ready to submit.
|
||||
type SubmitStatus string
|
||||
|
||||
const (
|
||||
// SubmitStatusOK indicates this change is ready to submit - all submit requirements are met.
|
||||
SubmitStatusOK SubmitStatus = "OK"
|
||||
|
||||
// SubmitStatusNotReady indicates this change cannot yet be submitted.
|
||||
SubmitStatusNotReady SubmitStatus = "NOT_READY"
|
||||
|
||||
// SubmitStatusRuleError indicates the submit rules could not be evaluted. Administrator intervention is required.
|
||||
SubmitStatusRuleError SubmitStatus = "RULE_ERROR"
|
||||
)
|
||||
|
||||
// LabelStatus indicates whether this label permits submission and if the label can be granted by anyone.
|
||||
type LabelStatus string
|
||||
|
||||
const (
|
||||
// LabelStatusOK indicates that this label provides what is necessary for submission (e.g. CR+2).
|
||||
LabelStatusOK LabelStatus = "OK"
|
||||
|
||||
// LabelStatusReject indicates this label prevents submission (e.g. CR-2).
|
||||
LabelStatusReject LabelStatus = "REJECT"
|
||||
|
||||
// LabelStatusNeed indicates this label is required for submission, but has not been satisfied (e.g. CR0).
|
||||
LabelStatusNeed LabelStatus = "NEED"
|
||||
|
||||
// LabelStatusMay indicates this label is not required for submission. It may or may not be set.
|
||||
LabelStatusMay LabelStatus = "MAY"
|
||||
|
||||
// LabelStatusImpossible indicates this label is required for submission, but cannot be satisfied. The ACLs on this label may be set incorrectly.
|
||||
LabelStatusImpossible LabelStatus = "IMPOSSIBLE"
|
||||
)
|
||||
|
||||
// Label represents the status of a particular label.
|
||||
type Label struct {
|
||||
Label string `json:"label"`
|
||||
Status LabelStatus `json:"status"`
|
||||
By Account `json:"by"`
|
||||
}
|
||||
|
||||
// Requirement represents a submit requirement.
|
||||
type Requirement struct {
|
||||
FallbackText string `json:"fallbackText"`
|
||||
Type string `json:"type"`
|
||||
// TODO(lukegb): data
|
||||
}
|
||||
|
||||
// SubmitRecord represents the current submission state of a change.
|
||||
type SubmitRecord struct {
|
||||
Status SubmitStatus `json:"status"`
|
||||
Labels []Label `json:"labels"`
|
||||
Requirements []Requirement `json:"requirements"`
|
||||
}
|
||||
|
||||
// Change represents a Gerrit CL.
|
||||
type Change struct {
|
||||
Project string `json:"project"`
|
||||
Branch string `json:"branch"`
|
||||
Topic string `json:"topic"`
|
||||
ID string `json:"id"`
|
||||
Number int `json:"number"`
|
||||
Subject string `json:"subject"`
|
||||
Owner Account `json:"owner"`
|
||||
URL string `json:"url"`
|
||||
CommitMessage string `json:"commitMessage"`
|
||||
CreatedOn Time `json:"createdOn"`
|
||||
LastUpdated *Time `json:"lastUpdated"`
|
||||
Open bool `json:"open"`
|
||||
Status ChangeStatus `json:"status"`
|
||||
Private bool `json:"private"`
|
||||
WIP bool `json:"wip"`
|
||||
Comments []Message `json:"comments"`
|
||||
TrackingIDs []TrackingID `json:"trackingIds"`
|
||||
CurrentPatchSet *PatchSet `json:"currentPatchSet"`
|
||||
PatchSets []PatchSet `json:"patchSets"`
|
||||
DependsOn []Dependency `json:"dependsOn"`
|
||||
NeededBy []Dependency `json:"neededBy"`
|
||||
SubmitRecords []SubmitRecord `json:"submitRecord"`
|
||||
AllReviewers []Account `json:"allReviewers"`
|
||||
}
|
||||
|
||||
// RefUpdate represents a change in a ref.
|
||||
type RefUpdate struct {
|
||||
OldRev string `json:"oldRev"`
|
||||
NewRev string `json:"newRev"`
|
||||
RefName string `json:"refName"`
|
||||
Project string `json:"project"`
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue