diff --git a/Gopkg.lock b/Gopkg.lock index 2bb58e6c..11cf2f13 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -214,11 +214,11 @@ [[projects]] branch = "master" - digest = "1:a1562fb3021983f533a27d02ebf36f1bc1ab327660d611d6e948970b54087792" + digest = "1:2befa342040f385b214cfd400887b584d5eba4e4b25a0ebaea839ddb0d59c586" name = "github.com/shurcooL/githubv4" packages = ["."] pruneopts = "UT" - revision = "b5f70540eee0ebfb6a27b52fc5b131be76415539" + revision = "068505affed7d8555196a48eb3e0ed43410aa8e8" [[projects]] branch = "master" diff --git a/README.md b/README.md index 8449aaed..8e14cebb 100644 --- a/README.md +++ b/README.md @@ -116,23 +116,31 @@ The web UI interact with the backend through a GraphQL API. The schema is availa ### Importer implementations -| | Github | Launchpad | -| ----------------------------------------------- | :----------------: | :----------------: | -| **incremental**
(can import more than once) | :heavy_check_mark: | :x: | -| **with resume**
(download only new data) | :x: | :x: | -| **identities** | :heavy_check_mark: | :heavy_check_mark: | -| identities update | :x: | :x: | -| **bug** | :heavy_check_mark: | :heavy_check_mark: | -| comments | :heavy_check_mark: | :heavy_check_mark: | -| comment editions | :heavy_check_mark: | :x: | -| labels | :heavy_check_mark: | :x: | -| status | :heavy_check_mark: | :x: | -| title edition | :heavy_check_mark: | :x: | -| **automated test suite** | :x: | :x: | +| | Github | Launchpad | +| --- | --- | --- | +| **incremental**
(can import more than once) | :heavy_check_mark: | :x: | +| **with resume**
(download only new data) | :x: | :x: | +| **identities** | :heavy_check_mark: | :heavy_check_mark: | +| identities update | :x: | :x: | +| **bug** | :heavy_check_mark: | :heavy_check_mark: | +| comments | :heavy_check_mark: | :heavy_check_mark: | +| comment editions | :heavy_check_mark: | :x: | +| labels | :heavy_check_mark: | :x: | +| status | :heavy_check_mark: | :x: | +| title edition | :heavy_check_mark: | :x: | +| **automated test suite** | :heavy_check_mark: | :x: | ### Exporter implementations -Todo ! +| | Github | Launchpad | +| --- | --- | --- | +| **bug** | :heavy_check_mark: | :x: | +| comments | :heavy_check_mark: | :x: | +| comment editions | :heavy_check_mark: | :x: | +| labels | :heavy_check_mark: | :x: | +| status | :heavy_check_mark: | :x: | +| title edition | :heavy_check_mark: | :x: | +| **automated test suite** | :heavy_check_mark: | :x: | ## Internals diff --git a/bridge/core/bridge.go b/bridge/core/bridge.go index 1b960e0e..d04e88e4 100644 --- a/bridge/core/bridge.go +++ b/bridge/core/bridge.go @@ -297,20 +297,20 @@ func (b *Bridge) ImportAll(since time.Time) error { return importer.ImportAll(b.repo, since) } -func (b *Bridge) ExportAll(since time.Time) error { +func (b *Bridge) ExportAll(since time.Time) (<-chan ExportResult, error) { exporter := b.getExporter() if exporter == nil { - return ErrExportNotSupported + return nil, ErrExportNotSupported } err := b.ensureConfig() if err != nil { - return err + return nil, err } err = b.ensureInit() if err != nil { - return err + return nil, err } return exporter.ExportAll(b.repo, since) diff --git a/bridge/core/export.go b/bridge/core/export.go new file mode 100644 index 00000000..080ced80 --- /dev/null +++ b/bridge/core/export.go @@ -0,0 +1,101 @@ +package core + +import "fmt" + +type ExportEvent int + +const ( + _ ExportEvent = iota + ExportEventBug + ExportEventComment + ExportEventCommentEdition + ExportEventStatusChange + ExportEventTitleEdition + ExportEventLabelChange + ExportEventNothing +) + +type ExportResult struct { + Err error + Event ExportEvent + ID string + Reason string +} + +func (er ExportResult) String() string { + switch er.Event { + case ExportEventBug: + return "new issue" + case ExportEventComment: + return "new comment" + case ExportEventCommentEdition: + return "updated comment" + case ExportEventStatusChange: + return "changed status" + case ExportEventTitleEdition: + return "changed title" + case ExportEventLabelChange: + return "changed label" + case ExportEventNothing: + return fmt.Sprintf("no event: %v", er.Reason) + default: + panic("unknown export result") + } +} + +func NewExportError(err error, reason string) ExportResult { + return ExportResult{ + Err: err, + Reason: reason, + } +} + +func NewExportNothing(id string, reason string) ExportResult { + return ExportResult{ + ID: id, + Reason: reason, + Event: ExportEventNothing, + } +} + +func NewExportBug(id string) ExportResult { + return ExportResult{ + ID: id, + Event: ExportEventBug, + } +} + +func NewExportComment(id string) ExportResult { + return ExportResult{ + ID: id, + Event: ExportEventComment, + } +} + +func NewExportCommentEdition(id string) ExportResult { + return ExportResult{ + ID: id, + Event: ExportEventCommentEdition, + } +} + +func NewExportStatusChange(id string) ExportResult { + return ExportResult{ + ID: id, + Event: ExportEventStatusChange, + } +} + +func NewExportLabelChange(id string) ExportResult { + return ExportResult{ + ID: id, + Event: ExportEventLabelChange, + } +} + +func NewExportTitleEdition(id string) ExportResult { + return ExportResult{ + ID: id, + Event: ExportEventTitleEdition, + } +} diff --git a/bridge/core/interfaces.go b/bridge/core/interfaces.go index 37fdb3d7..76d66fb4 100644 --- a/bridge/core/interfaces.go +++ b/bridge/core/interfaces.go @@ -34,5 +34,5 @@ type Importer interface { type Exporter interface { Init(conf Configuration) error - ExportAll(repo *cache.RepoCache, since time.Time) error + ExportAll(repo *cache.RepoCache, since time.Time) (<-chan ExportResult, error) } diff --git a/bridge/github/export.go b/bridge/github/export.go new file mode 100644 index 00000000..b4351bdb --- /dev/null +++ b/bridge/github/export.go @@ -0,0 +1,795 @@ +package github + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + "sync" + "time" + + "github.com/pkg/errors" + "github.com/shurcooL/githubv4" + + "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/util/git" +) + +var ( + ErrMissingIdentityToken = errors.New("missing identity token") +) + +// githubExporter implement the Exporter interface +type githubExporter struct { + conf core.Configuration + + // cache identities clients + identityClient map[string]*githubv4.Client + + // map identities with their tokens + identityToken map[string]string + + // github repository ID + repositoryID string + + // cache identifiers used to speed up exporting operations + // cleared for each bug + cachedOperationIDs map[string]string + + // cache labels used to speed up exporting labels events + cachedLabels map[string]string +} + +// Init . +func (ge *githubExporter) Init(conf core.Configuration) error { + ge.conf = conf + //TODO: initialize with multiple tokens + ge.identityToken = make(map[string]string) + ge.identityClient = make(map[string]*githubv4.Client) + ge.cachedOperationIDs = make(map[string]string) + ge.cachedLabels = make(map[string]string) + return nil +} + +// getIdentityClient return a githubv4 API client configured with the access token of the given identity. +// if no client were found it will initialize it from the known tokens map and cache it for next use +func (ge *githubExporter) getIdentityClient(id string) (*githubv4.Client, error) { + client, ok := ge.identityClient[id] + if ok { + return client, nil + } + + // get token + token, ok := ge.identityToken[id] + if !ok { + return nil, ErrMissingIdentityToken + } + + // create client + client = buildClient(token) + // cache client + ge.identityClient[id] = client + + return client, nil +} + +// ExportAll export all event made by the current user to Github +func (ge *githubExporter) ExportAll(repo *cache.RepoCache, since time.Time) (<-chan core.ExportResult, error) { + out := make(chan core.ExportResult) + + user, err := repo.GetUserIdentity() + if err != nil { + return nil, err + } + + ge.identityToken[user.Id()] = ge.conf[keyToken] + + // get repository node id + ge.repositoryID, err = getRepositoryNodeID( + ge.conf[keyOwner], + ge.conf[keyProject], + ge.conf[keyToken], + ) + + if err != nil { + return nil, err + } + + go func() { + defer close(out) + + var allIdentitiesIds []string + for id := range ge.identityToken { + allIdentitiesIds = append(allIdentitiesIds, id) + } + + allBugsIds := repo.AllBugsIds() + + for _, id := range allBugsIds { + b, err := repo.ResolveBug(id) + if err != nil { + out <- core.NewExportError(err, id) + return + } + + snapshot := b.Snapshot() + + // ignore issues created before since date + // TODO: compare the Lamport time instead of using the unix time + if snapshot.CreatedAt.Before(since) { + out <- core.NewExportNothing(b.Id(), "bug created before the since date") + continue + } + + if snapshot.HasAnyActor(allIdentitiesIds...) { + // try to export the bug and it associated events + ge.exportBug(b, since, out) + } else { + out <- core.NewExportNothing(id, "not an actor") + } + } + }() + + return out, nil +} + +// exportBug publish bugs and related events +func (ge *githubExporter) exportBug(b *cache.BugCache, since time.Time, out chan<- core.ExportResult) { + snapshot := b.Snapshot() + + var bugGithubID string + var bugGithubURL string + var bugCreationHash string + + // Special case: + // if a user try to export a bug that is not already exported to Github (or imported + // from Github) and we do not have the token of the bug author, there is nothing we can do. + + // first operation is always createOp + createOp := snapshot.Operations[0].(*bug.CreateOperation) + author := snapshot.Author + + // skip bug if origin is not allowed + origin, ok := snapshot.GetCreateMetadata(keyOrigin) + if ok && origin != target { + out <- core.NewExportNothing(b.Id(), fmt.Sprintf("issue tagged with origin: %s", origin)) + return + } + + // get github bug ID + githubID, ok := snapshot.GetCreateMetadata(keyGithubId) + if ok { + githubURL, ok := snapshot.GetCreateMetadata(keyGithubUrl) + if !ok { + // if we find github ID, github URL must be found too + err := fmt.Errorf("expected to find github issue URL") + out <- core.NewExportError(err, b.Id()) + } + + // extract owner and project + owner, project, err := splitURL(githubURL) + if err != nil { + err := fmt.Errorf("bad project url: %v", err) + out <- core.NewExportError(err, b.Id()) + return + } + + // ignore issue comming from other repositories + if owner != ge.conf[keyOwner] && project != ge.conf[keyProject] { + out <- core.NewExportNothing(b.Id(), fmt.Sprintf("skipping issue from url:%s", githubURL)) + return + } + + out <- core.NewExportNothing(b.Id(), "bug already exported") + // will be used to mark operation related to a bug as exported + bugGithubID = githubID + bugGithubURL = githubURL + + } else { + // check that we have a token for operation author + client, err := ge.getIdentityClient(author.Id()) + if err != nil { + // if bug is still not exported and we do not have the author stop the execution + out <- core.NewExportNothing(b.Id(), fmt.Sprintf("missing author token")) + return + } + + // create bug + id, url, err := createGithubIssue(client, ge.repositoryID, createOp.Title, createOp.Message) + if err != nil { + err := errors.Wrap(err, "exporting github issue") + out <- core.NewExportError(err, b.Id()) + return + } + + out <- core.NewExportBug(b.Id()) + + hash, err := createOp.Hash() + if err != nil { + err := errors.Wrap(err, "comment hash") + out <- core.NewExportError(err, b.Id()) + return + } + + // mark bug creation operation as exported + if err := markOperationAsExported(b, hash, id, url); err != nil { + err := errors.Wrap(err, "marking operation as exported") + out <- core.NewExportError(err, b.Id()) + return + } + + // commit operation to avoid creating multiple issues with multiple pushes + if err := b.CommitAsNeeded(); err != nil { + err := errors.Wrap(err, "bug commit") + out <- core.NewExportError(err, b.Id()) + return + } + + // cache bug github ID and URL + bugGithubID = id + bugGithubURL = url + } + + // get createOp hash + hash, err := createOp.Hash() + if err != nil { + out <- core.NewExportError(err, b.Id()) + return + } + + bugCreationHash = hash.String() + + // cache operation github id + ge.cachedOperationIDs[bugCreationHash] = bugGithubID + + for _, op := range snapshot.Operations[1:] { + // ignore SetMetadata operations + if _, ok := op.(*bug.SetMetadataOperation); ok { + continue + } + + // get operation hash + hash, err := op.Hash() + if err != nil { + err := errors.Wrap(err, "operation hash") + out <- core.NewExportError(err, b.Id()) + return + } + + // ignore operations already existing in github (due to import or export) + // cache the ID of already exported or imported issues and events from Github + if id, ok := op.GetMetadata(keyGithubId); ok { + ge.cachedOperationIDs[hash.String()] = id + out <- core.NewExportNothing(hash.String(), "already exported operation") + continue + } + + opAuthor := op.GetAuthor() + client, err := ge.getIdentityClient(opAuthor.Id()) + if err != nil { + out <- core.NewExportNothing(hash.String(), "missing operation author token") + continue + } + + var id, url string + switch op.(type) { + case *bug.AddCommentOperation: + opr := op.(*bug.AddCommentOperation) + + // send operation to github + id, url, err = addCommentGithubIssue(client, bugGithubID, opr.Message) + if err != nil { + err := errors.Wrap(err, "adding comment") + out <- core.NewExportError(err, b.Id()) + return + } + + out <- core.NewExportComment(hash.String()) + + // cache comment id + ge.cachedOperationIDs[hash.String()] = id + + case *bug.EditCommentOperation: + + opr := op.(*bug.EditCommentOperation) + targetHash := opr.Target.String() + + // Since github doesn't consider the issue body as a comment + if targetHash == bugCreationHash { + + // case bug creation operation: we need to edit the Github issue + if err := updateGithubIssueBody(client, bugGithubID, opr.Message); err != nil { + err := errors.Wrap(err, "editing issue") + out <- core.NewExportError(err, b.Id()) + return + } + + out <- core.NewExportCommentEdition(hash.String()) + + id = bugGithubID + url = bugGithubURL + + } else { + + // case comment edition operation: we need to edit the Github comment + commentID, ok := ge.cachedOperationIDs[targetHash] + if !ok { + panic("unexpected error: comment id not found") + } + + eid, eurl, err := editCommentGithubIssue(client, commentID, opr.Message) + if err != nil { + err := errors.Wrap(err, "editing comment") + out <- core.NewExportError(err, b.Id()) + return + } + + out <- core.NewExportCommentEdition(hash.String()) + + // use comment id/url instead of issue id/url + id = eid + url = eurl + } + + case *bug.SetStatusOperation: + opr := op.(*bug.SetStatusOperation) + if err := updateGithubIssueStatus(client, bugGithubID, opr.Status); err != nil { + err := errors.Wrap(err, "editing status") + out <- core.NewExportError(err, b.Id()) + return + } + + out <- core.NewExportStatusChange(hash.String()) + + id = bugGithubID + url = bugGithubURL + + case *bug.SetTitleOperation: + opr := op.(*bug.SetTitleOperation) + if err := updateGithubIssueTitle(client, bugGithubID, opr.Title); err != nil { + err := errors.Wrap(err, "editing title") + out <- core.NewExportError(err, b.Id()) + return + } + + out <- core.NewExportTitleEdition(hash.String()) + + id = bugGithubID + url = bugGithubURL + + case *bug.LabelChangeOperation: + opr := op.(*bug.LabelChangeOperation) + if err := ge.updateGithubIssueLabels(client, bugGithubID, opr.Added, opr.Removed); err != nil { + err := errors.Wrap(err, "updating labels") + out <- core.NewExportError(err, b.Id()) + return + } + + out <- core.NewExportLabelChange(hash.String()) + + id = bugGithubID + url = bugGithubURL + + default: + panic("unhandled operation type case") + } + + // mark operation as exported + if err := markOperationAsExported(b, hash, id, url); err != nil { + err := errors.Wrap(err, "marking operation as exported") + out <- core.NewExportError(err, b.Id()) + return + } + + // commit at each operation export to avoid exporting same events multiple times + if err := b.CommitAsNeeded(); err != nil { + err := errors.Wrap(err, "bug commit") + out <- core.NewExportError(err, b.Id()) + return + } + } +} + +// getRepositoryNodeID request github api v3 to get repository node id +func getRepositoryNodeID(owner, project, token string) (string, error) { + url := fmt.Sprintf("%s/repos/%s/%s", githubV3Url, owner, project) + + client := &http.Client{ + Timeout: defaultTimeout, + } + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return "", err + } + + // need the token for private repositories + req.Header.Set("Authorization", fmt.Sprintf("token %s", token)) + + resp, err := client.Do(req) + if err != nil { + return "", err + } + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("HTTP error %v retrieving repository node id", resp.StatusCode) + } + + aux := struct { + NodeID string `json:"node_id"` + }{} + + data, _ := ioutil.ReadAll(resp.Body) + err = resp.Body.Close() + if err != nil { + return "", err + } + + err = json.Unmarshal(data, &aux) + if err != nil { + return "", err + } + + return aux.NodeID, nil +} + +func markOperationAsExported(b *cache.BugCache, target git.Hash, githubID, githubURL string) error { + _, err := b.SetMetadata( + target, + map[string]string{ + keyGithubId: githubID, + keyGithubUrl: githubURL, + }, + ) + + return err +} + +// get label from github +func (ge *githubExporter) getGithubLabelID(gc *githubv4.Client, label string) (string, error) { + q := &labelQuery{} + variables := map[string]interface{}{ + "label": githubv4.String(label), + "owner": githubv4.String(ge.conf[keyOwner]), + "name": githubv4.String(ge.conf[keyProject]), + } + + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := gc.Query(ctx, q, variables); err != nil { + return "", err + } + + // if label id is empty, it means there is no such label in this Github repository + if q.Repository.Label.ID == "" { + return "", fmt.Errorf("label not found") + } + + return q.Repository.Label.ID, nil +} + +// create a new label and return it github id +// NOTE: since createLabel mutation is still in preview mode we use github api v3 to create labels +// see https://developer.github.com/v4/mutation/createlabel/ and https://developer.github.com/v4/previews/#labels-preview +func (ge *githubExporter) createGithubLabel(label, color string) (string, error) { + url := fmt.Sprintf("%s/repos/%s/%s/labels", githubV3Url, ge.conf[keyOwner], ge.conf[keyProject]) + + client := &http.Client{ + Timeout: defaultTimeout, + } + + params := struct { + Name string `json:"name"` + Color string `json:"color"` + Description string `json:"description"` + }{ + Name: label, + Color: color, + } + + data, err := json.Marshal(params) + if err != nil { + return "", err + } + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + return "", err + } + + // need the token for private repositories + req.Header.Set("Authorization", fmt.Sprintf("token %s", ge.conf[keyToken])) + + resp, err := client.Do(req) + if err != nil { + return "", err + } + + if resp.StatusCode != http.StatusCreated { + return "", fmt.Errorf("error creating label: response status %v", resp.StatusCode) + } + + aux := struct { + ID int `json:"id"` + NodeID string `json:"node_id"` + Color string `json:"color"` + }{} + + data, _ = ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + + err = json.Unmarshal(data, &aux) + if err != nil { + return "", err + } + + return aux.NodeID, nil +} + +/** +// create github label using api v4 +func (ge *githubExporter) createGithubLabelV4(gc *githubv4.Client, label, labelColor string) (string, error) { + m := createLabelMutation{} + input := createLabelInput{ + RepositoryID: ge.repositoryID, + Name: githubv4.String(label), + Color: githubv4.String(labelColor), + } + + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := gc.Mutate(ctx, &m, input, nil); err != nil { + return "", err + } + + return m.CreateLabel.Label.ID, nil +} +*/ + +func (ge *githubExporter) getOrCreateGithubLabelID(gc *githubv4.Client, repositoryID string, label bug.Label) (string, error) { + // try to get label id + labelID, err := ge.getGithubLabelID(gc, string(label)) + if err == nil { + return labelID, nil + } + + // RGBA to hex color + rgba := label.RGBA() + hexColor := fmt.Sprintf("%.2x%.2x%.2x", rgba.R, rgba.G, rgba.B) + + labelID, err = ge.createGithubLabel(string(label), hexColor) + if err != nil { + return "", err + } + + return labelID, nil +} + +func (ge *githubExporter) getLabelsIDs(gc *githubv4.Client, repositoryID string, labels []bug.Label) ([]githubv4.ID, error) { + ids := make([]githubv4.ID, 0, len(labels)) + var err error + + // check labels ids + for _, label := range labels { + id, ok := ge.cachedLabels[string(label)] + if !ok { + // try to query label id + id, err = ge.getOrCreateGithubLabelID(gc, repositoryID, label) + if err != nil { + return nil, errors.Wrap(err, "get or create github label") + } + + // cache label id + ge.cachedLabels[string(label)] = id + } + + ids = append(ids, githubv4.ID(id)) + } + + return ids, nil +} + +// create a github issue and return it ID +func createGithubIssue(gc *githubv4.Client, repositoryID, title, body string) (string, string, error) { + m := &createIssueMutation{} + input := githubv4.CreateIssueInput{ + RepositoryID: repositoryID, + Title: githubv4.String(title), + Body: (*githubv4.String)(&body), + } + + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := gc.Mutate(ctx, m, input, nil); err != nil { + return "", "", err + } + + issue := m.CreateIssue.Issue + return issue.ID, issue.URL, nil +} + +// add a comment to an issue and return it ID +func addCommentGithubIssue(gc *githubv4.Client, subjectID string, body string) (string, string, error) { + m := &addCommentToIssueMutation{} + input := githubv4.AddCommentInput{ + SubjectID: subjectID, + Body: githubv4.String(body), + } + + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := gc.Mutate(ctx, m, input, nil); err != nil { + return "", "", err + } + + node := m.AddComment.CommentEdge.Node + return node.ID, node.URL, nil +} + +func editCommentGithubIssue(gc *githubv4.Client, commentID, body string) (string, string, error) { + m := &updateIssueCommentMutation{} + input := githubv4.UpdateIssueCommentInput{ + ID: commentID, + Body: githubv4.String(body), + } + + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := gc.Mutate(ctx, m, input, nil); err != nil { + return "", "", err + } + + return commentID, m.UpdateIssueComment.IssueComment.URL, nil +} + +func updateGithubIssueStatus(gc *githubv4.Client, id string, status bug.Status) error { + m := &updateIssueMutation{} + + // set state + var state githubv4.IssueState + + switch status { + case bug.OpenStatus: + state = githubv4.IssueStateOpen + case bug.ClosedStatus: + state = githubv4.IssueStateClosed + default: + panic("unknown bug state") + } + + input := githubv4.UpdateIssueInput{ + ID: id, + State: &state, + } + + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := gc.Mutate(ctx, m, input, nil); err != nil { + return err + } + + return nil +} + +func updateGithubIssueBody(gc *githubv4.Client, id string, body string) error { + m := &updateIssueMutation{} + input := githubv4.UpdateIssueInput{ + ID: id, + Body: (*githubv4.String)(&body), + } + + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := gc.Mutate(ctx, m, input, nil); err != nil { + return err + } + + return nil +} + +func updateGithubIssueTitle(gc *githubv4.Client, id, title string) error { + m := &updateIssueMutation{} + input := githubv4.UpdateIssueInput{ + ID: id, + Title: (*githubv4.String)(&title), + } + + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := gc.Mutate(ctx, m, input, nil); err != nil { + return err + } + + return nil +} + +// update github issue labels +func (ge *githubExporter) updateGithubIssueLabels(gc *githubv4.Client, labelableID string, added, removed []bug.Label) error { + var errs []string + var wg sync.WaitGroup + + parentCtx := context.Background() + + if len(added) > 0 { + wg.Add(1) + go func() { + defer wg.Done() + + addedIDs, err := ge.getLabelsIDs(gc, labelableID, added) + if err != nil { + errs = append(errs, errors.Wrap(err, "getting added labels ids").Error()) + return + } + + m := &addLabelsToLabelableMutation{} + inputAdd := githubv4.AddLabelsToLabelableInput{ + LabelableID: labelableID, + LabelIDs: addedIDs, + } + + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + // add labels + if err := gc.Mutate(ctx, m, inputAdd, nil); err != nil { + errs = append(errs, err.Error()) + } + }() + } + + if len(removed) > 0 { + wg.Add(1) + go func() { + defer wg.Done() + + removedIDs, err := ge.getLabelsIDs(gc, labelableID, removed) + if err != nil { + errs = append(errs, errors.Wrap(err, "getting added labels ids").Error()) + return + } + + m2 := &removeLabelsFromLabelableMutation{} + inputRemove := githubv4.RemoveLabelsFromLabelableInput{ + LabelableID: labelableID, + LabelIDs: removedIDs, + } + + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + // remove label labels + if err := gc.Mutate(ctx, m2, inputRemove, nil); err != nil { + errs = append(errs, err.Error()) + } + }() + } + + wg.Wait() + + if len(errs) == 0 { + return nil + } + + return fmt.Errorf("label change error: %v", strings.Join(errs, "\n")) +} diff --git a/bridge/github/export_mutation.go b/bridge/github/export_mutation.go new file mode 100644 index 00000000..cf77f344 --- /dev/null +++ b/bridge/github/export_mutation.go @@ -0,0 +1,74 @@ +package github + +type createIssueMutation struct { + CreateIssue struct { + Issue struct { + ID string `graphql:"id"` + URL string `graphql:"url"` + } + } `graphql:"createIssue(input:$input)"` +} + +type updateIssueMutation struct { + UpdateIssue struct { + Issue struct { + ID string `graphql:"id"` + URL string `graphql:"url"` + } + } `graphql:"updateIssue(input:$input)"` +} + +type addCommentToIssueMutation struct { + AddComment struct { + CommentEdge struct { + Node struct { + ID string `graphql:"id"` + URL string `graphql:"url"` + } + } + } `graphql:"addComment(input:$input)"` +} + +type updateIssueCommentMutation struct { + UpdateIssueComment struct { + IssueComment struct { + ID string `graphql:"id"` + URL string `graphql:"url"` + } `graphql:"issueComment"` + } `graphql:"updateIssueComment(input:$input)"` +} + +type removeLabelsFromLabelableMutation struct { + AddLabels struct { + Labelable struct { + Typename string `graphql:"__typename"` + } + } `graphql:"removeLabelsFromLabelable(input:$input)"` +} + +type addLabelsToLabelableMutation struct { + RemoveLabels struct { + Labelable struct { + Typename string `graphql:"__typename"` + } + } `graphql:"addLabelsToLabelable(input:$input)"` +} + +/** +type createLabelMutation struct { + CreateLabel struct { + Label struct { + ID string `graphql:"id"` + } `graphql:"label"` + } `graphql:"createLabel(input: $input)"` +} + +type createLabelInput struct { + Color githubv4.String `json:"color"` + Description *githubv4.String `json:"description,omitempty"` + Name githubv4.String `json:"name"` + RepositoryID githubv4.ID `json:"repositoryId"` + + ClientMutationID *githubv4.String `json:"clientMutationId,omitempty"` +} +*/ diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go new file mode 100644 index 00000000..80660e77 --- /dev/null +++ b/bridge/github/export_test.go @@ -0,0 +1,340 @@ +package github + +import ( + "bytes" + "encoding/json" + "fmt" + "math/rand" + "net/http" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/bug" + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/repository" + "github.com/MichaelMure/git-bug/util/interrupt" +) + +const ( + testRepoBaseName = "git-bug-test-github-exporter" +) + +type testCase struct { + name string + bug *cache.BugCache + numOrOp int // number of original operations +} + +func testCases(t *testing.T, repo *cache.RepoCache, identity *cache.IdentityCache) []*testCase { + // simple bug + simpleBug, _, err := repo.NewBug("simple bug", "new bug") + require.NoError(t, err) + + // bug with comments + bugWithComments, _, err := repo.NewBug("bug with comments", "new bug") + require.NoError(t, err) + + _, err = bugWithComments.AddComment("new comment") + require.NoError(t, err) + + // bug with label changes + bugLabelChange, _, err := repo.NewBug("bug label change", "new bug") + require.NoError(t, err) + + _, _, err = bugLabelChange.ChangeLabels([]string{"bug"}, nil) + require.NoError(t, err) + + _, _, err = bugLabelChange.ChangeLabels([]string{"core"}, nil) + require.NoError(t, err) + + _, _, err = bugLabelChange.ChangeLabels(nil, []string{"bug"}) + require.NoError(t, err) + + // bug with comments editions + bugWithCommentEditions, createOp, err := repo.NewBug("bug with comments editions", "new bug") + require.NoError(t, err) + + createOpHash, err := createOp.Hash() + require.NoError(t, err) + + _, err = bugWithCommentEditions.EditComment(createOpHash, "first comment edited") + require.NoError(t, err) + + commentOp, err := bugWithCommentEditions.AddComment("first comment") + require.NoError(t, err) + + commentOpHash, err := commentOp.Hash() + require.NoError(t, err) + + _, err = bugWithCommentEditions.EditComment(commentOpHash, "first comment edited") + require.NoError(t, err) + + // bug status changed + bugStatusChanged, _, err := repo.NewBug("bug status changed", "new bug") + require.NoError(t, err) + + _, err = bugStatusChanged.Close() + require.NoError(t, err) + + _, err = bugStatusChanged.Open() + require.NoError(t, err) + + // bug title changed + bugTitleEdited, _, err := repo.NewBug("bug title edited", "new bug") + require.NoError(t, err) + + _, err = bugTitleEdited.SetTitle("bug title edited again") + require.NoError(t, err) + + return []*testCase{ + &testCase{ + name: "simple bug", + bug: simpleBug, + numOrOp: 1, + }, + &testCase{ + name: "bug with comments", + bug: bugWithComments, + numOrOp: 2, + }, + &testCase{ + name: "bug label change", + bug: bugLabelChange, + numOrOp: 4, + }, + &testCase{ + name: "bug with comment editions", + bug: bugWithCommentEditions, + numOrOp: 4, + }, + &testCase{ + name: "bug changed status", + bug: bugStatusChanged, + numOrOp: 3, + }, + &testCase{ + name: "bug title edited", + bug: bugTitleEdited, + numOrOp: 2, + }, + } +} + +func TestPushPull(t *testing.T) { + // repo owner + user := os.Getenv("GITHUB_TEST_USER") + + // token must have 'repo' and 'delete_repo' scopes + token := os.Getenv("GITHUB_TOKEN_ADMIN") + if token == "" { + t.Skip("Env var GITHUB_TOKEN_ADMIN missing") + } + + // create repo backend + repo := repository.CreateTestRepo(false) + defer repository.CleanupTestRepos(t, repo) + + backend, err := cache.NewRepoCache(repo) + require.NoError(t, err) + + // set author identity + author, err := backend.NewIdentity("test identity", "test@test.org") + require.NoError(t, err) + + err = backend.SetUserIdentity(author) + require.NoError(t, err) + + defer backend.Close() + interrupt.RegisterCleaner(backend.Close) + + tests := testCases(t, backend, author) + + // generate project name + projectName := generateRepoName() + + // create target Github repository + err = createRepository(projectName, token) + require.NoError(t, err) + + fmt.Println("created repository", projectName) + + // Make sure to remove the Github repository when the test end + defer func(t *testing.T) { + if err := deleteRepository(projectName, user, token); err != nil { + t.Fatal(err) + } + fmt.Println("deleted repository:", projectName) + }(t) + + interrupt.RegisterCleaner(func() error { + return deleteRepository(projectName, user, token) + }) + + // initialize exporter + exporter := &githubExporter{} + err = exporter.Init(core.Configuration{ + keyOwner: user, + keyProject: projectName, + keyToken: token, + }) + require.NoError(t, err) + + start := time.Now() + + // export all bugs + events, err := exporter.ExportAll(backend, time.Time{}) + require.NoError(t, err) + + for result := range events { + require.NoError(t, result.Err) + } + require.NoError(t, err) + + fmt.Printf("test repository exported in %f seconds\n", time.Since(start).Seconds()) + + repoTwo := repository.CreateTestRepo(false) + defer repository.CleanupTestRepos(t, repoTwo) + + // create a second backend + backendTwo, err := cache.NewRepoCache(repoTwo) + require.NoError(t, err) + + importer := &githubImporter{} + err = importer.Init(core.Configuration{ + keyOwner: user, + keyProject: projectName, + keyToken: token, + }) + require.NoError(t, err) + + // import all exported bugs to the second backend + err = importer.ImportAll(backendTwo, time.Time{}) + require.NoError(t, err) + + require.Len(t, backendTwo.AllBugsIds(), len(tests)) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // for each operation a SetMetadataOperation will be added + // so number of operations should double + require.Len(t, tt.bug.Snapshot().Operations, tt.numOrOp*2) + + // verify operation have correct metadata + for _, op := range tt.bug.Snapshot().Operations { + // Check if the originals operations (*not* SetMetadata) are tagged properly + if _, ok := op.(*bug.SetMetadataOperation); !ok { + _, haveIDMetadata := op.GetMetadata(keyGithubId) + require.True(t, haveIDMetadata) + + _, haveURLMetada := op.GetMetadata(keyGithubUrl) + require.True(t, haveURLMetada) + } + } + + // get bug github ID + bugGithubID, ok := tt.bug.Snapshot().GetCreateMetadata(keyGithubId) + require.True(t, ok) + + // retrieve bug from backendTwo + importedBug, err := backendTwo.ResolveBugCreateMetadata(keyGithubId, bugGithubID) + require.NoError(t, err) + + // verify bug have same number of original operations + require.Len(t, importedBug.Snapshot().Operations, tt.numOrOp) + + // verify bugs are taged with origin=github + issueOrigin, ok := importedBug.Snapshot().GetCreateMetadata(keyOrigin) + require.True(t, ok) + require.Equal(t, issueOrigin, target) + + //TODO: maybe more tests to ensure bug final state + }) + } +} + +func generateRepoName() string { + rand.Seed(time.Now().UnixNano()) + var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + b := make([]rune, 8) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return fmt.Sprintf("%s-%s", testRepoBaseName, string(b)) +} + +// create repository need a token with scope 'repo' +func createRepository(project, token string) error { + // This function use the V3 Github API because repository creation is not supported yet on the V4 API. + url := fmt.Sprintf("%s/user/repos", githubV3Url) + + params := struct { + Name string `json:"name"` + Description string `json:"description"` + Private bool `json:"private"` + HasIssues bool `json:"has_issues"` + }{ + Name: project, + Description: "git-bug exporter temporary test repository", + Private: true, + HasIssues: true, + } + + data, err := json.Marshal(params) + if err != nil { + return err + } + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + return err + } + + // need the token for private repositories + req.Header.Set("Authorization", fmt.Sprintf("token %s", token)) + + client := &http.Client{ + Timeout: defaultTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + + return resp.Body.Close() +} + +// delete repository need a token with scope 'delete_repo' +func deleteRepository(project, owner, token string) error { + // This function use the V3 Github API because repository removal is not supported yet on the V4 API. + url := fmt.Sprintf("%s/repos/%s/%s", githubV3Url, owner, project) + + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return err + } + + // need the token for private repositories + req.Header.Set("Authorization", fmt.Sprintf("token %s", token)) + + client := &http.Client{ + Timeout: defaultTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("error deleting repository") + } + + return nil +} diff --git a/bridge/github/github.go b/bridge/github/github.go index 3e717ee9..176bdd84 100644 --- a/bridge/github/github.go +++ b/bridge/github/github.go @@ -17,7 +17,7 @@ func init() { type Github struct{} func (*Github) Target() string { - return "github" + return target } func (*Github) NewImporter() core.Importer { @@ -25,7 +25,7 @@ func (*Github) NewImporter() core.Importer { } func (*Github) NewExporter() core.Exporter { - return nil + return &githubExporter{} } func buildClient(token string) *githubv4.Client { diff --git a/bridge/github/import.go b/bridge/github/import.go index 6cfd022e..c162d70e 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -16,6 +16,7 @@ import ( ) const ( + keyOrigin = "origin" keyGithubId = "github-id" keyGithubUrl = "github-url" keyGithubLogin = "github-login" @@ -113,6 +114,7 @@ func (gi *githubImporter) ensureIssue(repo *cache.RepoCache, issue issueTimeline cleanText, nil, map[string]string{ + keyOrigin: target, keyGithubId: parseId(issue.Id), keyGithubUrl: issue.Url.String(), }) @@ -147,6 +149,7 @@ func (gi *githubImporter) ensureIssue(repo *cache.RepoCache, issue issueTimeline cleanText, nil, map[string]string{ + keyOrigin: target, keyGithubId: parseId(issue.Id), keyGithubUrl: issue.Url.String(), }, @@ -502,7 +505,7 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache, return nil, err } - var q userQuery + var q ghostQuery variables := map[string]interface{}{ "login": githubv4.String("ghost"), @@ -510,7 +513,11 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache, gc := buildClient(gi.conf[keyToken]) - err = gc.Query(context.TODO(), &q, variables) + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + err = gc.Query(ctx, &q, variables) if err != nil { return nil, err } @@ -522,7 +529,7 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache, return repo.NewIdentityRaw( name, - string(q.User.Email), + "", string(q.User.Login), string(q.User.AvatarUrl), map[string]string{ diff --git a/bridge/github/import_query.go b/bridge/github/import_query.go index 4d5886f6..89d7859d 100644 --- a/bridge/github/import_query.go +++ b/bridge/github/import_query.go @@ -160,11 +160,18 @@ type commentEditQuery struct { } `graphql:"repository(owner: $owner, name: $name)"` } -type userQuery struct { +type ghostQuery struct { User struct { Login githubv4.String AvatarUrl githubv4.String Name *githubv4.String - Email githubv4.String } `graphql:"user(login: $login)"` } + +type labelQuery struct { + Repository struct { + Label struct { + ID string `graphql:"id"` + } `graphql:"label(name: $label)"` + } `graphql:"repository(owner: $owner, name: $name)"` +} diff --git a/bridge/github/import_test.go b/bridge/github/import_test.go index 24356f34..5ba87993 100644 --- a/bridge/github/import_test.go +++ b/bridge/github/import_test.go @@ -190,7 +190,7 @@ func Test_Importer(t *testing.T) { assert.Equal(t, op.(*bug.EditCommentOperation).Author.Name(), ops[i].(*bug.EditCommentOperation).Author.Name()) default: - panic("Unknown operation type") + panic("unknown operation type") } } }) diff --git a/bridge/github/iterator.go b/bridge/github/iterator.go index fcf72b8f..6d63cf42 100644 --- a/bridge/github/iterator.go +++ b/bridge/github/iterator.go @@ -59,7 +59,7 @@ type iterator struct { commentEdit commentEditIterator } -// NewIterator create and initalize a new iterator +// NewIterator create and initialize a new iterator func NewIterator(owner, project, token string, since time.Time) *iterator { i := &iterator{ gc: buildClient(token), @@ -147,7 +147,11 @@ func (i *iterator) Error() error { } func (i *iterator) queryIssue() bool { - if err := i.gc.Query(context.TODO(), &i.timeline.query, i.timeline.variables); err != nil { + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := i.gc.Query(ctx, &i.timeline.query, i.timeline.variables); err != nil { i.err = err return false } @@ -220,7 +224,12 @@ func (i *iterator) NextTimelineItem() bool { // more timelines, query them i.timeline.variables["timelineAfter"] = i.timeline.query.Repository.Issues.Nodes[0].Timeline.PageInfo.EndCursor - if err := i.gc.Query(context.TODO(), &i.timeline.query, i.timeline.variables); err != nil { + + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := i.gc.Query(ctx, &i.timeline.query, i.timeline.variables); err != nil { i.err = err return false } @@ -236,7 +245,11 @@ func (i *iterator) TimelineItemValue() timelineItem { } func (i *iterator) queryIssueEdit() bool { - if err := i.gc.Query(context.TODO(), &i.issueEdit.query, i.issueEdit.variables); err != nil { + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := i.gc.Query(ctx, &i.issueEdit.query, i.issueEdit.variables); err != nil { i.err = err //i.timeline.issueEdit.index = -1 return false @@ -334,7 +347,11 @@ func (i *iterator) IssueEditValue() userContentEdit { } func (i *iterator) queryCommentEdit() bool { - if err := i.gc.Query(context.TODO(), &i.commentEdit.query, i.commentEdit.variables); err != nil { + parentCtx := context.Background() + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + if err := i.gc.Query(ctx, &i.commentEdit.query, i.commentEdit.variables); err != nil { i.err = err return false } diff --git a/bug/operation.go b/bug/operation.go index 8e77eed8..daef7b8c 100644 --- a/bug/operation.go +++ b/bug/operation.go @@ -49,6 +49,8 @@ type Operation interface { GetMetadata(key string) (string, bool) // AllMetadata return all metadata for this operation AllMetadata() map[string]string + // GetAuthor return the author identity + GetAuthor() identity.Interface } func hashRaw(data []byte) git.Hash { @@ -222,3 +224,8 @@ func (op *OpBase) AllMetadata() map[string]string { return result } + +// GetAuthor return author identity +func (op *OpBase) GetAuthor() identity.Interface { + return op.Author +} diff --git a/bug/snapshot.go b/bug/snapshot.go index a4661b14..114b1123 100644 --- a/bug/snapshot.go +++ b/bug/snapshot.go @@ -54,6 +54,11 @@ func (snap *Snapshot) LastEditUnix() int64 { return snap.Operations[len(snap.Operations)-1].GetUnixTime() } +// GetCreateMetadata return the creation metadata +func (snap *Snapshot) GetCreateMetadata(key string) (string, bool) { + return snap.Operations[0].GetMetadata(key) +} + // SearchTimelineItem will search in the timeline for an item matching the given hash func (snap *Snapshot) SearchTimelineItem(hash git.Hash) (TimelineItem, error) { for i := range snap.Timeline { @@ -87,5 +92,45 @@ func (snap *Snapshot) addParticipant(participant identity.Interface) { snap.Participants = append(snap.Participants, participant) } +// HasParticipant return true if the id is a participant +func (snap *Snapshot) HasParticipant(id string) bool { + for _, p := range snap.Participants { + if p.Id() == id { + return true + } + } + return false +} + +// HasAnyParticipant return true if one of the ids is a participant +func (snap *Snapshot) HasAnyParticipant(ids ...string) bool { + for _, id := range ids { + if snap.HasParticipant(id) { + return true + } + } + return false +} + +// HasActor return true if the id is a actor +func (snap *Snapshot) HasActor(id string) bool { + for _, p := range snap.Actors { + if p.Id() == id { + return true + } + } + return false +} + +// HasAnyActor return true if one of the ids is a actor +func (snap *Snapshot) HasAnyActor(ids ...string) bool { + for _, id := range ids { + if snap.HasActor(id) { + return true + } + } + return false +} + // Sign post method for gqlgen func (snap *Snapshot) IsAuthored() {} diff --git a/cache/bug_cache.go b/cache/bug_cache.go index aad6dab8..758fb0b7 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -254,6 +254,24 @@ func (c *BugCache) EditCommentRaw(author *IdentityCache, unixTime int64, target return op, c.notifyUpdated() } +func (c *BugCache) SetMetadata(target git.Hash, newMetadata map[string]string) (*bug.SetMetadataOperation, error) { + author, err := c.repoCache.GetUserIdentity() + if err != nil { + return nil, err + } + + return c.SetMetadataRaw(author, time.Now().Unix(), target, newMetadata) +} + +func (c *BugCache) SetMetadataRaw(author *IdentityCache, unixTime int64, target git.Hash, newMetadata map[string]string) (*bug.SetMetadataOperation, error) { + op, err := bug.SetMetadata(c.bug, author.Identity, unixTime, target, newMetadata) + if err != nil { + return nil, err + } + + return op, c.notifyUpdated() +} + func (c *BugCache) Commit() error { err := c.bug.Commit(c.repoCache.repo) if err != nil { diff --git a/commands/bridge_pull.go b/commands/bridge_pull.go index c7a22d6d..2edabfaf 100644 --- a/commands/bridge_pull.go +++ b/commands/bridge_pull.go @@ -3,11 +3,12 @@ package commands import ( "time" + "github.com/spf13/cobra" + "github.com/MichaelMure/git-bug/bridge" "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/util/interrupt" - "github.com/spf13/cobra" ) func runBridgePull(cmd *cobra.Command, args []string) error { @@ -44,6 +45,7 @@ var bridgePullCmd = &cobra.Command{ Short: "Pull updates.", PreRunE: loadRepo, RunE: runBridgePull, + Args: cobra.MaximumNArgs(1), } func init() { diff --git a/commands/bridge_push.go b/commands/bridge_push.go new file mode 100644 index 00000000..11f5ca82 --- /dev/null +++ b/commands/bridge_push.go @@ -0,0 +1,62 @@ +package commands + +import ( + "fmt" + "time" + + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/bridge" + "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/util/interrupt" +) + +func runBridgePush(cmd *cobra.Command, args []string) error { + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + interrupt.RegisterCleaner(backend.Close) + + var b *core.Bridge + + if len(args) == 0 { + b, err = bridge.DefaultBridge(backend) + } else { + b, err = bridge.LoadBridge(backend, args[0]) + } + + if err != nil { + return err + } + + // TODO: by default export only new events + out, err := b.ExportAll(time.Time{}) + if err != nil { + return err + } + + for result := range out { + if result.Err != nil { + fmt.Println(result.Err, result.Reason) + } else { + fmt.Printf("%s: %s\n", result.String(), result.ID) + } + } + + return nil +} + +var bridgePushCmd = &cobra.Command{ + Use: "push []", + Short: "Push updates.", + PreRunE: loadRepo, + RunE: runBridgePush, + Args: cobra.MaximumNArgs(1), +} + +func init() { + bridgeCmd.AddCommand(bridgePushCmd) +} diff --git a/doc/man/git-bug-bridge-push.1 b/doc/man/git-bug-bridge-push.1 new file mode 100644 index 00000000..1257781b --- /dev/null +++ b/doc/man/git-bug-bridge-push.1 @@ -0,0 +1,29 @@ +.TH "GIT-BUG" "1" "Apr 2019" "Generated from git-bug's source code" "" +.nh +.ad l + + +.SH NAME +.PP +git\-bug\-bridge\-push \- Push updates. + + +.SH SYNOPSIS +.PP +\fBgit\-bug bridge push [] [flags]\fP + + +.SH DESCRIPTION +.PP +Push updates. + + +.SH OPTIONS +.PP +\fB\-h\fP, \fB\-\-help\fP[=false] + help for push + + +.SH SEE ALSO +.PP +\fBgit\-bug\-bridge(1)\fP diff --git a/doc/man/git-bug-bridge.1 b/doc/man/git-bug-bridge.1 index 9c198716..dfede4e0 100644 --- a/doc/man/git-bug-bridge.1 +++ b/doc/man/git-bug-bridge.1 @@ -26,4 +26,4 @@ Configure and use bridges to other bug trackers. .SH SEE ALSO .PP -\fBgit\-bug(1)\fP, \fBgit\-bug\-bridge\-configure(1)\fP, \fBgit\-bug\-bridge\-pull(1)\fP, \fBgit\-bug\-bridge\-rm(1)\fP +\fBgit\-bug(1)\fP, \fBgit\-bug\-bridge\-configure(1)\fP, \fBgit\-bug\-bridge\-pull(1)\fP, \fBgit\-bug\-bridge\-push(1)\fP, \fBgit\-bug\-bridge\-rm(1)\fP diff --git a/doc/md/git-bug_bridge.md b/doc/md/git-bug_bridge.md index 7d01a300..dfd61e29 100644 --- a/doc/md/git-bug_bridge.md +++ b/doc/md/git-bug_bridge.md @@ -21,5 +21,6 @@ git-bug bridge [flags] * [git-bug](git-bug.md) - A bug tracker embedded in Git. * [git-bug bridge configure](git-bug_bridge_configure.md) - Configure a new bridge. * [git-bug bridge pull](git-bug_bridge_pull.md) - Pull updates. +* [git-bug bridge push](git-bug_bridge_push.md) - Push updates. * [git-bug bridge rm](git-bug_bridge_rm.md) - Delete a configured bridge. diff --git a/doc/md/git-bug_bridge_push.md b/doc/md/git-bug_bridge_push.md new file mode 100644 index 00000000..35436dd6 --- /dev/null +++ b/doc/md/git-bug_bridge_push.md @@ -0,0 +1,22 @@ +## git-bug bridge push + +Push updates. + +### Synopsis + +Push updates. + +``` +git-bug bridge push [] [flags] +``` + +### Options + +``` + -h, --help help for push +``` + +### SEE ALSO + +* [git-bug bridge](git-bug_bridge.md) - Configure and use bridges to other bug trackers. + diff --git a/misc/bash_completion/git-bug b/misc/bash_completion/git-bug index 741dcc49..ae3242e5 100644 --- a/misc/bash_completion/git-bug +++ b/misc/bash_completion/git-bug @@ -351,6 +351,26 @@ _git-bug_bridge_pull() noun_aliases=() } +_git-bug_bridge_push() +{ + last_command="git-bug_bridge_push" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _git-bug_bridge_rm() { last_command="git-bug_bridge_rm" @@ -380,6 +400,7 @@ _git-bug_bridge() commands=() commands+=("configure") commands+=("pull") + commands+=("push") commands+=("rm") flags=() diff --git a/misc/powershell_completion/git-bug b/misc/powershell_completion/git-bug index 7eff1cda..b89897a3 100644 --- a/misc/powershell_completion/git-bug +++ b/misc/powershell_completion/git-bug @@ -50,6 +50,7 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock { 'git-bug;bridge' { [CompletionResult]::new('configure', 'configure', [CompletionResultType]::ParameterValue, 'Configure a new bridge.') [CompletionResult]::new('pull', 'pull', [CompletionResultType]::ParameterValue, 'Pull updates.') + [CompletionResult]::new('push', 'push', [CompletionResultType]::ParameterValue, 'Push updates.') [CompletionResult]::new('rm', 'rm', [CompletionResultType]::ParameterValue, 'Delete a configured bridge.') break } @@ -71,6 +72,9 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock { 'git-bug;bridge;pull' { break } + 'git-bug;bridge;push' { + break + } 'git-bug;bridge;rm' { break } diff --git a/misc/zsh_completion/git-bug b/misc/zsh_completion/git-bug index ed676724..1e2ed511 100644 --- a/misc/zsh_completion/git-bug +++ b/misc/zsh_completion/git-bug @@ -116,6 +116,7 @@ function _git-bug_bridge { commands=( "configure:Configure a new bridge." "pull:Pull updates." + "push:Push updates." "rm:Delete a configured bridge." ) _describe "command" commands @@ -129,6 +130,9 @@ function _git-bug_bridge { pull) _git-bug_bridge_pull ;; + push) + _git-bug_bridge_push + ;; rm) _git-bug_bridge_rm ;; @@ -149,6 +153,10 @@ function _git-bug_bridge_pull { _arguments } +function _git-bug_bridge_push { + _arguments +} + function _git-bug_bridge_rm { _arguments } diff --git a/vendor/github.com/shurcooL/githubv4/.travis.yml b/vendor/github.com/shurcooL/githubv4/.travis.yml index 93b1fcdb..437c57db 100644 --- a/vendor/github.com/shurcooL/githubv4/.travis.yml +++ b/vendor/github.com/shurcooL/githubv4/.travis.yml @@ -12,5 +12,5 @@ install: script: - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d -s .) - - go tool vet . + - go vet ./... - go test -v -race ./... diff --git a/vendor/github.com/shurcooL/githubv4/README.md b/vendor/github.com/shurcooL/githubv4/README.md index 0d389ef0..319a6428 100644 --- a/vendor/github.com/shurcooL/githubv4/README.md +++ b/vendor/github.com/shurcooL/githubv4/README.md @@ -48,7 +48,7 @@ func main() { ### Simple Query -To make a query, you need to define a Go type that corresponds to the GitHub GraphQL schema, and contains the fields you're interested in querying. You can look up the GitHub GraphQL schema at https://developer.github.com/v4/reference/query/. +To make a query, you need to define a Go type that corresponds to the GitHub GraphQL schema, and contains the fields you're interested in querying. You can look up the GitHub GraphQL schema at https://developer.github.com/v4/query/. For example, to make the following GraphQL query: @@ -89,7 +89,7 @@ fmt.Println("CreatedAt:", query.Viewer.CreatedAt) ### Scalar Types -For each scalar in the GitHub GraphQL schema listed at https://developer.github.com/v4/reference/scalar/, there is a corresponding Go type in package `githubv4`. +For each scalar in the GitHub GraphQL schema listed at https://developer.github.com/v4/scalar/, there is a corresponding Go type in package `githubv4`. You can use these types when writing queries: @@ -127,7 +127,7 @@ var query struct { // Call client.Query() and use results in query... ``` -The [`DateTime`](https://developer.github.com/v4/reference/scalar/datetime/) scalar is described as "an ISO-8601 encoded UTC date string". If you wanted to fetch in that form without parsing it into a `time.Time`, you can use the `string` type. For example, this would work: +The [`DateTime`](https://developer.github.com/v4/scalar/datetime/) scalar is described as "an ISO-8601 encoded UTC date string". If you wanted to fetch in that form without parsing it into a `time.Time`, you can use the `string` type. For example, this would work: ```Go // import "html/template" @@ -336,7 +336,7 @@ for { } ``` -There is more than one way to perform pagination. Consider additional fields inside [`PageInfo`](https://developer.github.com/v4/reference/object/pageinfo/) object. +There is more than one way to perform pagination. Consider additional fields inside [`PageInfo`](https://developer.github.com/v4/object/pageinfo/) object. ### Mutations diff --git a/vendor/github.com/shurcooL/githubv4/enum.go b/vendor/github.com/shurcooL/githubv4/enum.go index 8293a23b..0ab92800 100644 --- a/vendor/github.com/shurcooL/githubv4/enum.go +++ b/vendor/github.com/shurcooL/githubv4/enum.go @@ -36,6 +36,24 @@ const ( CommentCannotUpdateReasonLoginRequired CommentCannotUpdateReason = "LOGIN_REQUIRED" // You must be logged in to update this comment. CommentCannotUpdateReasonMaintenance CommentCannotUpdateReason = "MAINTENANCE" // Repository is under maintenance. CommentCannotUpdateReasonVerifiedEmailRequired CommentCannotUpdateReason = "VERIFIED_EMAIL_REQUIRED" // At least one email address must be verified to update this comment. + CommentCannotUpdateReasonDenied CommentCannotUpdateReason = "DENIED" // You cannot update this comment. +) + +// CommitContributionOrderField represents properties by which commit contribution connections can be ordered. +type CommitContributionOrderField string + +// Properties by which commit contribution connections can be ordered. +const ( + CommitContributionOrderFieldOccurredAt CommitContributionOrderField = "OCCURRED_AT" // Order commit contributions by when they were made. + CommitContributionOrderFieldCommitCount CommitContributionOrderField = "COMMIT_COUNT" // Order commit contributions by how many commits they represent. +) + +// ContributionOrderField represents properties by which contribution connections can be ordered. +type ContributionOrderField string + +// Properties by which contribution connections can be ordered. +const ( + ContributionOrderFieldOccurredAt ContributionOrderField = "OCCURRED_AT" // Order contributions by when they were made. ) // DefaultRepositoryPermissionField represents the possible default permissions for repositories. @@ -49,18 +67,28 @@ const ( DefaultRepositoryPermissionFieldAdmin DefaultRepositoryPermissionField = "ADMIN" // Can read, write, and administrate repos by default. ) +// DeploymentOrderField represents properties by which deployment connections can be ordered. +type DeploymentOrderField string + +// Properties by which deployment connections can be ordered. +const ( + DeploymentOrderFieldCreatedAt DeploymentOrderField = "CREATED_AT" // Order collection by creation time. +) + // DeploymentState represents the possible states in which a deployment can be. type DeploymentState string // The possible states in which a deployment can be. const ( - DeploymentStateAbandoned DeploymentState = "ABANDONED" // The pending deployment was not updated after 30 minutes. - DeploymentStateActive DeploymentState = "ACTIVE" // The deployment is currently active. - DeploymentStateDestroyed DeploymentState = "DESTROYED" // An inactive transient deployment. - DeploymentStateError DeploymentState = "ERROR" // The deployment experienced an error. - DeploymentStateFailure DeploymentState = "FAILURE" // The deployment has failed. - DeploymentStateInactive DeploymentState = "INACTIVE" // The deployment is inactive. - DeploymentStatePending DeploymentState = "PENDING" // The deployment is pending. + DeploymentStateAbandoned DeploymentState = "ABANDONED" // The pending deployment was not updated after 30 minutes. + DeploymentStateActive DeploymentState = "ACTIVE" // The deployment is currently active. + DeploymentStateDestroyed DeploymentState = "DESTROYED" // An inactive transient deployment. + DeploymentStateError DeploymentState = "ERROR" // The deployment experienced an error. + DeploymentStateFailure DeploymentState = "FAILURE" // The deployment has failed. + DeploymentStateInactive DeploymentState = "INACTIVE" // The deployment is inactive. + DeploymentStatePending DeploymentState = "PENDING" // The deployment is pending. + DeploymentStateQueued DeploymentState = "QUEUED" // The deployment has queued. + DeploymentStateInProgress DeploymentState = "IN_PROGRESS" // The deployment is in progress. ) // DeploymentStatusState represents the possible states for a deployment status. @@ -68,11 +96,13 @@ type DeploymentStatusState string // The possible states for a deployment status. const ( - DeploymentStatusStatePending DeploymentStatusState = "PENDING" // The deployment is pending. - DeploymentStatusStateSuccess DeploymentStatusState = "SUCCESS" // The deployment was successful. - DeploymentStatusStateFailure DeploymentStatusState = "FAILURE" // The deployment has failed. - DeploymentStatusStateInactive DeploymentStatusState = "INACTIVE" // The deployment is inactive. - DeploymentStatusStateError DeploymentStatusState = "ERROR" // The deployment experienced an error. + DeploymentStatusStatePending DeploymentStatusState = "PENDING" // The deployment is pending. + DeploymentStatusStateSuccess DeploymentStatusState = "SUCCESS" // The deployment was successful. + DeploymentStatusStateFailure DeploymentStatusState = "FAILURE" // The deployment has failed. + DeploymentStatusStateInactive DeploymentStatusState = "INACTIVE" // The deployment is inactive. + DeploymentStatusStateError DeploymentStatusState = "ERROR" // The deployment experienced an error. + DeploymentStatusStateQueued DeploymentStatusState = "QUEUED" // The deployment is queued. + DeploymentStatusStateInProgress DeploymentStatusState = "IN_PROGRESS" // The deployment is in progress. ) // GistOrderField represents properties by which gist connections can be ordered. @@ -115,9 +145,20 @@ const ( GitSignatureStateExpiredKey GitSignatureState = "EXPIRED_KEY" // Signing key expired. GitSignatureStateOcspPending GitSignatureState = "OCSP_PENDING" // Valid signature, pending certificate revocation checking. GitSignatureStateOcspError GitSignatureState = "OCSP_ERROR" // Valid siganture, though certificate revocation check failed. + GitSignatureStateBadCert GitSignatureState = "BAD_CERT" // The signing certificate or its chain could not be verified. GitSignatureStateOcspRevoked GitSignatureState = "OCSP_REVOKED" // One or more certificates in chain has been revoked. ) +// IdentityProviderConfigurationState represents the possible states in which authentication can be configured with an identity provider. +type IdentityProviderConfigurationState string + +// The possible states in which authentication can be configured with an identity provider. +const ( + IdentityProviderConfigurationStateEnforced IdentityProviderConfigurationState = "ENFORCED" // Authentication with an identity provider is configured and enforced. + IdentityProviderConfigurationStateConfigured IdentityProviderConfigurationState = "CONFIGURED" // Authentication with an identity provider is configured but not enforced. + IdentityProviderConfigurationStateUnconfigured IdentityProviderConfigurationState = "UNCONFIGURED" // Authentication with an identity provider is not configured. +) + // IssueOrderField represents properties by which issue connections can be ordered. type IssueOrderField string @@ -136,6 +177,7 @@ const ( IssuePubSubTopicUpdated IssuePubSubTopic = "UPDATED" // The channel ID for observing issue updates. IssuePubSubTopicMarkasread IssuePubSubTopic = "MARKASREAD" // The channel ID for marking an issue as read. IssuePubSubTopicTimeline IssuePubSubTopic = "TIMELINE" // The channel ID for updating items on the issue timeline. + IssuePubSubTopicState IssuePubSubTopic = "STATE" // The channel ID for observing issue state updates. ) // IssueState represents the possible states of an issue. @@ -147,6 +189,39 @@ const ( IssueStateClosed IssueState = "CLOSED" // An issue that has been closed. ) +// IssueTimelineItemsItemType represents the possible item types found in a timeline. +type IssueTimelineItemsItemType string + +// The possible item types found in a timeline. +const ( + IssueTimelineItemsItemTypeIssueComment IssueTimelineItemsItemType = "ISSUE_COMMENT" // Represents a comment on an Issue. + IssueTimelineItemsItemTypeCrossReferencedEvent IssueTimelineItemsItemType = "CROSS_REFERENCED_EVENT" // Represents a mention made by one issue or pull request to another. + IssueTimelineItemsItemTypeAddedToProjectEvent IssueTimelineItemsItemType = "ADDED_TO_PROJECT_EVENT" // Represents a 'added_to_project' event on a given issue or pull request. + IssueTimelineItemsItemTypeAssignedEvent IssueTimelineItemsItemType = "ASSIGNED_EVENT" // Represents an 'assigned' event on any assignable object. + IssueTimelineItemsItemTypeClosedEvent IssueTimelineItemsItemType = "CLOSED_EVENT" // Represents a 'closed' event on any `Closable`. + IssueTimelineItemsItemTypeCommentDeletedEvent IssueTimelineItemsItemType = "COMMENT_DELETED_EVENT" // Represents a 'comment_deleted' event on a given issue or pull request. + IssueTimelineItemsItemTypeConvertedNoteToIssueEvent IssueTimelineItemsItemType = "CONVERTED_NOTE_TO_ISSUE_EVENT" // Represents a 'converted_note_to_issue' event on a given issue or pull request. + IssueTimelineItemsItemTypeDemilestonedEvent IssueTimelineItemsItemType = "DEMILESTONED_EVENT" // Represents a 'demilestoned' event on a given issue or pull request. + IssueTimelineItemsItemTypeLabeledEvent IssueTimelineItemsItemType = "LABELED_EVENT" // Represents a 'labeled' event on a given issue or pull request. + IssueTimelineItemsItemTypeLockedEvent IssueTimelineItemsItemType = "LOCKED_EVENT" // Represents a 'locked' event on a given issue or pull request. + IssueTimelineItemsItemTypeMentionedEvent IssueTimelineItemsItemType = "MENTIONED_EVENT" // Represents a 'mentioned' event on a given issue or pull request. + IssueTimelineItemsItemTypeMilestonedEvent IssueTimelineItemsItemType = "MILESTONED_EVENT" // Represents a 'milestoned' event on a given issue or pull request. + IssueTimelineItemsItemTypeMovedColumnsInProjectEvent IssueTimelineItemsItemType = "MOVED_COLUMNS_IN_PROJECT_EVENT" // Represents a 'moved_columns_in_project' event on a given issue or pull request. + IssueTimelineItemsItemTypePinnedEvent IssueTimelineItemsItemType = "PINNED_EVENT" // Represents a 'pinned' event on a given issue or pull request. + IssueTimelineItemsItemTypeReferencedEvent IssueTimelineItemsItemType = "REFERENCED_EVENT" // Represents a 'referenced' event on a given `ReferencedSubject`. + IssueTimelineItemsItemTypeRemovedFromProjectEvent IssueTimelineItemsItemType = "REMOVED_FROM_PROJECT_EVENT" // Represents a 'removed_from_project' event on a given issue or pull request. + IssueTimelineItemsItemTypeRenamedTitleEvent IssueTimelineItemsItemType = "RENAMED_TITLE_EVENT" // Represents a 'renamed' event on a given issue or pull request. + IssueTimelineItemsItemTypeReopenedEvent IssueTimelineItemsItemType = "REOPENED_EVENT" // Represents a 'reopened' event on any `Closable`. + IssueTimelineItemsItemTypeSubscribedEvent IssueTimelineItemsItemType = "SUBSCRIBED_EVENT" // Represents a 'subscribed' event on a given `Subscribable`. + IssueTimelineItemsItemTypeTransferredEvent IssueTimelineItemsItemType = "TRANSFERRED_EVENT" // Represents a 'transferred' event on a given issue or pull request. + IssueTimelineItemsItemTypeUnassignedEvent IssueTimelineItemsItemType = "UNASSIGNED_EVENT" // Represents an 'unassigned' event on any assignable object. + IssueTimelineItemsItemTypeUnlabeledEvent IssueTimelineItemsItemType = "UNLABELED_EVENT" // Represents an 'unlabeled' event on a given issue or pull request. + IssueTimelineItemsItemTypeUnlockedEvent IssueTimelineItemsItemType = "UNLOCKED_EVENT" // Represents an 'unlocked' event on a given issue or pull request. + IssueTimelineItemsItemTypeUserBlockedEvent IssueTimelineItemsItemType = "USER_BLOCKED_EVENT" // Represents a 'user_blocked' event on a given user. + IssueTimelineItemsItemTypeUnpinnedEvent IssueTimelineItemsItemType = "UNPINNED_EVENT" // Represents an 'unpinned' event on a given issue or pull request. + IssueTimelineItemsItemTypeUnsubscribedEvent IssueTimelineItemsItemType = "UNSUBSCRIBED_EVENT" // Represents an 'unsubscribed' event on a given `Subscribable`. +) + // LanguageOrderField represents properties by which language connections can be ordered. type LanguageOrderField string @@ -225,6 +300,34 @@ const ( OrganizationInvitationTypeEmail OrganizationInvitationType = "EMAIL" // The invitation was to an email address. ) +// OrganizationMemberRole represents the possible roles within an organization for its members. +type OrganizationMemberRole string + +// The possible roles within an organization for its members. +const ( + OrganizationMemberRoleMember OrganizationMemberRole = "MEMBER" // The user is a member of the organization. + OrganizationMemberRoleAdmin OrganizationMemberRole = "ADMIN" // The user is an administrator of the organization. +) + +// PinnableItemType represents represents items that can be pinned to a profile page or dashboard. +type PinnableItemType string + +// Represents items that can be pinned to a profile page or dashboard. +const ( + PinnableItemTypeRepository PinnableItemType = "REPOSITORY" // A repository. + PinnableItemTypeGist PinnableItemType = "GIST" // A gist. + PinnableItemTypeIssue PinnableItemType = "ISSUE" // An issue. +) + +// ProjectCardArchivedState represents the possible archived states of a project card. +type ProjectCardArchivedState string + +// The possible archived states of a project card. +const ( + ProjectCardArchivedStateArchived ProjectCardArchivedState = "ARCHIVED" // A project card that is archived. + ProjectCardArchivedStateNotArchived ProjectCardArchivedState = "NOT_ARCHIVED" // A project card that is not archived. +) + // ProjectCardState represents various content states of a ProjectCard. type ProjectCardState string @@ -235,6 +338,16 @@ const ( ProjectCardStateRedacted ProjectCardState = "REDACTED" // The card is redacted. ) +// ProjectColumnPurpose represents the semantic purpose of the column - todo, in progress, or done. +type ProjectColumnPurpose string + +// The semantic purpose of the column - todo, in progress, or done. +const ( + ProjectColumnPurposeTodo ProjectColumnPurpose = "TODO" // The column contains cards still to be worked on. + ProjectColumnPurposeInProgress ProjectColumnPurpose = "IN_PROGRESS" // The column contains cards which are currently being worked on. + ProjectColumnPurposeDone ProjectColumnPurpose = "DONE" // The column contains cards which are complete. +) + // ProjectOrderField represents properties by which project connections can be ordered. type ProjectOrderField string @@ -254,6 +367,15 @@ const ( ProjectStateClosed ProjectState = "CLOSED" // The project is closed. ) +// PullRequestOrderField represents properties by which pull_requests connections can be ordered. +type PullRequestOrderField string + +// Properties by which pull_requests connections can be ordered. +const ( + PullRequestOrderFieldCreatedAt PullRequestOrderField = "CREATED_AT" // Order pull_requests by creation time. + PullRequestOrderFieldUpdatedAt PullRequestOrderField = "UPDATED_AT" // Order pull_requests by update time. +) + // PullRequestPubSubTopic represents the possible PubSub channels for a pull request. type PullRequestPubSubTopic string @@ -263,6 +385,16 @@ const ( PullRequestPubSubTopicMarkasread PullRequestPubSubTopic = "MARKASREAD" // The channel ID for marking an pull request as read. PullRequestPubSubTopicHeadRef PullRequestPubSubTopic = "HEAD_REF" // The channel ID for observing head ref updates. PullRequestPubSubTopicTimeline PullRequestPubSubTopic = "TIMELINE" // The channel ID for updating items on the pull request timeline. + PullRequestPubSubTopicState PullRequestPubSubTopic = "STATE" // The channel ID for observing pull request state updates. +) + +// PullRequestReviewCommentState represents the possible states of a pull request review comment. +type PullRequestReviewCommentState string + +// The possible states of a pull request review comment. +const ( + PullRequestReviewCommentStatePending PullRequestReviewCommentState = "PENDING" // A comment that is part of a pending review. + PullRequestReviewCommentStateSubmitted PullRequestReviewCommentState = "SUBMITTED" // A comment that is part of a submitted review. ) // PullRequestReviewEvent represents the possible events to perform on a pull request review. @@ -298,6 +430,55 @@ const ( PullRequestStateMerged PullRequestState = "MERGED" // A pull request that has been closed by being merged. ) +// PullRequestTimelineItemsItemType represents the possible item types found in a timeline. +type PullRequestTimelineItemsItemType string + +// The possible item types found in a timeline. +const ( + PullRequestTimelineItemsItemTypePullRequestCommit PullRequestTimelineItemsItemType = "PULL_REQUEST_COMMIT" // Represents a Git commit part of a pull request. + PullRequestTimelineItemsItemTypePullRequestCommitCommentThread PullRequestTimelineItemsItemType = "PULL_REQUEST_COMMIT_COMMENT_THREAD" // Represents a commit comment thread part of a pull request. + PullRequestTimelineItemsItemTypePullRequestReview PullRequestTimelineItemsItemType = "PULL_REQUEST_REVIEW" // A review object for a given pull request. + PullRequestTimelineItemsItemTypePullRequestReviewThread PullRequestTimelineItemsItemType = "PULL_REQUEST_REVIEW_THREAD" // A threaded list of comments for a given pull request. + PullRequestTimelineItemsItemTypePullRequestRevisionMarker PullRequestTimelineItemsItemType = "PULL_REQUEST_REVISION_MARKER" // Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits. + PullRequestTimelineItemsItemTypeBaseRefChangedEvent PullRequestTimelineItemsItemType = "BASE_REF_CHANGED_EVENT" // Represents a 'base_ref_changed' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeBaseRefForcePushedEvent PullRequestTimelineItemsItemType = "BASE_REF_FORCE_PUSHED_EVENT" // Represents a 'base_ref_force_pushed' event on a given pull request. + PullRequestTimelineItemsItemTypeDeployedEvent PullRequestTimelineItemsItemType = "DEPLOYED_EVENT" // Represents a 'deployed' event on a given pull request. + PullRequestTimelineItemsItemTypeDeploymentEnvironmentChangedEvent PullRequestTimelineItemsItemType = "DEPLOYMENT_ENVIRONMENT_CHANGED_EVENT" // Represents a 'deployment_environment_changed' event on a given pull request. + PullRequestTimelineItemsItemTypeHeadRefDeletedEvent PullRequestTimelineItemsItemType = "HEAD_REF_DELETED_EVENT" // Represents a 'head_ref_deleted' event on a given pull request. + PullRequestTimelineItemsItemTypeHeadRefForcePushedEvent PullRequestTimelineItemsItemType = "HEAD_REF_FORCE_PUSHED_EVENT" // Represents a 'head_ref_force_pushed' event on a given pull request. + PullRequestTimelineItemsItemTypeHeadRefRestoredEvent PullRequestTimelineItemsItemType = "HEAD_REF_RESTORED_EVENT" // Represents a 'head_ref_restored' event on a given pull request. + PullRequestTimelineItemsItemTypeMergedEvent PullRequestTimelineItemsItemType = "MERGED_EVENT" // Represents a 'merged' event on a given pull request. + PullRequestTimelineItemsItemTypeReviewDismissedEvent PullRequestTimelineItemsItemType = "REVIEW_DISMISSED_EVENT" // Represents a 'review_dismissed' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeReviewRequestedEvent PullRequestTimelineItemsItemType = "REVIEW_REQUESTED_EVENT" // Represents an 'review_requested' event on a given pull request. + PullRequestTimelineItemsItemTypeReviewRequestRemovedEvent PullRequestTimelineItemsItemType = "REVIEW_REQUEST_REMOVED_EVENT" // Represents an 'review_request_removed' event on a given pull request. + PullRequestTimelineItemsItemTypeIssueComment PullRequestTimelineItemsItemType = "ISSUE_COMMENT" // Represents a comment on an Issue. + PullRequestTimelineItemsItemTypeCrossReferencedEvent PullRequestTimelineItemsItemType = "CROSS_REFERENCED_EVENT" // Represents a mention made by one issue or pull request to another. + PullRequestTimelineItemsItemTypeAddedToProjectEvent PullRequestTimelineItemsItemType = "ADDED_TO_PROJECT_EVENT" // Represents a 'added_to_project' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeAssignedEvent PullRequestTimelineItemsItemType = "ASSIGNED_EVENT" // Represents an 'assigned' event on any assignable object. + PullRequestTimelineItemsItemTypeClosedEvent PullRequestTimelineItemsItemType = "CLOSED_EVENT" // Represents a 'closed' event on any `Closable`. + PullRequestTimelineItemsItemTypeCommentDeletedEvent PullRequestTimelineItemsItemType = "COMMENT_DELETED_EVENT" // Represents a 'comment_deleted' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeConvertedNoteToIssueEvent PullRequestTimelineItemsItemType = "CONVERTED_NOTE_TO_ISSUE_EVENT" // Represents a 'converted_note_to_issue' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeDemilestonedEvent PullRequestTimelineItemsItemType = "DEMILESTONED_EVENT" // Represents a 'demilestoned' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeLabeledEvent PullRequestTimelineItemsItemType = "LABELED_EVENT" // Represents a 'labeled' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeLockedEvent PullRequestTimelineItemsItemType = "LOCKED_EVENT" // Represents a 'locked' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeMentionedEvent PullRequestTimelineItemsItemType = "MENTIONED_EVENT" // Represents a 'mentioned' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeMilestonedEvent PullRequestTimelineItemsItemType = "MILESTONED_EVENT" // Represents a 'milestoned' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeMovedColumnsInProjectEvent PullRequestTimelineItemsItemType = "MOVED_COLUMNS_IN_PROJECT_EVENT" // Represents a 'moved_columns_in_project' event on a given issue or pull request. + PullRequestTimelineItemsItemTypePinnedEvent PullRequestTimelineItemsItemType = "PINNED_EVENT" // Represents a 'pinned' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeReferencedEvent PullRequestTimelineItemsItemType = "REFERENCED_EVENT" // Represents a 'referenced' event on a given `ReferencedSubject`. + PullRequestTimelineItemsItemTypeRemovedFromProjectEvent PullRequestTimelineItemsItemType = "REMOVED_FROM_PROJECT_EVENT" // Represents a 'removed_from_project' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeRenamedTitleEvent PullRequestTimelineItemsItemType = "RENAMED_TITLE_EVENT" // Represents a 'renamed' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeReopenedEvent PullRequestTimelineItemsItemType = "REOPENED_EVENT" // Represents a 'reopened' event on any `Closable`. + PullRequestTimelineItemsItemTypeSubscribedEvent PullRequestTimelineItemsItemType = "SUBSCRIBED_EVENT" // Represents a 'subscribed' event on a given `Subscribable`. + PullRequestTimelineItemsItemTypeTransferredEvent PullRequestTimelineItemsItemType = "TRANSFERRED_EVENT" // Represents a 'transferred' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeUnassignedEvent PullRequestTimelineItemsItemType = "UNASSIGNED_EVENT" // Represents an 'unassigned' event on any assignable object. + PullRequestTimelineItemsItemTypeUnlabeledEvent PullRequestTimelineItemsItemType = "UNLABELED_EVENT" // Represents an 'unlabeled' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeUnlockedEvent PullRequestTimelineItemsItemType = "UNLOCKED_EVENT" // Represents an 'unlocked' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeUserBlockedEvent PullRequestTimelineItemsItemType = "USER_BLOCKED_EVENT" // Represents a 'user_blocked' event on a given user. + PullRequestTimelineItemsItemTypeUnpinnedEvent PullRequestTimelineItemsItemType = "UNPINNED_EVENT" // Represents an 'unpinned' event on a given issue or pull request. + PullRequestTimelineItemsItemTypeUnsubscribedEvent PullRequestTimelineItemsItemType = "UNSUBSCRIBED_EVENT" // Represents an 'unsubscribed' event on a given `Subscribable`. +) + // ReactionContent represents emojis that can be attached to Issues, Pull Requests and Comments. type ReactionContent string @@ -309,6 +490,8 @@ const ( ReactionContentHooray ReactionContent = "HOORAY" // Represents the 🎉 emoji. ReactionContentConfused ReactionContent = "CONFUSED" // Represents the 😕 emoji. ReactionContentHeart ReactionContent = "HEART" // Represents the ❤️ emoji. + ReactionContentRocket ReactionContent = "ROCKET" // Represents the 🚀 emoji. + ReactionContentEyes ReactionContent = "EYES" // Represents the 👀 emoji. ) // ReactionOrderField represents a list of fields that reactions can be ordered by. @@ -337,6 +520,18 @@ const ( ReleaseOrderFieldName ReleaseOrderField = "NAME" // Order releases alphabetically by name. ) +// ReportedContentClassifiers represents the reasons a piece of content can be reported or minimized. +type ReportedContentClassifiers string + +// The reasons a piece of content can be reported or minimized. +const ( + ReportedContentClassifiersSpam ReportedContentClassifiers = "SPAM" // A spammy piece of content. + ReportedContentClassifiersAbuse ReportedContentClassifiers = "ABUSE" // An abusive or harassing piece of content. + ReportedContentClassifiersOffTopic ReportedContentClassifiers = "OFF_TOPIC" // An irrelevant piece of content. + ReportedContentClassifiersOutdated ReportedContentClassifiers = "OUTDATED" // An outdated piece of content. + ReportedContentClassifiersResolved ReportedContentClassifiers = "RESOLVED" // The content has been resolved. +) + // RepositoryAffiliation represents the affiliation of a user to a repository. type RepositoryAffiliation string @@ -396,9 +591,11 @@ type RepositoryPermission string // The access level to a repository. const ( - RepositoryPermissionAdmin RepositoryPermission = "ADMIN" // Can read, clone, push, and add collaborators. - RepositoryPermissionWrite RepositoryPermission = "WRITE" // Can read, clone and push. - RepositoryPermissionRead RepositoryPermission = "READ" // Can read and clone. + RepositoryPermissionAdmin RepositoryPermission = "ADMIN" // Can read, clone, and push to this repository. Can also manage issues, pull requests, and repository settings, including adding collaborators. + RepositoryPermissionMaintain RepositoryPermission = "MAINTAIN" // Can read, clone, and push to this repository. They can also manage issues, pull requests, and some repository settings. + RepositoryPermissionWrite RepositoryPermission = "WRITE" // Can read, clone, and push to this repository. Can also manage issues and pull requests. + RepositoryPermissionTriage RepositoryPermission = "TRIAGE" // Can read and clone this repository. Can also manage issues and pull requests. + RepositoryPermissionRead RepositoryPermission = "READ" // Can read and clone this repository. Can also open and comment on issues and pull requests. ) // RepositoryPrivacy represents the privacy of a repository. @@ -420,6 +617,55 @@ const ( SearchTypeUser SearchType = "USER" // Returns results matching users and organizations on GitHub. ) +// SecurityAdvisoryEcosystem represents the possible ecosystems of a security vulnerability's package. +type SecurityAdvisoryEcosystem string + +// The possible ecosystems of a security vulnerability's package. +const ( + SecurityAdvisoryEcosystemRubygems SecurityAdvisoryEcosystem = "RUBYGEMS" // Ruby gems hosted at RubyGems.org. + SecurityAdvisoryEcosystemNpm SecurityAdvisoryEcosystem = "NPM" // JavaScript packages hosted at npmjs.com. + SecurityAdvisoryEcosystemPip SecurityAdvisoryEcosystem = "PIP" // Python packages hosted at PyPI.org. + SecurityAdvisoryEcosystemMaven SecurityAdvisoryEcosystem = "MAVEN" // Java artifacts hosted at the Maven central repository. + SecurityAdvisoryEcosystemNuget SecurityAdvisoryEcosystem = "NUGET" // .NET packages hosted at the NuGet Gallery. +) + +// SecurityAdvisoryIdentifierType represents identifier formats available for advisories. +type SecurityAdvisoryIdentifierType string + +// Identifier formats available for advisories. +const ( + SecurityAdvisoryIdentifierTypeCve SecurityAdvisoryIdentifierType = "CVE" // Common Vulnerabilities and Exposures Identifier. + SecurityAdvisoryIdentifierTypeGhsa SecurityAdvisoryIdentifierType = "GHSA" // GitHub Security Advisory ID. +) + +// SecurityAdvisoryOrderField represents properties by which security advisory connections can be ordered. +type SecurityAdvisoryOrderField string + +// Properties by which security advisory connections can be ordered. +const ( + SecurityAdvisoryOrderFieldPublishedAt SecurityAdvisoryOrderField = "PUBLISHED_AT" // Order advisories by publication time. + SecurityAdvisoryOrderFieldUpdatedAt SecurityAdvisoryOrderField = "UPDATED_AT" // Order advisories by update time. +) + +// SecurityAdvisorySeverity represents severity of the vulnerability. +type SecurityAdvisorySeverity string + +// Severity of the vulnerability. +const ( + SecurityAdvisorySeverityLow SecurityAdvisorySeverity = "LOW" // Low. + SecurityAdvisorySeverityModerate SecurityAdvisorySeverity = "MODERATE" // Moderate. + SecurityAdvisorySeverityHigh SecurityAdvisorySeverity = "HIGH" // High. + SecurityAdvisorySeverityCritical SecurityAdvisorySeverity = "CRITICAL" // Critical. +) + +// SecurityVulnerabilityOrderField represents properties by which security vulnerability connections can be ordered. +type SecurityVulnerabilityOrderField string + +// Properties by which security vulnerability connections can be ordered. +const ( + SecurityVulnerabilityOrderFieldUpdatedAt SecurityVulnerabilityOrderField = "UPDATED_AT" // Order vulnerability by update time. +) + // StarOrderField represents properties by which star connections can be ordered. type StarOrderField string @@ -445,7 +691,7 @@ type SubscriptionState string // The possible states of a subscription. const ( - SubscriptionStateUnsubscribed SubscriptionState = "UNSUBSCRIBED" // The User is only notified when particpating or @mentioned. + SubscriptionStateUnsubscribed SubscriptionState = "UNSUBSCRIBED" // The User is only notified when participating or @mentioned. SubscriptionStateSubscribed SubscriptionState = "SUBSCRIBED" // The User is notified of all conversations. SubscriptionStateIgnored SubscriptionState = "IGNORED" // The User is never notified. ) @@ -527,3 +773,23 @@ const ( TopicSuggestionDeclineReasonPersonalPreference TopicSuggestionDeclineReason = "PERSONAL_PREFERENCE" // The viewer does not like the suggested topic. TopicSuggestionDeclineReasonTooGeneral TopicSuggestionDeclineReason = "TOO_GENERAL" // The suggested topic is too general for the repository. ) + +// UserBlockDuration represents the possible durations that a user can be blocked for. +type UserBlockDuration string + +// The possible durations that a user can be blocked for. +const ( + UserBlockDurationOneDay UserBlockDuration = "ONE_DAY" // The user was blocked for 1 day. + UserBlockDurationThreeDays UserBlockDuration = "THREE_DAYS" // The user was blocked for 3 days. + UserBlockDurationOneWeek UserBlockDuration = "ONE_WEEK" // The user was blocked for 7 days. + UserBlockDurationOneMonth UserBlockDuration = "ONE_MONTH" // The user was blocked for 30 days. + UserBlockDurationPermanent UserBlockDuration = "PERMANENT" // The user was blocked permanently. +) + +// UserStatusOrderField represents properties by which user status connections can be ordered. +type UserStatusOrderField string + +// Properties by which user status connections can be ordered. +const ( + UserStatusOrderFieldUpdatedAt UserStatusOrderField = "UPDATED_AT" // Order user statuses by when they were updated. +) diff --git a/vendor/github.com/shurcooL/githubv4/gen.go b/vendor/github.com/shurcooL/githubv4/gen.go index 43ad1e35..628ce57b 100644 --- a/vendor/github.com/shurcooL/githubv4/gen.go +++ b/vendor/github.com/shurcooL/githubv4/gen.go @@ -86,12 +86,12 @@ package githubv4 {{- define "enum" -}} -// {{.name}} {{.description | endSentence}} +// {{.name}} {{.description | clean | endSentence}} type {{.name}} string -// {{.description | fullSentence}} +// {{.description | clean | fullSentence}} const ({{range .enumValues}} - {{$.name}}{{.name | enumIdentifier}} {{$.name}} = {{.name | quote}} // {{.description | fullSentence}}{{end}} + {{$.name}}{{.name | enumIdentifier}} {{$.name}} = {{.name | quote}} // {{.description | clean | fullSentence}}{{end}} ) {{- end -}} `), @@ -110,12 +110,12 @@ type Input interface{} {{- define "inputObject" -}} -// {{.name}} {{.description | endSentence}} +// {{.name}} {{.description | clean | endSentence}} type {{.name}} struct {{"{"}}{{range .inputFields}}{{if eq .type.kind "NON_NULL"}} - // {{.description | fullSentence}} (Required.) + // {{.description | clean | fullSentence}} (Required.) {{.name | identifier}} {{.type | type}} ` + "`" + `json:"{{.name}}"` + "`" + `{{end}}{{end}} {{range .inputFields}}{{if ne .type.kind "NON_NULL"}} - // {{.description | fullSentence}} (Optional.) + // {{.description | clean | fullSentence}} (Optional.) {{.name | identifier}} {{.type | type}} ` + "`" + `json:"{{.name}},omitempty"` + "`" + `{{end}}{{end}} } {{- end -}} @@ -167,6 +167,7 @@ func t(text string) *template.Template { "identifier": func(name string) string { return ident.ParseLowerCamelCase(name).ToMixedCaps() }, "enumIdentifier": func(name string) string { return ident.ParseScreamingSnakeCase(name).ToMixedCaps() }, "type": typeString, + "clean": func(s string) string { return strings.Join(strings.Fields(s), " ") }, "endSentence": func(s string) string { s = strings.ToLower(s[0:1]) + s[1:] switch { diff --git a/vendor/github.com/shurcooL/githubv4/input.go b/vendor/github.com/shurcooL/githubv4/input.go index ce1976f8..da00e3b3 100644 --- a/vendor/github.com/shurcooL/githubv4/input.go +++ b/vendor/github.com/shurcooL/githubv4/input.go @@ -4,7 +4,7 @@ package githubv4 // Input represents one of the Input structs: // -// AcceptTopicSuggestionInput, AddCommentInput, AddProjectCardInput, AddProjectColumnInput, AddPullRequestReviewCommentInput, AddPullRequestReviewInput, AddReactionInput, AddStarInput, CommitAuthor, CreateProjectInput, DeclineTopicSuggestionInput, DeleteProjectCardInput, DeleteProjectColumnInput, DeleteProjectInput, DeletePullRequestReviewInput, DismissPullRequestReviewInput, DraftPullRequestReviewComment, GistOrder, IssueOrder, LanguageOrder, LockLockableInput, MilestoneOrder, MoveProjectCardInput, MoveProjectColumnInput, ProjectOrder, ReactionOrder, RefOrder, ReleaseOrder, RemoveOutsideCollaboratorInput, RemoveReactionInput, RemoveStarInput, RepositoryOrder, RequestReviewsInput, StarOrder, SubmitPullRequestReviewInput, TeamMemberOrder, TeamOrder, TeamRepositoryOrder, UpdateProjectCardInput, UpdateProjectColumnInput, UpdateProjectInput, UpdatePullRequestReviewCommentInput, UpdatePullRequestReviewInput, UpdateSubscriptionInput, UpdateTopicsInput. +// AcceptTopicSuggestionInput, AddAssigneesToAssignableInput, AddCommentInput, AddLabelsToLabelableInput, AddProjectCardInput, AddProjectColumnInput, AddPullRequestReviewCommentInput, AddPullRequestReviewInput, AddReactionInput, AddStarInput, ChangeUserStatusInput, ClearLabelsFromLabelableInput, CloneProjectInput, CloseIssueInput, ClosePullRequestInput, CommitAuthor, CommitContributionOrder, ContributionOrder, ConvertProjectCardNoteToIssueInput, CreateBranchProtectionRuleInput, CreateContentAttachmentInput, CreateIssueInput, CreateProjectInput, CreatePullRequestInput, DeclineTopicSuggestionInput, DeleteBranchProtectionRuleInput, DeleteIssueCommentInput, DeleteIssueInput, DeleteProjectCardInput, DeleteProjectColumnInput, DeleteProjectInput, DeletePullRequestReviewCommentInput, DeletePullRequestReviewInput, DeploymentOrder, DismissPullRequestReviewInput, DraftPullRequestReviewComment, GistOrder, ImportProjectInput, IssueFilters, IssueOrder, LanguageOrder, LockLockableInput, MergePullRequestInput, MilestoneOrder, MinimizeCommentInput, MoveProjectCardInput, MoveProjectColumnInput, PinIssueInput, ProjectCardImport, ProjectColumnImport, ProjectOrder, PullRequestOrder, ReactionOrder, RefOrder, ReleaseOrder, RemoveAssigneesFromAssignableInput, RemoveLabelsFromLabelableInput, RemoveOutsideCollaboratorInput, RemoveReactionInput, RemoveStarInput, ReopenIssueInput, ReopenPullRequestInput, RepositoryOrder, RequestReviewsInput, ResolveReviewThreadInput, SecurityAdvisoryIdentifierFilter, SecurityAdvisoryOrder, SecurityVulnerabilityOrder, StarOrder, SubmitPullRequestReviewInput, TeamMemberOrder, TeamOrder, TeamRepositoryOrder, UnlockLockableInput, UnmarkIssueAsDuplicateInput, UnminimizeCommentInput, UnpinIssueInput, UnresolveReviewThreadInput, UpdateBranchProtectionRuleInput, UpdateIssueCommentInput, UpdateIssueInput, UpdateProjectCardInput, UpdateProjectColumnInput, UpdateProjectInput, UpdatePullRequestInput, UpdatePullRequestReviewCommentInput, UpdatePullRequestReviewInput, UpdateSubscriptionInput, UpdateTopicsInput, UserStatusOrder. type Input interface{} // AcceptTopicSuggestionInput is an autogenerated input type of AcceptTopicSuggestion. @@ -18,6 +18,17 @@ type AcceptTopicSuggestionInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// AddAssigneesToAssignableInput is an autogenerated input type of AddAssigneesToAssignable. +type AddAssigneesToAssignableInput struct { + // The id of the assignable object to add assignees to. (Required.) + AssignableID ID `json:"assignableId"` + // The id of users to add as assignees. (Required.) + AssigneeIDs []ID `json:"assigneeIds"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // AddCommentInput is an autogenerated input type of AddComment. type AddCommentInput struct { // The Node ID of the subject to modify. (Required.) @@ -29,6 +40,17 @@ type AddCommentInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// AddLabelsToLabelableInput is an autogenerated input type of AddLabelsToLabelable. +type AddLabelsToLabelableInput struct { + // The id of the labelable object to add labels to. (Required.) + LabelableID ID `json:"labelableId"` + // The ids of the labels to add. (Required.) + LabelIDs []ID `json:"labelIds"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // AddProjectCardInput is an autogenerated input type of AddProjectCard. type AddProjectCardInput struct { // The Node ID of the ProjectColumn. (Required.) @@ -109,6 +131,69 @@ type AddStarInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// ChangeUserStatusInput is an autogenerated input type of ChangeUserStatus. +type ChangeUserStatusInput struct { + + // The emoji to represent your status. Can either be a native Unicode emoji or an emoji name with colons, e.g., :grinning:. (Optional.) + Emoji *String `json:"emoji,omitempty"` + // A short description of your current status. (Optional.) + Message *String `json:"message,omitempty"` + // The ID of the organization whose members will be allowed to see the status. If omitted, the status will be publicly visible. (Optional.) + OrganizationID *ID `json:"organizationId,omitempty"` + // Whether this status should indicate you are not fully available on GitHub, e.g., you are away. (Optional.) + LimitedAvailability *Boolean `json:"limitedAvailability,omitempty"` + // If set, the user status will not be shown after this date. (Optional.) + ExpiresAt *DateTime `json:"expiresAt,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// ClearLabelsFromLabelableInput is an autogenerated input type of ClearLabelsFromLabelable. +type ClearLabelsFromLabelableInput struct { + // The id of the labelable object to clear the labels from. (Required.) + LabelableID ID `json:"labelableId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// CloneProjectInput is an autogenerated input type of CloneProject. +type CloneProjectInput struct { + // The owner ID to create the project under. (Required.) + TargetOwnerID ID `json:"targetOwnerId"` + // The source project to clone. (Required.) + SourceID ID `json:"sourceId"` + // Whether or not to clone the source project's workflows. (Required.) + IncludeWorkflows Boolean `json:"includeWorkflows"` + // The name of the project. (Required.) + Name String `json:"name"` + + // The description of the project. (Optional.) + Body *String `json:"body,omitempty"` + // The visibility of the project, defaults to false (private). (Optional.) + Public *Boolean `json:"public,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// CloseIssueInput is an autogenerated input type of CloseIssue. +type CloseIssueInput struct { + // ID of the issue to be closed. (Required.) + IssueID ID `json:"issueId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// ClosePullRequestInput is an autogenerated input type of ClosePullRequest. +type ClosePullRequestInput struct { + // ID of the pull request to be closed. (Required.) + PullRequestID ID `json:"pullRequestId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // CommitAuthor specifies an author for filtering Git commits. type CommitAuthor struct { @@ -118,6 +203,108 @@ type CommitAuthor struct { Emails *[]String `json:"emails,omitempty"` } +// CommitContributionOrder represents ordering options for commit contribution connections. +type CommitContributionOrder struct { + // The field by which to order commit contributions. (Required.) + Field CommitContributionOrderField `json:"field"` + // The ordering direction. (Required.) + Direction OrderDirection `json:"direction"` +} + +// ContributionOrder represents ordering options for contribution connections. +type ContributionOrder struct { + // The field by which to order contributions. (Required.) + Field ContributionOrderField `json:"field"` + // The ordering direction. (Required.) + Direction OrderDirection `json:"direction"` +} + +// ConvertProjectCardNoteToIssueInput is an autogenerated input type of ConvertProjectCardNoteToIssue. +type ConvertProjectCardNoteToIssueInput struct { + // The ProjectCard ID to convert. (Required.) + ProjectCardID ID `json:"projectCardId"` + // The ID of the repository to create the issue in. (Required.) + RepositoryID ID `json:"repositoryId"` + + // The title of the newly created issue. Defaults to the card's note text. (Optional.) + Title *String `json:"title,omitempty"` + // The body of the newly created issue. (Optional.) + Body *String `json:"body,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// CreateBranchProtectionRuleInput is an autogenerated input type of CreateBranchProtectionRule. +type CreateBranchProtectionRuleInput struct { + // The global relay id of the repository in which a new branch protection rule should be created in. (Required.) + RepositoryID ID `json:"repositoryId"` + // The glob-like pattern used to determine matching branches. (Required.) + Pattern String `json:"pattern"` + + // Are approving reviews required to update matching branches. (Optional.) + RequiresApprovingReviews *Boolean `json:"requiresApprovingReviews,omitempty"` + // Number of approving reviews required to update matching branches. (Optional.) + RequiredApprovingReviewCount *Int `json:"requiredApprovingReviewCount,omitempty"` + // Are commits required to be signed. (Optional.) + RequiresCommitSignatures *Boolean `json:"requiresCommitSignatures,omitempty"` + // Can admins overwrite branch protection. (Optional.) + IsAdminEnforced *Boolean `json:"isAdminEnforced,omitempty"` + // Are status checks required to update matching branches. (Optional.) + RequiresStatusChecks *Boolean `json:"requiresStatusChecks,omitempty"` + // Are branches required to be up to date before merging. (Optional.) + RequiresStrictStatusChecks *Boolean `json:"requiresStrictStatusChecks,omitempty"` + // Are reviews from code owners required to update matching branches. (Optional.) + RequiresCodeOwnerReviews *Boolean `json:"requiresCodeOwnerReviews,omitempty"` + // Will new commits pushed to matching branches dismiss pull request review approvals. (Optional.) + DismissesStaleReviews *Boolean `json:"dismissesStaleReviews,omitempty"` + // Is dismissal of pull request reviews restricted. (Optional.) + RestrictsReviewDismissals *Boolean `json:"restrictsReviewDismissals,omitempty"` + // A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches. (Optional.) + ReviewDismissalActorIDs *[]ID `json:"reviewDismissalActorIds,omitempty"` + // Is pushing to matching branches restricted. (Optional.) + RestrictsPushes *Boolean `json:"restrictsPushes,omitempty"` + // A list of User or Team IDs allowed to push to matching branches. (Optional.) + PushActorIDs *[]ID `json:"pushActorIds,omitempty"` + // List of required status check contexts that must pass for commits to be accepted to matching branches. (Optional.) + RequiredStatusCheckContexts *[]String `json:"requiredStatusCheckContexts,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// CreateContentAttachmentInput is an autogenerated input type of CreateContentAttachment. +type CreateContentAttachmentInput struct { + // The node ID of the content_reference. (Required.) + ContentReferenceID ID `json:"contentReferenceId"` + // The title of the content attachment. (Required.) + Title String `json:"title"` + // The body of the content attachment, which may contain markdown. (Required.) + Body String `json:"body"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// CreateIssueInput is an autogenerated input type of CreateIssue. +type CreateIssueInput struct { + // The Node ID of the repository. (Required.) + RepositoryID ID `json:"repositoryId"` + // The title for the issue. (Required.) + Title String `json:"title"` + + // The body for the issue description. (Optional.) + Body *String `json:"body,omitempty"` + // The Node ID for the user assignee for this issue. (Optional.) + AssigneeIDs *[]ID `json:"assigneeIds,omitempty"` + // The Node ID of the milestone for this issue. (Optional.) + MilestoneID *ID `json:"milestoneId,omitempty"` + // An array of Node IDs of labels for this issue. (Optional.) + LabelIDs *[]ID `json:"labelIds,omitempty"` + // An array of Node IDs for projects associated with this issue. (Optional.) + ProjectIDs *[]ID `json:"projectIds,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // CreateProjectInput is an autogenerated input type of CreateProject. type CreateProjectInput struct { // The owner ID to create the project under. (Required.) @@ -131,6 +318,25 @@ type CreateProjectInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// CreatePullRequestInput is an autogenerated input type of CreatePullRequest. +type CreatePullRequestInput struct { + // The Node ID of the repository. (Required.) + RepositoryID ID `json:"repositoryId"` + // The name of the branch you want your changes pulled into. This should be an existing branch on the current repository. You cannot update the base branch on a pull request to point to another repository. (Required.) + BaseRefName String `json:"baseRefName"` + // The name of the branch where your changes are implemented. For cross-repository pull requests in the same network, namespace `head_ref_name` with a user like this: `username:branch`. (Required.) + HeadRefName String `json:"headRefName"` + // The title of the pull request. (Required.) + Title String `json:"title"` + + // The contents of the pull request. (Optional.) + Body *String `json:"body,omitempty"` + // Indicates whether maintainers can modify the pull request. (Optional.) + MaintainerCanModify *Boolean `json:"maintainerCanModify,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // DeclineTopicSuggestionInput is an autogenerated input type of DeclineTopicSuggestion. type DeclineTopicSuggestionInput struct { // The Node ID of the repository. (Required.) @@ -144,6 +350,33 @@ type DeclineTopicSuggestionInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// DeleteBranchProtectionRuleInput is an autogenerated input type of DeleteBranchProtectionRule. +type DeleteBranchProtectionRuleInput struct { + // The global relay id of the branch protection rule to be deleted. (Required.) + BranchProtectionRuleID ID `json:"branchProtectionRuleId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// DeleteIssueCommentInput is an autogenerated input type of DeleteIssueComment. +type DeleteIssueCommentInput struct { + // The ID of the comment to delete. (Required.) + ID ID `json:"id"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// DeleteIssueInput is an autogenerated input type of DeleteIssue. +type DeleteIssueInput struct { + // The ID of the issue to delete. (Required.) + IssueID ID `json:"issueId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // DeleteProjectCardInput is an autogenerated input type of DeleteProjectCard. type DeleteProjectCardInput struct { // The id of the card to delete. (Required.) @@ -171,6 +404,15 @@ type DeleteProjectInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// DeletePullRequestReviewCommentInput is an autogenerated input type of DeletePullRequestReviewComment. +type DeletePullRequestReviewCommentInput struct { + // The ID of the comment to delete. (Required.) + ID ID `json:"id"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // DeletePullRequestReviewInput is an autogenerated input type of DeletePullRequestReview. type DeletePullRequestReviewInput struct { // The Node ID of the pull request review to delete. (Required.) @@ -180,6 +422,14 @@ type DeletePullRequestReviewInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// DeploymentOrder represents ordering options for deployment connections. +type DeploymentOrder struct { + // The field to order deployments by. (Required.) + Field DeploymentOrderField `json:"field"` + // The ordering direction. (Required.) + Direction OrderDirection `json:"direction"` +} + // DismissPullRequestReviewInput is an autogenerated input type of DismissPullRequestReview. type DismissPullRequestReviewInput struct { // The Node ID of the pull request review to modify. (Required.) @@ -209,6 +459,44 @@ type GistOrder struct { Direction OrderDirection `json:"direction"` } +// ImportProjectInput is an autogenerated input type of ImportProject. +type ImportProjectInput struct { + // The name of the Organization or User to create the Project under. (Required.) + OwnerName String `json:"ownerName"` + // The name of Project. (Required.) + Name String `json:"name"` + // A list of columns containing issues and pull requests. (Required.) + ColumnImports []ProjectColumnImport `json:"columnImports"` + + // The description of Project. (Optional.) + Body *String `json:"body,omitempty"` + // Whether the Project is public or not. (Optional.) + Public *Boolean `json:"public,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// IssueFilters represents ways in which to filter lists of issues. +type IssueFilters struct { + + // List issues assigned to given name. Pass in `null` for issues with no assigned user, and `*` for issues assigned to any user. (Optional.) + Assignee *String `json:"assignee,omitempty"` + // List issues created by given name. (Optional.) + CreatedBy *String `json:"createdBy,omitempty"` + // List issues where the list of label names exist on the issue. (Optional.) + Labels *[]String `json:"labels,omitempty"` + // List issues where the given name is mentioned in the issue. (Optional.) + Mentioned *String `json:"mentioned,omitempty"` + // List issues by given milestone argument. If an string representation of an integer is passed, it should refer to a milestone by its number field. Pass in `null` for issues with no milestone, and `*` for issues that are assigned to any milestone. (Optional.) + Milestone *String `json:"milestone,omitempty"` + // List issues that have been updated at or after the given date. (Optional.) + Since *DateTime `json:"since,omitempty"` + // List issues filtered by the list of states given. (Optional.) + States *[]IssueState `json:"states,omitempty"` + // List issues subscribed to by viewer. (Optional.) + ViewerSubscribed *Boolean `json:"viewerSubscribed,omitempty"` +} + // IssueOrder represents ways in which lists of issues can be ordered upon return. type IssueOrder struct { // The field in which to order issues by. (Required.) @@ -236,6 +524,21 @@ type LockLockableInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// MergePullRequestInput is an autogenerated input type of MergePullRequest. +type MergePullRequestInput struct { + // ID of the pull request to be merged. (Required.) + PullRequestID ID `json:"pullRequestId"` + + // Commit headline to use for the merge commit; if omitted, a default message will be used. (Optional.) + CommitHeadline *String `json:"commitHeadline,omitempty"` + // Commit body to use for the merge commit; if omitted, a default message will be used. (Optional.) + CommitBody *String `json:"commitBody,omitempty"` + // OID that the pull request head ref must match to allow merge; if omitted, no check is performed. (Optional.) + ExpectedHeadOid *GitObjectID `json:"expectedHeadOid,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // MilestoneOrder represents ordering options for milestone connections. type MilestoneOrder struct { // The field to order milestones by. (Required.) @@ -244,6 +547,17 @@ type MilestoneOrder struct { Direction OrderDirection `json:"direction"` } +// MinimizeCommentInput is an autogenerated input type of MinimizeComment. +type MinimizeCommentInput struct { + // The Node ID of the subject to modify. (Required.) + SubjectID ID `json:"subjectId"` + // The classification of comment. (Required.) + Classifier ReportedContentClassifiers `json:"classifier"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // MoveProjectCardInput is an autogenerated input type of MoveProjectCard. type MoveProjectCardInput struct { // The id of the card to move. (Required.) @@ -268,6 +582,34 @@ type MoveProjectColumnInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// PinIssueInput is an autogenerated input type of PinIssue. +type PinIssueInput struct { + // The ID of the issue to be pinned. (Required.) + IssueID ID `json:"issueId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// ProjectCardImport represents an issue or PR and its owning repository to be used in a project card. +type ProjectCardImport struct { + // Repository name with owner (owner/repository). (Required.) + Repository String `json:"repository"` + // The issue or pull request number. (Required.) + Number Int `json:"number"` +} + +// ProjectColumnImport represents a project column and a list of its issues and PRs. +type ProjectColumnImport struct { + // The name of the column. (Required.) + ColumnName String `json:"columnName"` + // The position of the column, starting from 0. (Required.) + Position Int `json:"position"` + + // A list of issues and pull requests in the column. (Optional.) + Issues *[]ProjectCardImport `json:"issues,omitempty"` +} + // ProjectOrder represents ways in which lists of projects can be ordered upon return. type ProjectOrder struct { // The field in which to order projects by. (Required.) @@ -276,6 +618,14 @@ type ProjectOrder struct { Direction OrderDirection `json:"direction"` } +// PullRequestOrder represents ways in which lists of issues can be ordered upon return. +type PullRequestOrder struct { + // The field in which to order pull requests by. (Required.) + Field PullRequestOrderField `json:"field"` + // The direction in which to order pull requests by the specified field. (Required.) + Direction OrderDirection `json:"direction"` +} + // ReactionOrder represents ways in which lists of reactions can be ordered upon return. type ReactionOrder struct { // The field in which to order reactions by. (Required.) @@ -300,6 +650,28 @@ type ReleaseOrder struct { Direction OrderDirection `json:"direction"` } +// RemoveAssigneesFromAssignableInput is an autogenerated input type of RemoveAssigneesFromAssignable. +type RemoveAssigneesFromAssignableInput struct { + // The id of the assignable object to remove assignees from. (Required.) + AssignableID ID `json:"assignableId"` + // The id of users to remove as assignees. (Required.) + AssigneeIDs []ID `json:"assigneeIds"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// RemoveLabelsFromLabelableInput is an autogenerated input type of RemoveLabelsFromLabelable. +type RemoveLabelsFromLabelableInput struct { + // The id of the Labelable to remove labels from. (Required.) + LabelableID ID `json:"labelableId"` + // The ids of labels to remove. (Required.) + LabelIDs []ID `json:"labelIds"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // RemoveOutsideCollaboratorInput is an autogenerated input type of RemoveOutsideCollaborator. type RemoveOutsideCollaboratorInput struct { // The ID of the outside collaborator to remove. (Required.) @@ -331,6 +703,24 @@ type RemoveStarInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// ReopenIssueInput is an autogenerated input type of ReopenIssue. +type ReopenIssueInput struct { + // ID of the issue to be opened. (Required.) + IssueID ID `json:"issueId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// ReopenPullRequestInput is an autogenerated input type of ReopenPullRequest. +type ReopenPullRequestInput struct { + // ID of the pull request to be reopened. (Required.) + PullRequestID ID `json:"pullRequestId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // RepositoryOrder represents ordering options for repository connections. type RepositoryOrder struct { // The field to order repositories by. (Required.) @@ -354,6 +744,39 @@ type RequestReviewsInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// ResolveReviewThreadInput is an autogenerated input type of ResolveReviewThread. +type ResolveReviewThreadInput struct { + // The ID of the thread to resolve. (Required.) + ThreadID ID `json:"threadId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// SecurityAdvisoryIdentifierFilter represents an advisory identifier to filter results on. +type SecurityAdvisoryIdentifierFilter struct { + // The identifier type. (Required.) + Type SecurityAdvisoryIdentifierType `json:"type"` + // The identifier string. Supports exact or partial matching. (Required.) + Value String `json:"value"` +} + +// SecurityAdvisoryOrder represents ordering options for security advisory connections. +type SecurityAdvisoryOrder struct { + // The field to order security advisories by. (Required.) + Field SecurityAdvisoryOrderField `json:"field"` + // The ordering direction. (Required.) + Direction OrderDirection `json:"direction"` +} + +// SecurityVulnerabilityOrder represents ordering options for security vulnerability connections. +type SecurityVulnerabilityOrder struct { + // The field to order security vulnerabilities by. (Required.) + Field SecurityVulnerabilityOrderField `json:"field"` + // The ordering direction. (Required.) + Direction OrderDirection `json:"direction"` +} + // StarOrder represents ways in which star connections can be ordered. type StarOrder struct { // The field in which to order nodes by. (Required.) @@ -399,13 +822,133 @@ type TeamRepositoryOrder struct { Direction OrderDirection `json:"direction"` } +// UnlockLockableInput is an autogenerated input type of UnlockLockable. +type UnlockLockableInput struct { + // ID of the issue or pull request to be unlocked. (Required.) + LockableID ID `json:"lockableId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// UnmarkIssueAsDuplicateInput is an autogenerated input type of UnmarkIssueAsDuplicate. +type UnmarkIssueAsDuplicateInput struct { + // ID of the issue or pull request currently marked as a duplicate. (Required.) + DuplicateID ID `json:"duplicateId"` + // ID of the issue or pull request currently considered canonical/authoritative/original. (Required.) + CanonicalID ID `json:"canonicalId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// UnminimizeCommentInput is an autogenerated input type of UnminimizeComment. +type UnminimizeCommentInput struct { + // The Node ID of the subject to modify. (Required.) + SubjectID ID `json:"subjectId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// UnpinIssueInput is an autogenerated input type of UnpinIssue. +type UnpinIssueInput struct { + // The ID of the issue to be unpinned. (Required.) + IssueID ID `json:"issueId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// UnresolveReviewThreadInput is an autogenerated input type of UnresolveReviewThread. +type UnresolveReviewThreadInput struct { + // The ID of the thread to unresolve. (Required.) + ThreadID ID `json:"threadId"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// UpdateBranchProtectionRuleInput is an autogenerated input type of UpdateBranchProtectionRule. +type UpdateBranchProtectionRuleInput struct { + // The global relay id of the branch protection rule to be updated. (Required.) + BranchProtectionRuleID ID `json:"branchProtectionRuleId"` + + // The glob-like pattern used to determine matching branches. (Optional.) + Pattern *String `json:"pattern,omitempty"` + // Are approving reviews required to update matching branches. (Optional.) + RequiresApprovingReviews *Boolean `json:"requiresApprovingReviews,omitempty"` + // Number of approving reviews required to update matching branches. (Optional.) + RequiredApprovingReviewCount *Int `json:"requiredApprovingReviewCount,omitempty"` + // Are commits required to be signed. (Optional.) + RequiresCommitSignatures *Boolean `json:"requiresCommitSignatures,omitempty"` + // Can admins overwrite branch protection. (Optional.) + IsAdminEnforced *Boolean `json:"isAdminEnforced,omitempty"` + // Are status checks required to update matching branches. (Optional.) + RequiresStatusChecks *Boolean `json:"requiresStatusChecks,omitempty"` + // Are branches required to be up to date before merging. (Optional.) + RequiresStrictStatusChecks *Boolean `json:"requiresStrictStatusChecks,omitempty"` + // Are reviews from code owners required to update matching branches. (Optional.) + RequiresCodeOwnerReviews *Boolean `json:"requiresCodeOwnerReviews,omitempty"` + // Will new commits pushed to matching branches dismiss pull request review approvals. (Optional.) + DismissesStaleReviews *Boolean `json:"dismissesStaleReviews,omitempty"` + // Is dismissal of pull request reviews restricted. (Optional.) + RestrictsReviewDismissals *Boolean `json:"restrictsReviewDismissals,omitempty"` + // A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches. (Optional.) + ReviewDismissalActorIDs *[]ID `json:"reviewDismissalActorIds,omitempty"` + // Is pushing to matching branches restricted. (Optional.) + RestrictsPushes *Boolean `json:"restrictsPushes,omitempty"` + // A list of User or Team IDs allowed to push to matching branches. (Optional.) + PushActorIDs *[]ID `json:"pushActorIds,omitempty"` + // List of required status check contexts that must pass for commits to be accepted to matching branches. (Optional.) + RequiredStatusCheckContexts *[]String `json:"requiredStatusCheckContexts,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// UpdateIssueCommentInput is an autogenerated input type of UpdateIssueComment. +type UpdateIssueCommentInput struct { + // The ID of the IssueComment to modify. (Required.) + ID ID `json:"id"` + // The updated text of the comment. (Required.) + Body String `json:"body"` + + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + +// UpdateIssueInput is an autogenerated input type of UpdateIssue. +type UpdateIssueInput struct { + // The ID of the Issue to modify. (Required.) + ID ID `json:"id"` + + // The title for the issue. (Optional.) + Title *String `json:"title,omitempty"` + // The body for the issue description. (Optional.) + Body *String `json:"body,omitempty"` + // An array of Node IDs of users for this issue. (Optional.) + AssigneeIDs *[]ID `json:"assigneeIds,omitempty"` + // The Node ID of the milestone for this issue. (Optional.) + MilestoneID *ID `json:"milestoneId,omitempty"` + // An array of Node IDs of labels for this issue. (Optional.) + LabelIDs *[]ID `json:"labelIds,omitempty"` + // The desired issue state. (Optional.) + State *IssueState `json:"state,omitempty"` + // An array of Node IDs for projects associated with this issue. (Optional.) + ProjectIDs *[]ID `json:"projectIds,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // UpdateProjectCardInput is an autogenerated input type of UpdateProjectCard. type UpdateProjectCardInput struct { // The ProjectCard ID to update. (Required.) ProjectCardID ID `json:"projectCardId"` - // The note of ProjectCard. (Required.) - Note String `json:"note"` + // Whether or not the ProjectCard should be archived. (Optional.) + IsArchived *Boolean `json:"isArchived,omitempty"` + // The note of ProjectCard. (Optional.) + Note *String `json:"note,omitempty"` // A unique identifier for the client performing the mutation. (Optional.) ClientMutationID *String `json:"clientMutationId,omitempty"` } @@ -438,6 +981,23 @@ type UpdateProjectInput struct { ClientMutationID *String `json:"clientMutationId,omitempty"` } +// UpdatePullRequestInput is an autogenerated input type of UpdatePullRequest. +type UpdatePullRequestInput struct { + // The Node ID of the pull request. (Required.) + PullRequestID ID `json:"pullRequestId"` + + // The name of the branch you want your changes pulled into. This should be an existing branch on the current repository. (Optional.) + BaseRefName *String `json:"baseRefName,omitempty"` + // The title of the pull request. (Optional.) + Title *String `json:"title,omitempty"` + // The contents of the pull request. (Optional.) + Body *String `json:"body,omitempty"` + // Indicates whether maintainers can modify the pull request. (Optional.) + MaintainerCanModify *Boolean `json:"maintainerCanModify,omitempty"` + // A unique identifier for the client performing the mutation. (Optional.) + ClientMutationID *String `json:"clientMutationId,omitempty"` +} + // UpdatePullRequestReviewCommentInput is an autogenerated input type of UpdatePullRequestReviewComment. type UpdatePullRequestReviewCommentInput struct { // The Node ID of the comment to modify. (Required.) @@ -481,3 +1041,11 @@ type UpdateTopicsInput struct { // A unique identifier for the client performing the mutation. (Optional.) ClientMutationID *String `json:"clientMutationId,omitempty"` } + +// UserStatusOrder represents ordering options for user status connections. +type UserStatusOrder struct { + // The field to order user statuses by. (Required.) + Field UserStatusOrderField `json:"field"` + // The ordering direction. (Required.) + Direction OrderDirection `json:"direction"` +}