When there is no login detected, list all possible logins to select

This commit is contained in:
Lunny Xiao
2025-10-03 12:06:31 -07:00
parent 4f33146b70
commit 376476150e
61 changed files with 270 additions and 201 deletions

View File

@@ -6,7 +6,6 @@ package context
import (
"errors"
"fmt"
"log"
"os"
"path"
"strconv"
@@ -16,15 +15,11 @@ import (
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/debug"
"code.gitea.io/tea/modules/git"
"code.gitea.io/tea/modules/theme"
"code.gitea.io/tea/modules/utils"
"github.com/charmbracelet/huh"
gogit "github.com/go-git/go-git/v5"
"github.com/urfave/cli/v3"
)
var errNotAGiteaRepo = errors.New("No Gitea login found. You might want to specify --repo (and --login) to work outside of a repository")
var ErrNotAGiteaRepo = errors.New("No Gitea login found. You might want to specify --repo (and --login) to work outside of a repository")
// TeaContext contains all context derived during command initialization and wraps cli.Context
type TeaContext struct {
@@ -65,108 +60,8 @@ type CtxRequirement struct {
RemoteRepo bool
}
// InitCommand resolves the application context, and returns the active login, and if
// available the repo slug. It does this by reading the config file for logins, parsing
// the remotes of the .git repo specified in repoFlag or $PWD, and using overrides from
// command flags. If a local git repo can't be found, repo slug values are unset.
func InitCommand(cmd *cli.Command) *TeaContext {
// these flags are used as overrides to the context detection via local git repo
repoFlag := cmd.String("repo")
loginFlag := cmd.String("login")
remoteFlag := cmd.String("remote")
var (
c TeaContext
err error
repoPath string // empty means PWD
repoFlagPathExists bool
)
// check if repoFlag can be interpreted as path to local repo.
if len(repoFlag) != 0 {
if repoFlagPathExists, err = utils.DirExists(repoFlag); err != nil {
log.Fatal(err.Error())
}
if repoFlagPathExists {
repoPath = repoFlag
}
}
if len(remoteFlag) == 0 {
remoteFlag = config.GetPreferences().FlagDefaults.Remote
}
if repoPath == "" {
if repoPath, err = os.Getwd(); err != nil {
log.Fatal(err.Error())
}
}
// try to read local git repo & extract context: if repoFlag specifies a valid path, read repo in that dir,
// otherwise attempt PWD. if no repo is found, continue with default login
if c.LocalRepo, c.Login, c.RepoSlug, err = contextFromLocalRepo(repoPath, remoteFlag); err != nil {
if err == errNotAGiteaRepo || err == gogit.ErrRepositoryNotExists {
// we can deal with that, commands needing the optional values use ctx.Ensure()
} else {
log.Fatal(err.Error())
}
}
if len(repoFlag) != 0 && !repoFlagPathExists {
// if repoFlag is not a valid path, use it to override repoSlug
c.RepoSlug = repoFlag
}
// override config user with env variable
envLogin := GetLoginByEnvVar()
if envLogin != nil {
_, err := utils.ValidateAuthenticationMethod(envLogin.URL, envLogin.Token, "", "", false, "", "")
if err != nil {
log.Fatal(err.Error())
}
c.Login = envLogin
}
// override login from flag, or use default login if repo based detection failed
if len(loginFlag) != 0 {
c.Login = config.GetLoginByName(loginFlag)
if c.Login == nil {
log.Fatalf("Login name '%s' does not exist", loginFlag)
}
} else if c.Login == nil {
if c.Login, err = config.GetDefaultLogin(); err != nil {
if err.Error() == "No available login" {
// TODO: maybe we can directly start interact.CreateLogin() (only if
// we're sure we can interactively!), as gh cli does.
fmt.Println(`No gitea login configured. To start using tea, first run
tea login add
and then run your command again.`)
}
os.Exit(1)
}
fallback := false
if err := huh.NewConfirm().
Title(fmt.Sprintf("NOTE: no gitea login detected, whether falling back to login '%s'?", c.Login.Name)).
Value(&fallback).
WithTheme(theme.GetTheme()).
Run(); err != nil {
log.Fatalf("Get confirm failed: %v", err)
}
if !fallback {
os.Exit(1)
}
}
// parse reposlug (owner falling back to login owner if reposlug contains only repo name)
c.Owner, c.Repo = utils.GetOwnerAndRepo(c.RepoSlug, c.Login.User)
c.Command = cmd
c.Output = cmd.String("output")
return &c
}
// contextFromLocalRepo discovers login & repo slug from the default branch remote of the given local repo
func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.Login, string, error) {
// ContextFromLocalRepo discovers login & repo slug from the default branch remote of the given local repo
func ContextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.Login, string, error) {
repo, err := git.RepoFromPath(repoPath)
if err != nil {
return nil, nil, "", err
@@ -178,7 +73,7 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
debug.Printf("Get git config %v of %s in repo %s", gitConfig, remoteValue, repoPath)
if len(gitConfig.Remotes) == 0 {
return repo, nil, "", errNotAGiteaRepo
return repo, nil, "", ErrNotAGiteaRepo
}
// When no preferred value is given, choose a remote to find a
@@ -229,7 +124,7 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
}
}
return repo, nil, "", errNotAGiteaRepo
return repo, nil, "", ErrNotAGiteaRepo
}
// MatchLogins matches the given remoteURL against the provided logins and returns
@@ -274,7 +169,7 @@ func MatchLogins(remoteURL string, logins []config.Login) (*config.Login, string
}
}
}
return nil, "", errNotAGiteaRepo
return nil, "", ErrNotAGiteaRepo
}
// GetLoginByEnvVar returns a login based on environment variables, or nil if no login can be created