Browse Source

Add command line parameter support

master
NGnius 5 years ago
parent
commit
c867792535
2 changed files with 157 additions and 20 deletions
  1. +2
    -0
      .gitignore
  2. +155
    -20
      rxsm-updater/rxsm-updater.go

+ 2
- 0
.gitignore View File

@@ -12,3 +12,5 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# binary
rxsm-updater/rxsm-updater

+ 155
- 20
rxsm-updater/rxsm-updater.go View File

@@ -6,52 +6,186 @@ import (
"runtime"
"os"
"io"
"log"
"fmt"
"errors"
"archive/zip"
"path/filepath"
"flag"
)

const (
DownloadTempFile string = "rxsm-update.zip"
DefaultZipFile string = "rxsm-update.zip"
RXSMUpdaterVersion string = "v0.0.1"
DefaultUpdateServer string = "https://rxsm-update.exmods.org"
)

var (
// command line flag arguments
Zipfile string = DefaultZipFile
Target string
Unattended bool
PlatformStream string
UpdateServer string
TargetBinary string
)

func init() {
fmt.Println("RXSM Updater version", RXSMUpdaterVersion)
fmt.Println("This program updates RXSM headlessly. Use rxsm-updater --help to view advanced features")
fmt.Println(os.Args)
flag.StringVar(&Zipfile, "zip", DefaultZipFile, "The zipfile to extract from (and download to, in unattended mode)")
flag.StringVar(&Target, "target", "", "The directory to target for the update (the directory containing RXSM)")
flag.BoolVar(&Unattended, "unattended", false, "Download & extract the RXSM update (WIP)")
flag.StringVar(&PlatformStream, "stream", "release", "The update stream to use when retrieving updates in unattended mode")
flag.StringVar(&UpdateServer, "server", DefaultUpdateServer, "The web server to use for retrieving update information")
}

func main() {
InstallRXSMUpdate(func(int, string){})
flag.Parse()
if Unattended {
fmt.Println("Attempting WIP Unattended update")
fmt.Println("This is not advised, and will fail if an updater update is necessary")
URL, isUpdatable, ok := CheckForUpdate(UpdateServer, "", runtime.GOOS+"/"+runtime.GOARCH+"/"+PlatformStream)
if !(ok && isUpdatable && URL != "") {
fmt.Println("No update found")
return
}
downloadErr := DownloadRXSMUpdate(URL, func(int, string){})
if downloadErr != nil {
fmt.Println(downloadErr)
return
}
proc, forkErr := ForkRXSMUpdate()
if forkErr != nil {
fmt.Println(forkErr)
return
}
fmt.Println("Extraction forked to", proc.Pid)
} else {
fmt.Println("Installing from", Zipfile)
installErr := InstallRXSMUpdate(func(int, string){})
if installErr != nil {
fmt.Println(installErr)
return
}
}
fmt.Println("Operation complete")
// TODO: implement callback for Qt window
}

func InstallRXSMUpdate(statusCallback func(progress int, description string)) {
f, openErr := os.Open(DownloadTempFile)
func DownloadRXSMUpdateQuiet(URL string) {
DownloadRXSMUpdate(URL, func(int, string){})
}

func DownloadRXSMUpdate(URL string, statusCallback func(progress int, description string)) (error){
// progress is out of 100
statusCallback(25, "Downloading")
fmt.Println("Downloading update from "+URL)
f, createErr := os.Create(Zipfile)
if createErr != nil {
fmt.Println("Error creating temporary update file")
statusCallback(-1, "Error creating update temporary file")
return createErr
}
defer f.Close()
ok := DownloadUpdate(URL, f)
if !ok {
fmt.Println("Error downloading update")
statusCallback(-1, "Download failed")
return errors.New("download failed in update API")
}
statusCallback(50, "Installing Updater")
f.Sync()
f.Seek(0,0)
fStat, statErr := f.Stat()
if statErr != nil {
fmt.Println("Error getting download temp file stat")
return statErr
}
fmt.Println("Downloaded", fStat.Size(), "bytes")
statusCallback(75, "Extracting Updater")
// TODO: implement way to have this actually work
zipFile, zipErr := zip.NewReader(f, fStat.Size())
if zipErr != nil {
fmt.Println("Error creating zip reader")
fmt.Println(zipErr)
return zipErr
}
for _, f := range zipFile.File {
if !f.FileHeader.Mode().IsDir() {
filename := filepath.Clean(f.FileHeader.Name)
var updaterFilename string
if runtime.GOOS == "windows" {
updaterFilename = "rxsm-updater.exe"
TargetBinary = "rxsm-updater2.exe"
} else {
updaterFilename = "rxsm-updater"
TargetBinary = "rxsm-updater2"
}
if len(filename) >= len(updaterFilename) && filename[:len(updaterFilename)] == updaterFilename {
fileReadCloser, openErr := f.Open()
if openErr != nil {
fmt.Println("Error opening updater in zip archive")
return openErr
}
defer fileReadCloser.Close()
destFile, createErr := os.Create(TargetBinary)
if createErr != nil {
fmt.Println("Error creating updater file")
return createErr
}
defer destFile.Close()
_, copyErr := io.Copy(destFile, fileReadCloser)
if copyErr != nil {
fmt.Println("Error copying/extracting updater")
return copyErr
}
}
}
}
statusCallback(100, "Complete")
return nil
}

func ForkRXSMUpdate() (process *os.Process, err error) {
if runtime.GOOS == "windows" {
return os.StartProcess(TargetBinary, []string{TargetBinary, "--zip", Zipfile, "--target", Target}, nil)
} else {
return os.StartProcess(TargetBinary, []string{TargetBinary, "--zip", Zipfile, "--target", Target}, nil)
}
}

func InstallRXSMUpdate(statusCallback func(progress int, description string)) (error){
f, openErr := os.Open(Zipfile)
if openErr != nil {
log.Println("Error opening zip file")
log.Println(openErr)
return
fmt.Println("Error opening zip file")
statusCallback(-1, "Opening zip file failed")
return openErr
}
statusCallback(1, "Extracting")
f.Sync()
f.Seek(0,0)
fStat, statErr := f.Stat()
if statErr != nil {
log.Println("Error retrieving file stats")
log.Println(statErr)
statusCallback(-1, "Extracting failed")
fmt.Println("Error retrieving file stats")
statusCallback(-1, "Extracting zip file failed")
return statErr
}
unpackErr := unpackRXSMInstall(f, fStat.Size(), func(p int, d string){statusCallback(1, d)})
if unpackErr != nil {
log.Println("Error extracting update")
log.Println(unpackErr)
statusCallback(-1, "Extracting failed")
return
fmt.Println("Error extracting update")
statusCallback(-1, "Unpacking failed")
return unpackErr
}
statusCallback(2, "Installing")
// TODO: is there installation required?
rmErr := os.Remove(DownloadTempFile)
rmErr := os.Remove(Zipfile)
if rmErr != nil {
log.Println("Error removing download file")
log.Println(rmErr)
fmt.Println("Error removing download file")
statusCallback(-1, "Installing failed")
return
return rmErr
}
return nil
}

func unpackRXSMInstallPath(path string) (error) {
@@ -93,9 +227,10 @@ func unpackRXSMInstall(reader io.ReaderAt, size int64, statusCallback func(progr
filename = filename[len(runtime.GOOS)+1:]
}
}
filename = filepath.Join(Target, filename)
dirErr := os.MkdirAll(filepath.Dir(filename), os.ModeDir | os.ModePerm)
if dirErr != nil {
log.Println("Dir err "+filepath.Dir(filename))
fmt.Println("Dir err "+filepath.Dir(filename))
return dirErr
}
newFile, createErr := os.Create(filename)