198 lines
4.8 KiB
Go
198 lines
4.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"kmanage/pkg/komodo"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
type completionCache struct {
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Repos []string `json:"repos"`
|
|
Owners []string `json:"owners"`
|
|
Stacks []string `json:"stacks"`
|
|
Accounts []string `json:"accounts"`
|
|
}
|
|
|
|
func getCachePath() (string, error) {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(home, ".kmanage_cache.json"), nil
|
|
}
|
|
|
|
func loadCache() *completionCache {
|
|
path, err := getCachePath()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
var cache completionCache
|
|
if err := json.Unmarshal(data, &cache); err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Cache TTL is 3 minutes
|
|
if time.Since(cache.Timestamp) > 3*time.Minute {
|
|
return nil
|
|
}
|
|
|
|
return &cache
|
|
}
|
|
|
|
func saveCache(cache *completionCache) {
|
|
path, err := getCachePath()
|
|
if err != nil {
|
|
return
|
|
}
|
|
cache.Timestamp = time.Now()
|
|
data, _ := json.Marshal(cache)
|
|
_ = os.WriteFile(path, data, 0600)
|
|
}
|
|
|
|
// completeGitAccountNames provides dynamic auto-completion for configured Git account names.
|
|
func completeGitAccountNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
|
cfg, err := LoadAppConfig()
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
var accounts []string
|
|
for k := range cfg.GitAccounts {
|
|
accounts = append(accounts, k)
|
|
}
|
|
|
|
return filterCompletions(accounts, toComplete), cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// completeRepoNames dynamically suggests repository names for Cobra auto-completion.
|
|
func completeRepoNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
|
if len(args) > 0 {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
cached := loadCache()
|
|
if cached != nil && len(cached.Repos) > 0 {
|
|
return filterCompletions(cached.Repos, toComplete), cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
accountFlag, _ := cmd.Flags().GetString("account")
|
|
allAccountsFlag, _ := cmd.Flags().GetBool("all-accounts")
|
|
|
|
clients, err := getSelectedGitClients(accountFlag, allAccountsFlag)
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
var repoNames []string
|
|
ownerMap := make(map[string]bool)
|
|
|
|
for _, client := range clients {
|
|
repos, err := client.ListRepositories()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
for _, r := range repos {
|
|
repoNames = append(repoNames, r.FullName)
|
|
owner := r.Owner.UserName
|
|
if owner == "" && strings.Contains(r.FullName, "/") {
|
|
owner = strings.SplitN(r.FullName, "/", 2)[0]
|
|
}
|
|
if owner != "" {
|
|
ownerMap[owner] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
var ownerNames []string
|
|
for o := range ownerMap {
|
|
ownerNames = append(ownerNames, o)
|
|
}
|
|
|
|
cfg, _ := LoadAppConfig()
|
|
var accountNames []string
|
|
if cfg != nil {
|
|
for a := range cfg.GitAccounts {
|
|
accountNames = append(accountNames, a)
|
|
}
|
|
}
|
|
|
|
saveCache(&completionCache{
|
|
Repos: repoNames,
|
|
Owners: ownerNames,
|
|
Accounts: accountNames,
|
|
})
|
|
|
|
return filterCompletions(repoNames, toComplete), cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// completeOwnerNames dynamically suggests repository owner/organization names.
|
|
func completeOwnerNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
|
cached := loadCache()
|
|
if cached != nil && len(cached.Owners) > 0 {
|
|
return filterCompletions(cached.Owners, toComplete), cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
_, _ = completeRepoNames(cmd, args, toComplete)
|
|
cached = loadCache()
|
|
if cached != nil {
|
|
return filterCompletions(cached.Owners, toComplete), cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// completeStackNames dynamically suggests stack names from Komodo.
|
|
func completeStackNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
|
client, err := getKomodoClient()
|
|
if err != nil {
|
|
url := viper.GetString("komodo_url")
|
|
key := viper.GetString("komodo_key")
|
|
secret := viper.GetString("komodo_secret")
|
|
if url == "" || key == "" || secret == "" {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
client = komodo.NewClient(url, key, secret)
|
|
}
|
|
|
|
stacks, err := client.ListStacks()
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
var stackNames []string
|
|
for _, s := range stacks {
|
|
stackNames = append(stackNames, s.Name)
|
|
}
|
|
|
|
return filterCompletions(stackNames, toComplete), cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
func filterCompletions(items []string, prefix string) []string {
|
|
if prefix == "" {
|
|
return items
|
|
}
|
|
var matches []string
|
|
lowerPrefix := strings.ToLower(prefix)
|
|
for _, item := range items {
|
|
if strings.HasPrefix(strings.ToLower(item), lowerPrefix) {
|
|
matches = append(matches, item)
|
|
}
|
|
}
|
|
return matches
|
|
}
|