Skip to content

Commit

Permalink
Breaking changes for v2.0.0 (#95)
Browse files Browse the repository at this point in the history
* Fix up repository package

* Refactor api package

* Fix up browser package

* Fix up config package

* Fix up template package

* Wrap shurcool Errors in GQLError (#104)

* Rename CurrentRepository to Current

* Fix up tests

* Change all references of GQL to GraphQL and references of gql to graphQL

* Update to Go 1.20

* Update package to V2

* Update README
  • Loading branch information
samcoe committed Apr 16, 2023
1 parent 026e976 commit 9330cac
Show file tree
Hide file tree
Showing 37 changed files with 1,079 additions and 1,006 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.19"
go-version: "1.20"

- name: Checkout repository
uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
go: ["1.19"]
go: ["1.20"]

runs-on: ${{ matrix.os }}

Expand Down
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,41 @@

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

- [`CurrentRepository()`](https://pkg.go.dev/github.com/cli/go-gh#CurrentRepository) respects the value of the `GH_REPO` environment variable and reads from git remote configuration as fallback.
- [`repository.Current()`](https://pkg.go.dev/github.com/cli/go-gh/v2/pkg/repository#current) 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](https://pkg.go.dev/github.com/cli/go-gh/pkg/term) are determined by taking environment variables `GH_FORCE_TTY`, `NO_COLOR`, `CLICOLOR`, etc. into account.
- [Terminal capabilities](https://pkg.go.dev/github.com/cli/go-gh/v2/pkg/term) are determined by taking environment variables `GH_FORCE_TTY`, `NO_COLOR`, `CLICOLOR`, etc. into account.

- Generating [table](https://pkg.go.dev/github.com/cli/go-gh/pkg/tableprinter) or [Go template](https://pkg.go.dev/github.com/cli/go-gh/pkg/template) output uses the same engine as gh.
- Generating [table](https://pkg.go.dev/github.com/cli/go-gh/v2/pkg/tableprinter) or [Go template](https://pkg.go.dev/github.com/cli/go-gh/pkg/template) output uses the same engine as gh.

- The [`browser`](https://pkg.go.dev/github.com/cli/go-gh/pkg/browser) module activates the user's preferred web browser.
- The [`browser`](https://pkg.go.dev/github.com/cli/go-gh/v2/pkg/browser) module activates the user's preferred web browser.

## Usage

See the full `go-gh` [reference documentation](https://pkg.go.dev/github.com/cli/go-gh) for more information
See the full `go-gh` [reference documentation](https://pkg.go.dev/github.com/cli/go-gh/v2) for more information

```golang
package main

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

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

// Shell out to a gh command and read its output
// 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)

// Use an API client to retrieve repository tags.
client, err := api.DefaultRESTClient()
if err != nil {
log.Fatal(err)
}
Expand All @@ -59,7 +59,6 @@ See [examples][] for more demonstrations of usage.

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


[extensions]: https://docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions
[examples]: ./example_gh_test.go
[contributing]: ./.github/CONTRIBUTING.md
51 changes: 26 additions & 25 deletions example_gh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (
"regexp"
"time"

gh "github.com/cli/go-gh"
"github.com/cli/go-gh/pkg/api"
"github.com/cli/go-gh/pkg/tableprinter"
"github.com/cli/go-gh/pkg/term"
gh "github.com/cli/go-gh/v2"
"github.com/cli/go-gh/v2/pkg/api"
"github.com/cli/go-gh/v2/pkg/repository"
"github.com/cli/go-gh/v2/pkg/tableprinter"
"github.com/cli/go-gh/v2/pkg/term"
graphql "github.com/cli/shurcooL-graphql"
)

Expand All @@ -29,8 +30,8 @@ func ExampleExec() {
}

// Get tags from cli/cli repository using REST API.
func ExampleRESTClient_simple() {
client, err := gh.RESTClient(nil)
func ExampleDefaultRESTClient() {
client, err := api.DefaultRESTClient()
if err != nil {
log.Fatal(err)
}
Expand All @@ -44,14 +45,14 @@ func ExampleRESTClient_simple() {

// Get tags from cli/cli repository using REST API.
// Specifying host, auth token, headers and logging to stdout.
func ExampleRESTClient_advanced() {
func ExampleRESTClient() {
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)
client, err := api.NewRESTClient(opts)
if err != nil {
log.Fatal(err)
}
Expand All @@ -68,7 +69,7 @@ func ExampleRESTClient_request() {
opts := api.ClientOptions{
Headers: map[string]string{"Accept": "application/octet-stream"},
}
client, err := gh.RESTClient(&opts)
client, err := api.NewRESTClient(opts)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -102,7 +103,7 @@ func ExampleRESTClient_pagination() {
}
return "", false
}
client, err := gh.RESTClient(nil)
client, err := api.DefaultRESTClient()
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -132,9 +133,9 @@ func ExampleRESTClient_pagination() {
}
}

// Query tags from cli/cli repository using GQL API.
func ExampleGQLClient_simple() {
client, err := gh.GQLClient(nil)
// Query tags from cli/cli repository using GraphQL API.
func ExampleDefaultGraphQLClient() {
client, err := api.DefaultGraphQLClient()
if err != nil {
log.Fatal(err)
}
Expand All @@ -160,14 +161,14 @@ func ExampleGQLClient_simple() {
fmt.Println(query)
}

// Query tags from cli/cli repository using GQL API.
// Query tags from cli/cli repository using GraphQL API.
// Enable caching and request timeout.
func ExampleGQLClient_advanced() {
func ExampleGraphQLClient() {
opts := api.ClientOptions{
EnableCache: true,
Timeout: 5 * time.Second,
}
client, err := gh.GQLClient(&opts)
client, err := api.NewGraphQLClient(opts)
if err != nil {
log.Fatal(err)
}
Expand All @@ -193,9 +194,9 @@ func ExampleGQLClient_advanced() {
fmt.Println(query)
}

// Add a star to the cli/go-gh repository using the GQL API.
func ExampleGQLClient_mutate_simple() {
client, err := gh.GQLClient(nil)
// Add a star to the cli/go-gh repository using the GraphQL API.
func ExampleGraphQLClient_mutate() {
client, err := api.DefaultGraphQLClient()
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -228,9 +229,9 @@ func ExampleGQLClient_mutate_simple() {
fmt.Println(mutation.AddStar.Starrable.Repository.StargazerCount)
}

// Query releases from cli/cli repository using GQL API with paginated results.
func ExampleGQLClient_pagination() {
client, err := gh.GQLClient(nil)
// Query releases from cli/cli repository using GraphQL API with paginated results.
func ExampleGraphQLClient_pagination() {
client, err := api.DefaultGraphQLClient()
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -268,12 +269,12 @@ func ExampleGQLClient_pagination() {
}

// Get repository for the current directory.
func ExampleCurrentRepository() {
repo, err := gh.CurrentRepository()
func ExampleCurrent() {
repo, err := repository.Current()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s/%s/%s\n", repo.Host(), repo.Owner(), repo.Name())
fmt.Printf("%s/%s/%s\n", repo.Host, repo.Owner, repo.Name)
}

// Print tabular data to a terminal or in machine-readable format for scripts.
Expand Down
134 changes: 0 additions & 134 deletions gh.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,11 @@ package gh
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"os"
"os/exec"

iapi "github.com/cli/go-gh/internal/api"
"github.com/cli/go-gh/internal/git"
irepo "github.com/cli/go-gh/internal/repository"
"github.com/cli/go-gh/pkg/api"
"github.com/cli/go-gh/pkg/auth"
"github.com/cli/go-gh/pkg/config"
repo "github.com/cli/go-gh/pkg/repository"
"github.com/cli/go-gh/pkg/ssh"
"github.com/cli/safeexec"
)

Expand Down Expand Up @@ -76,127 +66,3 @@ func run(ctx context.Context, ghExe string, env []string, stdin io.Reader, stdou
}
return nil
}

// 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.
func RESTClient(opts *api.ClientOptions) (api.RESTClient, error) {
if opts == nil {
opts = &api.ClientOptions{}
}
if optionsNeedResolution(opts) {
err := resolveOptions(opts)
if err != nil {
return nil, err
}
}
return iapi.NewRESTClient(opts.Host, opts), nil
}

// 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.
func GQLClient(opts *api.ClientOptions) (api.GQLClient, error) {
if opts == nil {
opts = &api.ClientOptions{}
}
if optionsNeedResolution(opts) {
err := resolveOptions(opts)
if err != nil {
return nil, err
}
}
return iapi.NewGQLClient(opts.Host, opts), nil
}

// 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 HTTPClient(opts *api.ClientOptions) (*http.Client, error) {
if opts == nil {
opts = &api.ClientOptions{}
}
if optionsNeedResolution(opts) {
err := resolveOptions(opts)
if err != nil {
return nil, err
}
}
client := iapi.NewHTTPClient(opts)
return &client, nil
}

// CurrentRepository uses git remotes to determine the GitHub repository
// the current directory is tracking.
func CurrentRepository() (repo.Repository, error) {
override := os.Getenv("GH_REPO")
if override != "" {
return repo.Parse(override)
}

remotes, err := git.Remotes()
if err != nil {
return nil, err
}
if len(remotes) == 0 {
return nil, errors.New("unable to determine current repository, no git remotes configured for this repository")
}

translator := ssh.NewTranslator()
for _, r := range remotes {
if r.FetchURL != nil {
r.FetchURL = translator.Translate(r.FetchURL)
}
if r.PushURL != nil {
r.PushURL = translator.Translate(r.PushURL)
}
}

hosts := auth.KnownHosts()

filteredRemotes := remotes.FilterByHosts(hosts)
if len(filteredRemotes) == 0 {
return nil, errors.New("unable to determine current repository, none of the git remotes configured for this repository point to a known GitHub host")
}

r := filteredRemotes[0]
return irepo.New(r.Host, r.Owner, r.Repo), nil
}

func optionsNeedResolution(opts *api.ClientOptions) bool {
if opts.Host == "" {
return true
}
if opts.AuthToken == "" {
return true
}
if opts.UnixDomainSocket == "" && opts.Transport == nil {
return true
}
return false
}

func resolveOptions(opts *api.ClientOptions) error {
cfg, _ := config.Read()
if opts.Host == "" {
opts.Host, _ = auth.DefaultHost()
}
if opts.AuthToken == "" {
opts.AuthToken, _ = auth.TokenForHost(opts.Host)
if opts.AuthToken == "" {
return fmt.Errorf("authentication token not found for host %s", opts.Host)
}
}
if opts.UnixDomainSocket == "" && cfg != nil {
opts.UnixDomainSocket, _ = cfg.Get([]string{"http_unix_socket"})
}
return nil
}
Loading

0 comments on commit 9330cac

Please sign in to comment.