gh

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 27, 2023 License: MIT Imports: 15 Imported by: 179

README

Go library for the GitHub CLI

go-gh is a collection of Go modules to make authoring GitHub CLI extensions easier.

Modules from this library will obey GitHub CLI conventions by default:

  • CurrentRepository() respects the value of the GH_REPO environment variable and reads from git remote configuration as fallback.

  • GitHub API requests will be authenticated using the same mechanism as gh, i.e. using the values of GH_TOKEN and GH_HOST environment variables and falling back to the user's stored OAuth token.

  • Terminal capabilities are determined by taking environment variables GH_FORCE_TTY, NO_COLOR, CLICOLOR, etc. into account.

  • Generating table or Go template output uses the same engine as gh.

  • The browser module activates the user's preferred web browser.

Usage

See the full go-gh reference documentation for more information

package main

import (
	"fmt"
	"log"
	"github.com/cli/go-gh"
)

func main() {
	// These examples assume `gh` is installed and has been authenticated

	// Shell out to a gh command and read its output
	issueList, _, err := gh.Exec("issue", "list", "--repo", "cli/cli", "--limit", "5")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(issueList.String())
	
	// Use an API helper to grab repository tags
	client, err := gh.RESTClient(nil)
	if err != nil {
		log.Fatal(err)
	}
	response := []struct{
		Name string
	}{}
	err = client.Get("repos/cli/cli/tags", &response)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}

See examples for more demonstrations of usage.

Contributing

If anything feels off, or if you feel that some functionality is missing, please check out our contributing docs. There you will find instructions for sharing your feedback and for submitting pull requests to the project. Thank you!

Documentation

Overview

Package gh is a library for CLI Go applications to help interface with the gh CLI tool, and the GitHub API.

Note that the examples in this package assume gh and git are installed. They do not run in the Go Playground used by pkg.go.dev.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CurrentRepository

func CurrentRepository() (repo.Repository, error)

CurrentRepository uses git remotes to determine the GitHub repository the current directory is tracking.

Example

Get repository for the current directory.

package main

import (
	"fmt"
	"log"

	gh "github.com/cli/go-gh"
)

func main() {
	repo, err := gh.CurrentRepository()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s/%s/%s\n", repo.Host(), repo.Owner(), repo.Name())
}
Output:

func Exec

func Exec(args ...string) (stdOut, stdErr bytes.Buffer, err error)

Exec gh command with provided arguments.

Example

Execute 'gh issue list -R cli/cli', and print the output.

package main

import (
	"fmt"
	"log"

	gh "github.com/cli/go-gh"
)

func main() {
	args := []string{"issue", "list", "-R", "cli/cli"}
	stdOut, stdErr, err := gh.Exec(args...)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(stdOut.String())
	fmt.Println(stdErr.String())
}
Output:

func GQLClient

func GQLClient(opts *api.ClientOptions) (api.GQLClient, error)

GQLClient builds a client to send requests to GitHub GraphQL API endpoints. As part of the configuration a hostname, auth token, default set of headers, and unix domain socket are resolved from the gh environment configuration. These behaviors can be overridden using the opts argument.

Example (Advanced)

Query tags from cli/cli repository using GQL API. Enable caching and request timeout.

package main

import (
	"fmt"
	"log"
	"time"

	gh "github.com/cli/go-gh"
	"github.com/cli/go-gh/pkg/api"

	graphql "github.com/cli/shurcooL-graphql"
)

func main() {
	opts := api.ClientOptions{
		EnableCache: true,
		Timeout:     5 * time.Second,
	}
	client, err := gh.GQLClient(&opts)
	if err != nil {
		log.Fatal(err)
	}
	var query struct {
		Repository struct {
			Refs struct {
				Nodes []struct {
					Name string
				}
			} `graphql:"refs(refPrefix: $refPrefix, last: $last)"`
		} `graphql:"repository(owner: $owner, name: $name)"`
	}
	variables := map[string]interface{}{
		"refPrefix": graphql.String("refs/tags/"),
		"last":      graphql.Int(30),
		"owner":     graphql.String("cli"),
		"name":      graphql.String("cli"),
	}
	err = client.Query("RepositoryTags", &query, variables)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(query)
}
Output:

Example (Mutate_simple)

Add a star to the cli/go-gh repository using the GQL API.

package main

import (
	"fmt"
	"log"

	gh "github.com/cli/go-gh"
)

func main() {
	client, err := gh.GQLClient(nil)
	if err != nil {
		log.Fatal(err)
	}
	var mutation struct {
		AddStar struct {
			Starrable struct {
				Repository struct {
					StargazerCount int
				} `graphql:"... on Repository"`
				Gist struct {
					StargazerCount int
				} `graphql:"... on Gist"`
			}
		} `graphql:"addStar(input: $input)"`
	}
	// Note that the shurcooL/githubv4 package has defined input structs generated from the
	// GraphQL schema that can be used instead of writing your own.
	type AddStarInput struct {
		StarrableID string `json:"starrableId"`
	}
	variables := map[string]interface{}{
		"input": AddStarInput{
			StarrableID: "R_kgDOF_MgQQ",
		},
	}
	err = client.Mutate("AddStar", &mutation, variables)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(mutation.AddStar.Starrable.Repository.StargazerCount)
}
Output:

Example (Pagination)

Query releases from cli/cli repository using GQL API with paginated results.

package main

import (
	"fmt"
	"log"

	gh "github.com/cli/go-gh"

	graphql "github.com/cli/shurcooL-graphql"
)

func main() {
	client, err := gh.GQLClient(nil)
	if err != nil {
		log.Fatal(err)
	}
	var query struct {
		Repository struct {
			Releases struct {
				Nodes []struct {
					Name string
				}
				PageInfo struct {
					HasNextPage bool
					EndCursor   string
				}
			} `graphql:"releases(first: 30, after: $endCursor)"`
		} `graphql:"repository(owner: $owner, name: $name)"`
	}
	variables := map[string]interface{}{
		"owner":     graphql.String("cli"),
		"name":      graphql.String("cli"),
		"endCursor": (*graphql.String)(nil),
	}
	page := 1
	for {
		if err := client.Query("RepositoryReleases", &query, variables); err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Page: %d\n", page)
		fmt.Println(query.Repository.Releases.Nodes)
		if !query.Repository.Releases.PageInfo.HasNextPage {
			break
		}
		variables["endCursor"] = graphql.String(query.Repository.Releases.PageInfo.EndCursor)
		page++
	}
}
Output:

Example (Simple)

Query tags from cli/cli repository using GQL API.

package main

import (
	"fmt"
	"log"

	gh "github.com/cli/go-gh"

	graphql "github.com/cli/shurcooL-graphql"
)

func main() {
	client, err := gh.GQLClient(nil)
	if err != nil {
		log.Fatal(err)
	}
	var query struct {
		Repository struct {
			Refs struct {
				Nodes []struct {
					Name string
				}
			} `graphql:"refs(refPrefix: $refPrefix, last: $last)"`
		} `graphql:"repository(owner: $owner, name: $name)"`
	}
	variables := map[string]interface{}{
		"refPrefix": graphql.String("refs/tags/"),
		"last":      graphql.Int(30),
		"owner":     graphql.String("cli"),
		"name":      graphql.String("cli"),
	}
	err = client.Query("RepositoryTags", &query, variables)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(query)
}
Output:

func HTTPClient added in v0.0.2

func HTTPClient(opts *api.ClientOptions) (*http.Client, error)

HTTPClient builds a client that can be passed to another library. As part of the configuration a hostname, auth token, default set of headers, and unix domain socket are resolved from the gh environment configuration. These behaviors can be overridden using the opts argument. In this instance providing opts.Host will not change the destination of your request as it is the responsibility of the consumer to configure this. However, if opts.Host does not match the request host, the auth token will not be added to the headers. This is to protect against the case where tokens could be sent to an arbitrary host.

func RESTClient

func RESTClient(opts *api.ClientOptions) (api.RESTClient, error)

RESTClient builds a client to send requests to GitHub REST API endpoints. As part of the configuration a hostname, auth token, default set of headers, and unix domain socket are resolved from the gh environment configuration. These behaviors can be overridden using the opts argument.

Example (Advanced)

Get tags from cli/cli repository using REST API. Specifying host, auth token, headers and logging to stdout.

package main

import (
	"fmt"
	"log"
	"os"

	gh "github.com/cli/go-gh"
	"github.com/cli/go-gh/pkg/api"
)

func main() {
	opts := api.ClientOptions{
		Host:      "github.com",
		AuthToken: "xxxxxxxxxx", // Replace with valid auth token.
		Headers:   map[string]string{"Time-Zone": "America/Los_Angeles"},
		Log:       os.Stdout,
	}
	client, err := gh.RESTClient(&opts)
	if err != nil {
		log.Fatal(err)
	}
	response := []struct{ Name string }{}
	err = client.Get("repos/cli/cli/tags", &response)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
Output:

Example (Pagination)

Get releases from cli/cli repository using REST API with paginated results.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"regexp"

	gh "github.com/cli/go-gh"
)

func main() {
	var linkRE = regexp.MustCompile(`<([^>]+)>;\s*rel="([^"]+)"`)
	findNextPage := func(response *http.Response) (string, bool) {
		for _, m := range linkRE.FindAllStringSubmatch(response.Header.Get("Link"), -1) {
			if len(m) > 2 && m[2] == "next" {
				return m[1], true
			}
		}
		return "", false
	}
	client, err := gh.RESTClient(nil)
	if err != nil {
		log.Fatal(err)
	}
	requestPath := "repos/cli/cli/releases"
	page := 1
	for {
		response, err := client.Request(http.MethodGet, requestPath, nil)
		if err != nil {
			log.Fatal(err)
		}
		data := []struct{ Name string }{}
		decoder := json.NewDecoder(response.Body)
		err = decoder.Decode(&data)
		if err != nil {
			log.Fatal(err)
		}
		if err := response.Body.Close(); err != nil {
			log.Fatal(err)
		}
		fmt.Printf("Page: %d\n", page)
		fmt.Println(data)
		var hasNextPage bool
		if requestPath, hasNextPage = findNextPage(response); !hasNextPage {
			break
		}
		page++
	}
}
Output:

Example (Request)

Get release asset from cli/cli repository using REST API.

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"

	gh "github.com/cli/go-gh"
	"github.com/cli/go-gh/pkg/api"
)

func main() {
	opts := api.ClientOptions{
		Headers: map[string]string{"Accept": "application/octet-stream"},
	}
	client, err := gh.RESTClient(&opts)
	if err != nil {
		log.Fatal(err)
	}
	// URL to cli/cli release v2.14.2 checksums.txt
	assetURL := "repos/cli/cli/releases/assets/71589494"
	response, err := client.Request(http.MethodGet, assetURL, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer response.Body.Close()
	f, err := os.CreateTemp("", "*_checksums.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	_, err = io.Copy(f, response.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Asset downloaded to %s\n", f.Name())
}
Output:

Example (Simple)

Get tags from cli/cli repository using REST API.

package main

import (
	"fmt"
	"log"

	gh "github.com/cli/go-gh"
)

func main() {
	client, err := gh.RESTClient(nil)
	if err != nil {
		log.Fatal(err)
	}
	response := []struct{ Name string }{}
	err = client.Get("repos/cli/cli/tags", &response)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
Output:

Types

This section is empty.

Directories

Path Synopsis
internal
api
git
set
yamlmap
Package yamlmap is a wrapper of gopkg.in/yaml.v3 for interacting with yaml data as if it were a map.
Package yamlmap is a wrapper of gopkg.in/yaml.v3 for interacting with yaml data as if it were a map.
pkg
api
Package api is a set of types for interacting with the GitHub API.
Package api is a set of types for interacting with the GitHub API.
auth
Package auth is a set of functions for retrieving authentication tokens and authenticated hosts.
Package auth is a set of functions for retrieving authentication tokens and authenticated hosts.
browser
Package browser facilitates opening of URLs in a web browser.
Package browser facilitates opening of URLs in a web browser.
config
Package config is a set of types for interacting with the gh configuration files.
Package config is a set of types for interacting with the gh configuration files.
jq
Package jq facilitates processing of JSON strings using jq expressions.
Package jq facilitates processing of JSON strings using jq expressions.
jsonpretty
Package jsonpretty implements a terminal pretty-printer for JSON.
Package jsonpretty implements a terminal pretty-printer for JSON.
markdown
Package markdown facilitates rendering markdown in the terminal.
Package markdown facilitates rendering markdown in the terminal.
repository
Package repository is a set of types and functions for modeling and interacting with GitHub repositories.
Package repository is a set of types and functions for modeling and interacting with GitHub repositories.
ssh
Package ssh resolves local SSH hostname aliases.
Package ssh resolves local SSH hostname aliases.
tableprinter
Package tableprinter facilitates rendering column-formatted data to a terminal and TSV-formatted data to a script or a file.
Package tableprinter facilitates rendering column-formatted data to a terminal and TSV-formatted data to a script or a file.
template
Package template facilitates processing of JSON strings using Go templates.
Package template facilitates processing of JSON strings using Go templates.
term
Package term provides information about the terminal that the current process is connected to (if any), for example measuring the dimensions of the terminal and inspecting whether it's safe to output color.
Package term provides information about the terminal that the current process is connected to (if any), for example measuring the dimensions of the terminal and inspecting whether it's safe to output color.
text
Package text is a set of utility functions for text processing and outputting to the terminal.
Package text is a set of utility functions for text processing and outputting to the terminal.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL