From ededb8ae5fe2975b7aadefcd8fe4f70a623cfae9 Mon Sep 17 00:00:00 2001 From: Zino Kader Date: Tue, 21 Feb 2023 21:01:45 +0100 Subject: [PATCH] feat: compare version to relay, extract latest version in install script (#45) --- .github/workflows/ci.yml | 2 + Dockerfile | 4 +- Makefile | 18 ++++----- README.md | 6 +-- cmd/portal/serve.go | 14 +++++-- internal/rendezvous/handlers.go | 21 ++++++++++ internal/rendezvous/routes.go | 5 ++- internal/rendezvous/server.go | 10 ++++- internal/semver/semver.go | 71 +++++++++++---------------------- internal/semver/semver_test.go | 8 ++-- scripts/install.sh | 17 ++++---- ui/constants.go | 4 +- ui/receiver/receiver.go | 35 +++++++--------- ui/sender/sender.go | 32 ++++++--------- ui/ui.go | 9 +++-- 15 files changed, 130 insertions(+), 126 deletions(-) mode change 100644 => 100755 scripts/install.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c8fd93..91507bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,8 @@ jobs: - name: E2E Test if: github.event_name == 'pull_request' + env: + PORTAL_VERSION: 'v1.1.1' # version does not matter for outcome of test, just for building image run: make test-e2e - name: Upload code coverage to Codecov diff --git a/Dockerfile b/Dockerfile index b2ba0a0..e4e9e60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ # Mutli-stage build. FROM golang:1.18-alpine3.14 as build-stage +ARG version # Copy source code and build binary RUN mkdir /usr/app COPY . /usr/app WORKDIR /usr/app -RUN go build -o app ./cmd/portal/*.go - +RUN CGO=0 go build -ldflags="-s -X main.version=${version}" -o app ./cmd/portal/ # Copy binary from build container and build image. FROM alpine:3.14 RUN mkdir /usr/app diff --git a/Makefile b/Makefile index d0c7f6d..4687b3a 100644 --- a/Makefile +++ b/Makefile @@ -5,23 +5,23 @@ LINKER_FLAGS = '-s -X main.version=${PORTAL_VERSION}' lint: golangci-lint run --timeout 5m ./... -build: - go build -o portal-bin ./cmd/portal/ +build: + go build -ldflags=${LINKER_FLAGS} -o portal-bin ./cmd/portal/ build-production: CGO=0 go build -ldflags=${LINKER_FLAGS} -o portal ./cmd/portal/ -image: - docker build --tag rendezvous:latest . +build-wasm: + GOOS=js GOARCH=wasm go build -o portal.wasm ./cmd/wasm/main.go + +image: + docker build --build-arg version=${PORTAL_VERSION} --tag rendezvous:latest . serve: image docker run -dp 8080:8080 rendezvous:latest -build-wasm: - GOOS=js GOARCH=wasm go build -o portal.wasm ./cmd/wasm/main.go - test: - go test -v -race -covermode=atomic -coverprofile=coverage.out -failfast -short ./... + go test -ldflags=${LINKER_FLAGS} -v -race -covermode=atomic -coverprofile=coverage.out -failfast -short ./... test-e2e: image - go test -v -race -covermode=atomic -coverprofile=coverage.out -failfast ./... + go test -ldflags=${LINKER_FLAGS} -v -race -covermode=atomic -coverprofile=coverage.out -failfast ./... diff --git a/README.md b/README.md index 0c6d079..6096054 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,13 @@ brew install SpatiumPortae/homebrew-portal/portal ### Manual -Either get the [latest release](https://github.com/ZinoKader/portal/releases/latest) and install it manually, _or_ run +Either get the [latest release](https://github.com/SpatiumPortae/portal/releases/latest) and install it manually, _or_ run ```bash -curl -s https://raw.githubusercontent.com/ZinoKader/portal/master/scripts/install.sh | bash +curl -s https://raw.githubusercontent.com/SpatiumPortae/portal/master/scripts/install.sh | bash ``` -> if permission denied for moving the files to /../bin, replace _" | bash"_ with _" | sudo bash"_
+> if permission is denied for moving the files to /../bin, replace the trailing _"bash"_ with _"sudo bash"_
(the script is in the repo, so you can check it out before you blindly trust in it!) ## The application diff --git a/cmd/portal/serve.go b/cmd/portal/serve.go index 11e768f..bd5a132 100644 --- a/cmd/portal/serve.go +++ b/cmd/portal/serve.go @@ -1,7 +1,10 @@ package main import ( + "fmt" + "github.com/SpatiumPortae/portal/internal/rendezvous" + "github.com/SpatiumPortae/portal/internal/semver" "github.com/spf13/cobra" ) @@ -11,16 +14,21 @@ var serveCmd = &cobra.Command{ Short: "Serve the rendezvous-server", Long: "The serve command serves the rendezvous-server locally.", Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { port, _ := cmd.Flags().GetInt("port") - server := rendezvous.NewServer(port) + ver, err := semver.Parse(version) + if err != nil { + return fmt.Errorf("server requires version to be set: %w", err) + } + server := rendezvous.NewServer(port, ver) server.Start() + return nil }, } // Add `port` flag. // NOTE: The `port` flag is required and not managed through viper. func init() { - serveCmd.Flags().IntP("port", "p", 0, "Port to run the portal rendezvous server on") + serveCmd.Flags().IntP("port", "p", 0, "port to run the portal rendezvous server on") _ = serveCmd.MarkFlagRequired("port") } diff --git a/internal/rendezvous/handlers.go b/internal/rendezvous/handlers.go index 8b9140b..92ab78a 100644 --- a/internal/rendezvous/handlers.go +++ b/internal/rendezvous/handlers.go @@ -213,6 +213,7 @@ func (s *Server) handleEstablishReceiver() http.HandlerFunc { if err != nil { w.WriteHeader(http.StatusBadRequest) logger.Error("exchanging salt", zap.Error(err)) + return } // Start forwarder and relay @@ -231,6 +232,26 @@ func (s *Server) handleEstablishReceiver() http.HandlerFunc { } } +//nolint:errcheck +func (s *Server) handleVersionCheck() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + logger, err := logger.FromContext(ctx) + if err != nil { + return + } + + response, err := json.Marshal(s.version) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + logger.Error("failed to marshal server version", zap.Error(err)) + return + } + w.Header().Set("Content-Type", "application/json") + w.Write(response) + } +} + //nolint:errcheck func (s *Server) ping() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/rendezvous/routes.go b/internal/rendezvous/routes.go index 40508ba..e921cdb 100644 --- a/internal/rendezvous/routes.go +++ b/internal/rendezvous/routes.go @@ -6,9 +6,12 @@ import ( ) func (s *Server) routes() { + s.router.Use(logger.Middleware(s.logger)) s.router.HandleFunc("/ping", s.ping()) + s.router.HandleFunc("/version", s.handleVersionCheck()) + portal := s.router.PathPrefix("").Subrouter() - portal.Use(logger.Middleware(s.logger), conn.Middleware()) + portal.Use(conn.Middleware()) portal.HandleFunc("/establish-sender", s.handleEstablishSender()) portal.HandleFunc("/establish-receiver", s.handleEstablishReceiver()) } diff --git a/internal/rendezvous/server.go b/internal/rendezvous/server.go index dd7f367..7aee78d 100644 --- a/internal/rendezvous/server.go +++ b/internal/rendezvous/server.go @@ -9,6 +9,7 @@ import ( "time" "github.com/SpatiumPortae/portal/internal/logger" + "github.com/SpatiumPortae/portal/internal/semver" "github.com/gorilla/mux" "go.uber.org/zap" ) @@ -21,10 +22,11 @@ type Server struct { ids *IDs signal chan os.Signal logger *zap.Logger + version *semver.Version } // NewServer constructs a new Server struct and setups the routes. -func NewServer(port int) *Server { +func NewServer(port int, version semver.Version) *Server { router := &mux.Router{} lgr := logger.New() stdLoggerWrapper, _ := zap.NewStdLogAt(lgr, zap.ErrorLevel) @@ -40,6 +42,7 @@ func NewServer(port int) *Server { mailboxes: &Mailboxes{&sync.Map{}}, ids: &IDs{&sync.Map{}}, logger: lgr, + version: &version, } s.routes() return s @@ -68,7 +71,10 @@ func serve(s *Server, ctx context.Context) (err error) { } }() - s.logger.Info(fmt.Sprintf("serving rendezvous server at: %s", s.httpServer.Addr)) + s.logger. + With(zap.String("version", s.version.String())). + With(zap.String("address", s.httpServer.Addr)). + Info("serving rendezvous server") <-ctx.Done() ctxShutdown, cancel := context.WithTimeout(context.Background(), 5*time.Second) diff --git a/internal/semver/semver.go b/internal/semver/semver.go index 408de52..cd98400 100644 --- a/internal/semver/semver.go +++ b/internal/semver/semver.go @@ -1,6 +1,7 @@ package semver import ( + "context" "encoding/json" "errors" "fmt" @@ -12,7 +13,7 @@ import ( const pattern = `^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$` -var ParseError = errors.New("provided string cannot be parsed into semver") +var ErrParse = errors.New("could not parse provided string into semantic version") type Comparsion int @@ -27,7 +28,9 @@ const ( ) type Version struct { - major, minor, patch int + Major int `json:"major,omitempty"` + Minor int `json:"minor,omitempty"` + Patch int `json:"patch,omitempty"` } // Parse parses the the provided string into a semver representation. @@ -37,19 +40,19 @@ func Parse(s string) (Version, error) { return Version{}, fmt.Errorf("compiling regex: %w", err) } if !re.MatchString(s) { - return Version{}, ParseError + return Version{}, ErrParse } split := strings.Split(s[1:], ".") ver := Version{} - ver.major, err = strconv.Atoi(split[0]) + ver.Major, err = strconv.Atoi(split[0]) if err != nil { return Version{}, fmt.Errorf("parsing Major to int: %w", err) } - ver.minor, err = strconv.Atoi(split[1]) + ver.Minor, err = strconv.Atoi(split[1]) if err != nil { return Version{}, fmt.Errorf("parsing Minor to int: %w", err) } - ver.patch, err = strconv.Atoi(split[2]) + ver.Patch, err = strconv.Atoi(split[2]) if err != nil { return Version{}, fmt.Errorf("parsing Patch to int: %w", err) } @@ -59,65 +62,37 @@ func Parse(s string) (Version, error) { // String returns a string representation of the semver. func (sv Version) String() string { - return fmt.Sprintf("v%d.%d.%d", sv.major, sv.minor, sv.patch) + return fmt.Sprintf("v%d.%d.%d", sv.Major, sv.Minor, sv.Patch) } // Compare compares the semver against the provided oracle statement. -// Return -1 if the semver is less than the oracle statement, 1 if -// the oracle statement is larger than the semver and 0 if they are equal. func (sv Version) Compare(oracle Version) Comparsion { switch { - case sv.major < oracle.major: + case sv.Major < oracle.Major: return CompareOldMajor - case sv.major > oracle.major: + case sv.Major > oracle.Major: return CompareNewMajor - case sv.minor < oracle.minor: + case sv.Minor < oracle.Minor: return CompareOldMinor - case sv.minor > oracle.minor: + case sv.Minor > oracle.Minor: return CompareNewMinor - case sv.patch < oracle.patch: + case sv.Patch < oracle.Patch: return CompareOldPatch - case sv.patch > oracle.patch: + case sv.Patch > oracle.Patch: return CompareNewPatch default: return CompareEqual } } -func (sv Version) Major() int { - return sv.major -} - -func (sv Version) Minor() int { - return sv.minor -} - -func (sv Version) Patch() int { - return sv.patch -} - -func GetPortalLatest() (Version, error) { - r, err := http.Get("https://api.github.com/repos/SpatiumPortae/portal/releases?per_page=1") +func GetRendezvousVersion(ctx context.Context, addr string) (Version, error) { + r, err := http.Get(fmt.Sprintf("http://%s/version", addr)) if err != nil { - return Version{}, fmt.Errorf("fetching the latest tag from github: %w", err) - } - type tag struct { - Name string `json:"tag_name"` - } - var tags []tag - if err := json.NewDecoder(r.Body).Decode(&tags); err != nil { - return Version{}, fmt.Errorf("decoding response from github: %w", err) - } - if len(tags) < 1 { - return Version{}, fmt.Errorf("no tags returned from github: %w", err) + return Version{}, fmt.Errorf("fetching the latest version from relay: %w", err) } - vers := make([]Version, len(tags)) - for i := range tags { - v, err := Parse(tags[i].Name) - if err != nil { - return Version{}, fmt.Errorf("unable to parse tag to semver: %w", err) - } - vers[i] = v + var version Version + if err := json.NewDecoder(r.Body).Decode(&version); err != nil { + return Version{}, fmt.Errorf("decoding version response from relay: %w", err) } - return vers[0], nil + return version, nil } diff --git a/internal/semver/semver_test.go b/internal/semver/semver_test.go index 71d5495..2a91ba0 100644 --- a/internal/semver/semver_test.go +++ b/internal/semver/semver_test.go @@ -26,22 +26,22 @@ func TestParse(t *testing.T) { t.Run("no leading v", func(t *testing.T) { s := "0.0.1" _, err := semver.Parse(s) - assert.Equal(t, semver.ParseError, err) + assert.Equal(t, semver.ErrParse, err) }) t.Run("major leading 0", func(t *testing.T) { s := "v01.0.1" _, err := semver.Parse(s) - assert.Equal(t, semver.ParseError, err) + assert.Equal(t, semver.ErrParse, err) }) t.Run("minor leading 0", func(t *testing.T) { s := "v0.01.1" _, err := semver.Parse(s) - assert.Equal(t, semver.ParseError, err) + assert.Equal(t, semver.ErrParse, err) }) t.Run("patch leading 0", func(t *testing.T) { s := "v0.1.01" _, err := semver.Parse(s) - assert.Equal(t, semver.ParseError, err) + assert.Equal(t, semver.ErrParse, err) }) }) } diff --git a/scripts/install.sh b/scripts/install.sh old mode 100644 new mode 100755 index c227937..afa8135 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -15,10 +15,10 @@ function install { USER="SpatiumPortae" PROG="portal" MOVE="true" - RELEASE="1.0.3" INSECURE="false" OUT_DIR="/usr/local/bin" - GH="https://github.com" + GH="https://github.com/SpatiumPortae/portal" + GH_API="https://api.github.com/repos/SpatiumPortae/portal" # bash check [ ! "$BASH_VERSION" ] && fail "Please use bash instead" [ ! -d $OUT_DIR ] && fail "output directory missing: $OUT_DIR" @@ -41,6 +41,9 @@ function install { else fail "neither wget/curl are installed" fi + # set release version + RELEASES_API="$GH_API/releases/latest" + RELEASE=$($GET $RELEASES_API | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | cut -c2-) # find OS case `uname -s` in Darwin) OS="darwin";; @@ -60,23 +63,23 @@ function install { FTYPE="" case "${OS}_${ARCH}" in "darwin_arm") - URL="https://github.com/SpatiumPortae/portal/releases/download/v$RELEASE/portal_$RELEASE\_macOS_arm64.tar.gz" + URL="$GH/releases/download/v$RELEASE/portal_$RELEASE\_macOS_arm64.tar.gz" FTYPE=".tar.gz" ;; "darwin_amd64") - URL="https://github.com/SpatiumPortae/portal/releases/download/v$RELEASE/portal_$RELEASE\_macOS_x86_64.tar.gz" + URL="$GH/releases/download/v$RELEASE/portal_$RELEASE\_macOS_x86_64.tar.gz" FTYPE=".tar.gz" ;; "linux_arm") - URL="https://github.com/SpatiumPortae/portal/releases/download/v$RELEASE/portal_$RELEASE\_Linux_arm64.tar.gz" + URL="$GH/releases/download/v$RELEASE/portal_$RELEASE\_Linux_arm64.tar.gz" FTYPE=".tar.gz" ;; "linux_386") - URL="https://github.com/SpatiumPortae/portal/releases/download/v$RELEASE/portal_$RELEASE\_Linux_x86_32.tar.gz" + URL="$GH/portal/releases/download/v$RELEASE/portal_$RELEASE\_Linux_x86_32.tar.gz" FTYPE=".tar.gz" ;; "linux_amd64") - URL="https://github.com/SpatiumPortae/portal/releases/download/v$RELEASE/portal_$RELEASE\_Linux_x86_64.tar.gz" + URL="$GH/portal/releases/download/v$RELEASE/portal_$RELEASE\_Linux_x86_64.tar.gz" FTYPE=".tar.gz" ;; *) fail "No asset for platform ${OS}-${ARCH}";; diff --git a/ui/constants.go b/ui/constants.go index d9858e2..3ed634e 100644 --- a/ui/constants.go +++ b/ui/constants.go @@ -84,7 +84,7 @@ var ItalicText = BaseStyle.Copy().Italic(true).Render var BoldText = BaseStyle.Copy().Bold(true).Render var ErrorText = BaseStyle.Copy().Foreground(lipgloss.Color(ERROR_COLOR)).Render var WarningText = BaseStyle.Copy().Foreground(lipgloss.Color(WARNING_COLOR)).Render -var CheckText = BaseStyle.Copy().Foreground(lipgloss.Color(CHECK_COLOR)).Render +var SuccessText = BaseStyle.Copy().Foreground(lipgloss.Color(CHECK_COLOR)).Render var CopyKeyHelpText = BaseStyle.Render("password → clipboard") -var CopyKeyActiveHelpText = CheckText("✓") + HelpStyle(" password → clipboard") +var CopyKeyActiveHelpText = SuccessText("✓") + HelpStyle(" password → clipboard") diff --git a/ui/receiver/receiver.go b/ui/receiver/receiver.go index 045f8da..064c386 100644 --- a/ui/receiver/receiver.go +++ b/ui/receiver/receiver.go @@ -2,6 +2,7 @@ package receiver import ( "context" + "errors" "fmt" "math" "os" @@ -31,7 +32,6 @@ const ( showReceivingProgress showDecompressing showFinished - showError ) // ------------------------------------------------------ Messages ----------------------------------------------------- @@ -66,7 +66,6 @@ type model struct { state uiState transferType transfer.Type password string - errorMessage string ctx context.Context msgs chan interface{} @@ -108,7 +107,7 @@ func New(addr string, password string, opts ...Option) *tea.Program { func (m model) Init() tea.Cmd { var versionCmd tea.Cmd if m.version != nil { - versionCmd = ui.VersionCmd(*m.version) + versionCmd = ui.VersionCmd(m.ctx, m.rendezvousAddr) } return tea.Sequence(versionCmd, tea.Batch(m.spinner.Tick, connectCmd(m.rendezvousAddr))) } @@ -117,20 +116,19 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case ui.VersionMsg: var message string - switch m.version.Compare(msg.Latest) { + switch m.version.Compare(msg.ServerVersion) { case semver.CompareNewMajor, - semver.CompareNewMinor, - semver.CompareNewPatch: + semver.CompareOldMajor: //lint:ignore ST1005 error string displayed in UI - return m, ui.ErrorCmd(fmt.Errorf("Your version is (%s) is incompatible with the latest version (%s)", m.version, msg.Latest)) - case semver.CompareOldMajor: - message = ui.WarningText(fmt.Sprintf("New major version available (%s -> %s)", m.version, msg.Latest)) - case semver.CompareOldMinor: - message = ui.WarningText(fmt.Sprintf("New minor version available (%s -> %s)", m.version, msg.Latest)) - case semver.CompareOldPatch: - message = ui.WarningText(fmt.Sprintf("New patch available (%s -> %s)", m.version, msg.Latest)) + return m, ui.ErrorCmd(fmt.Errorf("Portal version (%s) incompatible with server version (%s)", m.version, msg.ServerVersion)) + case semver.CompareNewMinor, + semver.CompareNewPatch: + message = ui.WarningText(fmt.Sprintf("Portal version (%s) newer than server version (%s)", m.version, msg.ServerVersion)) + case semver.CompareOldMinor, + semver.CompareOldPatch: + message = ui.WarningText(fmt.Sprintf("Server version (%s) newer than Portal version (%s)", m.version, msg.ServerVersion)) case semver.CompareEqual: - message = ui.CheckText(fmt.Sprintf("On latest version (%s)", m.version)) + message = ui.SuccessText(fmt.Sprintf("Portal version (%s) compatible with server version (%s)", m.version, msg.ServerVersion)) } return m, ui.TaskCmd(message, nil) @@ -193,9 +191,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, ui.QuitCmd() case ui.ErrorMsg: - m.state = showError - m.errorMessage = msg.Error() - return m, nil + return m, ui.ErrorCmd(errors.New(msg.Error())) case tea.KeyMsg: switch { @@ -228,7 +224,7 @@ func (m model) View() string { switch m.state { case showEstablishing: - return "\n" + + return ui.PadText + ui.LogSeparator(m.width) + ui.PadText + ui.InfoStyle(fmt.Sprintf("%s Establishing connection with sender", m.spinner.View())) + "\n\n" + ui.PadText + m.help.View(m.keys) + "\n\n" @@ -266,9 +262,6 @@ func (m model) View() string { ui.PadText + m.transferProgress.View() + "\n\n" + m.fileTable.View() - case showError: - return ui.ErrorText(m.errorMessage) - default: return "" } diff --git a/ui/sender/sender.go b/ui/sender/sender.go index 21eaac1..656a484 100644 --- a/ui/sender/sender.go +++ b/ui/sender/sender.go @@ -36,7 +36,6 @@ const ( showPassword uiState = iota showSendingProgress showFinished - showError ) // ------------------------------------------------------ Messages ----------------------------------------------------- @@ -71,7 +70,6 @@ func WithVersion(version semver.Version) Option { type model struct { state uiState // defaults to 0 (showPassword) transferType transfer.Type // defaults to 0 (Unknown) - errorMessage string readyToSend bool ctx context.Context @@ -120,7 +118,7 @@ func New(filenames []string, addr string, opts ...Option) *tea.Program { func (m model) Init() tea.Cmd { var versionCmd tea.Cmd if m.version != nil { - versionCmd = ui.VersionCmd(*m.version) + versionCmd = ui.VersionCmd(m.ctx, m.rendezvousAddr) } return tea.Sequence(versionCmd, tea.Batch(m.spinner.Tick, readFilesCmd(m.fileNames), connectCmd(m.ctx, m.rendezvousAddr))) } @@ -132,20 +130,19 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case ui.VersionMsg: var message string - switch m.version.Compare(msg.Latest) { + switch m.version.Compare(msg.ServerVersion) { case semver.CompareNewMajor, - semver.CompareNewMinor, - semver.CompareNewPatch: + semver.CompareOldMajor: //lint:ignore ST1005 error string displayed in UI - return m, ui.ErrorCmd(fmt.Errorf("Your version is (%s) is incompatible with the latest version (%s)", m.version, msg.Latest)) - case semver.CompareOldMajor: - message = ui.WarningText(fmt.Sprintf("New major version available (%s -> %s)", m.version, msg.Latest)) - case semver.CompareOldMinor: - message = ui.WarningText(fmt.Sprintf("New minor version available (%s -> %s)", m.version, msg.Latest)) - case semver.CompareOldPatch: - message = ui.WarningText(fmt.Sprintf("New patch available (%s -> %s)", m.version, msg.Latest)) + return m, ui.ErrorCmd(fmt.Errorf("Portal version (%s) incompatible with server version (%s)", m.version, msg.ServerVersion)) + case semver.CompareNewMinor, + semver.CompareNewPatch: + message = ui.WarningText(fmt.Sprintf("Portal version (%s) newer than server version (%s)", m.version, msg.ServerVersion)) + case semver.CompareOldMinor, + semver.CompareOldPatch: + message = ui.WarningText(fmt.Sprintf("Server version (%s) newer than Portal version (%s)", m.version, msg.ServerVersion)) case semver.CompareEqual: - message = ui.CheckText(fmt.Sprintf("On latest version (%s)", m.version)) + message = ui.SuccessText(fmt.Sprintf("Portal version (%s) compatible with server version (%s)", m.version, msg.ServerVersion)) } return m, ui.TaskCmd(message, nil) @@ -246,9 +243,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, ui.TaskCmd(message, ui.QuitCmd()) case ui.ErrorMsg: - m.state = showError - m.errorMessage = msg.Error() - return m, nil + return m, ui.ErrorCmd(errors.New(msg.Error())) case tea.KeyMsg: switch { @@ -342,9 +337,6 @@ func (m model) View() string { ui.PadText + m.transferProgress.View() + "\n\n" + m.fileTable.View() - case showError: - return ui.ErrorText(m.errorMessage) - default: return "" } diff --git a/ui/ui.go b/ui/ui.go index 54fc124..9c6adf9 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -1,6 +1,7 @@ package ui import ( + "context" "fmt" "math" "sort" @@ -33,7 +34,7 @@ type TransferStateMessage struct { } type VersionMsg struct { - Latest semver.Version + ServerVersion semver.Version } // ------------------------------------------------------ Spinners ----------------------------------------------------- @@ -122,14 +123,14 @@ func QuitCmd() tea.Cmd { } } -func VersionCmd(version semver.Version) tea.Cmd { +func VersionCmd(ctx context.Context, rendezvousAddr string) tea.Cmd { return func() tea.Msg { - latest, err := semver.GetPortalLatest() + ver, err := semver.GetRendezvousVersion(ctx, rendezvousAddr) if err != nil { return ErrorMsg(err) } return VersionMsg{ - Latest: latest, + ServerVersion: ver, } } }