Added option to filter multiple assets in release

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
This commit is contained in:
Frederic BIDON 2019-12-16 20:12:11 +01:00
parent 80762b859d
commit 0148193fae
No known key found for this signature in database
GPG Key ID: FAE956AB61DCDA59
4 changed files with 68 additions and 21 deletions

2
go.mod
View File

@ -16,3 +16,5 @@ require (
google.golang.org/appengine v1.3.0 // indirect google.golang.org/appengine v1.3.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
) )
go 1.13

View File

@ -12,7 +12,13 @@ import (
var reVersion = regexp.MustCompile(`\d+\.\d+\.\d+`) var reVersion = regexp.MustCompile(`\d+\.\d+\.\d+`)
func findAssetFromReleasse(rel *github.RepositoryRelease, suffixes []string, targetVersion string) (*github.ReleaseAsset, semver.Version, bool) { func findAssetFromRelease(rel *github.RepositoryRelease,
suffixes []string, targetVersion string, opts ...Option) (*github.ReleaseAsset, semver.Version, bool) {
settings := defaultSettings()
for _, apply := range opts {
apply(settings)
}
if targetVersion != "" && targetVersion != rel.GetTagName() { if targetVersion != "" && targetVersion != rel.GetTagName() {
log.Println("Skip", rel.GetTagName(), "not matching to specified version", targetVersion) log.Println("Skip", rel.GetTagName(), "not matching to specified version", targetVersion)
return nil, semver.Version{}, false return nil, semver.Version{}, false
@ -49,7 +55,16 @@ func findAssetFromReleasse(rel *github.RepositoryRelease, suffixes []string, tar
for _, asset := range rel.Assets { for _, asset := range rel.Assets {
name := asset.GetName() name := asset.GetName()
for _, s := range suffixes { for _, s := range suffixes {
if strings.HasSuffix(name, s) { if strings.HasSuffix(name, s) { // require version, arch etc
if len(settings.filters) > 0 {
for _, filter := range settings.filters { // extra condition to select among several release artifacts
if filter.MatchString(name) {
return &asset, ver, true
}
}
break
}
// default: assume single artifact
return &asset, ver, true return &asset, ver, true
} }
} }
@ -68,7 +83,8 @@ func findValidationAsset(rel *github.RepositoryRelease, validationName string) (
return nil, false return nil, false
} }
func findReleaseAndAsset(rels []*github.RepositoryRelease, targetVersion string) (*github.RepositoryRelease, *github.ReleaseAsset, semver.Version, bool) { func findReleaseAndAsset(rels []*github.RepositoryRelease,
targetVersion string, opts ...Option) (*github.RepositoryRelease, *github.ReleaseAsset, semver.Version, bool) {
// Generate candidates // Generate candidates
suffixes := make([]string, 0, 2*7*2) suffixes := make([]string, 0, 2*7*2)
for _, sep := range []rune{'_', '-'} { for _, sep := range []rune{'_', '-'} {
@ -90,7 +106,7 @@ func findReleaseAndAsset(rels []*github.RepositoryRelease, targetVersion string)
// Returned list from GitHub API is in the order of the date when created. // Returned list from GitHub API is in the order of the date when created.
// ref: https://github.com/rhysd/go-github-selfupdate/issues/11 // ref: https://github.com/rhysd/go-github-selfupdate/issues/11
for _, rel := range rels { for _, rel := range rels {
if a, v, ok := findAssetFromReleasse(rel, suffixes, targetVersion); ok { if a, v, ok := findAssetFromRelease(rel, suffixes, targetVersion, opts...); ok {
// Note: any version with suffix is less than any version without suffix. // Note: any version with suffix is less than any version without suffix.
// e.g. 0.0.1 > 0.0.1-beta // e.g. 0.0.1 > 0.0.1-beta
if release == nil || v.GTE(ver) { if release == nil || v.GTE(ver) {
@ -115,13 +131,13 @@ func findReleaseAndAsset(rels []*github.RepositoryRelease, targetVersion string)
// where 'foo' is a command name. '-' can also be used as a separator. File can be compressed with zip, gzip, zxip, tar&zip or tar&zxip. // where 'foo' is a command name. '-' can also be used as a separator. File can be compressed with zip, gzip, zxip, tar&zip or tar&zxip.
// So the asset can have a file extension for the corresponding compression format such as '.zip'. // So the asset can have a file extension for the corresponding compression format such as '.zip'.
// On Windows, '.exe' also can be contained such as 'foo_windows_amd64.exe.zip'. // On Windows, '.exe' also can be contained such as 'foo_windows_amd64.exe.zip'.
func (up *Updater) DetectLatest(slug string) (release *Release, found bool, err error) { func (up *Updater) DetectLatest(slug string, opts ...Option) (release *Release, found bool, err error) {
return up.DetectVersion(slug, "") return up.DetectVersion(slug, "", opts...)
} }
// DetectVersion tries to get the given version of the repository on Github. `slug` means `owner/name` formatted string. // DetectVersion tries to get the given version of the repository on Github. `slug` means `owner/name` formatted string.
// And version indicates the required version. // And version indicates the required version.
func (up *Updater) DetectVersion(slug string, version string) (release *Release, found bool, err error) { func (up *Updater) DetectVersion(slug string, version string, opts ...Option) (release *Release, found bool, err error) {
repo := strings.Split(slug, "/") repo := strings.Split(slug, "/")
if len(repo) != 2 || repo[0] == "" || repo[1] == "" { if len(repo) != 2 || repo[0] == "" || repo[1] == "" {
return nil, false, fmt.Errorf("Invalid slug format. It should be 'owner/name': %s", slug) return nil, false, fmt.Errorf("Invalid slug format. It should be 'owner/name': %s", slug)
@ -138,7 +154,7 @@ func (up *Updater) DetectVersion(slug string, version string) (release *Release,
return nil, false, err return nil, false, err
} }
rel, asset, ver, found := findReleaseAndAsset(rels, version) rel, asset, ver, found := findReleaseAndAsset(rels, version, opts...)
if !found { if !found {
return nil, false, nil return nil, false, nil
} }
@ -162,7 +178,7 @@ func (up *Updater) DetectVersion(slug string, version string) (release *Release,
} }
if up.validator != nil { if up.validator != nil {
validationName := asset.GetName()+up.validator.Suffix() validationName := asset.GetName() + up.validator.Suffix()
validationAsset, ok := findValidationAsset(rel, validationName) validationAsset, ok := findValidationAsset(rel, validationName)
if !ok { if !ok {
return nil, false, fmt.Errorf("Failed finding validation file %q", validationName) return nil, false, fmt.Errorf("Failed finding validation file %q", validationName)
@ -175,11 +191,11 @@ func (up *Updater) DetectVersion(slug string, version string) (release *Release,
// DetectLatest detects the latest release of the slug (owner/repo). // DetectLatest detects the latest release of the slug (owner/repo).
// This function is a shortcut version of updater.DetectLatest() method. // This function is a shortcut version of updater.DetectLatest() method.
func DetectLatest(slug string) (*Release, bool, error) { func DetectLatest(slug string, opts ...Option) (*Release, bool, error) {
return DefaultUpdater().DetectLatest(slug) return DefaultUpdater().DetectLatest(slug, opts...)
} }
// DetectVersion detects the given release of the slug (owner/repo) from its version. // DetectVersion detects the given release of the slug (owner/repo) from its version.
func DetectVersion(slug string, version string) (*Release, bool, error) { func DetectVersion(slug string, version string, opts ...Option) (*Release, bool, error) {
return DefaultUpdater().DetectVersion(slug, version) return DefaultUpdater().DetectVersion(slug, version, opts...)
} }

29
selfupdate/options.go Normal file
View File

@ -0,0 +1,29 @@
package selfupdate
import (
"fmt"
"regexp"
)
type settings struct {
filters []*regexp.Regexp
}
// Option defines an optional feature for the self updater
type Option func(*settings)
func defaultSettings() *settings {
return &settings{}
}
// AssetFilter sets a filter to select the proper released asset
// from releases with multiple artifacts. The filter is regexp string.
func AssetFilter(filter string) Option {
return func(s *settings) {
rex, err := regexp.Compile(filter)
if err != nil {
panic(fmt.Sprintf("invalid regexp passed as option: %v", err))
}
s.filters = append(s.filters, rex)
}
}

View File

@ -106,7 +106,7 @@ func (up *Updater) UpdateTo(rel *Release, cmdPath string) error {
// UpdateCommand updates a given command binary to the latest version. // UpdateCommand updates a given command binary to the latest version.
// 'slug' represents 'owner/name' repository on GitHub and 'current' means the current version. // 'slug' represents 'owner/name' repository on GitHub and 'current' means the current version.
func (up *Updater) UpdateCommand(cmdPath string, current semver.Version, slug string) (*Release, error) { func (up *Updater) UpdateCommand(cmdPath string, current semver.Version, slug string, opts ...Option) (*Release, error) {
if runtime.GOOS == "windows" && !strings.HasSuffix(cmdPath, ".exe") { if runtime.GOOS == "windows" && !strings.HasSuffix(cmdPath, ".exe") {
// Ensure to add '.exe' to given path on Windows // Ensure to add '.exe' to given path on Windows
cmdPath = cmdPath + ".exe" cmdPath = cmdPath + ".exe"
@ -124,7 +124,7 @@ func (up *Updater) UpdateCommand(cmdPath string, current semver.Version, slug st
cmdPath = p cmdPath = p
} }
rel, ok, err := up.DetectLatest(slug) rel, ok, err := up.DetectLatest(slug, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -145,12 +145,12 @@ func (up *Updater) UpdateCommand(cmdPath string, current semver.Version, slug st
// UpdateSelf updates the running executable itself to the latest version. // UpdateSelf updates the running executable itself to the latest version.
// 'slug' represents 'owner/name' repository on GitHub and 'current' means the current version. // 'slug' represents 'owner/name' repository on GitHub and 'current' means the current version.
func (up *Updater) UpdateSelf(current semver.Version, slug string) (*Release, error) { func (up *Updater) UpdateSelf(current semver.Version, slug string, opts ...Option) (*Release, error) {
cmdPath, err := os.Executable() cmdPath, err := os.Executable()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return up.UpdateCommand(cmdPath, current, slug) return up.UpdateCommand(cmdPath, current, slug, opts...)
} }
// UpdateTo downloads an executable from assetURL and replace the current binary with the downloaded one. // UpdateTo downloads an executable from assetURL and replace the current binary with the downloaded one.
@ -169,12 +169,12 @@ func UpdateTo(assetURL, cmdPath string) error {
// UpdateCommand updates a given command binary to the latest version. // UpdateCommand updates a given command binary to the latest version.
// This function is a shortcut version of updater.UpdateCommand. // This function is a shortcut version of updater.UpdateCommand.
func UpdateCommand(cmdPath string, current semver.Version, slug string) (*Release, error) { func UpdateCommand(cmdPath string, current semver.Version, slug string, opts ...Option) (*Release, error) {
return DefaultUpdater().UpdateCommand(cmdPath, current, slug) return DefaultUpdater().UpdateCommand(cmdPath, current, slug, opts...)
} }
// UpdateSelf updates the running executable itself to the latest version. // UpdateSelf updates the running executable itself to the latest version.
// This function is a shortcut version of updater.UpdateSelf. // This function is a shortcut version of updater.UpdateSelf.
func UpdateSelf(current semver.Version, slug string) (*Release, error) { func UpdateSelf(current semver.Version, slug string, opts ...Option) (*Release, error) {
return DefaultUpdater().UpdateSelf(current, slug) return DefaultUpdater().UpdateSelf(current, slug, opts...)
} }