Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 #95

Merged
merged 13 commits into from
Apr 16, 2023
Merged
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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

Expand Down Expand Up @@ -36,7 +36,7 @@ func main() {
log.Fatal(err)
}
fmt.Println(issueList.String())

// Use an API helper to grab repository tags
client, err := gh.RESTClient(nil)
if err != nil {
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
33 changes: 17 additions & 16 deletions example_gh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

gh "github.com/cli/go-gh"
"github.com/cli/go-gh/pkg/api"
"github.com/cli/go-gh/pkg/repository"
"github.com/cli/go-gh/pkg/tableprinter"
"github.com/cli/go-gh/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 @@ -133,8 +134,8 @@ func ExampleRESTClient_pagination() {
}

// Query tags from cli/cli repository using GQL API.
func ExampleGQLClient_simple() {
client, err := gh.GQLClient(nil)
func ExampleDefaultGQLClient() {
client, err := api.DefaultGQLClient()
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -162,12 +163,12 @@ func ExampleGQLClient_simple() {

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

// Add a star to the cli/go-gh repository using the GQL API.
func ExampleGQLClient_mutate_simple() {
client, err := gh.GQLClient(nil)
func ExampleGQLClient_mutate() {
client, err := api.DefaultGQLClient()
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -230,7 +231,7 @@ func ExampleGQLClient_mutate_simple() {

// Query releases from cli/cli repository using GQL API with paginated results.
func ExampleGQLClient_pagination() {
client, err := gh.GQLClient(nil)
client, err := api.DefaultGQLClient()
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