Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support self-hosted GitHub Enterprise servers
Browse files Browse the repository at this point in the history
twpayne committed May 2, 2024
1 parent a1196a7 commit 4d5ad3d
Showing 13 changed files with 243 additions and 100 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# `gitHubLatestRelease` *owner-repo*
# `gitHubLatestRelease` *host-owner-repo*

`gitHubLatestRelease` calls the GitHub API to retrieve the latest release about
the given *owner-repo*, returning structured data as defined by the [GitHub Go
API
the given *host-owner-repo*, returning structured data as defined by the [GitHub
Go API
bindings](https://pkg.go.dev/github.com/google/go-github/v57/github#RepositoryRelease).

Calls to `gitHubLatestRelease` are cached so calling `gitHubLatestRelease` with
the same *owner-repo* will only result in one call to the GitHub API.
the same *host-owner-repo* will only result in one call to the GitHub API.

!!! example

Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# `gitHubLatestReleaseAssetURL` *owner-repo* *pattern*
# `gitHubLatestReleaseAssetURL` *host-owner-repo* *pattern*

`gitHubLatestReleaseAssetURL` calls the GitHub API to retrieve the latest
release about the given *owner-repo*, returning structured data as defined by
the [GitHub Go API
release about the given *host-owner-repo*, returning structured data as defined
by the [GitHub Go API
bindings](https://pkg.go.dev/github.com/google/go-github/v61/github#RepositoryRelease).
It then iterates through all the release's assets, returning the first one that
matches *pattern*. *pattern* is a shell pattern as [described in
`path.Match`](https://pkg.go.dev/path#Match).

Calls to `gitHubLatestReleaseAssetURL` are cached so calling
`gitHubLatestReleaseAssetURL` with the same *owner-repo* will only result in one
call to the GitHub API.
`gitHubLatestReleaseAssetURL` with the same *host-owner-repo* will only result
in one call to the GitHub API.

!!! example

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# `gitHubLatestTag` *owner-repo*
# `gitHubLatestTag` *host-owner-repo*

`gitHubLatestTag` calls the GitHub API to retrieve the latest tag for the given
*owner-repo*, returning structured data as defined by the [GitHub Go API
*host-owner-repo*, returning structured data as defined by the [GitHub Go API
bindings](https://pkg.go.dev/github.com/google/go-github/v57/github#RepositoryTag).

Calls to `gitHubLatestTag` are cached the same as [`githubTags`](gitHubTags.md),
so calling `gitHubLatestTag` with the same *owner-repo* will only result in one
call to the GitHub API.
so calling `gitHubLatestTag` with the same *host-owner-repo* will only result in
one call to the GitHub API.

!!! example

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# `gitHubReleases` *owner-repo*
# `gitHubReleases` *host-owner-repo*

`gitHubReleases` calls the GitHub API to retrieve the first page of releases for
the given *owner-repo*, returning structured data as defined by the [GitHub Go
API
the given *host-owner-repo*, returning structured data as defined by the [GitHub
Go API
bindings](https://pkg.go.dev/github.com/google/go-github/v57/github#RepositoryRelease).

Calls to `gitHubReleases` are cached so calling `gitHubReleases` with the same
*owner-repo* will only result in one call to the GitHub API.
*host-owner-repo* will only result in one call to the GitHub API.

!!! example

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# `gitHubTags` *owner-repo*
# `gitHubTags` *host-owner-repo*

`gitHubTags` calls the GitHub API to retrieve the first page of tags for
the given *owner-repo*, returning structured data as defined by the [GitHub Go
`gitHubTags` calls the GitHub API to retrieve the first page of tags for the
given *host-owner-repo*, returning structured data as defined by the [GitHub Go
API
bindings](https://pkg.go.dev/github.com/google/go-github/v57/github#RepositoryTag).

Calls to `gitHubTags` are cached so calling `gitHubTags` with the
same *owner-repo* will only result in one call to the GitHub API.
same *host-owner-repo* will only result in one call to the GitHub API.

!!! example

Original file line number Diff line number Diff line change
@@ -2,20 +2,49 @@

The `gitHub*` template functions return data from the GitHub API.

All functions take a *host-owner-repo* argument of the form:

[host/]owner/repo

The optional `host` specifies the host and defaults to `github.com` if omitted.
`owner` and `repo` specify the repository owner and name respectively.

By default, chezmoi makes anonymous GitHub API requests, which are subject to
[GitHub's rate
limits](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting)
(currently 60 requests per hour per source IP address). chezmoi caches results
from identical GitHub API requests for the period defined in
`gitHub.refreshPeriod` (default one minute).

If any of the environment variables `$CHEZMOI_GITHUB_ACCESS_TOKEN`,
`$GITHUB_ACCESS_TOKEN`, or `$GITHUB_TOKEN` are found, then the first one found
will be used to authenticate the GitHub API requests which have a higher rate
limit (currently 5,000 requests per hour per user).
For `github.com` repos, if any of the environment variables

* `$CHEZMOI_GITHUB_ACCESS_TOKEN`
* `$CHEZMOI_GITHUB_TOKEN`
* `$GITHUB_ACCESS_TOKEN`
* `$GITHUB_TOKEN`

are found, then the first one found will be used to
authenticate the GitHub API requests which have a higher rate limit (currently
5,000 requests per hour per user).

In practice, GitHub API rate limits are high enough chezmoi's caching of results
mean that you should rarely need to set a token, unless you are sharing a source
IP address with many other GitHub users. If needed, the GitHub documentation
describes how to [create a personal access
IP address with many other GitHub users or accessing a private repo. If needed,
the GitHub documentation describes how to [create a personal access
token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token).

For non-`github.com` repos, e.g. self-hosted GitHub Enterprise repos, if any of
the environment variables

* `$CHEZMOI_`*`HOST`*`_ACCESS_TOKEN`
* `$`*`HOST`*`_ACCESS_TOKEN`

are found then the first one will be used to authenticate requests, where
*`HOST`* is the host converted to uppercase and with all non-letter characters
replaced with underscores.

!!! example

Given the host `git.example.com`, chezmoi will look for the
`$CHEZMOI_GIT_EXAMPLE_COM_ACCESS_TOKEN` and `$GIT_EXAMPLE_COM_ACCESS_TOKEN`
environment variables.
49 changes: 41 additions & 8 deletions internal/chezmoi/github.go
Original file line number Diff line number Diff line change
@@ -11,13 +11,8 @@ import (

// NewGitHubClient returns a new github.Client configured with an access token
// and a http client, if available.
func NewGitHubClient(ctx context.Context, httpClient *http.Client) *github.Client {
for _, key := range []string{
"CHEZMOI_GITHUB_ACCESS_TOKEN",
"CHEZMOI_GITHUB_TOKEN",
"GITHUB_ACCESS_TOKEN",
"GITHUB_TOKEN",
} {
func NewGitHubClient(ctx context.Context, httpClient *http.Client, host string) (*github.Client, error) {
for _, key := range accessTokenEnvKeys(host) {
if accessToken := os.Getenv(key); accessToken != "" {
httpClient = oauth2.NewClient(
context.WithValue(ctx, oauth2.HTTPClient, httpClient),
@@ -27,5 +22,43 @@ func NewGitHubClient(ctx context.Context, httpClient *http.Client) *github.Clien
break
}
}
return github.NewClient(httpClient)
gitHubClient := github.NewClient(httpClient)
if host == "github.com" {
return gitHubClient, nil
}
return gitHubClient.WithEnterpriseURLs(
"https://"+host+"/api/v3/",
"https://"+host+"/api/uploads/",
)
}

func accessTokenEnvKeys(host string) []string {
if host == "github.com" {
return []string{
"CHEZMOI_GITHUB_ACCESS_TOKEN",
"CHEZMOI_GITHUB_TOKEN",
"GITHUB_ACCESS_TOKEN",
"GITHUB_TOKEN",
}
}
hostKey := makeHostKey(host)
return []string{
"CHEZMOI_" + hostKey + "_ACCESS_TOKEN",
hostKey + "_ACCESS_TOKEN",
}
}

func makeHostKey(host string) string {
hostKey := make([]byte, 0, len(host))
for _, b := range []byte(host) {
switch {
case 'A' <= b && b <= 'Z':
hostKey = append(hostKey, b)
case 'a' <= b && b <= 'z':
hostKey = append(hostKey, b-'a'+'A')
default:
hostKey = append(hostKey, '_')
}
}
return string(hostKey)
}
35 changes: 35 additions & 0 deletions internal/chezmoi/github_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package chezmoi

import (
"testing"

"github.com/alecthomas/assert/v2"
)

func TestAccessTokenEnvKeys(t *testing.T) {
for _, tc := range []struct {
host string
expected []string
}{
{
host: "github.com",
expected: []string{
"CHEZMOI_GITHUB_ACCESS_TOKEN",
"CHEZMOI_GITHUB_TOKEN",
"GITHUB_ACCESS_TOKEN",
"GITHUB_TOKEN",
},
},
{
host: "git.example.com",
expected: []string{
"CHEZMOI_GIT_EXAMPLE_COM_ACCESS_TOKEN",
"GIT_EXAMPLE_COM_ACCESS_TOKEN",
},
},
} {
t.Run(tc.host, func(t *testing.T) {
assert.Equal(t, tc.expected, accessTokenEnvKeys(tc.host))
})
}
}
5 changes: 5 additions & 0 deletions internal/cmd/config.go
Original file line number Diff line number Diff line change
@@ -340,6 +340,11 @@ func newConfig(options ...configOption) (*Config, error) {
homeDir: userHomeDir,
templateFuncs: sprig.TxtFuncMap(),

// Password manager data.
gitHub: gitHubData{
clientsByHost: make(map[string]gitHubClientResult),
},

// Command configurations.
apply: applyCmdConfig{
filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone),
5 changes: 4 additions & 1 deletion internal/cmd/doctorcmd.go
Original file line number Diff line number Diff line change
@@ -659,7 +659,10 @@ func (c *latestVersionCheck) Run(system chezmoi.System, homeDirAbsPath chezmoi.A

ctx := context.Background()

gitHubClient := chezmoi.NewGitHubClient(ctx, c.httpClient)
gitHubClient, err := chezmoi.NewGitHubClient(ctx, c.httpClient, "github.com")
if err != nil {
return checkResultFailed, err.Error()
}
rr, _, err := gitHubClient.Repositories.GetLatestRelease(ctx, "twpayne", "chezmoi")
var rateLimitErr *github.RateLimitError
var abuseRateLimitErr *github.AbuseRateLimitError
144 changes: 86 additions & 58 deletions internal/cmd/githubtemplatefuncs.go
Original file line number Diff line number Diff line change
@@ -43,13 +43,23 @@ var (
gitHubTagsStateBucket = []byte("gitHubTagsState")
)

type gitHubHostOwnerRepo struct {
Host string
Owner string
Repo string
}

type gitHubClientResult struct {
client *github.Client
err error
}

type gitHubData struct {
client *github.Client
clientErr error
clientsByHost map[string]gitHubClientResult
keysCache map[string][]*github.Key
latestReleaseCache map[string]map[string]*github.RepositoryRelease
releasesCache map[string]map[string][]*github.RepositoryRelease
tagsCache map[string]map[string][]*github.RepositoryTag
latestReleaseCache map[gitHubHostOwnerRepo]*github.RepositoryRelease
releasesCache map[gitHubHostOwnerRepo][]*github.RepositoryRelease
tagsCache map[gitHubHostOwnerRepo][]*github.RepositoryTag
}

func (c *Config) gitHubKeysTemplateFunc(user string) []*github.Key {
@@ -72,7 +82,7 @@ func (c *Config) gitHubKeysTemplateFunc(user string) []*github.Key {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

gitHubClient, err := c.getGitHubClient(ctx)
gitHubClient, err := c.getGitHubClient(ctx, "github.com")
if err != nil {
panic(err)
}
@@ -108,8 +118,8 @@ func (c *Config) gitHubKeysTemplateFunc(user string) []*github.Key {
return allKeys
}

func (c *Config) gitHubLatestReleaseAssetURLTemplateFunc(ownerRepo, pattern string) string {
release, err := c.gitHubLatestRelease(ownerRepo)
func (c *Config) gitHubLatestReleaseAssetURLTemplateFunc(hostOwnerRepo, pattern string) string {
release, err := c.gitHubLatestRelease(hostOwnerRepo)
if err != nil {
panic(err)
}
@@ -127,18 +137,18 @@ func (c *Config) gitHubLatestReleaseAssetURLTemplateFunc(ownerRepo, pattern stri
return ""
}

func (c *Config) gitHubLatestRelease(ownerRepo string) (*github.RepositoryRelease, error) {
owner, repo, err := gitHubSplitOwnerRepo(ownerRepo)
func (c *Config) gitHubLatestRelease(hostOwnerRepo string) (*github.RepositoryRelease, error) {
hor, err := parseGitHubHostOwnerRepo(hostOwnerRepo)
if err != nil {
return nil, err
}

if release := c.gitHub.latestReleaseCache[owner][repo]; release != nil {
if release := c.gitHub.latestReleaseCache[hor]; release != nil {
return release, nil
}

now := time.Now()
gitHubLatestReleaseKey := []byte(owner + "/" + repo)
gitHubLatestReleaseKey := hor.Key()
if c.GitHub.RefreshPeriod != 0 {
var gitHubLatestReleaseStateValue gitHubLatestReleaseState
switch ok, err := chezmoi.PersistentStateGet(c.persistentState, gitHubLatestReleaseStateBucket, gitHubLatestReleaseKey, &gitHubLatestReleaseStateValue); {
@@ -152,12 +162,12 @@ func (c *Config) gitHubLatestRelease(ownerRepo string) (*github.RepositoryReleas
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

gitHubClient, err := c.getGitHubClient(ctx)
gitHubClient, err := c.getGitHubClient(ctx, hor.Host)
if err != nil {
return nil, err
}

release, _, err := gitHubClient.Repositories.GetLatestRelease(ctx, owner, repo)
release, _, err := gitHubClient.Repositories.GetLatestRelease(ctx, hor.Owner, hor.Repo)
if err != nil {
return nil, err
}
@@ -170,26 +180,23 @@ func (c *Config) gitHubLatestRelease(ownerRepo string) (*github.RepositoryReleas
}

if c.gitHub.latestReleaseCache == nil {
c.gitHub.latestReleaseCache = make(map[string]map[string]*github.RepositoryRelease)
c.gitHub.latestReleaseCache = make(map[gitHubHostOwnerRepo]*github.RepositoryRelease)
}
if c.gitHub.latestReleaseCache[owner] == nil {
c.gitHub.latestReleaseCache[owner] = make(map[string]*github.RepositoryRelease)
}
c.gitHub.latestReleaseCache[owner][repo] = release
c.gitHub.latestReleaseCache[hor] = release

return release, nil
}

func (c *Config) gitHubLatestReleaseTemplateFunc(ownerRepo string) *github.RepositoryRelease {
release, err := c.gitHubLatestRelease(ownerRepo)
func (c *Config) gitHubLatestReleaseTemplateFunc(hostOwnerRepo string) *github.RepositoryRelease {
release, err := c.gitHubLatestRelease(hostOwnerRepo)
if err != nil {
panic(err)
}
return release
}

func (c *Config) gitHubLatestTagTemplateFunc(ownerRepo string) *github.RepositoryTag {
tags, err := c.getGitHubTags(ownerRepo)
func (c *Config) gitHubLatestTagTemplateFunc(hostOwnerRepo string) *github.RepositoryTag {
tags, err := c.getGitHubTags(hostOwnerRepo)
if err != nil {
panic(err)
}
@@ -201,18 +208,18 @@ func (c *Config) gitHubLatestTagTemplateFunc(ownerRepo string) *github.Repositor
return nil
}

func (c *Config) gitHubReleasesTemplateFunc(ownerRepo string) []*github.RepositoryRelease {
owner, repo, err := gitHubSplitOwnerRepo(ownerRepo)
func (c *Config) gitHubReleasesTemplateFunc(hostOwnerRepo string) []*github.RepositoryRelease {
hor, err := parseGitHubHostOwnerRepo(hostOwnerRepo)
if err != nil {
panic(err)
}

if releases := c.gitHub.releasesCache[owner][repo]; releases != nil {
if releases := c.gitHub.releasesCache[hor]; releases != nil {
return releases
}

now := time.Now()
gitHubReleasesKey := []byte(owner + "/" + repo)
gitHubReleasesKey := hor.Key()
if c.GitHub.RefreshPeriod != 0 {
var gitHubReleasesStateValue gitHubReleasesState
switch ok, err := chezmoi.PersistentStateGet(c.persistentState, gitHubReleasesStateBucket, gitHubReleasesKey, &gitHubReleasesStateValue); {
@@ -226,12 +233,12 @@ func (c *Config) gitHubReleasesTemplateFunc(ownerRepo string) []*github.Reposito
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

gitHubClient, err := c.getGitHubClient(ctx)
gitHubClient, err := c.getGitHubClient(ctx, hor.Host)
if err != nil {
panic(err)
}

releases, _, err := gitHubClient.Repositories.ListReleases(ctx, owner, repo, nil)
releases, _, err := gitHubClient.Repositories.ListReleases(ctx, hor.Owner, hor.Repo, nil)
if err != nil {
panic(err)
}
@@ -244,37 +251,34 @@ func (c *Config) gitHubReleasesTemplateFunc(ownerRepo string) []*github.Reposito
}

if c.gitHub.releasesCache == nil {
c.gitHub.releasesCache = make(map[string]map[string][]*github.RepositoryRelease)
}
if c.gitHub.releasesCache[owner] == nil {
c.gitHub.releasesCache[owner] = make(map[string][]*github.RepositoryRelease)
c.gitHub.releasesCache = make(map[gitHubHostOwnerRepo][]*github.RepositoryRelease)
}
c.gitHub.releasesCache[owner][repo] = releases
c.gitHub.releasesCache[hor] = releases

return releases
}

func (c *Config) gitHubTagsTemplateFunc(ownerRepo string) []*github.RepositoryTag {
tags, err := c.getGitHubTags(ownerRepo)
func (c *Config) gitHubTagsTemplateFunc(hostOwnerRepo string) []*github.RepositoryTag {
tags, err := c.getGitHubTags(hostOwnerRepo)
if err != nil {
panic(err)
}

return tags
}

func (c *Config) getGitHubTags(ownerRepo string) ([]*github.RepositoryTag, error) {
owner, repo, err := gitHubSplitOwnerRepo(ownerRepo)
func (c *Config) getGitHubTags(hostOwnerRepo string) ([]*github.RepositoryTag, error) {
hor, err := parseGitHubHostOwnerRepo(hostOwnerRepo)
if err != nil {
return nil, err
}

if tags := c.gitHub.tagsCache[owner][repo]; tags != nil {
if tags := c.gitHub.tagsCache[hor]; tags != nil {
return tags, nil
}

now := time.Now()
gitHubTagsKey := []byte(owner + "/" + repo)
gitHubTagsKey := hor.Key()
if c.GitHub.RefreshPeriod != 0 {
var gitHubTagsStateValue gitHubTagsState
switch ok, err := chezmoi.PersistentStateGet(c.persistentState, gitHubTagsStateBucket, gitHubTagsKey, &gitHubTagsStateValue); {
@@ -288,12 +292,12 @@ func (c *Config) getGitHubTags(ownerRepo string) ([]*github.RepositoryTag, error
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

gitHubClient, err := c.getGitHubClient(ctx)
gitHubClient, err := c.getGitHubClient(ctx, hor.Host)
if err != nil {
return nil, err
}

tags, _, err := gitHubClient.Repositories.ListTags(ctx, owner, repo, nil)
tags, _, err := gitHubClient.Repositories.ListTags(ctx, hor.Owner, hor.Repo, nil)
if err != nil {
return nil, err
}
@@ -306,35 +310,59 @@ func (c *Config) getGitHubTags(ownerRepo string) ([]*github.RepositoryTag, error
}

if c.gitHub.tagsCache == nil {
c.gitHub.tagsCache = make(map[string]map[string][]*github.RepositoryTag)
c.gitHub.tagsCache = make(map[gitHubHostOwnerRepo][]*github.RepositoryTag)
}
if c.gitHub.tagsCache[owner] == nil {
c.gitHub.tagsCache[owner] = make(map[string][]*github.RepositoryTag)
}
c.gitHub.tagsCache[owner][repo] = tags
c.gitHub.tagsCache[hor] = tags

return tags, nil
}

func (c *Config) getGitHubClient(ctx context.Context) (*github.Client, error) {
if c.gitHub.client != nil || c.gitHub.clientErr != nil {
return c.gitHub.client, c.gitHub.clientErr
func (c *Config) getGitHubClient(ctx context.Context, host string) (*github.Client, error) {
if gitHubClientResult, ok := c.gitHub.clientsByHost[host]; ok {
return gitHubClientResult.client, gitHubClientResult.err
}

httpClient, err := c.getHTTPClient()
if err != nil {
c.gitHub.clientErr = err
c.gitHub.clientsByHost[host] = gitHubClientResult{
err: err,
}
return nil, err
}

gitHubClient, err := chezmoi.NewGitHubClient(ctx, httpClient, host)
if err != nil {
return nil, err
}
c.gitHub.clientsByHost[host] = gitHubClientResult{
client: gitHubClient,
}

return gitHubClient, nil
}

c.gitHub.client = chezmoi.NewGitHubClient(ctx, httpClient)
return c.gitHub.client, nil
func parseGitHubHostOwnerRepo(hostOwnerRepo string) (gitHubHostOwnerRepo, error) {
switch components := strings.Split(hostOwnerRepo, "/"); len(components) {
case 2:
return gitHubHostOwnerRepo{
Host: "github.com",
Owner: components[0],
Repo: components[1],
}, nil
case 3:
return gitHubHostOwnerRepo{
Host: components[0],
Owner: components[1],
Repo: components[2],
}, nil
default:
return gitHubHostOwnerRepo{}, fmt.Errorf("%s: not a [host/]owner/repo", hostOwnerRepo)
}
}

func gitHubSplitOwnerRepo(ownerRepo string) (string, string, error) {
owner, repo, ok := strings.Cut(ownerRepo, "/")
if !ok {
return "", "", fmt.Errorf("%s: not an owner/repo", ownerRepo)
func (hor gitHubHostOwnerRepo) Key() []byte {
if hor.Host == "github.com" {
return []byte(hor.Owner + "/" + hor.Repo)
}
return owner, repo, nil
return []byte(hor.Host + "/" + hor.Owner + "/" + hor.Repo)
}
7 changes: 5 additions & 2 deletions internal/cmd/upgradecmd.go
Original file line number Diff line number Diff line change
@@ -79,10 +79,13 @@ func (c *Config) runUpgradeCmd(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
client := chezmoi.NewGitHubClient(ctx, httpClient)
gitHubClient, err := chezmoi.NewGitHubClient(ctx, httpClient, "github.com")
if err != nil {
return err
}

// Get the latest release.
rr, _, err := client.Repositories.GetLatestRelease(ctx, "twpayne", "chezmoi")
rr, _, err := gitHubClient.Repositories.GetLatestRelease(ctx, "twpayne", "chezmoi")
if err != nil {
return err
}
15 changes: 11 additions & 4 deletions internal/cmds/execute-template/main.go
Original file line number Diff line number Diff line change
@@ -33,11 +33,15 @@ type gitHubClient struct {
client *github.Client
}

func newGitHubClient(ctx context.Context) *gitHubClient {
func newGitHubClient(ctx context.Context, host string) (*gitHubClient, error) {
client, err := chezmoi.NewGitHubClient(ctx, http.DefaultClient, host)
if err != nil {
return nil, err
}
return &gitHubClient{
ctx: ctx,
client: chezmoi.NewGitHubClient(ctx, http.DefaultClient),
}
client: client,
}, nil
}

func (c *gitHubClient) gitHubListReleases(ownerRepo string) []*github.RepositoryRelease {
@@ -99,7 +103,10 @@ func run() error {
templateName := path.Base(flag.Arg(0))
buffer := &bytes.Buffer{}
funcMap := sprig.TxtFuncMap()
gitHubClient := newGitHubClient(context.Background())
gitHubClient, err := newGitHubClient(context.Background(), "github.com")
if err != nil {
return err
}
funcMap["exists"] = func(name string) bool {
switch _, err := os.Stat(name); {
case err == nil:

0 comments on commit 4d5ad3d

Please sign in to comment.