mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-25 11:21:02 +00:00
Fix cli version comparison and improve setup command (#3518)
This commit is contained in:
parent
0b76e465c1
commit
c2a8464512
11 changed files with 155 additions and 165 deletions
|
@ -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
|
||||
|
@ -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"))
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ import (
|
|||
var Command = &cli.Command{
|
||||
Name: "setup",
|
||||
Usage: "setup the woodpecker-cli for the first time",
|
||||
Args: true,
|
||||
ArgsUsage: "[server-url]",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "server-url",
|
||||
|
@ -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)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/huh/spinner"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
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 {
|
||||
|
@ -13,4 +11,7 @@ type NewVersion struct {
|
|||
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"
|
||||
)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
|
@ -17,14 +18,18 @@ import (
|
|||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
upstreamVersion := versionData.Latest
|
||||
if strings.HasPrefix(version.String(), "next-") {
|
||||
upstreamVersion = versionData.Next
|
||||
} else if strings.HasSuffix(version.String(), "rc-") {
|
||||
upstreamVersion = versionData.RC
|
||||
}
|
||||
|
||||
installedVersion := strings.TrimPrefix(version.Version, "v")
|
||||
upstreamVersion = strings.TrimPrefix(upstreamVersion, "v")
|
||||
|
||||
// using the latest release
|
||||
if release.TagName == version.String() && !force {
|
||||
if installedVersion == upstreamVersion && !force {
|
||||
log.Debug().Msgf("No new version available")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
log.Debug().Msgf("Latest version: %s", release.TagName)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
61
cli/update/updater_test.go
Normal file
61
cli/update/updater_test.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package update
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/version"
|
||||
)
|
||||
|
||||
func TestCheckForUpdate(t *testing.T) {
|
||||
version.Version = "1.0.0"
|
||||
fixtureHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/version.json" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = io.WriteString(w, `{"latest": "1.0.1", "next": "1.0.2", "rc": "1.0.3"}`)
|
||||
}
|
||||
ts := httptest.NewServer(http.HandlerFunc(fixtureHandler))
|
||||
defer ts.Close()
|
||||
|
||||
newVersion, err := checkForUpdate(context.Background(), ts.URL+"/version.json", false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to check for updates: %v", err)
|
||||
}
|
||||
|
||||
if newVersion == nil || newVersion.Version != "1.0.1" {
|
||||
t.Fatalf("Expected a new version 1.0.1, got: %s", newVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadNewVersion(t *testing.T) {
|
||||
downloadFilePath := "/woodpecker-cli_linux_amd64.tar.gz"
|
||||
|
||||
fixtureHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != downloadFilePath {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = io.WriteString(w, `blob`)
|
||||
}
|
||||
ts := httptest.NewServer(http.HandlerFunc(fixtureHandler))
|
||||
defer ts.Close()
|
||||
|
||||
file, err := downloadNewVersion(context.Background(), ts.URL+downloadFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to download new version: %v", err)
|
||||
}
|
||||
|
||||
if file == "" {
|
||||
t.Fatalf("Expected a file path, got: %s", file)
|
||||
}
|
||||
|
||||
_ = os.Remove(file)
|
||||
}
|
9
go.mod
9
go.mod
|
@ -12,8 +12,8 @@ require (
|
|||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||
github.com/caddyserver/certmagic v0.20.0
|
||||
github.com/cenkalti/backoff/v4 v4.2.1
|
||||
github.com/charmbracelet/bubbles v0.18.0
|
||||
github.com/charmbracelet/bubbletea v0.25.0
|
||||
github.com/charmbracelet/huh v0.3.0
|
||||
github.com/charmbracelet/huh/spinner v0.0.0-20240306161957-71f31c155b08
|
||||
github.com/distribution/reference v0.5.0
|
||||
github.com/docker/cli v24.0.9+incompatible
|
||||
github.com/docker/docker v24.0.9+incompatible
|
||||
|
@ -81,7 +81,10 @@ require (
|
|||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/sonic v1.10.2 // indirect
|
||||
github.com/catppuccin/go v0.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/charmbracelet/bubbles v0.18.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.25.0 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
|
@ -137,7 +140,7 @@ require (
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -40,6 +40,8 @@ github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZF
|
|||
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
||||
github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
|
||||
github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
|
@ -48,6 +50,10 @@ github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/
|
|||
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
||||
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
||||
github.com/charmbracelet/huh v0.3.0 h1:CxPplWkgW2yUTDDG0Z4S5HH8SJOosWHd4LxCvi0XsKE=
|
||||
github.com/charmbracelet/huh v0.3.0/go.mod h1:fujUdKX8tC45CCSaRQdw789O6uaCRwx8l2NDyKfC4jA=
|
||||
github.com/charmbracelet/huh/spinner v0.0.0-20240306161957-71f31c155b08 h1:kO5eMMxyCJ6m7gdpGQ7OomrMdfsKVPgC4aB/focl/HE=
|
||||
github.com/charmbracelet/huh/spinner v0.0.0-20240306161957-71f31c155b08/go.mod h1:nrBG0YEHaxdbqHXW1xvG1hPqkuac9Eg7RTMvogiXuz0=
|
||||
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
||||
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
|
@ -345,8 +351,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
|||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="flex flex-col gap-4 m-auto">
|
||||
<div class="text-center text-wp-text-100">
|
||||
<img src="../../assets/logo.svg" alt="CLI" class="w-32 m-auto mb-8" />
|
||||
<WoodpeckerLogo preserveAspectRatio="xMinYMin slice" class="w-32 m-auto mb-8" />
|
||||
<template v-if="state === 'confirm'">
|
||||
<h1 class="text-4xl font-bold">{{ $t('login_to_cli') }}</h1>
|
||||
<p class="text-2xl">{{ $t('login_to_cli_description') }}</p>
|
||||
|
@ -32,6 +32,7 @@ import { ref } from 'vue';
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import WoodpeckerLogo from '~/assets/logo.svg?component';
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
|
||||
|
|
Loading…
Reference in a new issue