github: allow using a regular expression to match assets

safeguard against "undefined behavior" when a release has more than one asset

Signed-off-by: unilock <unilock@fennet.rentals>
This commit is contained in:
unilock 2024-04-15 15:30:22 -04:00
parent bae4a6be64
commit d54da349d5
2 changed files with 49 additions and 15 deletions

View File

@ -8,7 +8,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings"
"github.com/packwiz/packwiz/core" "github.com/packwiz/packwiz/core"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -39,6 +38,15 @@ var installCmd = &cobra.Command{
var slug string var slug string
var branch string var branch string
// Regex to match potential release assets against.
// The default will match any asset with a name that does *not* end with:
// - "-api.jar"
// - "-dev.jar"
// - "-sources.jar"
// In most cases, this will only match one asset.
// TODO: Hopefully.
regex := `^.+(?<!-api|-dev|-sources)\.jar$`
// Check if the argument is a valid GitHub repository URL; if so, extract the slug from the URL. // Check if the argument is a valid GitHub repository URL; if so, extract the slug from the URL.
// Otherwise, interpret the argument as a slug directly. // Otherwise, interpret the argument as a slug directly.
matches := GithubRegex.FindStringSubmatch(args[0]) matches := GithubRegex.FindStringSubmatch(args[0])
@ -58,8 +66,11 @@ var installCmd = &cobra.Command{
if branchFlag != "" { if branchFlag != "" {
branch = branchFlag branch = branchFlag
} }
if regexFlag != "" {
regex = regexFlag
}
err = installMod(repo, branch, pack) err = installMod(repo, branch, regex, pack)
if err != nil { if err != nil {
fmt.Printf("Failed to add project: %s\n", err) fmt.Printf("Failed to add project: %s\n", err)
os.Exit(1) os.Exit(1)
@ -67,13 +78,13 @@ var installCmd = &cobra.Command{
}, },
} }
func installMod(repo Repo, branch string, pack core.Pack) error { func installMod(repo Repo, branch string, regex string, pack core.Pack) error {
latestRelease, err := getLatestRelease(repo.FullName, branch) latestRelease, err := getLatestRelease(repo.FullName, branch)
if err != nil { if err != nil {
return fmt.Errorf("failed to get latest release: %v", err) return fmt.Errorf("failed to get latest release: %v", err)
} }
return installRelease(repo, latestRelease, pack) return installRelease(repo, latestRelease, regex, pack)
} }
func getLatestRelease(slug string, branch string) (Release, error) { func getLatestRelease(slug string, branch string) (Release, error) {
@ -108,21 +119,28 @@ func getLatestRelease(slug string, branch string) (Release, error) {
return releases[0], nil return releases[0], nil
} }
func installRelease(repo Repo, release Release, pack core.Pack) error { func installRelease(repo Repo, release Release, regex string, pack core.Pack) error {
var files = release.Assets expr := regexp.MustCompile(regex)
if len(files) == 0 { if len(release.Assets) == 0 {
return errors.New("release doesn't have any files attached") return errors.New("release doesn't have any assets attached")
} }
// TODO: add some way to allow users to pick which file to install? var files []Asset
var file = files[0]
for _, v := range release.Assets { for _, v := range release.Assets {
if strings.HasSuffix(v.Name, ".jar") { if expr.MatchString(v.Name) {
file = v files = append(files, v)
} }
} }
if len(files) > 1 {
// TODO: also print file names
return errors.New("release has more than one asset matching regex")
}
file := files[0]
// Install the file // Install the file
fmt.Printf("Installing %s from release %s\n", file.Name, release.TagName) fmt.Printf("Installing %s from release %s\n", file.Name, release.TagName)
index, err := pack.LoadIndex() index, err := pack.LoadIndex()
@ -136,6 +154,7 @@ func installRelease(repo Repo, release Release, pack core.Pack) error {
Slug: repo.FullName, Slug: repo.FullName,
Tag: release.TagName, Tag: release.TagName,
Branch: release.TargetCommitish, // TODO: if no branch is specified by the user, we shouldn't record it - in order to remain branch-agnostic in getLatestRelease() Branch: release.TargetCommitish, // TODO: if no branch is specified by the user, we shouldn't record it - in order to remain branch-agnostic in getLatestRelease()
Regex: regex, // TODO: ditto!
}.ToMap() }.ToMap()
if err != nil { if err != nil {
return err return err
@ -196,9 +215,11 @@ func installRelease(repo Repo, release Release, pack core.Pack) error {
} }
var branchFlag string var branchFlag string
var regexFlag string
func init() { func init() {
githubCmd.AddCommand(installCmd) githubCmd.AddCommand(installCmd)
installCmd.Flags().StringVar(&branchFlag, "branch", "", "The GitHub repository branch to retrieve releases for") installCmd.Flags().StringVar(&branchFlag, "branch", "", "The GitHub repository branch to retrieve releases for")
installCmd.Flags().StringVar(&regexFlag, "regex", "", "The regular expression to match releases against")
} }

View File

@ -3,6 +3,7 @@ package github
import ( import (
"errors" "errors"
"fmt" "fmt"
"regexp"
"strings" "strings"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@ -13,6 +14,7 @@ type ghUpdateData struct {
Slug string `mapstructure:"slug"` Slug string `mapstructure:"slug"`
Tag string `mapstructure:"tag"` Tag string `mapstructure:"tag"`
Branch string `mapstructure:"branch"` Branch string `mapstructure:"branch"`
Regex string `mapstructure:"regex"`
} }
type ghUpdater struct{} type ghUpdater struct{}
@ -51,18 +53,29 @@ func (u ghUpdater) CheckUpdate(mods []*core.Mod, pack core.Pack) ([]core.UpdateC
continue continue
} }
expr := regexp.MustCompile(data.Regex)
if len(newRelease.Assets) == 0 { if len(newRelease.Assets) == 0 {
results[i] = core.UpdateCheck{Error: errors.New("new release doesn't have any assets")} results[i] = core.UpdateCheck{Error: errors.New("new release doesn't have any assets")}
continue continue
} }
newFile := newRelease.Assets[0] var newFiles []Asset
for _, v := range newRelease.Assets { for _, v := range newRelease.Assets {
if strings.HasSuffix(v.Name, ".jar") { if expr.MatchString(v.Name) {
newFile = v newFiles = append(newFiles, v)
} }
} }
if len(newFiles) > 1 {
// TODO: also print file names
results[i] = core.UpdateCheck{Error: errors.New("release has more than one asset matching regex")}
continue
}
newFile := newFiles[0]
results[i] = core.UpdateCheck{ results[i] = core.UpdateCheck{
UpdateAvailable: true, UpdateAvailable: true,
UpdateString: mod.FileName + " -> " + newFile.Name, UpdateString: mod.FileName + " -> " + newFile.Name,