Skip to content

Commit

Permalink
feat: update command
Browse files Browse the repository at this point in the history
  • Loading branch information
steveiliop56 committed Sep 6, 2024
1 parent eb94b5b commit 188068c
Show file tree
Hide file tree
Showing 6 changed files with 495 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# CLI Build
/runtipi-cli-go
/runtipi-cli-go.bak

# CLI Created Files
/apps
Expand Down
190 changes: 190 additions & 0 deletions cmd/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package cmd

import (
"fmt"
"os"
"os/exec"
"path"

"github.com/Delta456/box-cli-maker"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/steveiliop56/runtipi-cli-go/internal/env"
"github.com/steveiliop56/runtipi-cli-go/internal/release"
"github.com/steveiliop56/runtipi-cli-go/internal/spinner"
"github.com/steveiliop56/runtipi-cli-go/internal/utils"
)

func init() {
updateCmd.Flags().BoolVar(&noPermissions, "no-permissions", false, "Skip setting permissions.")
updateCmd.Flags().StringVar(&envFile, "env-file", "", "Path to custom .env file")
rootCmd.AddCommand(updateCmd)
}

var updateCmd = &cobra.Command{
Use: "update",
Short: "Update to the latest version",
Long: "Use this command to update your runtipi instance to the latest version",
Run: func(cmd *cobra.Command, args []string) {
// Checks args
if len(args) == 0 {
utils.PrintError("Please provide a version to update too, you can use latest, nightly or a specific tag")
os.Exit(1)
}

// Define colors
blue := color.New(color.FgBlue).SprintFunc()

// Root folder
rootFolder, osErr := os.Getwd()

if osErr != nil {
utils.PrintError("Faild to get root folder")
fmt.Printf("Error: %s\n", osErr)
os.Exit(1)
}

// Define paths
cliPath := path.Join(rootFolder, "runtipi-cli-go")

// Start spinner
spinner.SetMessage("Updating runtipi...")
spinner.Start()

// Get versions
version := args[0]
currentVersion, currentVersionErr := env.GetEnvValue("TIPI_VERSION")
if currentVersionErr != nil {
utils.PrintError("Failed to get current environment version")
fmt.Printf("Error: %s\n", currentVersionErr)
os.Exit(1)
}

spinner.PrintUpdate("Updating from " + blue(currentVersion) + " to " + blue(version))

// Validate
spinner.SetMessage("Validating version")

isValid, validateErr := release.ValidateVersion(version)

if validateErr != nil {
spinner.Fail("Error in validating version")
spinner.Stop()
fmt.Printf("Error: %s\n", validateErr)
os.Exit(1)
}

if !isValid {
spinner.Fail("Version is not valid")
spinner.Stop()
os.Exit(1)
}

spinner.Succeed("Version is valid")

// Compare versions
spinner.SetMessage("Comparing versions...")

versionToUpdate := ""

if version == "latest" {
latestVersion, latestVersionErr := release.GetLatestVersion()
if latestVersionErr != nil {
spinner.Fail("Failed to get latest version")
spinner.Stop()
fmt.Printf("Error: %s\n", latestVersionErr)
os.Exit(1)
}
versionToUpdate = latestVersion
} else if version == "nightly" {
spinner.Fail("Nightly currently not supported")
spinner.Stop()
os.Exit(1)
} else {
if currentVersion != "nightly" {
isMajor, isMajorErr := release.IsMajorBump(version, currentVersion)

if isMajorErr != nil {
spinner.Fail("Failed to compare versions")
spinner.Stop()
fmt.Printf("Error: %s\n", isMajorErr)
os.Exit(1)
}

if isMajor {
spinner.Fail("You are trying to update to a new major version. Please update manually using the update instructions on the website. https://runtipi.io/docs/reference/breaking-updates")
spinner.Stop()
os.Exit(1)
}
}

versionToUpdate = version
}

spinner.Succeed("Versions compared")

// Backup CLI
spinner.SetMessage("Backing up current CLI")

backupErr := release.BackupCurrentCLI()

if backupErr != nil {
spinner.Fail("Failed to backup current CLI, no modification were made")
spinner.Stop()
fmt.Printf("Error: %s\n", backupErr)
os.Exit(1)
}

spinner.Succeed("CLI backed up")

// Download latest CLI
spinner.SetMessage("Downloading latest CLI")

downloadErr := release.DownloadLatestCLI(versionToUpdate)

if downloadErr != nil {
spinner.Fail("Failed to download latest CLI, please copy the runtipi-cli-go.bak file to runtipi-cli-go and try again")
spinner.Stop()
fmt.Printf("Error: %s\n", downloadErr)
os.Exit(1)
}

spinner.Succeed("New CLI downloaded successfully")

// Start new CLI
spinner.SetMessage("Starting new CLI")

cliArgs := []string{"start"}

if envFile != "" {
cliArgs = append(cliArgs, "--env-file")
cliArgs = append(cliArgs, envFile)
}

if noPermissions {
cliArgs = append(cliArgs, "--no-permissions")
}

_, startErr := exec.Command(cliPath, cliArgs...).Output()

if startErr != nil {
spinner.Fail("Failed to start the new CLI, please copy the runtipi-cli-go.bak file to runtipi-cli-go and try again")
spinner.Stop()
fmt.Printf("Error: %s\n", downloadErr)
os.Exit(1)
}

spinner.Succeed("New CLI started successfully, you are good to go")

// Succeed
spinner.Stop()

internalIp, _ := env.GetEnvValue("INTERNAL_IP")
nginxPort, _ := env.GetEnvValue("NGINX_PORT")

boxMessage := "Visit http://" + internalIp + ":" + nginxPort + " to access the dashboard\n\nFind documentation and guides at: https://runtipi.io\n\nTipi is entirely written in TypeScript and we are looking for contributors!"

Box := box.New(box.Config{Py: 2, Px: 2, Type: "Double", Color: "Green", TitlePos: "Top", ContentAlign: "Center"})
Box.Print("Runtipi updated successfully 🎉", boxMessage)
},
}
155 changes: 155 additions & 0 deletions internal/release/release.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package release

import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path"
"strconv"
"strings"

"github.com/steveiliop56/runtipi-cli-go/internal/system"
)

type GithubRelease struct {
TagName string `json:"tag_name"`
Status string `json:"status"`
}

func IsMajorBump(newVersion string, currentVersion string) (bool, error) {
newVersionMajor := strings.Split(strings.Replace(newVersion, "v", "", 1), ".")[0]
currentVersionMajor := strings.Split(strings.Replace(currentVersion, "v", "", 1), ".")[0]

newVersionMajorInt, newVersionMajorIntErr := strconv.ParseInt(newVersionMajor, 10, 64)

if newVersionMajorIntErr != nil {
return false, newVersionMajorIntErr
}

currentVersionMajorInt, currentVersionMajorIntErr := strconv.ParseInt(currentVersionMajor, 10, 64)

if currentVersionMajorIntErr != nil {
return false, currentVersionMajorIntErr
}

if newVersionMajorInt > currentVersionMajorInt {
return true, nil
}

return false, nil
}

func GetLatestVersion() (string, error) {
apiUrl := "https://api.github.com/repos/steveiliop56/runtipi-cli-go/releases/latest"

response, requestErr := http.Get(apiUrl)

if requestErr != nil {
return "", requestErr
}

defer response.Body.Close()

release := new(GithubRelease)

jsonErr := json.NewDecoder(response.Body).Decode(&release)

if jsonErr != nil {
return "", jsonErr
}

return release.TagName, nil
}

func ValidateVersion(version string) (bool, error) {
apiUrl := "https://api.github.com/repos/steveiliop56/runtipi-cli-go/releases/tags/" + version

response, requestErr := http.Get(apiUrl)

if requestErr != nil {
return false, requestErr
}

defer response.Body.Close()

release := new(GithubRelease)

jsonErr := json.NewDecoder(response.Body).Decode(&release)

if jsonErr != nil {
return false, jsonErr
}

if release.Status == "404" {
return false, nil
}

return true, nil
}

func DownloadLatestCLI(version string) (error) {
arch := system.GetArch()
assetUrl := fmt.Sprintf("https://github.com/steveiliop56/runtipi-cli-go/releases/download/%s/runtipi-cli-go-%s", version, arch)

rootFolder, osErr := os.Getwd()

if osErr != nil {
return osErr
}

cliPath := path.Join(rootFolder, "runtipi-cli-go")

os.Remove(cliPath)

create, createErr := os.Create(cliPath)

if createErr != nil {
return createErr
}

defer create.Close()

response, requestErr := http.Get(assetUrl)

if requestErr != nil {
return requestErr
}

defer response.Body.Close()

_, writeErr := io.Copy(create, response.Body)

if writeErr != nil {
return writeErr
}

_, chmodErr := exec.Command("chmod", "+x", cliPath).Output()

if chmodErr != nil {
return chmodErr
}

return nil
}

func BackupCurrentCLI() (error) {
rootFolder, osErr := os.Getwd()

if osErr != nil {
return osErr
}

cliPath := path.Join(rootFolder, "runtipi-cli-go")
cliBackupPath := path.Join(rootFolder, "runtipi-cli-go.bak")

_, copyErr := exec.Command("cp", cliPath, cliBackupPath).Output()

if copyErr != nil {
return copyErr
}

return nil
}
Loading

0 comments on commit 188068c

Please sign in to comment.