feat!: rebuild tea on Cobra with explicit noun/verb commands (#1126)

- **switch tea to the Cobra command tree:** `internal/app` wires the packages from parts 1–4 into explicit noun/verb commands with `--repo`, `--json` / `--jq`, `--sudo` and `--debug`; `main.go` points at it. Build, release and docs tooling (`tools/cli-docs`, Makefile, goreleaser, snap, CI) and README / CONTRIBUTING are updated.
- **remove the urfave/cli command tree:** deletes `cmd/`, `modules/`, the old integration tests, contrib completion scripts and the generated `docs/CLI.md`, and drops their dependencies. This commit only deletes.

## Breaking changes

This is a breaking release. It requires Gitea 28 or newer; configuration moves to separate `config.yml` and `hosts.yml` files and requires fresh authentication, and command names, aliases, flags and output formats change. `docs/migration-1.0.md` maps every old command, flag and environment variable to its replacement.

## Issues

fixes #1118
fixes #1116
fixes #1115
fixes #1094
fixes #1091
fixes #1073
fixes #1052
fixes #1032
fixes #1023
fixes #966
fixes #908
fixes #833
fixes #831
fixes #817
fixes #804
fixes #779
fixes #770
fixes #737
fixes #697
fixes #726
fixes #677
fixes #672
fixes #633
fixes #632
fixes #560
fixes #556
fixes #523
fixes #516
fixes #510
fixes #504
fixes #500
fixes #482
fixes #406
fixes #403
fixes #373
fixes #351
fixes #344
fixes #326
fixes #325
fixes #323
fixes #246
fixes #240
fixes #232
fixes #197
fixes #172
fixes #141
fixes #69
fixes #1110
fixes #569
fixes #480
fixes #666
fixes #670
fixes #457
fixes #643
fixes #498
fixes #490
fixes #1069
fixes #834
fixes #602
fixes #328
fixes #417
fixes #571
fixes #1029
fixes #489
fixes #1050
fixes #162
fixes #430
fixes #1087
fixes #683

Reviewed-on: https://gitea.com/gitea/tea/pulls/1126
Reviewed-by: TheFox0x7 <95654+thefox0x7@noreply.gitea.com>
This commit is contained in:
bircni committed 2026-10-11 17:23:06 +00:00
1 parent 12e415a7a7
commit f02245449b
456 files changed
+18892 -34015

No files matched your search

-155
View File
@@ -1,155 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package login
import (
"bufio"
"context"
"fmt"
"net/url"
"os"
"strings"
"github.com/urfave/cli/v3"
"gitea.dev/tea/modules/config"
"gitea.dev/tea/modules/task"
)
// CmdLoginHelper represents to login a gitea helper.
var CmdLoginHelper = cli.Command{
Name: "helper",
Aliases: []string{"git-credential"},
Usage: "Act as a git credential helper for stored Gitea logins",
Description: `Speaks git's credential helper protocol so that HTTPS push and clone
operations against your configured Gitea instances authenticate silently
using the tokens tea already stores.
Typical use is automatic: 'tea login add --git-credentials' (or 'tea login
helper setup' for existing logins) registers '!tea login helper' as a
credential helper in ~/.gitconfig. Git then invokes the 'get' subcommand
when it needs credentials for a configured host.`,
Commands: []*cli.Command{
{
Name: "store",
Aliases: []string{"erase"},
Usage: "No-op (git credential protocol store/erase)",
Description: "No-op invoked by git on credential store/erase; tea persists credentials only in its own config",
Action: func(requestCtx context.Context, _ *cli.Command) error {
return nil
},
},
{
Name: "setup",
Usage: "Register tea as a git credential helper for every configured login",
Description: "Register tea as a git credential helper in ~/.gitconfig for every configured login",
Action: func(requestCtx context.Context, _ *cli.Command) error {
logins, err := config.GetLogins()
if err != nil {
return err
}
for _, login := range logins {
added, err := task.SetupHelper(login)
if err != nil {
return err
} else if added {
fmt.Printf("Added \"%s\"\n", login.Name)
} else {
fmt.Printf("\"%s\" has already been added!\n", login.Name)
}
}
return nil
},
},
{
Name: "get",
Usage: "Return the stored token for a URL (git credential protocol)",
Description: "Return the stored token for a given URL in git's credential helper protocol (called by git, reads request from stdin)",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "login",
Aliases: []string{"l"},
Usage: "Use a specific login",
},
},
Action: func(requestCtx context.Context, cmd *cli.Command) error {
wants := map[string]string{}
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
line := s.Text()
if line == "" {
break
}
parts := strings.SplitN(line, "=", 2)
if len(parts) < 2 {
continue
}
key, value := parts[0], parts[1]
if key == "url" {
u, err := url.Parse(value)
if err != nil {
return err
}
wants["protocol"] = u.Scheme
wants["host"] = u.Host
wants["path"] = u.Path
wants["username"] = u.User.Username()
wants["password"], _ = u.User.Password()
} else {
wants[key] = value
}
}
if len(wants["host"]) == 0 {
return fmt.Errorf("hostname is required")
} else if len(wants["protocol"]) == 0 {
wants["protocol"] = "http"
}
// Use --login flag if provided, otherwise fall back to host lookup
var userConfig *config.Login
if loginName := cmd.String("login"); loginName != "" {
var lookupErr error
userConfig, lookupErr = config.GetLoginByName(loginName)
if lookupErr != nil {
return lookupErr
}
if userConfig == nil {
return fmt.Errorf("login '%s' not found", loginName)
}
} else {
var lookupErr error
userConfig, lookupErr = config.GetLoginByHost(wants["host"])
if lookupErr != nil {
return lookupErr
}
if userConfig == nil {
return fmt.Errorf("no login found for host '%s'", wants["host"])
}
}
if len(userConfig.GetAccessToken()) == 0 {
return fmt.Errorf("user not set")
}
host, err := url.Parse(userConfig.URL)
if err != nil {
return err
}
// Refresh token if expired or near expiry (updates userConfig in place)
if err = userConfig.RefreshOAuthTokenIfNeeded(); err != nil {
return err
}
_, err = fmt.Fprintf(os.Stdout, "protocol=%s\nhost=%s\nusername=%s\npassword=%s\n", host.Scheme, host.Host, userConfig.User, userConfig.GetAccessToken())
if err != nil {
return err
}
return nil
},
},
},
}