Files

256 lines
7.3 KiB
Go

package cmd
import (
"fmt"
"os"
"strings"
"text/tabwriter"
"kmanage/pkg/git"
"github.com/spf13/cobra"
)
var gitCmd = &cobra.Command{
Use: "git",
Short: "Git repository management (Multi-Account: Gitea / Forgejo / GitHub)",
Long: `Manage Git repositories, accounts, webhooks, and PR settings across multiple Git providers.`,
}
var gitConfigureCmd = &cobra.Command{
Use: "configure [flags]",
Short: "Configure a Git provider account",
Long: `Saves Git account metadata to ~/.kmanage.yaml and securely stores the Personal Access Token in the OS keyring.
Examples:
# Configure a Gitea account:
kmanage git configure --name gitea --url https://git.example.com --token my-access-token
# Configure a GitHub account:
kmanage git configure --name github --type github --token ghp_myGitHubToken`,
RunE: func(cmd *cobra.Command, args []string) error {
name, _ := cmd.Flags().GetString("name")
accType, _ := cmd.Flags().GetString("type")
url, _ := cmd.Flags().GetString("url")
token, _ := cmd.Flags().GetString("token")
username, _ := cmd.Flags().GetString("user")
if token == "" {
_ = cmd.Help()
fmt.Fprintln(os.Stderr)
return fmt.Errorf("❌ Error: The flag '--token' (or '-t') is required")
}
if name == "" {
if strings.Contains(url, "github.com") || accType == "github" {
name = "github"
} else {
name = "gitea"
}
}
if url == "" && (accType == "github" || name == "github") {
url = "https://api.github.com"
if accType == "" {
accType = "github"
}
}
if url == "" {
_ = cmd.Help()
fmt.Fprintln(os.Stderr)
return fmt.Errorf("❌ Error: The flag '--url' (or '-u') is required")
}
if err := SaveGitAccount(name, accType, url, token, username); err != nil {
return fmt.Errorf("failed to save Git account: %w", err)
}
fmt.Printf("✅ Git account '%s' (%s) successfully saved (token stored in OS keyring).\n", name, url)
return nil
},
}
var gitAccountsCmd = &cobra.Command{
Use: "accounts",
Aliases: []string{"account-list", "list-accounts"},
Short: "List all configured Git accounts",
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := LoadAppConfig()
if err != nil {
return err
}
if len(cfg.GitAccounts) == 0 {
fmt.Println("No Git accounts configured. Run 'kmanage git configure --name <name> --url <url> --token <token>'")
return nil
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
noHeader, _ := cmd.Flags().GetBool("no-header")
if !noHeader {
fmt.Fprintln(w, "ACCOUNT\tTYPE\tURL\tDEFAULT")
fmt.Fprintln(w, "-------\t----\t---\t-------")
}
for name, acc := range cfg.GitAccounts {
isDefault := "no"
if name == cfg.DefaultGitAccount {
isDefault = "yes (*)"
}
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, acc.Type, acc.URL, isDefault)
}
return w.Flush()
},
}
var gitSetDefaultCmd = &cobra.Command{
Use: "set-default <account_name>",
Short: "Set the default active Git account",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
accountName := args[0]
if err := SetDefaultGitAccount(accountName); err != nil {
return err
}
fmt.Printf("✅ Default Git account set to '%s'.\n", accountName)
return nil
},
}
var gitDeleteAccountCmd = &cobra.Command{
Use: "delete-account <account_name>",
Aliases: []string{"rm-account", "del-account"},
Short: "Delete a configured Git account",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
accountName := args[0]
if err := DeleteGitAccount(accountName); err != nil {
return err
}
fmt.Printf("✅ Git account '%s' successfully deleted.\n", accountName)
return nil
},
}
var gitListCmd = &cobra.Command{
Use: "list",
Short: "List Git resources (e.g. repos)",
RunE: func(cmd *cobra.Command, args []string) error {
return runListRepos(cmd, args)
},
}
var gitListReposCmd = &cobra.Command{
Use: "repos",
Short: "List all repositories along with their webhooks",
RunE: runListRepos,
}
func runListRepos(cmd *cobra.Command, args []string) error {
accountFlag, _ := cmd.Flags().GetString("account")
allAccountsFlag, _ := cmd.Flags().GetBool("all-accounts")
clients, err := getSelectedGitClients(accountFlag, allAccountsFlag)
if err != nil {
return err
}
var allReposWithHooks []git.RepositoryWithHooks
for _, client := range clients {
reposWithHooks, err := client.ListRepositoriesWithHooks()
if err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Warning retrieving account '%s': %v\n", client.AccountName, err)
continue
}
allReposWithHooks = append(allReposWithHooks, reposWithHooks...)
}
if len(allReposWithHooks) == 0 {
fmt.Println("No repositories found.")
return nil
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
noHeader, _ := cmd.Flags().GetBool("no-header")
if !noHeader {
if allAccountsFlag || len(clients) > 1 {
fmt.Fprintln(w, "ACCOUNT\tREPOSITORY\tSTATUS\tBRANCH\tPR AUTO-DEL\tWEBHOOKS")
fmt.Fprintln(w, "-------\t----------\t------\t------\t-----------\t--------")
} else {
fmt.Fprintln(w, "REPOSITORY\tSTATUS\tBRANCH\tPR AUTO-DEL\tWEBHOOKS")
fmt.Fprintln(w, "----------\t------\t------\t-----------\t--------")
}
}
for _, item := range allReposWithHooks {
r := item.Repository
visibility := "public"
if r.Private {
visibility = "private"
}
if r.Fork {
visibility += " (fork)"
}
branch := r.DefaultBranch
if branch == "" {
branch = "-"
}
prAutoDel := "no"
if r.IsAutoDeleteBranch() {
prAutoDel = "yes"
}
var hookStrings []string
for _, h := range item.Hooks {
target := h.TargetURL()
statusStr := "active"
if !h.Active {
statusStr = "inactive"
}
hookStrings = append(hookStrings, fmt.Sprintf("%s (%s)", target, statusStr))
}
webhooksDisplay := "-"
if len(hookStrings) > 0 {
webhooksDisplay = strings.Join(hookStrings, ", ")
}
if allAccountsFlag || len(clients) > 1 {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", r.AccountName, r.FullName, visibility, branch, prAutoDel, webhooksDisplay)
} else {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", r.FullName, visibility, branch, prAutoDel, webhooksDisplay)
}
}
return w.Flush()
}
func init() {
rootCmd.AddCommand(gitCmd)
gitCmd.AddCommand(gitConfigureCmd)
gitCmd.AddCommand(gitAccountsCmd)
gitCmd.AddCommand(gitSetDefaultCmd)
gitCmd.AddCommand(gitDeleteAccountCmd)
gitCmd.AddCommand(gitListCmd)
gitListCmd.AddCommand(gitListReposCmd)
gitConfigureCmd.Flags().StringP("name", "n", "", "Unique name for the Git account (e.g. 'gitea', 'github', 'work')")
gitConfigureCmd.Flags().StringP("type", "", "", "Git provider type: 'gitea', 'forgejo', or 'github' (optional, auto-detected)")
gitConfigureCmd.Flags().StringP("url", "u", "", "Git API Base URL (e.g. https://git.example.com or https://api.github.com)")
gitConfigureCmd.Flags().StringP("token", "t", "", "Git Personal Access Token (required)")
gitConfigureCmd.Flags().StringP("user", "", "", "Optional username")
for _, c := range []*cobra.Command{gitListCmd, gitListReposCmd} {
c.Flags().StringP("account", "A", "", "Select a specific Git account (default: configured default account)")
c.Flags().BoolP("all-accounts", "", false, "List repositories across ALL configured Git accounts")
_ = c.RegisterFlagCompletionFunc("account", completeGitAccountNames)
}
_ = gitSetDefaultCmd.RegisterFlagCompletionFunc("default", completeGitAccountNames)
}