Merge pull request #166 from MichaelMure/github-exporter

[Bridge] GitHub exporter
This commit is contained in:
Michael Muré 2019-07-06 16:32:57 +02:00 committed by GitHub
commit f4d4b2f413
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 2469 additions and 66 deletions

4
Gopkg.lock generated
View File

@ -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"

View File

@ -116,23 +116,31 @@ The web UI interact with the backend through a GraphQL API. The schema is availa
### Importer implementations
| | Github | Launchpad |
| ----------------------------------------------- | :----------------: | :----------------: |
| **incremental**<br/>(can import more than once) | :heavy_check_mark: | :x: |
| **with resume**<br/>(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**<br/>(can import more than once) | :heavy_check_mark: | :x: |
| **with resume**<br/>(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

View File

@ -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)

101
bridge/core/export.go Normal file
View File

@ -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,
}
}

View File

@ -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)
}

795
bridge/github/export.go Normal file
View File

@ -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"))
}

View File

@ -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"`
}
*/

View File

@ -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
}

View File

@ -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 {

View File

@ -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{

View File

@ -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)"`
}

View File

@ -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")
}
}
})

View File

@ -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
}

View File

@ -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
}

View File

@ -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() {}

18
cache/bug_cache.go vendored
View File

@ -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 {

View File

@ -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() {

62
commands/bridge_push.go Normal file
View File

@ -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 [<name>]",
Short: "Push updates.",
PreRunE: loadRepo,
RunE: runBridgePush,
Args: cobra.MaximumNArgs(1),
}
func init() {
bridgeCmd.AddCommand(bridgePushCmd)
}

View File

@ -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 [<name>] [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

View File

@ -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

View File

@ -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.

View File

@ -0,0 +1,22 @@
## git-bug bridge push
Push updates.
### Synopsis
Push updates.
```
git-bug bridge push [<name>] [flags]
```
### Options
```
-h, --help help for push
```
### SEE ALSO
* [git-bug bridge](git-bug_bridge.md) - Configure and use bridges to other bug trackers.

View File

@ -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=()

View File

@ -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
}

View File

@ -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
}

View File

@ -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 ./...

View File

@ -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

View File

@ -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.
)

View File

@ -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 {

View File

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