Skip to content

Commit

Permalink
feat: Add gitHubLatestReleaseAssetURL template function
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Apr 29, 2024
1 parent a49a7e0 commit a396e3a
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 89 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# `gitHubLatestReleaseAssetURL` *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
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.

!!! example

```
{{ gitHubLatestReleaseAssetURL "FiloSottile/age" (printf "age-*-%s-%s.tar.gz" .chezmoi.os .chezmoi.arch) }}
{{ gitHubLatestReleaseAssetURL "twpayne/chezmoi" (printf "chezmoi-%s-%s" .chezmoi.os .chezmoi.arch) }}
```
1 change: 1 addition & 0 deletions assets/chezmoi.io/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ nav:
- reference/templates/github-functions/index.md
- gitHubKeys: reference/templates/github-functions/gitHubKeys.md
- gitHubLatestRelease: reference/templates/github-functions/gitHubLatestRelease.md
- gitHubLatestReleaseAssetURL: reference/templates/github-functions/gitHubLatestReleaseAssetURL.md
- gitHubLatestTag: reference/templates/github-functions/gitHubLatestTag.md
- gitHubReleases: reference/templates/github-functions/gitHubReleases.md
- gitHubTags: reference/templates/github-functions/gitHubTags.md
Expand Down
163 changes: 82 additions & 81 deletions internal/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,87 +405,88 @@ func newConfig(options ...configOption) (*Config, error) {
// The completion template function is added in persistentPreRunRootE as
// it needs a *cobra.Command, which we don't yet have.
for key, value := range map[string]any{
"awsSecretsManager": c.awsSecretsManagerTemplateFunc,
"awsSecretsManagerRaw": c.awsSecretsManagerRawTemplateFunc,
"azureKeyVault": c.azureKeyVaultTemplateFunc,
"bitwarden": c.bitwardenTemplateFunc,
"bitwardenAttachment": c.bitwardenAttachmentTemplateFunc,
"bitwardenAttachmentByRef": c.bitwardenAttachmentByRefTemplateFunc,
"bitwardenFields": c.bitwardenFieldsTemplateFunc,
"bitwardenSecrets": c.bitwardenSecretsTemplateFunc,
"comment": c.commentTemplateFunc,
"dashlaneNote": c.dashlaneNoteTemplateFunc,
"dashlanePassword": c.dashlanePasswordTemplateFunc,
"decrypt": c.decryptTemplateFunc,
"deleteValueAtPath": c.deleteValueAtPathTemplateFunc,
"doppler": c.dopplerTemplateFunc,
"dopplerProjectJson": c.dopplerProjectJSONTemplateFunc,
"ejsonDecrypt": c.ejsonDecryptTemplateFunc,
"ejsonDecryptWithKey": c.ejsonDecryptWithKeyTemplateFunc,
"encrypt": c.encryptTemplateFunc,
"eqFold": c.eqFoldTemplateFunc,
"findExecutable": c.findExecutableTemplateFunc,
"findOneExecutable": c.findOneExecutableTemplateFunc,
"fromIni": c.fromIniTemplateFunc,
"fromJson": c.fromJsonTemplateFunc,
"fromJsonc": c.fromJsoncTemplateFunc,
"fromToml": c.fromTomlTemplateFunc,
"fromYaml": c.fromYamlTemplateFunc,
"gitHubKeys": c.gitHubKeysTemplateFunc,
"gitHubLatestRelease": c.gitHubLatestReleaseTemplateFunc,
"gitHubLatestTag": c.gitHubLatestTagTemplateFunc,
"gitHubReleases": c.gitHubReleasesTemplateFunc,
"gitHubTags": c.gitHubTagsTemplateFunc,
"glob": c.globTemplateFunc,
"gopass": c.gopassTemplateFunc,
"gopassRaw": c.gopassRawTemplateFunc,
"hcpVaultSecret": c.hcpVaultSecretTemplateFunc,
"hcpVaultSecretJson": c.hcpVaultSecretJSONTemplateFunc,
"hexDecode": c.hexDecodeTemplateFunc,
"hexEncode": c.hexEncodeTemplateFunc,
"include": c.includeTemplateFunc,
"includeTemplate": c.includeTemplateTemplateFunc,
"ioreg": c.ioregTemplateFunc,
"isExecutable": c.isExecutableTemplateFunc,
"joinPath": c.joinPathTemplateFunc,
"jq": c.jqTemplateFunc,
"keepassxc": c.keepassxcTemplateFunc,
"keepassxcAttachment": c.keepassxcAttachmentTemplateFunc,
"keepassxcAttribute": c.keepassxcAttributeTemplateFunc,
"keeper": c.keeperTemplateFunc,
"keeperDataFields": c.keeperDataFieldsTemplateFunc,
"keeperFindPassword": c.keeperFindPasswordTemplateFunc,
"keyring": c.keyringTemplateFunc,
"lastpass": c.lastpassTemplateFunc,
"lastpassRaw": c.lastpassRawTemplateFunc,
"lookPath": c.lookPathTemplateFunc,
"lstat": c.lstatTemplateFunc,
"mozillaInstallHash": c.mozillaInstallHashTemplateFunc,
"onepassword": c.onepasswordTemplateFunc,
"onepasswordDetailsFields": c.onepasswordDetailsFieldsTemplateFunc,
"onepasswordDocument": c.onepasswordDocumentTemplateFunc,
"onepasswordItemFields": c.onepasswordItemFieldsTemplateFunc,
"onepasswordRead": c.onepasswordReadTemplateFunc,
"output": c.outputTemplateFunc,
"pass": c.passTemplateFunc,
"passFields": c.passFieldsTemplateFunc,
"passhole": c.passholeTemplateFunc,
"passRaw": c.passRawTemplateFunc,
"pruneEmptyDicts": c.pruneEmptyDictsTemplateFunc,
"quoteList": c.quoteListTemplateFunc,
"rbw": c.rbwTemplateFunc,
"rbwFields": c.rbwFieldsTemplateFunc,
"replaceAllRegex": c.replaceAllRegexTemplateFunc,
"secret": c.secretTemplateFunc,
"secretJSON": c.secretJSONTemplateFunc,
"setValueAtPath": c.setValueAtPathTemplateFunc,
"splitList": c.splitListTemplateFunc,
"stat": c.statTemplateFunc,
"toIni": c.toIniTemplateFunc,
"toPrettyJson": c.toPrettyJsonTemplateFunc,
"toToml": c.toTomlTemplateFunc,
"toYaml": c.toYamlTemplateFunc,
"vault": c.vaultTemplateFunc,
"awsSecretsManager": c.awsSecretsManagerTemplateFunc,
"awsSecretsManagerRaw": c.awsSecretsManagerRawTemplateFunc,
"azureKeyVault": c.azureKeyVaultTemplateFunc,
"bitwarden": c.bitwardenTemplateFunc,
"bitwardenAttachment": c.bitwardenAttachmentTemplateFunc,
"bitwardenAttachmentByRef": c.bitwardenAttachmentByRefTemplateFunc,
"bitwardenFields": c.bitwardenFieldsTemplateFunc,
"bitwardenSecrets": c.bitwardenSecretsTemplateFunc,
"comment": c.commentTemplateFunc,
"dashlaneNote": c.dashlaneNoteTemplateFunc,
"dashlanePassword": c.dashlanePasswordTemplateFunc,
"decrypt": c.decryptTemplateFunc,
"deleteValueAtPath": c.deleteValueAtPathTemplateFunc,
"doppler": c.dopplerTemplateFunc,
"dopplerProjectJson": c.dopplerProjectJSONTemplateFunc,
"ejsonDecrypt": c.ejsonDecryptTemplateFunc,
"ejsonDecryptWithKey": c.ejsonDecryptWithKeyTemplateFunc,
"encrypt": c.encryptTemplateFunc,
"eqFold": c.eqFoldTemplateFunc,
"findExecutable": c.findExecutableTemplateFunc,
"findOneExecutable": c.findOneExecutableTemplateFunc,
"fromIni": c.fromIniTemplateFunc,
"fromJson": c.fromJsonTemplateFunc,
"fromJsonc": c.fromJsoncTemplateFunc,
"fromToml": c.fromTomlTemplateFunc,
"fromYaml": c.fromYamlTemplateFunc,
"gitHubKeys": c.gitHubKeysTemplateFunc,
"gitHubLatestRelease": c.gitHubLatestReleaseTemplateFunc,
"gitHubLatestReleaseAssetURL": c.gitHubLatestReleaseAssetURLTemplateFunc,
"gitHubLatestTag": c.gitHubLatestTagTemplateFunc,
"gitHubReleases": c.gitHubReleasesTemplateFunc,
"gitHubTags": c.gitHubTagsTemplateFunc,
"glob": c.globTemplateFunc,
"gopass": c.gopassTemplateFunc,
"gopassRaw": c.gopassRawTemplateFunc,
"hcpVaultSecret": c.hcpVaultSecretTemplateFunc,
"hcpVaultSecretJson": c.hcpVaultSecretJSONTemplateFunc,
"hexDecode": c.hexDecodeTemplateFunc,
"hexEncode": c.hexEncodeTemplateFunc,
"include": c.includeTemplateFunc,
"includeTemplate": c.includeTemplateTemplateFunc,
"ioreg": c.ioregTemplateFunc,
"isExecutable": c.isExecutableTemplateFunc,
"joinPath": c.joinPathTemplateFunc,
"jq": c.jqTemplateFunc,
"keepassxc": c.keepassxcTemplateFunc,
"keepassxcAttachment": c.keepassxcAttachmentTemplateFunc,
"keepassxcAttribute": c.keepassxcAttributeTemplateFunc,
"keeper": c.keeperTemplateFunc,
"keeperDataFields": c.keeperDataFieldsTemplateFunc,
"keeperFindPassword": c.keeperFindPasswordTemplateFunc,
"keyring": c.keyringTemplateFunc,
"lastpass": c.lastpassTemplateFunc,
"lastpassRaw": c.lastpassRawTemplateFunc,
"lookPath": c.lookPathTemplateFunc,
"lstat": c.lstatTemplateFunc,
"mozillaInstallHash": c.mozillaInstallHashTemplateFunc,
"onepassword": c.onepasswordTemplateFunc,
"onepasswordDetailsFields": c.onepasswordDetailsFieldsTemplateFunc,
"onepasswordDocument": c.onepasswordDocumentTemplateFunc,
"onepasswordItemFields": c.onepasswordItemFieldsTemplateFunc,
"onepasswordRead": c.onepasswordReadTemplateFunc,
"output": c.outputTemplateFunc,
"pass": c.passTemplateFunc,
"passFields": c.passFieldsTemplateFunc,
"passhole": c.passholeTemplateFunc,
"passRaw": c.passRawTemplateFunc,
"pruneEmptyDicts": c.pruneEmptyDictsTemplateFunc,
"quoteList": c.quoteListTemplateFunc,
"rbw": c.rbwTemplateFunc,
"rbwFields": c.rbwFieldsTemplateFunc,
"replaceAllRegex": c.replaceAllRegexTemplateFunc,
"secret": c.secretTemplateFunc,
"secretJSON": c.secretJSONTemplateFunc,
"setValueAtPath": c.setValueAtPathTemplateFunc,
"splitList": c.splitListTemplateFunc,
"stat": c.statTemplateFunc,
"toIni": c.toIniTemplateFunc,
"toPrettyJson": c.toPrettyJsonTemplateFunc,
"toToml": c.toTomlTemplateFunc,
"toYaml": c.toYamlTemplateFunc,
"vault": c.vaultTemplateFunc,
} {
c.addTemplateFunc(key, value)
}
Expand Down
44 changes: 36 additions & 8 deletions internal/cmd/githubtemplatefuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"context"
"fmt"
"path"
"strings"
"time"

Expand Down Expand Up @@ -107,14 +108,33 @@ func (c *Config) gitHubKeysTemplateFunc(user string) []*github.Key {
return allKeys
}

func (c *Config) gitHubLatestReleaseTemplateFunc(ownerRepo string) *github.RepositoryRelease {
owner, repo, err := gitHubSplitOwnerRepo(ownerRepo)
func (c *Config) gitHubLatestReleaseAssetURLTemplateFunc(ownerRepo, pattern string) string {
release, err := c.gitHubLatestRelease(ownerRepo)
if err != nil {
panic(err)
}
for _, asset := range release.Assets {
if asset.Name == nil {
continue
}
switch ok, err := path.Match(pattern, *asset.Name); {
case err != nil:
panic(err)
case ok:
return *asset.BrowserDownloadURL
}
}
return ""
}

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

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

now := time.Now()
Expand All @@ -123,9 +143,9 @@ func (c *Config) gitHubLatestReleaseTemplateFunc(ownerRepo string) *github.Repos
var gitHubLatestReleaseStateValue gitHubLatestReleaseState
switch ok, err := chezmoi.PersistentStateGet(c.persistentState, gitHubLatestReleaseStateBucket, gitHubLatestReleaseKey, &gitHubLatestReleaseStateValue); {
case err != nil:
panic(err)
return nil, err
case ok && now.Before(gitHubLatestReleaseStateValue.RequestedAt.Add(c.GitHub.RefreshPeriod)):
return gitHubLatestReleaseStateValue.Release
return gitHubLatestReleaseStateValue.Release, nil
}
}

Expand All @@ -134,19 +154,19 @@ func (c *Config) gitHubLatestReleaseTemplateFunc(ownerRepo string) *github.Repos

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

release, _, err := gitHubClient.Repositories.GetLatestRelease(ctx, owner, repo)
if err != nil {
panic(err)
return nil, err
}

if err := chezmoi.PersistentStateSet(c.persistentState, gitHubLatestReleaseStateBucket, gitHubLatestReleaseKey, &gitHubLatestReleaseState{
RequestedAt: now,
Release: release,
}); err != nil {
panic(err)
return nil, err
}

if c.gitHub.latestReleaseCache == nil {
Expand All @@ -157,6 +177,14 @@ func (c *Config) gitHubLatestReleaseTemplateFunc(ownerRepo string) *github.Repos
}
c.gitHub.latestReleaseCache[owner][repo] = release

return release, nil
}

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

Expand Down

0 comments on commit a396e3a

Please sign in to comment.