diff --git a/.travis.yml b/.travis.yml index 253b0f8..5d2de49 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,22 @@ language: go go: - - '1.8' +- '1.8' before_script: - - go get -u ./... +- go get -u ./... script: - - go test -cover ./... +- go test -cover ./... +before_deploy: +- go get -u github.com/inconshreveable/mousetrap +- make build +deploy: + provider: releases + api_key: + secure: HqimbJ16THwHzXi/1Qznhi4rSH7nu01+ziZR5zVj1J3XcEBzyD9Wu/yM5ZojEt190M+0yYJrADKq8ZvVQLvmbxM05mNN7a8oJlQI0IOvOEO0GCggicXPaLPiYMzEU+/IteCyfYmOtf4S2E59Lly4GnUXkR5wqE/itrKjfMlGCplP1ooYTD6+FltI7BCXN5bJPX271Aack7KAS/pz6S0T9jUpQ4eA5Fp/+C14aXLcV9KJfsGYgLNhAI2unClqUOMJT7mQQoAkaAERw77t4MAT0Z6unL/EGvvF9NQNi6UD8QLqfjyqPv5HcT+GP+MzYjR8NBOreGtufCGiQx4mM8AvTOkfRkNWgfHAVVA2kha/CMqScHYuzWpVbbLltmnuNoLnuMWXmC5LtaMZtJwIsDnYoz7fztFcbazGVVvEKVUvARfGyM29jZNNbbMiv92AFjRYF/1PegAs/MJ5TUzfTKD477McDY6CBHWA5L46RFoX+2klKq2M8vDZsThw3MvObJTrLAJw3ffeyIMV2k5Hsq8epHJNJ0Y+LSO+uxBqchXnhuhJLSjTmJXRGdKY6/bR7oQNVeW0Oi6nLuLE9HX6eIaUITpUvRgdMVfgf1ph8zjtEPygoedaY8OFQ4oLeN4dKr4q3OJy1p872uiHRtPvm0nJx7TgBzdd0EqOQh5lS5r/Si8= + skip_cleanup: true + file: + - ian-darwin-amd64 + - ian-linux-amd64 + - ian-windows-amd64.exe + on: + repo: thylong/ian + tags: true diff --git a/Makefile b/Makefile index 68d87ef..ad2f240 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ build: # Build for Linux amd64 GOOS=linux GOARCH=${GOARCH} go build -i -v -o ${NAME}-linux-${GOARCH} -ldflags="-X main.version=${VERSION}" ${PKG} # Build for Windows amd64 - GOOS=windows GOARCH=${GOARCH} go build -i -v -o ${NAME}-linux-${GOARCH}.exe -ldflags="-X main.version=${VERSION}" ${PKG} + GOOS=windows GOARCH=${GOARCH} go build -i -v -o ${NAME}-windows-${GOARCH}.exe -ldflags="-X main.version=${VERSION}" ${PKG} test: go test -cover ./... diff --git a/backend/config/config.go b/backend/config/config.go index 449d31e..5c00f88 100644 --- a/backend/config/config.go +++ b/backend/config/config.go @@ -21,6 +21,7 @@ import ( "os/exec" "os/user" "path/filepath" + "regexp" "strings" yaml "gopkg.in/yaml.v2" @@ -137,7 +138,13 @@ func SetupConfigFile(ConfigFileName string) { dotfilesRepositoryPrefix := "\ndotfiles:\n" dotfilesRepository := fmt.Sprintf(" repository: %s\n", env.GetDotfilesRepository()) - repositoryProvider := " provider: github" + + provider := "github" + re := regexp.MustCompile("([a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9])\\.[a-zA-Z]{2,}") + if len(re.FindStringSubmatch(dotfilesRepository)) > 1 { + provider = re.FindStringSubmatch(dotfilesRepository)[1] + } + repositoryProvider := fmt.Sprintf(" provider: %s", provider) configContent = append(configContent, fmt.Sprintf("%s%s%s", dotfilesRepositoryPrefix, dotfilesRepository, repositoryProvider)...) } diff --git a/backend/env/env.go b/backend/env/env.go index 66c7295..398fd83 100644 --- a/backend/env/env.go +++ b/backend/env/env.go @@ -214,7 +214,7 @@ func GenerateRepositoriesPath() string { // GetDotfilesRepository creates conf line containing the user's input. func GetDotfilesRepository() string { - fmt.Print("\nEnter the full path to your dotfiles Github repository\n(leave blank to skip): ") + fmt.Print("\nEnter the full path to your dotfiles repository\n(leave blank to skip): ") reader := bufio.NewReader(os.Stdin) if input, _ := reader.ReadString('\n'); input != "\n" && input != "" { return string(bytes.TrimSuffix([]byte(input), []byte("\n"))) diff --git a/cmd/self_update.go b/cmd/self_update.go new file mode 100644 index 0000000..5b2245a --- /dev/null +++ b/cmd/self_update.go @@ -0,0 +1,137 @@ +// Copyright © 2016 Theotime LEVEQUE theotime@protonmail.com +// +// 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" + "io/ioutil" + "net/http" + "os" + "time" + + "github.com/fatih/color" + "github.com/mitchellh/ioprogress" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var httpGet = http.Get +var version string +var ianLastCommit = "https://api.github.com/repos/thylong/ian/commits/master" +var ianLastRelease = "https://api.github.com/repos/thylong/ian/releases/latest" + +func init() { + RootCmd.AddCommand(selfUpdateCmd) +} + +var selfUpdateCmd = &cobra.Command{ + Use: "self-update", + Short: "Update ian to the last version", + Long: `Update ian to the last version.`, + Run: func(cmd *cobra.Command, args []string) { + localVersion := viper.GetString("VERSION") + + resp, err := httpGet(ianLastCommit) + if err != nil { + fmt.Printf("%v Cannot retrieve ian's last version infos\n", color.RedString("Error:")) + return + } + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Printf("%v Cannot retrieve ian's last version infos\n", color.RedString("Error:")) + return + } + defer resp.Body.Close() + + var jsonContent map[string]interface{} + err = json.Unmarshal(content, &jsonContent) + if err != nil { + fmt.Printf("%v Cannot retrieve ian's last version\n", color.RedString("Error:")) + return + } + remoteVersion := jsonContent["sha"].(string)[:7] + + if localVersion != remoteVersion { + fmt.Printf("ian version outdated... \n") + resp, err := httpGet(ianLastRelease) + if err != nil { + fmt.Printf("%v Cannot retrieve ian's last release infos\n", color.RedString("Error:")) + return + } + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Printf("%v Cannot retrieve ian's last release infos\n", color.RedString("Error:")) + return + } + defer resp.Body.Close() + + var lastReleaseContent map[string]interface{} + err = json.Unmarshal(content, &lastReleaseContent) + if err != nil { + fmt.Printf("%v Cannot retrieve ian's last version\n", color.RedString("Error:")) + return + } + fmt.Println(lastReleaseContent) + downloadURL := lastReleaseContent["assets"].([]interface{})[0].(map[string]interface{})["browser_download_url"] + resp, err = httpGet(downloadURL.(string)) + if err != nil { + fmt.Printf("%v Cannot download ian's last version\n", color.RedString("Error:")) + return + } + defer resp.Body.Close() + + progressR := &ioprogress.Reader{ + Reader: resp.Body, + Size: resp.ContentLength, + DrawInterval: 500 * time.Millisecond, + DrawFunc: ioprogress.DrawTerminalf(os.Stdout, func(progress, total int64) string { + bar := ioprogress.DrawTextFormatBar(40) + return fmt.Sprintf("%s %20s", bar(progress, total), ioprogress.DrawTextFormatBytes(progress, total)) + }), + } + + data, err := ioutil.ReadAll(progressR) + if err != nil { + fmt.Printf("%v ian's last version seems broken\n", color.RedString("Error:")) + return + } + dest, err := os.Executable() + if err != nil { + fmt.Printf("%v ian's last version seems not executable\n", color.RedString("Error:")) + return + } + + // Move the old version to a backup path that we can recover from + // in case the upgrade fails + destBackup := dest + ".bak" + if _, err := os.Stat(dest); err == nil { + os.Rename(dest, destBackup) + } + + fmt.Printf("Downloading ian's new version to %s\n", dest) + if err := ioutil.WriteFile(dest, data, 0755); err != nil { + os.Rename(destBackup, dest) + fmt.Printf("%v Failed to update ian\n", color.RedString("Error:")) + return + } + + // Removing backup + os.Remove(destBackup) + + fmt.Printf("ian updated with success to version %s\n", remoteVersion) + } + }, +} diff --git a/main.go b/main.go index 301a98c..c43201e 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,7 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/thylong/ian/cmd" ) @@ -27,6 +28,7 @@ var version = "undefined" func init() { cmd.RootCmd.AddCommand(versionCmd) + viper.Set("VERSION", version) } func main() {