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"`
+}