package main // show_config.go — the `config` command: print the configuration mgsh actually // resolved, and where it came from. With three layers (~/.mgshrc, the project's // .mgshrc, MGSH_*) and mirror targets that can be added or narrowed per // project, "what is in effect right now" is otherwise guesswork. import ( "fmt" "os" "sort" "strings" ) // configItem is one reported setting. type configItem struct{ key, value string } // showConfig prints the effective configuration, marking every value that does // not come from the global file with its source. func showConfig() { global := configFile() project := "" if PRJ != "" && fileExists(DIR+"/"+projectRC) { project = DIR + "/" + projectRC } fmt.Printf("%s %s\n", col(cDark, "global "), col(cBlue, global)) if project != "" { fmt.Printf("%s %s\n", col(cDark, "project"), col(cBlue, project)) } else if PRJ != "" { fmt.Printf("%s %s\n", col(cDark, "project"), col(cDark, "no "+projectRC+" in "+PRJ)) } fmt.Println() items := []configItem{ {"base", cfg.Base}, {"githost", cfg.GitHost}, {"gitport", cfg.GitPort}, {"gituser", cfg.GitUser}, {"gitpath", cfg.GitPath}, {"gitkey", cfg.GitKey}, {"gitname", cfg.GitName}, {"gitemail", cfg.GitEmail}, {"pushdefault", cfg.PushDefault}, {"mirror", cfg.Mirror}, {"secretscan", cfg.SecretScan}, {"remotes", cfg.RemoteNames}, } // which keys the active project's file actually sets, for the source column fromProject := map[string]bool{} if project != "" { if data, err := os.ReadFile(project); err == nil { for k := range parseConfig(string(data)) { fromProject[k] = true } } } for _, it := range items { if it.value == "" { continue } src := "" switch { case os.Getenv(envName(it.key)) != "": src = col(cYellow, " (env)") case fromProject[it.key]: src = col(cYellow, " ("+projectRC+")") } fmt.Printf(" %s%s%s\n", col(cGreen, padRight(it.key, 14)), it.value, src) } if k := sshKeyPath(); k != "" { fmt.Printf(" %s%s\n", col(cDark, padRight("ssh identity", 14)), col(cDark, k)) } fmt.Printf(" %s%s\n", col(cDark, padRight("clone url", 14)), col(cDark, URL)) // the project's real origin: it can differ from what the current settings // would produce, e.g. after moving the server or editing a project .mgshrc if o := originURL(); o != "" { fmt.Printf(" %s%s\n", col(cDark, padRight("origin", 14)), col(cDark, o)) } showRemotes() } // showRemotes lists the `pushremote` targets in push order, with the tokens // masked — `config` is the kind of output that ends up pasted into a bug report. func showRemotes() { targets, incomplete := cfg.mirrorTargets() fmt.Println() if len(targets) == 0 && len(incomplete) == 0 { fmt.Println(col(cDark, "no pushremote targets configured")) return } fmt.Println(col(cDark, "pushremote targets (in push order):")) for _, t := range targets { vis := "private" if strings.EqualFold(strings.TrimSpace(t.Vis), "public") { vis = "public" } kind := t.Type if kind == "" { kind = remoteKindName(detectRemoteKind(t.URL, "")) + " (detected)" } fmt.Printf(" %s%s %s\n", col(cGreen, padRight("@"+t.Name, 14)), t.URL, col(cDark, kind+", "+vis+", key "+maskSecret(t.Key))) } for _, n := range incomplete { fmt.Printf(" %s%s\n", col(cRed, padRight("@"+n, 14)), col(cRed, "incomplete: url or key missing")) } } // remoteKindName renders a remoteKind for display. func remoteKindName(k remoteKind) string { switch k { case kindGitHub: return "github" case kindGitLab: return "gitlab" default: return "gitea" } } // maskSecret reduces a token to a recognisable but useless stub. func maskSecret(s string) string { if s == "" { return "(unset)" } if len(s) <= 4 { return strings.Repeat("*", len(s)) } return s[:2] + strings.Repeat("*", len(s)-4) + s[len(s)-2:] } // envName maps a config key to its MGSH_* environment variable. func envName(key string) string { return "MGSH_" + strings.ToUpper(key) } // configKeys lists every flat setting name — that is, every key that also has // an MGSH_* override. The `remote..*` targets are a pattern, not a fixed // set, and are reported separately. func configKeys() []string { keys := []string{ "base", "githost", "gitport", "gituser", "gitpath", "gitkey", "gitname", "gitemail", "pushdefault", "remotes", "mirror", "secretscan", } sort.Strings(keys) return keys }