Skip to content

Commit

Permalink
Merge pull request #12 from depot/cli-notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobwgillespie authored Mar 23, 2022
2 parents 7c6c74d + 530aa9a commit 090d6fd
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 7 deletions.
1 change: 0 additions & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ builds:
main: ./cmd/depot
ldflags:
- -s -w -X github.com/depot/cli/internal/build.Version={{.Version}} -X github.com/depot/cli/internal/build.Date={{time "2006-01-02"}} -X github.com/depot/cli/internal/build.SentryEnvironment=release
- -X main.updaterEnabled=depot/cli
id: macos
goos: [darwin]
goarch: [amd64, arm64]
Expand Down
65 changes: 65 additions & 0 deletions cmd/depot/main.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package main

import (
"fmt"
"log"
"os"

"github.com/depot/cli/internal/build"
"github.com/depot/cli/internal/update"
"github.com/depot/cli/pkg/api"
"github.com/depot/cli/pkg/cmd/root"
"github.com/depot/cli/pkg/config"
"github.com/getsentry/sentry-go"
"github.com/mattn/go-isatty"
"github.com/mgutz/ansi"
)

func main() {
Expand All @@ -29,11 +35,70 @@ func runMain() int {
buildVersion := build.Version
buildDate := build.Date

updateMessageChan := make(chan *update.ReleaseInfo)
go func() {
rel, _ := checkForUpdate(buildVersion)
updateMessageChan <- rel
}()

rootCmd := root.NewCmdRoot(buildVersion, buildDate)

if err := rootCmd.Execute(); err != nil {
return 1
}

newRelease := <-updateMessageChan
if newRelease != nil {
isHomebrew := update.IsUnderHomebrew()
fmt.Fprintf(os.Stderr, "\n\n%s%s%s %s → %s\n",
ansi.Color("A new release of depot is available, released on ", "yellow"),
ansi.Color(newRelease.PublishedAt.Format("2006-01-02"), "yellow"),
ansi.Color(":", "yellow"),
ansi.Color(buildVersion, "cyan"),
ansi.Color(newRelease.Version, "cyan"))
if isHomebrew {
fmt.Fprintf(os.Stderr, "To upgrade, run: %s\n", "brew update && brew upgrade depot/tap/depot")
}
fmt.Fprintf(os.Stderr, "%s\n\n",
ansi.Color(fmt.Sprintf("https://github.com/depot/cli/releases/tag/v%s", newRelease.Version), "yellow"))
}

return 0
}

func checkForUpdate(currentVersion string) (*update.ReleaseInfo, error) {
if !shouldCheckForUpdate() {
return nil, nil
}

client, err := api.NewDepotFromEnv(config.GetApiToken())
if err != nil {
return nil, err
}

stateFilePath, err := config.StateFile()
if err != nil {
return nil, err
}

fmt.Println(stateFilePath)

return update.CheckForUpdate(client, stateFilePath, currentVersion)
}

func shouldCheckForUpdate() bool {
if os.Getenv("DEPOT_NO_UPDATE_NOTIFIER") != "" {
return false
}
return !isCI() && isTerminal(os.Stdout) && isTerminal(os.Stderr)
}

func isCI() bool {
return os.Getenv("CI") != "" || // GitHub Actions, Travis CI, CircleCI, Cirrus CI, GitLab CI, AppVeyor, CodeShip, dsari
os.Getenv("BUILD_NUMBER") != "" || // Jenkins, TeamCity
os.Getenv("RUN_ID") != "" // TaskCluster, dsari
}

func isTerminal(f *os.File) bool {
return isatty.IsTerminal(f.Fd()) || isatty.IsCygwinTerminal(f.Fd())
}
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ require (
github.com/docker/docker v20.10.7+incompatible
github.com/docker/go-units v0.4.0
github.com/getsentry/sentry-go v0.13.0
github.com/hashicorp/go-version v1.2.0
github.com/mattn/go-isatty v0.0.14
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/moby/buildkit v0.10.0-rc2.0.20220308185020-fdecd0ae108b
github.com/morikuni/aec v1.0.0
github.com/pkg/errors v0.9.1
Expand All @@ -20,6 +23,7 @@ require (
github.com/spf13/viper v1.10.1
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
google.golang.org/grpc v1.44.0
gopkg.in/yaml.v2 v2.4.0
)

require (
Expand Down Expand Up @@ -61,7 +65,6 @@ require (
github.com/klauspost/compress v1.15.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/miekg/pkcs11 v1.0.3 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
Expand Down Expand Up @@ -113,7 +116,6 @@ require (
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apimachinery v0.23.4 // indirect
k8s.io/client-go v0.23.4 // indirect
k8s.io/klog/v2 v2.30.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
Expand Down Expand Up @@ -948,6 +949,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=
Expand Down
82 changes: 82 additions & 0 deletions internal/update/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import (
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/cli/safeexec"
"github.com/depot/cli/pkg/api"
"github.com/hashicorp/go-version"
"gopkg.in/yaml.v2"
)

// Check whether the depot binary was found under the Homebrew prefix
Expand All @@ -29,3 +33,81 @@ func IsUnderHomebrew() bool {
brewBinPrefix := filepath.Join(strings.TrimSpace(string(brewPrefixBytes)), "bin") + string(filepath.Separator)
return strings.HasPrefix(binary, brewBinPrefix)
}

type ReleaseInfo struct {
Version string `json:"version"`
URL string `json:"url"`
PublishedAt time.Time `json:"publishedAt"`
}

type StateEntry struct {
CheckedForUpdateAt time.Time `yaml:"checkedForUpdateAt"`
LatestRelease ReleaseInfo `yaml:"latestRelease"`
}

func CheckForUpdate(client *api.Depot, stateFilePath, currentVersion string) (*ReleaseInfo, error) {
state, _ := readStateFile(stateFilePath)
if state != nil && time.Since(state.CheckedForUpdateAt) < time.Hour*1 {
return nil, nil
}

releaseResponse, err := client.LatestRelease()
if err != nil {
return nil, err
}
release := ReleaseInfo{Version: releaseResponse.Version, URL: releaseResponse.URL, PublishedAt: releaseResponse.PublishedAt}

state = &StateEntry{CheckedForUpdateAt: time.Now(), LatestRelease: release}
err = writeStateFile(stateFilePath, state)
if err != nil {
return nil, err
}

if versionGreaterThan(release.Version, currentVersion) {
return &release, nil
}

return nil, nil
}

func readStateFile(stateFilePath string) (*StateEntry, error) {
content, err := os.ReadFile(stateFilePath)
if err != nil {
return nil, err
}

var stateEntry StateEntry
err = yaml.Unmarshal(content, &stateEntry)
if err != nil {
return nil, err
}

return &stateEntry, nil
}

func writeStateFile(stateFilePath string, state *StateEntry) error {
content, err := yaml.Marshal(state)
if err != nil {
return err
}

err = os.MkdirAll(filepath.Dir(stateFilePath), 0755)
if err != nil {
return err
}

err = os.WriteFile(stateFilePath, content, 0600)
return err
}

func versionGreaterThan(a, b string) bool {
versionA, err := version.NewVersion(a)
if err != nil {
return false
}
versionB, err := version.NewVersion(b)
if err != nil {
return false
}
return versionA.GreaterThan(versionB)
}
18 changes: 18 additions & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package api
import (
"fmt"
"os"
"runtime"
"time"
)

type Depot struct {
Expand Down Expand Up @@ -52,3 +54,19 @@ func (d *Depot) FinishBuild(buildID string) error {
)
return err
}

type ReleaseResponse struct {
OK bool `json:"ok"`
Version string `json:"version"`
URL string `json:"url"`
PublishedAt time.Time `json:"publishedAt"`
}

func (d *Depot) LatestRelease() (*ReleaseResponse, error) {
return apiRequest[ReleaseResponse](
"GET",
fmt.Sprintf("%s/api/cli/release/%s/%s/latest", d.BaseURL, runtime.GOOS, runtime.GOARCH),
d.token,
nil,
)
}
16 changes: 12 additions & 4 deletions pkg/api/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
)
Expand All @@ -14,13 +15,20 @@ type ErrorResponse struct {
}

func apiRequest[Response interface{}](method, url, token string, payload interface{}) (*Response, error) {
jsonBytes, err := json.Marshal(payload)
if err != nil {
return nil, err
var requestBody io.Reader

if payload != nil {
jsonBytes, err := json.Marshal(payload)
if err != nil {
return nil, err
}
requestBody = bytes.NewReader(jsonBytes)
} else {
requestBody = nil
}

client := &http.Client{}
req, err := http.NewRequest(method, url, bytes.NewBuffer(jsonBytes))
req, err := http.NewRequest(method, url, requestBody)
if err != nil {
return nil, err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ func ClearApiToken() error {
viper.Set("api_token", "")
return viper.WriteConfig()
}

func StateFile() (string, error) {
return xdg.ConfigFile("depot/state.yaml")
}

0 comments on commit 090d6fd

Please sign in to comment.