Skip to content

Commit

Permalink
Fix cli version comparison and improve setup command (#3518)
Browse files Browse the repository at this point in the history
  • Loading branch information
anbraten authored Mar 28, 2024
1 parent 0b76e46 commit c2a8464
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 164 deletions.
4 changes: 2 additions & 2 deletions cli/common/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func Before(c *cli.Context) error {

log.Debug().Msg("Checking for updates ...")

newVersion, err := update.CheckForUpdate(waitForUpdateCheck, true)
newVersion, err := update.CheckForUpdate(waitForUpdateCheck, false)
if err != nil {
log.Error().Err(err).Msgf("Failed to check for updates")
return
Expand All @@ -58,7 +58,7 @@ func After(_ *cli.Context) error {
select {
case <-waitForUpdateCheck.Done():
// When the actual command already finished, we still wait 250ms for the update check to finish
case <-time.After(time.Millisecond * 250):
case <-time.After(time.Millisecond * 500):
log.Debug().Msg("Update check stopped due to timeout")
cancelWaitForUpdate(errors.New("update check timeout"))
}
Expand Down
9 changes: 7 additions & 2 deletions cli/setup/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (

// Command exports the setup command.
var Command = &cli.Command{
Name: "setup",
Usage: "setup the woodpecker-cli for the first time",
Name: "setup",
Usage: "setup the woodpecker-cli for the first time",
Args: true,
ArgsUsage: "[server-url]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "server-url",
Expand Down Expand Up @@ -45,6 +47,9 @@ func setup(c *cli.Context) error {
}

serverURL := c.String("server-url")
if serverURL == "" {
serverURL = c.Args().First()
}

if serverURL == "" {
serverURL, err = ui.Ask("Enter the URL of the woodpecker server", "https://ci.woodpecker-ci.org", true)
Expand Down
15 changes: 15 additions & 0 deletions cli/setup/token_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"runtime"
"time"

"github.com/charmbracelet/huh/spinner"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
)
Expand Down Expand Up @@ -37,13 +38,27 @@ func receiveTokenFromUI(c context.Context, serverURL string) (string, error) {
return "", err
}

spinnerCtx, spinnerDone := context.WithCancelCause(c)
go func() {
err = spinner.New().
Title("Waiting for token ...").
Context(spinnerCtx).
Run()
if err != nil {
return
}
}()

// wait for token to be received or timeout
select {
case token := <-tokenReceived:
spinnerDone(nil)
return token, nil
case <-c.Done():
spinnerDone(nil)
return "", c.Err()
case <-time.After(5 * time.Minute):
spinnerDone(nil)
return "", errors.New("timed out waiting for token")
}
}
Expand Down
79 changes: 13 additions & 66 deletions cli/setup/ui/ask.go
Original file line number Diff line number Diff line change
@@ -1,79 +1,26 @@
package ui

import (
"fmt"
"errors"
"strings"

"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh"
)

type askModel struct {
prompt string
required bool
textInput textinput.Model
err error
}

func (m askModel) Init() tea.Cmd {
return textinput.Blink
}

func (m askModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd

switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyEnter:
if !m.required || (m.required && strings.TrimSpace(m.textInput.Value()) != "") {
return m, tea.Quit
}
case tea.KeyCtrlC, tea.KeyEsc:
return m, tea.Quit
}
default:
return m, cmd
}

m.textInput, cmd = m.textInput.Update(msg)
return m, cmd
}

func (m askModel) View() string {
return fmt.Sprintf(
"%s\n\n%s\n\n%s",
m.prompt,
m.textInput.View(),
"(esc to quit)",
) + "\n"
}

func Ask(prompt, placeholder string, required bool) (string, error) {
ti := textinput.New()
ti.Placeholder = placeholder
ti.Focus()
ti.CharLimit = 156
ti.Width = 40

p := tea.NewProgram(askModel{
prompt: prompt,
textInput: ti,
required: required,
err: nil,
})

_m, err := p.Run()
var input string
err := huh.NewInput().
Title(prompt).
Value(&input).
Placeholder(placeholder).Validate(func(s string) error {
if required && strings.TrimSpace(s) == "" {
return errors.New("required")
}
return nil
}).Run()
if err != nil {
return "", err
}

m, ok := _m.(askModel)
if !ok {
return "", fmt.Errorf("unexpected model: %T", _m)
}

text := strings.TrimSpace(m.textInput.Value())

return text, nil
return strings.TrimSpace(input), nil
}
68 changes: 8 additions & 60 deletions cli/setup/ui/confirm.go
Original file line number Diff line number Diff line change
@@ -1,71 +1,19 @@
package ui

import (
"fmt"

"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh"
)

type confirmModel struct {
confirmed bool
prompt string
err error
}

func (m confirmModel) Init() tea.Cmd {
return textinput.Blink
}

func (m confirmModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd

switch msg := msg.(type) {
case tea.KeyMsg:
if msg.Runes != nil {
switch msg.Runes[0] {
case 'y':
m.confirmed = true
return m, tea.Quit
case 'n':
m.confirmed = false
return m, tea.Quit
}
}

switch msg.Type {
case tea.KeyCtrlC, tea.KeyEsc:
return m, tea.Quit
}
default:
return m, nil
}

return m, cmd
}

func (m confirmModel) View() string {
return fmt.Sprintf(
"%s y / n (esc to quit)",
m.prompt,
) + "\n"
}

func Confirm(prompt string) (bool, error) {
p := tea.NewProgram(confirmModel{
prompt: prompt,
err: nil,
})

_m, err := p.Run()
var confirm bool
err := huh.NewConfirm().
Title(prompt).
Affirmative("Yes!").
Negative("No.").
Value(&confirm).Run()
if err != nil {
return false, err
}

m, ok := _m.(confirmModel)
if !ok {
return false, fmt.Errorf("unexpected model: %T", _m)
}

return m.confirmed, nil
return confirm, err
}
15 changes: 8 additions & 7 deletions cli/update/types.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package update

type GithubRelease struct {
TagName string `json:"tag_name"`
Assets []struct {
Name string `json:"name"`
BrowserDownloadURL string `json:"browser_download_url"`
} `json:"assets"`
type VersionData struct {
Latest string `json:"latest"`
Next string `json:"next"`
RC string `json:"rc"`
}

type NewVersion struct {
Version string
AssetURL string
}

const githubReleaseURL = "https://api.github.com/repos/woodpecker-ci/woodpecker/releases/latest"
const (
woodpeckerVersionURL = "https://woodpecker-ci.org/version.json"
githubBinaryURL = "https://github.com/woodpecker-ci/woodpecker/releases/download/v%s/woodpecker-cli_%s_%s.tar.gz"
)
45 changes: 24 additions & 21 deletions cli/update/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,26 @@ import (
"os"
"path"
"runtime"
"strings"

"github.com/rs/zerolog/log"

"go.woodpecker-ci.org/woodpecker/v2/version"
)

func CheckForUpdate(ctx context.Context, force bool) (*NewVersion, error) {
return checkForUpdate(ctx, woodpeckerVersionURL, force)
}

func checkForUpdate(ctx context.Context, versionURL string, force bool) (*NewVersion, error) {
log.Debug().Msgf("Current version: %s", version.String())

if version.String() == "dev" && !force {
log.Debug().Msgf("Skipping update check for development version")
if (version.String() == "dev" || strings.HasPrefix(version.String(), "next-")) && !force {
log.Debug().Msgf("Skipping update check for development & next versions")
return nil, nil
}

req, err := http.NewRequestWithContext(ctx, "GET", githubReleaseURL, nil)
req, err := http.NewRequestWithContext(ctx, "GET", versionURL, nil)
if err != nil {
return nil, err
}
Expand All @@ -39,34 +44,32 @@ func CheckForUpdate(ctx context.Context, force bool) (*NewVersion, error) {
return nil, errors.New("failed to fetch the latest release")
}

var release GithubRelease
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
var versionData VersionData
if err := json.NewDecoder(resp.Body).Decode(&versionData); err != nil {
return nil, err
}

// using the latest release
if release.TagName == version.String() && !force {
return nil, nil
upstreamVersion := versionData.Latest
if strings.HasPrefix(version.String(), "next-") {
upstreamVersion = versionData.Next
} else if strings.HasSuffix(version.String(), "rc-") {
upstreamVersion = versionData.RC
}

log.Debug().Msgf("Latest version: %s", release.TagName)
installedVersion := strings.TrimPrefix(version.Version, "v")
upstreamVersion = strings.TrimPrefix(upstreamVersion, "v")

assetURL := ""
fileName := fmt.Sprintf("woodpecker-cli_%s_%s.tar.gz", runtime.GOOS, runtime.GOARCH)
for _, asset := range release.Assets {
if fileName == asset.Name {
assetURL = asset.BrowserDownloadURL
log.Debug().Msgf("Found asset for the current OS and arch: %s", assetURL)
break
}
// using the latest release
if installedVersion == upstreamVersion && !force {
log.Debug().Msgf("No new version available")
return nil, nil
}

if assetURL == "" {
return nil, errors.New("no asset found for the current OS")
}
log.Debug().Msgf("New version available: %s", upstreamVersion)

assetURL := fmt.Sprintf(githubBinaryURL, upstreamVersion, runtime.GOOS, runtime.GOARCH)
return &NewVersion{
Version: release.TagName,
Version: upstreamVersion,
AssetURL: assetURL,
}, nil
}
Expand Down
Loading

0 comments on commit c2a8464

Please sign in to comment.