1package util 2 3import ( 4 "errors" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "runtime" 12 "time" 13 "context" 14 15 "github.com/google/go-github/github" 16) 17 18// Adapted from github.com/apex/apex 19 20// UpdateAvailable contains the full repository information for the latest release of Semaphore 21var UpdateAvailable *github.RepositoryRelease 22 23// DoUpgrade checks for an update, and if available downloads the binary and installs it 24func DoUpgrade(version string) error { 25 fmt.Printf("current release is v%s\n", version) 26 27 if err := CheckUpdate(version); err != nil || UpdateAvailable == nil { 28 return err 29 } 30 31 asset := findAsset(UpdateAvailable) 32 if asset == nil { 33 return errors.New("cannot find binary for your system") 34 } 35 36 // create tmp file 37 tmpPath := filepath.Join(os.TempDir(), "semaphore-upgrade") 38 f, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0755) //nolint: gas 39 if err != nil { 40 return err 41 } 42 43 // download binary 44 fmt.Printf("downloading %s\n", *asset.BrowserDownloadURL) 45 res, err := http.Get(*asset.BrowserDownloadURL) 46 if err != nil { 47 return err 48 } 49 50 defer res.Body.Close() //nolint: errcheck 51 52 // copy it down 53 _, err = io.Copy(f, res.Body) 54 if err != nil { 55 return err 56 } 57 58 // replace it 59 cmdPath := FindSemaphore() 60 if len(cmdPath) == 0 { 61 return errors.New("Cannot find semaphore binary") 62 } 63 64 fmt.Printf("replacing %s\n", cmdPath) 65 err = os.Rename(tmpPath, cmdPath) 66 if err != nil { 67 return err 68 } 69 70 fmt.Println("visit https://github.com/ansible-semaphore/semaphore/releases for the changelog") 71 go func() { 72 time.Sleep(time.Second * 3) 73 os.Exit(0) 74 }() 75 76 return nil 77} 78 79// FindSemaphore looks in the PATH for the semaphore variable 80// if not found it will attempt to find the absolute path of the first 81// os argument, the semaphore command, and return it 82func FindSemaphore() string { 83 cmdPath, _ := exec.LookPath("semaphore") //nolint: gas 84 85 if len(cmdPath) == 0 { 86 cmdPath, _ = filepath.Abs(os.Args[0]) // nolint: gas 87 } 88 89 return cmdPath 90} 91 92// findAsset returns the binary for this platform. 93func findAsset(release *github.RepositoryRelease) *github.ReleaseAsset { 94 for _, asset := range release.Assets { 95 if *asset.Name == fmt.Sprintf("semaphore_%s_%s", runtime.GOOS, runtime.GOARCH) { 96 return &asset 97 } 98 } 99 100 return nil 101} 102 103// CheckUpdate uses the github client to check for new tags in the semaphore repo 104func CheckUpdate(version string) error { 105 // fetch releases 106 gh := github.NewClient(nil) 107 releases, _, err := gh.Repositories.ListReleases(context.TODO(), "ansible-semaphore", "semaphore", nil) 108 if err != nil { 109 return err 110 } 111 112 UpdateAvailable = nil 113 if (*releases[0].TagName)[1:] != version { 114 UpdateAvailable = releases[0] 115 } 116 117 return nil 118} 119