Add PR checks command, iostreams/text packages for colored table output, top-level run/workflow aliases matching gh CLI structure. Enhance actions, issues, PRs, releases, repos, labels, milestones, and wiki commands with improved flags, JSON output, and error handling.
210 lines
5.2 KiB
Go
210 lines
5.2 KiB
Go
package cmd
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"forgejo.zerova.net/sid/fgj-sid/internal/api"
|
|
"forgejo.zerova.net/sid/fgj-sid/internal/config"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
var authCmd = &cobra.Command{
|
|
Use: "auth",
|
|
Short: "Authenticate fgj with a Forgejo instance",
|
|
Long: "Manage authentication state for Forgejo instances.",
|
|
}
|
|
|
|
var authLoginCmd = &cobra.Command{
|
|
Use: "login",
|
|
Short: "Authenticate with a Forgejo instance",
|
|
Long: "Authenticate with a Forgejo instance using a personal access token.",
|
|
RunE: runAuthLogin,
|
|
}
|
|
|
|
var authStatusCmd = &cobra.Command{
|
|
Use: "status",
|
|
Short: "View authentication status",
|
|
Long: "Display the authentication status for configured Forgejo instances.",
|
|
RunE: runAuthStatus,
|
|
}
|
|
|
|
var authLogoutCmd = &cobra.Command{
|
|
Use: "logout",
|
|
Short: "Remove authentication for a Forgejo instance",
|
|
Long: "Remove authentication for a configured Forgejo instance.",
|
|
RunE: runAuthLogout,
|
|
}
|
|
|
|
var authTokenCmd = &cobra.Command{
|
|
Use: "token",
|
|
Short: "Print the stored authentication token",
|
|
Long: "Print the stored authentication token for a configured Forgejo instance.",
|
|
RunE: runAuthToken,
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(authCmd)
|
|
authCmd.AddCommand(authLoginCmd)
|
|
authCmd.AddCommand(authStatusCmd)
|
|
authCmd.AddCommand(authLogoutCmd)
|
|
authCmd.AddCommand(authTokenCmd)
|
|
|
|
authLoginCmd.Flags().String("hostname", "", "Forgejo instance hostname (e.g., codeberg.org)")
|
|
authLoginCmd.Flags().StringP("token", "t", "", "Personal access token")
|
|
authLogoutCmd.Flags().String("hostname", "", "Forgejo instance hostname (e.g., codeberg.org)")
|
|
authTokenCmd.Flags().String("hostname", "", "Forgejo instance hostname (e.g., codeberg.org)")
|
|
}
|
|
|
|
func runAuthLogin(cmd *cobra.Command, args []string) error {
|
|
hostname, _ := cmd.Flags().GetString("hostname")
|
|
token, _ := cmd.Flags().GetString("token")
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
if hostname == "" {
|
|
fmt.Fprint(ios.ErrOut, "Forgejo instance hostname (default: codeberg.org): ")
|
|
input, _ := reader.ReadString('\n')
|
|
hostname = strings.TrimSpace(input)
|
|
if hostname == "" {
|
|
hostname = "codeberg.org"
|
|
}
|
|
}
|
|
|
|
if token == "" {
|
|
fmt.Fprint(ios.ErrOut, "Personal access token: ")
|
|
tokenBytes, err := term.ReadPassword(int(syscall.Stdin))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read token: %w", err)
|
|
}
|
|
fmt.Fprintln(ios.ErrOut)
|
|
token = strings.TrimSpace(string(tokenBytes))
|
|
}
|
|
|
|
if token == "" {
|
|
return fmt.Errorf("token is required")
|
|
}
|
|
|
|
client, err := api.NewClient(hostname, token)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create client: %w", err)
|
|
}
|
|
|
|
ios.StartSpinner("Authenticating...")
|
|
user, _, err := client.GetMyUserInfo()
|
|
ios.StopSpinner()
|
|
if err != nil {
|
|
return fmt.Errorf("authentication failed: %w", err)
|
|
}
|
|
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
cfg.SetHost(hostname, config.HostConfig{
|
|
Hostname: hostname,
|
|
Token: token,
|
|
User: user.UserName,
|
|
GitProtocol: "https",
|
|
})
|
|
|
|
if err := cfg.Save(); err != nil {
|
|
return fmt.Errorf("failed to save config: %w", err)
|
|
}
|
|
|
|
cs := ios.ColorScheme()
|
|
fmt.Fprintf(ios.Out, "%s Authenticated as %s on %s\n", cs.SuccessIcon(), user.UserName, hostname)
|
|
|
|
return nil
|
|
}
|
|
|
|
func runAuthStatus(cmd *cobra.Command, args []string) error {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
if len(cfg.Hosts) == 0 {
|
|
fmt.Fprintln(ios.Out, "Not authenticated with any Forgejo instances")
|
|
fmt.Fprintln(ios.Out, "Run 'fgj auth login' to authenticate")
|
|
return nil
|
|
}
|
|
|
|
fmt.Fprintln(ios.Out, "Authenticated instances:")
|
|
for hostname, host := range cfg.Hosts {
|
|
cs := ios.ColorScheme()
|
|
fmt.Fprintf(ios.Out, " %s %s (user: %s)\n", cs.SuccessIcon(), hostname, host.User)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func runAuthLogout(cmd *cobra.Command, args []string) error {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
hostname, _ := cmd.Flags().GetString("hostname")
|
|
resolved, err := resolveAuthHostname(cfg, hostname)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
delete(cfg.Hosts, resolved)
|
|
if err := cfg.Save(); err != nil {
|
|
return fmt.Errorf("failed to save config: %w", err)
|
|
}
|
|
|
|
cs := ios.ColorScheme()
|
|
fmt.Fprintf(ios.Out, "%s Logged out from %s\n", cs.SuccessIcon(), resolved)
|
|
return nil
|
|
}
|
|
|
|
func runAuthToken(cmd *cobra.Command, args []string) error {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
hostname, _ := cmd.Flags().GetString("hostname")
|
|
resolved, err := resolveAuthHostname(cfg, hostname)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprintln(ios.Out, cfg.Hosts[resolved].Token)
|
|
return nil
|
|
}
|
|
|
|
func resolveAuthHostname(cfg *config.Config, hostname string) (string, error) {
|
|
if hostname == "" {
|
|
hostname = viper.GetString("hostname")
|
|
}
|
|
if hostname == "" {
|
|
hostname = os.Getenv("FGJ_HOST")
|
|
}
|
|
if hostname == "" {
|
|
hostname = getDetectedHost()
|
|
}
|
|
if hostname == "" && len(cfg.Hosts) == 1 {
|
|
for host := range cfg.Hosts {
|
|
hostname = host
|
|
}
|
|
}
|
|
if hostname == "" {
|
|
hostname = "codeberg.org"
|
|
}
|
|
|
|
if _, ok := cfg.Hosts[hostname]; !ok {
|
|
return "", fmt.Errorf("no configuration found for host %s", hostname)
|
|
}
|
|
|
|
return hostname, nil
|
|
}
|