Skip to content

Commit

Permalink
Backport of improved self-upgrade (de7659d)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasdille committed Oct 2, 2024
1 parent da2a731 commit aebdd16
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 62 deletions.
71 changes: 36 additions & 35 deletions cmd/uniget/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ var profileDDirectory = configRoot + "/profile.d"
var metadataFileName = "metadata.json"
var metadataFile = cacheDirectory + "/" + metadataFileName
var registry = "ghcr.io"
var projectRepository = "uniget-org/cli"
var imageRepository = "uniget-org/tools"
var githubOrganization = "uniget-org"
var projectRepository = githubOrganization + "/cli"
var imageRepository = githubOrganization + "/tools"
var toolSeparator = "/"
var registryImagePrefix = registry + "/" + imageRepository + toolSeparator
var tools tool.Tools
Expand Down Expand Up @@ -75,17 +76,17 @@ func checkClientVersionRequirement(tool *tool.Tool) {
}

logging.Debugf("Checking if client version %s is at least %s", version, requiredCliVersion)
v1, err := goversion.NewVersion(requiredCliVersion)
if err != nil {
panic(err)
}
v2, err := goversion.NewVersion(version)
if err != nil {
panic(err)
}

if v1.GreaterThan(v2) {

v1, err := goversion.NewVersion(requiredCliVersion)
if err != nil {
panic(err)
}
v2, err := goversion.NewVersion(version)
if err != nil {
panic(err)
}

if v1.GreaterThan(v2) {
logging.Error.Printfln("The tool %s requires at least version %s but you have %s", tool.Name, requiredCliVersion, version)
os.Exit(1)
}
Expand Down Expand Up @@ -204,7 +205,7 @@ func addViperBindings(flags *flag.FlagSet, cobraLongName string, viperName strin
}

if viperName != cobraLongName {
err = viper.BindEnv(viperName, strings.ToUpper(viper.GetEnvPrefix() + "_" + strings.ReplaceAll(cobraLongName, "-", "_")))
err = viper.BindEnv(viperName, strings.ToUpper(viper.GetEnvPrefix()+"_"+strings.ReplaceAll(cobraLongName, "-", "_")))
if err != nil {
fmt.Printf("unable to bind environment variable for flag %s: %s", cobraLongName, err)
os.Exit(1)
Expand Down Expand Up @@ -299,36 +300,36 @@ func main() {

pathRewriteRules = []tool.PathRewrite{
{
Source: "usr/local/",
Target: "",
Source: "usr/local/",
Target: "",
Operation: "REPLACE",
},
{
Source: "var/lib/uniget/",
Target: libDirectory + "/",
Source: "var/lib/uniget/",
Target: libDirectory + "/",
Operation: "REPLACE",
},
{
Source: "var/cache/uniget/",
Target: cacheDirectory + "/",
Source: "var/cache/uniget/",
Target: cacheDirectory + "/",
Operation: "REPLACE",
},
}
if ! viper.GetBool("user") {
if !viper.GetBool("user") {
logging.Debugf("Adding path rewrite rules for system installation")

if viper.GetBool("integratesystemd") || viper.GetBool("integrateall") {
pathRewriteRules = append(pathRewriteRules, tool.PathRewrite{
Source: "etc/systemd/",
Target: "/etc/systemd/",
Source: "etc/systemd/",
Target: "/etc/systemd/",
Operation: "REPLACE",
})
}

if viper.GetBool("integrateprofiled") || viper.GetBool("integrateall") {
pathRewriteRules = append(pathRewriteRules, tool.PathRewrite{
Source: "etc/profile.d/",
Target: "/etc/profile.d/",
Source: "etc/profile.d/",
Target: "/etc/profile.d/",
Operation: "REPLACE",
})
}
Expand All @@ -338,39 +339,39 @@ func main() {

if viper.GetBool("integratedockercliplugins") || viper.GetBool("integrateall") {
pathRewriteRules = append(pathRewriteRules, tool.PathRewrite{
Source: "libexec/docker/cli-plugins/",
Target: "./.docker/cli-plugins/",
Source: "libexec/docker/cli-plugins/",
Target: "./.docker/cli-plugins/",
Operation: "REPLACE",
})
}

if viper.GetBool("integratesystemd") || viper.GetBool("integrateall") {
pathRewriteRules = append(pathRewriteRules, tool.PathRewrite{
Source: "etc/systemd/user/",
Target: "./.config/systemd/user/",
Source: "etc/systemd/user/",
Target: "./.config/systemd/user/",
Operation: "REPLACE",
})
}

if viper.GetBool("integrateprofiled") || viper.GetBool("integrateall") {
pathRewriteRules = append(pathRewriteRules, tool.PathRewrite{
Source: "etc/profile.d/",
Target: "./.config/profile.d/",
Source: "etc/profile.d/",
Target: "./.config/profile.d/",
Operation: "REPLACE",
})
}

if viper.GetBool("integrateetc") || viper.GetBool("integrateall") {
pathRewriteRules = append(pathRewriteRules, tool.PathRewrite{
Source: "etc/",
Target: "./.config/",
Source: "etc/",
Target: "./.config/",
Operation: "REPLACE",
})
}
}
pathRewriteRules = append(pathRewriteRules, tool.PathRewrite{
Source: "",
Target: viper.GetString("target") + "/",
Source: "",
Target: viper.GetString("target") + "/",
Operation: "PREPEND",
})
if viper.GetBool("debug") {
Expand Down
98 changes: 71 additions & 27 deletions cmd/uniget/self-upgrade.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
package main

import (
"context"
"fmt"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"sort"

"github.com/google/go-github/github"
goversion "github.com/hashicorp/go-version"
"github.com/spf13/cobra"
"github.com/uniget-org/cli/pkg/archive"
"github.com/uniget-org/cli/pkg/logging"
)

var requestedVersion string
var allowPrereleaseVersion bool
var dryRun bool

func initSelfUpgradeCmd() {
rootCmd.AddCommand(selfUpgradeCmd)

selfUpgradeCmd.Flags().StringVar(&requestedVersion, "version", "latest", "Upgrade to a specific version")
selfUpgradeCmd.Flags().BoolVar(&allowPrereleaseVersion, "allow-prerelease", false, "Allow upgrading to prerelease version")
selfUpgradeCmd.Flags().BoolVar(&dryRun, "dry-run", false, "Do not perform the upgrade, only show what would be done")
}

var selfUpgradeCmd = &cobra.Command{
Expand All @@ -29,7 +36,10 @@ var selfUpgradeCmd = &cobra.Command{
Long: header + "\nUpgrade " + projectName + " to latest version",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
versionRegex := regexp.MustCompile(`^\d+\.\d+\.\d+(-[a-z]+\.\d+)?$`)
versionRegex, err := regexp.Compile(`^\d+\.\d+\.\d+(-[a-z]+\.\d+)?$`)
if err != nil {
return fmt.Errorf("cannot compile regexp: %w", err)
}
if !versionRegex.MatchString(version) {
return fmt.Errorf("invalid version %s", version)
}
Expand All @@ -51,6 +61,28 @@ var selfUpgradeCmd = &cobra.Command{
logging.Debugf("%s is available at %s\n", selfExe, path)
selfDir := filepath.Dir(path)

if allowPrereleaseVersion && requestedVersion == "latest" {
logging.Debugf("Allowing prerelease version")

githubClient := github.NewClient(nil)
releases, _, err := githubClient.Repositories.ListReleases(context.Background(), githubOrganization, "cli", &github.ListOptions{PerPage: 100})
if err != nil {
return fmt.Errorf("failed to list releases: %s", err)
}

versions := make([]*goversion.Version, 0)
for _, release := range releases {
version, err := goversion.NewSemver(*release.TagName)
if err != nil {
continue
}

versions = append(versions, version)
sort.Sort(goversion.Collection(versions))
}
requestedVersion = versions[len(versions)-1].String()
}

var url string
if requestedVersion == "latest" {
url = fmt.Sprintf("https://github.com/%s/releases/%s/download/uniget_%s_%s.tar.gz", projectRepository, requestedVersion, runtime.GOOS, arch)
Expand All @@ -59,27 +91,13 @@ var selfUpgradeCmd = &cobra.Command{
url = fmt.Sprintf("https://github.com/%s/releases/download/v%s/uniget_%s_%s.tar.gz", projectRepository, requestedVersion, runtime.GOOS, arch)
}

logging.Debugf("Downloading %s", url)
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
re, err := regexp.Compile(`\/uniget-org\/cli\/releases\/download\/(v\d+\.\d+\.\d+)\/`)
if err != nil {
return fmt.Errorf("cannot compile regexp: %w", err)
}

if re.MatchString(req.URL.Path) {
requestedVersion = re.FindStringSubmatch(req.URL.Path)[1]
}
return nil
},
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("failed to create request: %s", err)
logging.Debugf("Downloading from %s", url)
if dryRun {
logging.Info.Printfln("Would download version %s from %s", requestedVersion, url)
return nil
}
req.Header.Set("Accept", "application/octet-stream")
req.Header.Set("User-Agent", fmt.Sprintf("%s/%s", projectName, version))
resp, err := client.Do(req)

resp, err := downloadReleaseAsset(url)
if err != nil {
return fmt.Errorf("failed to download %s: %s", url, err)
}
Expand All @@ -89,16 +107,15 @@ var selfUpgradeCmd = &cobra.Command{
return fmt.Errorf("failed to download %s: %s", url, resp.Status)
}

v1, err := goversion.NewVersion(requestedVersion)
requestedVersionVersion, err := goversion.NewVersion(requestedVersion)
if err != nil {
panic(err)
return fmt.Errorf("failed to parse version %s: %s", requestedVersion, err)
}
v2, err := goversion.NewVersion(version)
versionVersion, err := goversion.NewVersion(version)
if err != nil {
panic(err)
return fmt.Errorf("failed to parse current version %s: %s", version, err)
}

if v1.LessThanOrEqual(v2) {
if requestedVersionVersion.LessThanOrEqual(versionVersion) {
logging.Info.Printfln("Latest version %s already installed.", version)
return nil
}
Expand All @@ -120,3 +137,30 @@ var selfUpgradeCmd = &cobra.Command{
return nil
},
}

func downloadReleaseAsset(url string) (*http.Response, error) {
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
re, err := regexp.Compile(`\/uniget-org\/cli\/releases\/download\/(v\d+\.\d+\.\d+)\/`)
if err != nil {
return fmt.Errorf("cannot compile regexp: %w", err)
}

if re.MatchString(req.URL.Path) {
requestedVersion = re.FindStringSubmatch(req.URL.Path)[1]
}
return nil
},
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %s", err)
}
req.Header.Set("Accept", "application/octet-stream")
req.Header.Set("User-Agent", fmt.Sprintf("%s/%s", projectName, version))
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to download %s: %s", url, err)
}
return resp, nil
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ toolchain go1.23.1

require (
github.com/charmbracelet/glamour v0.8.0
github.com/google/go-github v17.0.0+incompatible
github.com/hashicorp/go-version v1.7.0
github.com/jedib0t/go-pretty/v6 v6.5.9
github.com/muesli/mango-cobra v1.2.0
Expand Down Expand Up @@ -33,6 +34,7 @@ require (
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,13 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
Expand Down Expand Up @@ -230,6 +235,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
Expand Down

0 comments on commit aebdd16

Please sign in to comment.