// Copyright 2013 gopm authors. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package cmd import ( "encoding/json" "fmt" "net/http" "os" "os/exec" "path" "path/filepath" "runtime" "github.com/Unknwon/com" "github.com/codegangsta/cli" "github.com/gpmgo/gopm/doc" "github.com/gpmgo/gopm/log" ) var CmdUpdate = cli.Command{ Name: "update", Usage: "check and update gopm resources including itself", Description: `Command update checks updates of resources and gopm itself. gopm update Resources will be updated automatically after executed this command, but you have to confirm before updaing gopm itself.`, Action: runUpdate, Flags: []cli.Flag{ cli.BoolFlag{"verbose, v", "show process details"}, }, } func exePath() string { file, err := exec.LookPath(os.Args[0]) if err != nil { log.Error("Update", "Fail to execute exec.LookPath") log.Fatal("", err.Error()) } path, err := filepath.Abs(file) if err != nil { log.Error("Update", "Fail to get absolutely path") log.Fatal("", err.Error()) } return path } type version struct { Gopm int PackageNameList int `json:"package_name_list"` } func runUpdate(ctx *cli.Context) { setup(ctx) isAnythingUpdated := false // Load local version info. localVerInfo := loadLocalVerInfo() // Get remote version info. var remoteVerInfo version if err := com.HttpGetJSON(http.DefaultClient, "http://gopm.io/VERSION.json", &remoteVerInfo); err != nil { log.Error("Update", "Fail to fetch VERSION.json") log.Fatal("", err.Error()) } // Package name list. if remoteVerInfo.PackageNameList > localVerInfo.PackageNameList { log.Log("Updating pkgname.list...%v > %v", localVerInfo.PackageNameList, remoteVerInfo.PackageNameList) data, err := com.HttpGetBytes(http.DefaultClient, "https://raw2.github.com/gpmgo/docs/master/pkgname.list", nil) if err != nil { log.Error("Update", "Fail to update pkgname.list") log.Fatal("", err.Error()) } _, err = com.SaveFile(path.Join(doc.HomeDir, doc.PKG_NAME_LIST_PATH), data) if err != nil { log.Error("Update", "Fail to save pkgname.list") log.Fatal("", err.Error()) } log.Log("Update pkgname.list to %v succeed!", remoteVerInfo.PackageNameList) isAnythingUpdated = true } // Gopm. if remoteVerInfo.Gopm > localVerInfo.Gopm { log.Log("Updating gopm...%v > %v", localVerInfo.Gopm, remoteVerInfo.Gopm) installRepoPath = doc.HomeDir + "/repos" tmpDirPath := filepath.Join(doc.HomeDir, "temp") tmpBinPath := filepath.Join(tmpDirPath, "gopm") if runtime.GOOS == "windows" { tmpBinPath += ".exe" } os.MkdirAll(tmpDirPath, os.ModePerm) os.Remove(tmpBinPath) // Fetch code. args := []string{"bin", "-u", "-d"} if ctx.Bool("verbose") { args = append(args, "-v") } args = append(args, []string{"github.com/gpmgo/gopm", tmpDirPath}...) stdout, stderr, err := com.ExecCmd("gopm", args...) if err != nil { log.Error("Update", "Fail to execute 'gopm bin -u -d github.com/gpmgo/gopm "+tmpDirPath+"'") log.Fatal("", err.Error()) } if len(stderr) > 0 { fmt.Print(stderr) } if len(stdout) > 0 { fmt.Print(stdout) } // Check if previous steps were successful. if !com.IsExist(tmpBinPath) { log.Error("Update", "Fail to continue command") log.Fatal("", "Previous steps weren't successful, no binary produced") } movePath := exePath() log.Log("New binary will be replaced for %s", movePath) // Move binary to given directory. if runtime.GOOS != "windows" { err := os.Rename(tmpBinPath, movePath) if err != nil { log.Error("Update", "Fail to move binary") log.Fatal("", err.Error()) } os.Chmod(movePath+"/"+path.Base(tmpBinPath), os.ModePerm) } else { batPath := filepath.Join(tmpDirPath, "update.bat") f, err := os.Create(batPath) if err != nil { log.Error("Update", "Fail to generate bat file") log.Fatal("", err.Error()) } f.WriteString("@echo off\r\n") f.WriteString(fmt.Sprintf("ping -n 1 127.0.0.1>nul\r\ncopy \"%v\" \"%v\" >nul\r\ndel \"%v\" >nul\r\n\r\n", tmpBinPath, movePath, tmpBinPath)) //f.WriteString(fmt.Sprintf("del \"%v\"\r\n", batPath)) f.Close() attr := &os.ProcAttr{ Dir: workDir, Env: os.Environ(), Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, } _, err = os.StartProcess(batPath, []string{batPath}, attr) if err != nil { log.Error("Update", "Fail to start bat process") log.Fatal("", err.Error()) } } log.Success("SUCC", "Update", "Command execute successfully!") isAnythingUpdated = true } // Save JSON. f, err := os.Create(path.Join(doc.HomeDir, doc.VER_PATH)) if err != nil { log.Error("Update", "Fail to create VERSION.json") log.Fatal("", err.Error()) } if err := json.NewEncoder(f).Encode(&remoteVerInfo); err != nil { log.Error("Update", "Fail to encode VERSION.json") log.Fatal("", err.Error()) } if !isAnythingUpdated { log.Log("Nothing need to be updated") } log.Log("Exit old gopm") } func loadLocalVerInfo() (ver version) { verPath := path.Join(doc.HomeDir, doc.VER_PATH) // First time run should not exist. if !com.IsExist(verPath) { return ver } f, err := os.Open(verPath) if err != nil { log.Error("Update", "Fail to open VERSION.json") log.Fatal("", err.Error()) } if err := json.NewDecoder(f).Decode(&ver); err != nil { log.Error("Update", "Fail to decode VERSION.json") log.Fatal("", err.Error()) } return ver }