Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into case-insensitive-hostnames
Browse files Browse the repository at this point in the history
  • Loading branch information
mislav committed Mar 23, 2023
2 parents fc01505 + 32d3260 commit 49ee9b4
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 81 deletions.
111 changes: 97 additions & 14 deletions example_gh_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package gh_test

import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"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"
graphql "github.com/cli/shurcooL-graphql"
"github.com/shurcooL/githubv4"
)

// Execute 'gh issue list -R cli/cli', and print the output.
Expand Down Expand Up @@ -70,29 +72,66 @@ func ExampleRESTClient_request() {
if err != nil {
log.Fatal(err)
}

// URL to cli/cli release v2.14.2 checksums.txt
assetURL := "repos/cli/cli/releases/assets/71589494"
resp, err := client.Request("GET", assetURL, nil)
response, err := client.Request(http.MethodGet, assetURL, nil)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()

defer response.Body.Close()
f, err := os.CreateTemp("", "*_checksums.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()

_, err = io.Copy(f, resp.Body)
_, err = io.Copy(f, response.Body)
if err != nil {
log.Fatal(err)
}

fmt.Printf("Asset downloaded to %s\n", f.Name())
}

// Get releases from cli/cli repository using REST API with paginated results.
func ExampleRESTClient_pagination() {
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++
}
}

// Query tags from cli/cli repository using GQL API.
func ExampleGQLClient_simple() {
client, err := gh.GQLClient(nil)
Expand Down Expand Up @@ -155,12 +194,12 @@ func ExampleGQLClient_advanced() {
}

// Add a star to the cli/go-gh repository using the GQL API.
func ExampleGQLClient_Mutate_simple() {
func ExampleGQLClient_mutate_simple() {
client, err := gh.GQLClient(nil)
if err != nil {
log.Fatal(err)
}
var m struct {
var mutation struct {
AddStar struct {
Starrable struct {
Repository struct {
Expand All @@ -172,16 +211,60 @@ func ExampleGQLClient_Mutate_simple() {
}
} `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": githubv4.AddStarInput{
StarrableID: githubv4.NewID("R_kgDOF_MgQQ"),
"input": AddStarInput{
StarrableID: "R_kgDOF_MgQQ",
},
}
err = client.Mutate("AddStar", &m, variables)
err = client.Mutate("AddStar", &mutation, variables)
if err != nil {
log.Fatal(err)
}
fmt.Println(m.AddStar.Starrable.Repository.StargazerCount)
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)
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++
}
}

// Get repository for the current directory.
Expand Down
9 changes: 3 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ require (
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
github.com/muesli/reflow v0.3.0
github.com/muesli/termenv v0.12.0
github.com/shurcooL/githubv4 v0.0.0-20221229060216-a8d4a561cc93
github.com/stretchr/testify v1.7.0
github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e
golang.org/x/sys v0.4.0
golang.org/x/term v0.4.0
golang.org/x/sys v0.5.0
golang.org/x/term v0.5.0
gopkg.in/h2non/gock.v1 v1.1.2
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -40,10 +39,8 @@ require (
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 // indirect
github.com/yuin/goldmark v1.4.4 // indirect
github.com/yuin/goldmark-emoji v1.0.1 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/net v0.7.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
21 changes: 6 additions & 15 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
Expand Down Expand Up @@ -66,10 +65,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/shurcooL/githubv4 v0.0.0-20221229060216-a8d4a561cc93 h1:JNy04upyaTaAGVlUFAL+60/1nphmJtuTu36tLhbaqXk=
github.com/shurcooL/githubv4 v0.0.0-20221229060216-a8d4a561cc93/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 h1:B1PEwpArrNp4dkQrfxh/abbBAOZBVp0ds+fBEOUOqOc=
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand All @@ -82,10 +77,8 @@ github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18W
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -96,18 +89,16 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
26 changes: 16 additions & 10 deletions internal/api/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,33 +170,39 @@ func (fs *fileStorage) read(key string) (*http.Response, error) {
return res, err
}

func (fs *fileStorage) store(key string, res *http.Response) error {
func (fs *fileStorage) store(key string, res *http.Response) (storeErr error) {
cacheFile := fs.filePath(key)

fs.mu.Lock()
defer fs.mu.Unlock()

err := os.MkdirAll(filepath.Dir(cacheFile), 0755)
if err != nil {
return err
if storeErr = os.MkdirAll(filepath.Dir(cacheFile), 0755); storeErr != nil {
return
}

f, err := os.OpenFile(cacheFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
var f *os.File
if f, storeErr = os.OpenFile(cacheFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600); storeErr != nil {
return
}
defer f.Close()

defer func() {
if err := f.Close(); storeErr == nil && err != nil {
storeErr = err
}
}()

var origBody io.ReadCloser
if res.Body != nil {
origBody, res.Body = copyStream(res.Body)
defer res.Body.Close()
}
err = res.Write(f)

storeErr = res.Write(f)
if origBody != nil {
res.Body = origBody
}
return err

return
}

func copyStream(r io.ReadCloser) (io.ReadCloser, io.ReadCloser) {
Expand Down
35 changes: 32 additions & 3 deletions pkg/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ package auth

import (
"os"
"os/exec"
"strconv"
"strings"

"github.com/cli/go-gh/pkg/config"
"github.com/cli/safeexec"
)

const (
Expand All @@ -24,11 +26,29 @@ const (
oauthToken = "oauth_token"
)

// TokenForHost retrieves an authentication token and the source of
// that token for the specified host. The source can be either an
// environment variable or from the configuration file.
// TokenForHost retrieves an authentication token and the source of that token for the specified
// host. The source can be either an environment variable, configuration file, or the system
// keyring. In the latter case, this shells out to "gh auth token" to obtain the token.
//
// Returns "", "default" if no applicable token is found.
func TokenForHost(host string) (string, string) {
if token, source := TokenFromEnvOrConfig(host); token != "" {
return token, source
}

if ghExe, err := safeexec.LookPath("gh"); err == nil {
if token, source := tokenFromGh(ghExe, host); token != "" {
return token, source
}
}

return "", defaultSource
}

// TokenFromEnvOrConfig retrieves an authentication token from environment variables or the config
// file as fallback, but does not support reading the token from system keyring. Most consumers
// should use TokenForHost.
func TokenFromEnvOrConfig(host string) (string, string) {
cfg, _ := config.Read()
return tokenForHost(cfg, host)
}
Expand Down Expand Up @@ -65,6 +85,15 @@ func tokenForHost(cfg *config.Config, host string) (string, string) {
return "", defaultSource
}

func tokenFromGh(path string, host string) (string, string) {
cmd := exec.Command(path, "auth", "token", "--secure-storage", "--hostname", host)
result, err := cmd.Output()
if err != nil {
return "", "gh"
}
return strings.TrimSpace(string(result)), "gh"
}

// KnownHosts retrieves a list of hosts that have corresponding
// authentication tokens, either from environment variables
// or from the configuration file.
Expand Down

0 comments on commit 49ee9b4

Please sign in to comment.