diff --git a/cli/common/flags.go b/cli/common/flags.go
index e00b23a456..665b645788 100644
--- a/cli/common/flags.go
+++ b/cli/common/flags.go
@@ -21,6 +21,12 @@ import (
)
var GlobalFlags = append([]cli.Flag{
+ &cli.StringFlag{
+ EnvVars: []string{"WOODPECKER_CONFIG"},
+ Name: "config",
+ Aliases: []string{"c"},
+ Usage: "path to config file",
+ },
&cli.StringFlag{
EnvVars: []string{"WOODPECKER_TOKEN"},
Name: "token",
diff --git a/cli/common/hooks.go b/cli/common/hooks.go
index 82a8f9d200..5ff2a1242f 100644
--- a/cli/common/hooks.go
+++ b/cli/common/hooks.go
@@ -8,6 +8,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/urfave/cli/v2"
+ "go.woodpecker-ci.org/woodpecker/v2/cli/internal/config"
"go.woodpecker-ci.org/woodpecker/v2/cli/update"
)
@@ -17,7 +18,7 @@ var (
)
func Before(c *cli.Context) error {
- if err := SetupGlobalLogger(c); err != nil {
+ if err := setupGlobalLogger(c); err != nil {
return err
}
@@ -49,7 +50,7 @@ func Before(c *cli.Context) error {
}
}()
- return nil
+ return config.Load(c)
}
func After(_ *cli.Context) error {
diff --git a/cli/common/zerologger.go b/cli/common/zerologger.go
index 2d0e2aceab..d3e275898b 100644
--- a/cli/common/zerologger.go
+++ b/cli/common/zerologger.go
@@ -20,6 +20,6 @@ import (
"go.woodpecker-ci.org/woodpecker/v2/shared/logger"
)
-func SetupGlobalLogger(c *cli.Context) error {
+func setupGlobalLogger(c *cli.Context) error {
return logger.SetupGlobalLogger(c, false)
}
diff --git a/cli/internal/config/config.go b/cli/internal/config/config.go
new file mode 100644
index 0000000000..32a0472503
--- /dev/null
+++ b/cli/internal/config/config.go
@@ -0,0 +1,131 @@
+package config
+
+import (
+ "encoding/json"
+ "errors"
+ "os"
+
+ "github.com/adrg/xdg"
+ "github.com/rs/zerolog/log"
+ "github.com/urfave/cli/v2"
+ "github.com/zalando/go-keyring"
+)
+
+type Config struct {
+ ServerURL string `json:"server_url"`
+ Token string `json:"-"`
+ LogLevel string `json:"log_level"`
+}
+
+func Load(c *cli.Context) error {
+ // If the command is setup, we don't need to load the config
+ if firstArg := c.Args().First(); firstArg == "setup" {
+ return nil
+ }
+
+ config, err := Get(c, c.String("config"))
+ if err != nil {
+ return err
+ }
+
+ if config == nil && !c.IsSet("server-url") && !c.IsSet("token") {
+ log.Info().Msg("The woodpecker-cli is not yet set up. Please run `woodpecker-cli setup`")
+ return errors.New("woodpecker-cli is not setup")
+ }
+
+ if !c.IsSet("server") {
+ err = c.Set("server", config.ServerURL)
+ if err != nil {
+ return err
+ }
+ }
+
+ if !c.IsSet("token") {
+ err = c.Set("token", config.Token)
+ if err != nil {
+ return err
+ }
+ }
+
+ if !c.IsSet("log-level") {
+ err = c.Set("log-level", config.LogLevel)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func getConfigPath(configPath string) (string, error) {
+ if configPath != "" {
+ return configPath, nil
+ }
+
+ configPath, err := xdg.ConfigFile("woodpecker/config.json")
+ if err != nil {
+ return "", err
+ }
+
+ return configPath, nil
+}
+
+func Get(ctx *cli.Context, _configPath string) (*Config, error) {
+ configPath, err := getConfigPath(_configPath)
+ if err != nil {
+ return nil, err
+ }
+
+ content, err := os.ReadFile(configPath)
+ if err != nil && !os.IsNotExist(err) {
+ log.Debug().Err(err).Msg("Failed to read the config file")
+ return nil, err
+ } else if err != nil && os.IsNotExist(err) {
+ log.Debug().Msg("The config file does not exist")
+ return nil, nil
+ }
+
+ c := &Config{}
+ err = json.Unmarshal(content, c)
+ if err != nil {
+ return nil, err
+ }
+
+ // load token from keyring
+ service := ctx.App.Name
+ secret, err := keyring.Get(service, c.ServerURL)
+ if err != nil && !errors.Is(err, keyring.ErrNotFound) {
+ return nil, err
+ }
+ if err == nil {
+ c.Token = secret
+ }
+
+ return c, nil
+}
+
+func Save(ctx *cli.Context, _configPath string, c *Config) error {
+ config, err := json.Marshal(c)
+ if err != nil {
+ return err
+ }
+
+ configPath, err := getConfigPath(_configPath)
+ if err != nil {
+ return err
+ }
+
+ // save token to keyring
+ service := ctx.App.Name
+ err = keyring.Set(service, c.ServerURL, c.Token)
+ if err != nil {
+ return err
+ }
+
+ err = os.WriteFile(configPath, config, 0o600)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/cli/setup/setup.go b/cli/setup/setup.go
new file mode 100644
index 0000000000..b114cf750c
--- /dev/null
+++ b/cli/setup/setup.go
@@ -0,0 +1,88 @@
+package setup
+
+import (
+ "errors"
+ "strings"
+
+ "github.com/rs/zerolog/log"
+ "github.com/urfave/cli/v2"
+
+ "go.woodpecker-ci.org/woodpecker/v2/cli/internal/config"
+ "go.woodpecker-ci.org/woodpecker/v2/cli/setup/ui"
+)
+
+// Command exports the setup command.
+var Command = &cli.Command{
+ Name: "setup",
+ Usage: "setup the woodpecker-cli for the first time",
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "server-url",
+ Usage: "The URL of the woodpecker server",
+ },
+ &cli.StringFlag{
+ Name: "token",
+ Usage: "The token to authenticate with the woodpecker server",
+ },
+ },
+ Action: setup,
+}
+
+func setup(c *cli.Context) error {
+ _config, err := config.Get(c, c.String("config"))
+ if err != nil {
+ return err
+ } else if _config != nil {
+ setupAgain, err := ui.Confirm("The woodpecker-cli was already configured. Do you want to configure it again?")
+ if err != nil {
+ return err
+ }
+
+ if !setupAgain {
+ log.Info().Msg("Configuration skipped")
+ return nil
+ }
+ }
+
+ serverURL := c.String("server-url")
+
+ if serverURL == "" {
+ serverURL, err = ui.Ask("Enter the URL of the woodpecker server", "https://ci.woodpecker-ci.org", true)
+ if err != nil {
+ return err
+ }
+
+ if serverURL == "" {
+ return errors.New("server URL cannot be empty")
+ }
+ }
+
+ if !strings.Contains(serverURL, "://") {
+ serverURL = "https://" + serverURL
+ }
+
+ token := c.String("token")
+ if token == "" {
+ token, err = receiveTokenFromUI(c.Context, serverURL)
+ if err != nil {
+ return err
+ }
+
+ if token == "" {
+ return errors.New("no token received from the UI")
+ }
+ }
+
+ err = config.Save(c, c.String("config"), &config.Config{
+ ServerURL: serverURL,
+ Token: token,
+ LogLevel: "info",
+ })
+ if err != nil {
+ return err
+ }
+
+ log.Info().Msg("The woodpecker-cli has been successfully setup")
+
+ return nil
+}
diff --git a/cli/setup/token_fetcher.go b/cli/setup/token_fetcher.go
new file mode 100644
index 0000000000..54dd18eee3
--- /dev/null
+++ b/cli/setup/token_fetcher.go
@@ -0,0 +1,117 @@
+package setup
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/rand"
+ "net/http"
+ "os/exec"
+ "runtime"
+ "time"
+
+ "github.com/gin-gonic/gin"
+ "github.com/rs/zerolog/log"
+)
+
+func receiveTokenFromUI(c context.Context, serverURL string) (string, error) {
+ port := randomPort()
+
+ tokenReceived := make(chan string)
+
+ srv := &http.Server{Addr: fmt.Sprintf("127.0.0.1:%d", port)}
+ srv.Handler = setupRouter(tokenReceived)
+
+ go func() {
+ log.Debug().Msgf("Listening for token response on :%d", port)
+ _ = srv.ListenAndServe()
+ }()
+
+ defer func() {
+ log.Debug().Msg("Shutting down server")
+ _ = srv.Shutdown(c)
+ }()
+
+ err := openBrowser(fmt.Sprintf("%s/cli/auth?port=%d", serverURL, port))
+ if err != nil {
+ return "", err
+ }
+
+ // wait for token to be received or timeout
+ select {
+ case token := <-tokenReceived:
+ return token, nil
+ case <-c.Done():
+ return "", c.Err()
+ case <-time.After(5 * time.Minute):
+ return "", errors.New("timed out waiting for token")
+ }
+}
+
+func setupRouter(tokenReceived chan string) *gin.Engine {
+ gin.SetMode(gin.ReleaseMode)
+ e := gin.New()
+ e.UseRawPath = true
+ e.Use(gin.Recovery())
+
+ e.Use(func(c *gin.Context) {
+ c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
+ c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
+ c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
+ c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
+
+ if c.Request.Method == "OPTIONS" {
+ c.AbortWithStatus(204)
+ return
+ }
+
+ c.Next()
+ })
+
+ e.POST("/token", func(c *gin.Context) {
+ data := struct {
+ Token string `json:"token"`
+ }{}
+
+ err := c.BindJSON(&data)
+ if err != nil {
+ log.Debug().Err(err).Msg("Failed to bind JSON")
+ c.JSON(400, gin.H{
+ "error": "invalid request",
+ })
+ return
+ }
+
+ tokenReceived <- data.Token
+
+ c.JSON(200, gin.H{
+ "ok": "true",
+ })
+ })
+
+ return e
+}
+
+func openBrowser(url string) error {
+ var err error
+
+ log.Debug().Msgf("Opening browser with URL: %s", url)
+
+ switch runtime.GOOS {
+ case "linux":
+ err = exec.Command("xdg-open", url).Start()
+ case "windows":
+ err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
+ case "darwin":
+ err = exec.Command("open", url).Start()
+ default:
+ err = fmt.Errorf("unsupported platform")
+ }
+ return err
+}
+
+func randomPort() int {
+ s1 := rand.NewSource(time.Now().UnixNano())
+ r1 := rand.New(s1)
+ return r1.Intn(10000) + 20000
+}
diff --git a/cli/setup/ui/ask.go b/cli/setup/ui/ask.go
new file mode 100644
index 0000000000..2fa8c539e8
--- /dev/null
+++ b/cli/setup/ui/ask.go
@@ -0,0 +1,79 @@
+package ui
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/charmbracelet/bubbles/textinput"
+ tea "github.com/charmbracelet/bubbletea"
+)
+
+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()
+ 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
+}
diff --git a/cli/setup/ui/confirm.go b/cli/setup/ui/confirm.go
new file mode 100644
index 0000000000..350fdb2e4b
--- /dev/null
+++ b/cli/setup/ui/confirm.go
@@ -0,0 +1,71 @@
+package ui
+
+import (
+ "fmt"
+
+ "github.com/charmbracelet/bubbles/textinput"
+ tea "github.com/charmbracelet/bubbletea"
+)
+
+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()
+ if err != nil {
+ return false, err
+ }
+
+ m, ok := _m.(confirmModel)
+ if !ok {
+ return false, fmt.Errorf("unexpected model: %T", _m)
+ }
+
+ return m.confirmed, nil
+}
diff --git a/cmd/cli/app.go b/cmd/cli/app.go
index 92ba17237b..db6edca178 100644
--- a/cmd/cli/app.go
+++ b/cmd/cli/app.go
@@ -29,6 +29,7 @@ import (
"go.woodpecker-ci.org/woodpecker/v2/cli/registry"
"go.woodpecker-ci.org/woodpecker/v2/cli/repo"
"go.woodpecker-ci.org/woodpecker/v2/cli/secret"
+ "go.woodpecker-ci.org/woodpecker/v2/cli/setup"
"go.woodpecker-ci.org/woodpecker/v2/cli/update"
"go.woodpecker-ci.org/woodpecker/v2/cli/user"
"go.woodpecker-ci.org/woodpecker/v2/version"
@@ -58,6 +59,7 @@ func newApp() *cli.App {
lint.Command,
loglevel.Command,
cron.Command,
+ setup.Command,
update.Command,
}
diff --git a/go.mod b/go.mod
index ecdfa39c26..9476c522b6 100644
--- a/go.mod
+++ b/go.mod
@@ -7,10 +7,13 @@ require (
codeberg.org/6543/go-yaml2json v1.0.0
codeberg.org/6543/xyaml v1.1.0
github.com/6543/logfile-open v1.2.1
+ github.com/adrg/xdg v0.4.0
github.com/alessio/shellescape v1.4.2
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/distribution/reference v0.5.0
github.com/docker/cli v24.0.9+incompatible
github.com/docker/docker v24.0.9+incompatible
@@ -50,6 +53,7 @@ require (
github.com/urfave/cli/v2 v2.27.1
github.com/xanzy/go-gitlab v0.97.0
github.com/xeipuuv/gojsonschema v1.2.0
+ github.com/zalando/go-keyring v0.2.3
go.uber.org/multierr v1.11.0
golang.org/x/crypto v0.19.0
golang.org/x/net v0.21.0
@@ -72,13 +76,17 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
+ github.com/atotto/clipboard v0.1.4 // indirect
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/cespare/xxhash/v2 v2.2.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
+ github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
+ github.com/danieljoos/wincred v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
@@ -97,6 +105,7 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
+ github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
@@ -120,6 +129,7 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mholt/acmez v1.2.0 // indirect
@@ -127,6 +137,9 @@ 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/cancelreader v0.2.2 // indirect
+ github.com/muesli/reflow v0.3.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
@@ -135,7 +148,7 @@ require (
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
- github.com/rivo/uniseg v0.4.4 // indirect
+ github.com/rivo/uniseg v0.4.6 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
diff --git a/go.sum b/go.sum
index a15a4b2015..5034e80a45 100644
--- a/go.sum
+++ b/go.sum
@@ -20,8 +20,12 @@ github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6Xge
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
+github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
+github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
+github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -38,6 +42,12 @@ github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqy
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=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
+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/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=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
@@ -49,6 +59,8 @@ github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4M
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
+github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@@ -57,6 +69,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
+github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -81,8 +95,9 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
-github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/expr-lang/expr v1.16.1 h1:Na8CUcMdyGbnNpShY7kzcHCU7WqxuL+hnxgHZ4vaz/A=
@@ -138,6 +153,8 @@ github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
+github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -299,6 +316,9 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
+github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
+github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
@@ -325,6 +345,12 @@ 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/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=
+github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
@@ -364,9 +390,10 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
-github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
+github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -438,6 +465,8 @@ github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsr
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=
+github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
@@ -537,11 +566,13 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/web/components.d.ts b/web/components.d.ts
index 833a12b0d6..61ce4d6e81 100644
--- a/web/components.d.ts
+++ b/web/components.d.ts
@@ -110,6 +110,7 @@ declare module 'vue' {
Tabs: typeof import('./src/components/layout/scaffold/Tabs.vue')['default']
TextField: typeof import('./src/components/form/TextField.vue')['default']
UserAPITab: typeof import('./src/components/user/UserAPITab.vue')['default']
+ UserCLIAndAPITab: typeof import('./src/components/user/UserCLIAndAPITab.vue')['default']
UserGeneralTab: typeof import('./src/components/user/UserGeneralTab.vue')['default']
UserSecretsTab: typeof import('./src/components/user/UserSecretsTab.vue')['default']
Warning: typeof import('./src/components/atomic/Warning.vue')['default']
diff --git a/web/src/assets/locales/en.json b/web/src/assets/locales/en.json
index 8fde513d96..159120e5c3 100644
--- a/web/src/assets/locales/en.json
+++ b/web/src/assets/locales/en.json
@@ -486,15 +486,13 @@
"pr_warning": "Please be careful with this option as a bad actor can submit a malicious pull request that exposes your secrets."
}
},
- "api": {
- "api": "API",
- "desc": "Personal Access Token and API usage",
+ "cli_and_api": {
+ "cli_and_api": "CLI & API",
+ "desc": "Personal Access Token, CLI and API usage",
"token": "Personal Access Token",
- "shell_setup": "Shell setup",
"api_usage": "Example API Usage",
"cli_usage": "Example CLI Usage",
- "dl_cli": "Download CLI",
- "shell_setup_before": "do shell setup steps before",
+ "download_cli": "Download CLI",
"reset_token": "Reset token",
"swagger_ui": "Swagger UI"
}
@@ -508,5 +506,12 @@
"running_version": "You are running Woodpecker {0}",
"update_woodpecker": "Please update your Woodpecker instance to {0}",
"global_level_secret": "global secret",
- "org_level_secret": "organization secret"
+ "org_level_secret": "organization secret",
+ "login_to_cli": "Login to CLI",
+ "login_to_cli_description": "By continuing you will be logged in to the CLI.",
+ "abort": "Abort",
+ "cli_login_success": "Login to CLI successful",
+ "cli_login_failed": "Login to CLI failed",
+ "cli_login_denied": "Login to CLI denied",
+ "return_to_cli": "You can now close this tab and return to the CLI."
}
diff --git a/web/src/components/user/UserAPITab.vue b/web/src/components/user/UserCLIAndAPITab.vue
similarity index 64%
rename from web/src/components/user/UserAPITab.vue
rename to web/src/components/user/UserCLIAndAPITab.vue
index 2402612df5..21d80b9b5c 100644
--- a/web/src/components/user/UserAPITab.vue
+++ b/web/src/components/user/UserCLIAndAPITab.vue
@@ -1,43 +1,38 @@
-
-
+
+
-
+ {{
+ $t('user.settings.cli_and_api.download_cli')
+ }}
- {{ token }}
+ {{ usageWithCli }}
-
- {{ usageWithShell }}
+
+
+
+
+ {{ token }}
-
+
{{ $t('user.settings.api.swagger_ui') }}{{ $t('user.settings.cli_and_api.swagger_ui') }}
{{ usageWithCurl }}
-
-
-
- {{
- $t('user.settings.api.dl_cli')
- }}
-
- {{ usageWithCli }}
-