diff --git a/.goreleaser.yml b/.goreleaser.yml index 8aa06ede..72e0e6fb 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,6 +1,9 @@ archive: - format: binary - name_template: "dnote-{{ .Os }}-{{ .Arch }}" + format: tar.gz + format_overrides: + - goos: windows + format: zip + name_template: "dnote_{{ .Version }}_{{ .Os }}_{{ .Arch }}" checksum: name_template: 'dnote-{{ .Version }}-checksums.txt' @@ -9,8 +12,7 @@ builds: - binary: dnote ldflags: - - -X core.Version={{.Version}} - - -X main.apiEndpoint=https://api.dnote.io + - -X main.apiEndpoint={{ .Env.API_ENDPOINT }} -X main.versionTag={{ .Version }} goos: - darwin - linux diff --git a/Makefile b/Makefile index f6425ee2..0a12480c 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,11 @@ release: @echo "** Tagging and pushing..." @git tag -a $(VERSION) -m "$(VERSION)" @git push --tags - @goreleaser --rm-dist + @API_ENDPOINT=https://api.dnote.io goreleaser --rm-dist .PHONY: release build-snapshot: - @goreleaser --snapshot --rm-dist + @API_ENDPOINT=http://127.0.0.1:5000 goreleaser --snapshot --rm-dist .PHONY: build-snapshot clean: diff --git a/cmd/add/add.go b/cmd/add/add.go index 0a9c00da..13433828 100644 --- a/cmd/add/add.go +++ b/cmd/add/add.go @@ -71,6 +71,11 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { fmt.Printf("\n------------------------content------------------------\n") fmt.Printf("%s", content) fmt.Printf("\n-------------------------------------------------------\n") + + if err := core.CheckUpdate(ctx); err != nil { + log.Error(errors.Wrap(err, "automatically checking updates").Error()) + } + return nil } } diff --git a/cmd/root/root.go b/cmd/root/root.go index d30ce822..87cd16b5 100644 --- a/cmd/root/root.go +++ b/cmd/root/root.go @@ -4,7 +4,6 @@ import ( "github.com/dnote/cli/core" "github.com/dnote/cli/infra" "github.com/dnote/cli/migrate" - "github.com/dnote/cli/upgrade" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -68,10 +67,5 @@ func Prepare(ctx infra.DnoteCtx) error { return errors.Wrap(err, "Failed to perform migration") } - err = upgrade.AutoUpgrade(ctx) - if err != nil { - return errors.Wrap(err, "Failed to auto upgrade") - } - return nil } diff --git a/cmd/sync/sync.go b/cmd/sync/sync.go index 2a56176c..5b6ef422 100644 --- a/cmd/sync/sync.go +++ b/cmd/sync/sync.go @@ -117,6 +117,10 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { return errors.Wrap(err, "Failed to clear the action log") } + if err := core.CheckUpdate(ctx); err != nil { + log.Error(errors.Wrap(err, "automatically checking updates").Error()) + } + return nil } } @@ -170,7 +174,7 @@ func postActions(ctx infra.DnoteCtx, APIKey string, payload io.Reader) (*http.Re } req.Header.Set("Authorization", APIKey) - req.Header.Set("CLI-Version", core.Version) + req.Header.Set("CLI-Version", ctx.Version) client := http.Client{} resp, err := client.Do(req) diff --git a/cmd/upgrade/upgrade.go b/cmd/upgrade/upgrade.go deleted file mode 100644 index c3332ea5..00000000 --- a/cmd/upgrade/upgrade.go +++ /dev/null @@ -1,36 +0,0 @@ -package upgrade - -import ( - "github.com/dnote/cli/core" - "github.com/dnote/cli/infra" - "github.com/dnote/cli/upgrade" - "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -var example = ` - dnote upgrade` - -var deprecationWarning = `see https://github.com/dnote/cli/issues/96.` - -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { - cmd := &cobra.Command{ - Use: "upgrade", - Short: "Upgrades dnote", - Example: example, - RunE: newRun(ctx), - Deprecated: deprecationWarning, - } - - return cmd -} - -func newRun(ctx infra.DnoteCtx) core.RunEFunc { - return func(cmd *cobra.Command, args []string) error { - if err := upgrade.Upgrade(ctx); err != nil { - return errors.Wrap(err, "Failed to upgrade dnote") - } - - return nil - } -} diff --git a/cmd/version/version.go b/cmd/version/version.go index 0804a2aa..63c87f68 100644 --- a/cmd/version/version.go +++ b/cmd/version/version.go @@ -3,7 +3,6 @@ package version import ( "fmt" - "github.com/dnote/cli/core" "github.com/dnote/cli/infra" "github.com/spf13/cobra" ) @@ -14,7 +13,7 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { Short: "Print the version number of Dnote", Long: "Print the version number of Dnote", Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("dnote %s\n", core.Version) + fmt.Printf("dnote %s\n", ctx.Version) }, } diff --git a/core/core.go b/core/core.go index 57a36f38..59dc29bd 100644 --- a/core/core.go +++ b/core/core.go @@ -17,10 +17,7 @@ import ( "gopkg.in/yaml.v2" ) -const ( - // Version is the current version of dnote - Version = "master" - +var ( // TimestampFilename is the name of the file containing upgrade info TimestampFilename = "timestamps" // DnoteDirName is the name of the directory containing dnote files diff --git a/core/upgrade.go b/core/upgrade.go new file mode 100644 index 00000000..1ae18137 --- /dev/null +++ b/core/upgrade.go @@ -0,0 +1,100 @@ +package core + +import ( + "context" + "fmt" + "time" + + "github.com/dnote/cli/infra" + "github.com/dnote/cli/log" + "github.com/dnote/cli/utils" + "github.com/google/go-github/github" + "github.com/pkg/errors" +) + +// upgradeInterval is 3 weeks +var upgradeInterval int64 = 86400 * 7 * 3 + +// shouldCheckUpdate checks if update should be checked +func shouldCheckUpdate(ctx infra.DnoteCtx) (bool, error) { + timestamp, err := ReadTimestamp(ctx) + if err != nil { + return false, errors.Wrap(err, "Failed to get timestamp content") + } + + now := time.Now().Unix() + + return now-timestamp.LastUpgrade > upgradeInterval, nil +} + +func touchLastUpgrade(ctx infra.DnoteCtx) error { + timestamp, err := ReadTimestamp(ctx) + if err != nil { + return errors.Wrap(err, "Failed to get timestamp content") + } + + now := time.Now().Unix() + timestamp.LastUpgrade = now + + if err := WriteTimestamp(ctx, timestamp); err != nil { + return errors.Wrap(err, "Failed to write the updated timestamp to the file") + } + + return nil +} + +func checkVersion(ctx infra.DnoteCtx) error { + log.Infof("current version is %s\n", ctx.Version) + + // Fetch the latest version + gh := github.NewClient(nil) + releases, _, err := gh.Repositories.ListReleases(context.Background(), "dnote", "cli", nil) + if err != nil { + return errors.Wrap(err, "fetching releases") + } + + latest := releases[0] + latestVersion := (*latest.TagName)[1:] + + log.Infof("latest version is %s\n", latestVersion) + + if latestVersion == ctx.Version { + log.Success("you are up-to-date\n\n") + } else { + log.Infof("to upgrade, see https://github.com/dnote/cli/blob/master/README.md\n") + } + + return nil +} + +// CheckUpdate triggers update if needed +func CheckUpdate(ctx infra.DnoteCtx) error { + shouldCheck, err := shouldCheckUpdate(ctx) + if err != nil { + return errors.Wrap(err, "checking if dnote should check update") + } + if !shouldCheck { + return nil + } + + err = touchLastUpgrade(ctx) + if err != nil { + return errors.Wrap(err, "updating the last upgrade timestamp") + } + + fmt.Printf("\n") + willCheck, err := utils.AskConfirmation("check for upgrade?", true) + if err != nil { + return errors.Wrap(err, "getting user confirmation") + } + if !willCheck { + return nil + } + + err = checkVersion(ctx) + if err != nil { + return errors.Wrap(err, "checking version") + } + + return nil +} diff --git a/infra/main.go b/infra/main.go index 03f22a19..c90d5bdb 100644 --- a/infra/main.go +++ b/infra/main.go @@ -6,6 +6,7 @@ type DnoteCtx struct { HomeDir string DnoteDir string APIEndpoint string + Version string } // Config holds dnote configuration diff --git a/install.sh b/install.sh index 1da6603f..e55b38ef 100755 --- a/install.sh +++ b/install.sh @@ -12,7 +12,7 @@ not_supported() { exit 1 } -install() { +get_platform() { UNAME=$(uname) if [ "$UNAME" != "Linux" -a "$UNAME" != "Darwin" -a "$UNAME" != "OpenBSD" ] ; then @@ -22,46 +22,68 @@ install() { if [ "$UNAME" = "Darwin" ]; then OSX_ARCH=$(uname -m) if [ "${OSX_ARCH}" = "x86_64" ]; then - PLATFORM="darwin-amd64" + platform="darwin_amd64" else not_supported fi elif [ "$UNAME" = "Linux" ]; then LINUX_ARCH=$(uname -m) if [ "${LINUX_ARCH}" = "x86_64" ]; then - PLATFORM="linux-amd64" + platform="linux_amd64" elif [ "${LINUX_ARCH}" = "i686" ]; then - PLATFORM="linux-386" + platform="linux_386" else not_supported fi elif [ "$UNAME" = "OpenBSD" ]; then OPENBSD_ARCH=$(uname -m) if [ "${OPENBSD_ARCH}" = "x86_64" ]; then - PLATFORM="openbsd-amd64" + platform="openbsd_amd64" elif [ "${OPENBSD_ARCH}" = "i686" ]; then - PLATFORM="openbsd-386" + platform="openbsd_386" else not_supported fi fi - LATEST=$(curl -s https://api.github.com/repos/dnote/cli/tags | grep -Eo '"name":[ ]*"v[0-9]*\.[0-9]*\.[0-9]*",' | head -n 1 | sed 's/[," ]//g' | cut -d ':' -f 2) - URL="https://github.com/dnote/cli/releases/download/$LATEST/dnote-$PLATFORM" - DEST=${DEST:-/usr/local/bin/dnote} + echo $platform +} +get_version() { + LATEST=$(curl -s https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/tags | grep -Eo '"name":[ ]*"v[0-9]*\.[0-9]*\.[0-9]*",' | head -n 1 | sed 's/[," ]//g' | cut -d ':' -f 2) if [ -z $LATEST ]; then echo "Error fetching latest version. Please try again." exit 1 fi - echo "Downloading Dnote binary from $URL to $DEST" - if curl -L --progress-bar $URL -o $DEST; then - chmod +x $DEST + # remove the preceding 'v' + echo ${LATEST#v} +} + +execute() { + echo "downloading Dnote v${LATEST}..." + echo ${URL} + if curl -L --progress-bar $URL -o "${TMPDIR}/${TARBALL}"; then + (cd "${TMPDIR}" && tar -xzf "${TARBALL}") + + install -d "${BINDIR}" + install "${TMPDIR}/${BINARY}" "${BINDIR}/" + echo "Successfully installed Dnote" else echo "Installation failed. You might need elevated permission." + exit 1 fi } -install +REPO_OWNER=dnote +REPO_NAME=cli +PLATFORM=$(get_platform) +LATEST=$(get_version) +TARBALL="dnote_${LATEST}_${PLATFORM}.tar.gz" +URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/v${LATEST}/${TARBALL}" +TMPDIR="$(mktemp -d)" +BINDIR=${BINDIR:-/usr/local/bin} +BINARY=dnote + +execute diff --git a/main.go b/main.go index e6b690c1..0a7fdc21 100644 --- a/main.go +++ b/main.go @@ -19,13 +19,13 @@ import ( "github.com/dnote/cli/cmd/ls" "github.com/dnote/cli/cmd/remove" "github.com/dnote/cli/cmd/sync" - "github.com/dnote/cli/cmd/upgrade" "github.com/dnote/cli/cmd/version" "github.com/dnote/cli/cmd/view" ) -// apiEndpoint is populated during link time +// apiEndpoint and versionTag are populated during link time var apiEndpoint string +var versionTag = "master" func main() { ctx, err := newCtx() @@ -45,7 +45,6 @@ func main() { root.Register(ls.NewCmd(ctx)) root.Register(sync.NewCmd(ctx)) root.Register(version.NewCmd(ctx)) - root.Register(upgrade.NewCmd(ctx)) root.Register(cat.NewCmd(ctx)) root.Register(view.NewCmd(ctx)) @@ -66,6 +65,7 @@ func newCtx() (infra.DnoteCtx, error) { HomeDir: homeDir, DnoteDir: dnoteDir, APIEndpoint: apiEndpoint, + Version: versionTag, } return ret, nil diff --git a/migrate/migrations.go b/migrate/migrations.go index abef759c..d5f7ebc3 100644 --- a/migrate/migrations.go +++ b/migrate/migrations.go @@ -222,7 +222,7 @@ func migrateToV5(ctx infra.DnoteCtx) error { case migrateToV5ActionEditNote: var oldData migrateToV5PreEditNoteData if err = json.Unmarshal(action.Data, &oldData); err != nil { - return errors.Wrapf(err, "unmarshalling old data of an edit note action %s", action.ID) + return errors.Wrapf(err, "unmarshalling old data of an edit note action %d", action.ID) } migratedData := migrateToV5PostEditNoteData{ diff --git a/scripts/dev.sh b/scripts/dev.sh index 9befb7f2..0902a20e 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -1,3 +1,7 @@ #!/bin/bash -rm $(which dnote) $GOPATH/bin/cli && go install -ldflags "-X main.apiEndpoint=http://127.0.0.1:5000" . && ln -s $GOPATH/bin/cli /usr/local/bin/dnote +# dev.sh builds a new binary and replaces the old one in the PATH with it + +rm "$(which dnote)" $GOPATH/bin/cli +make build-snapshot +ln -s $GOPATH/src/github.com/dnote/cli/dist/darwin_amd64/dnote /usr/local/bin/dnote diff --git a/upgrade/upgrade.go b/upgrade/upgrade.go deleted file mode 100644 index 66ed102a..00000000 --- a/upgrade/upgrade.go +++ /dev/null @@ -1,181 +0,0 @@ -package upgrade - -import ( - "context" - "fmt" - "io" - "net/http" - "os" - "os/exec" - "path" - "runtime" - "time" - - "github.com/dnote/cli/core" - "github.com/dnote/cli/infra" - "github.com/dnote/cli/log" - "github.com/dnote/cli/utils" - "github.com/google/go-github/github" - "github.com/pkg/errors" -) - -// upgradeInterval is 3 weeks -var upgradeInterval int64 = 86400 * 7 * 3 - -// getAsset finds the asset to download from the liast of assets in a release -func getAsset(release *github.RepositoryRelease) *github.ReleaseAsset { - filename := fmt.Sprintf("dnote-%s-%s", runtime.GOOS, runtime.GOARCH) - - for _, asset := range release.Assets { - if *asset.Name == filename { - return &asset - } - } - - return nil -} - -// shouldCheckUpdate checks if update should be checked -func shouldCheckUpdate(ctx infra.DnoteCtx) (bool, error) { - timestamp, err := core.ReadTimestamp(ctx) - if err != nil { - return false, errors.Wrap(err, "Failed to get timestamp content") - } - - now := time.Now().Unix() - - return now-timestamp.LastUpgrade > upgradeInterval, nil -} - -func touchLastUpgrade(ctx infra.DnoteCtx) error { - timestamp, err := core.ReadTimestamp(ctx) - if err != nil { - return errors.Wrap(err, "Failed to get timestamp content") - } - - now := time.Now().Unix() - timestamp.LastUpgrade = now - - if err := core.WriteTimestamp(ctx, timestamp); err != nil { - return errors.Wrap(err, "Failed to write the updated timestamp to the file") - } - - return nil -} - -// AutoUpgrade triggers update if needed -func AutoUpgrade(ctx infra.DnoteCtx) error { - shouldCheck, err := shouldCheckUpdate(ctx) - if err != nil { - return errors.Wrap(err, "Failed to check if dnote should check update") - } - - if shouldCheck { - willCheck, err := utils.AskConfirmation("check for upgrade?", true) - if err != nil { - return errors.Wrap(err, "Failed to get user confirmation for checking upgrade") - } - - err = touchLastUpgrade(ctx) - if err != nil { - return errors.Wrap(err, "Failed to update last upgrade timestamp") - } - - if willCheck { - if err := Upgrade(ctx); err != nil { - return errors.Wrap(err, "Failed to upgrade") - } - } - } - - return nil -} - -func Upgrade(ctx infra.DnoteCtx) error { - log.Infof("current version is %s\n", core.Version) - - // Fetch the latest version - gh := github.NewClient(nil) - releases, _, err := gh.Repositories.ListReleases(context.Background(), "dnote", "cli", nil) - - if err != nil { - return err - } - - latest := releases[0] - latestVersion := (*latest.TagName)[1:] - - if err != nil { - return err - } - - log.Infof("latest version is %s\n", latestVersion) - - // Check if up to date - if latestVersion == core.Version { - log.Success("you are up-to-date\n\n") - err = touchLastUpgrade(ctx) - if err != nil { - return errors.Wrap(err, "Failed to update the upgrade timestamp") - } - - return nil - } - - asset := getAsset(latest) - if asset == nil { - err = touchLastUpgrade(ctx) - if err != nil { - return errors.Wrap(err, "Failed to update the upgrade timestamp") - } - - return errors.Errorf("Could not find the release for %s %s", runtime.GOOS, runtime.GOARCH) - } - - // Download temporary file - log.Infof("downloading: %s\n", latestVersion) - tmpPath := path.Join(os.TempDir(), "dnote_update") - - out, err := os.Create(tmpPath) - if err != nil { - return err - } - defer out.Close() - - resp, err := http.Get(*asset.BrowserDownloadURL) - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = io.Copy(out, resp.Body) - if err != nil { - return err - } - - // Override the binary - cmdPath, err := exec.LookPath("dnote") - if err != nil { - return err - } - - err = os.Rename(tmpPath, cmdPath) - if err != nil { - return err - } - - // Make it executable - err = os.Chmod(cmdPath, 0755) - if err != nil { - return err - } - - err = touchLastUpgrade(ctx) - if err != nil { - return errors.Wrap(err, "Upgrade is done, but failed to update the last_upgrade timestamp.") - } - - log.Successf("updated: v%s -> v%s\n", core.Version, latestVersion) - log.Plain("changelog: https://github.com/dnote/cli/releases\n\n") - return nil -} diff --git a/windows-install.bat b/windows-install.bat deleted file mode 100644 index 873e5459..00000000 --- a/windows-install.bat +++ /dev/null @@ -1,37 +0,0 @@ -@echo off -set DNOTEPATH=%PROGRAMFILES%\Dnote CLI -set DNOTEDL=dnote-windows-amd64.exe -set DNOTETARGET=%DNOTEPATH%\dnote.exe -echo Checking for directory... -if not exist "%DNOTEPATH%\" ( - echo Creating directory... - mkdir "%DNOTEPATH%" -) -echo Moving program to target directory... -move /Y %DNOTEDL% "%DNOTETARGET%" -echo "Adding directory to user PATH..." - -REM retrieve only the user's PATH from registry, -REM to avoid attempting (and probably failing) to overwrite the -REM system path - -set Key=HKCU\Environment -FOR /F "tokens=2* skip=1" %%G IN ('REG QUERY %Key% /v PATH') DO ( - echo %%H > user_path_backup.txt - set t=%%H - set "NEWPATH=" - :loop - for /f "delims=; tokens=1*" %%a in ("%t%") do ( - set t=%%b - if not "%%a" == "%DNOTEPATH%" ( - if defined NEWPATH ( - set NEWPATH=%NEWPATH%;%%a - ) else ( - set NEWPATH=%%a - ) - ) - ) - if defined t goto :loop -) -set NEWPATH=%NEWPATH%;%DNOTEPATH% -setx PATH "%NEWPATH%"