initial commit [141.14.129.234,mike]
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config holds all externally configurable settings. It is read entirely from
|
||||
// ~/.mgshrc and then overlaid with MGSH_* environment variables — there are no
|
||||
// built-in defaults. Missing required settings are a fatal error (see
|
||||
// missingRequired).
|
||||
type Config struct {
|
||||
Base string // project base directory
|
||||
GitHost string // git server host
|
||||
GitPort string // ssh port
|
||||
GitUser string // ssh user
|
||||
GitPath string // remote path holding the bare repos
|
||||
GitKey string // ssh key name (informational)
|
||||
GitName string // git user.name to set globally ("" = leave alone)
|
||||
GitEmail string // git user.email to set globally ("" = leave alone)
|
||||
PushDefault string // git push.default to set globally ("" = leave alone)
|
||||
Editor string // editor/opener used as fallback by `open` ("" = coda)
|
||||
RemoteURL string // public mirror server base URL (Gitea/GitHub/GitLab)
|
||||
RemoteKey string // API token for the mirror server (used by `pushremote`)
|
||||
RemoteType string // "gitea"|"github"|"gitlab" (auto-detected when empty)
|
||||
RemoteVis string // visibility of created repos: "private" (default)|"public"
|
||||
Mirror string // truthy -> `push` also mirrors via `pushremote`
|
||||
}
|
||||
|
||||
// requiredKeys lists the settings mgsh cannot run without.
|
||||
var requiredKeys = []string{"base", "githost", "gitport", "gituser", "gitpath"}
|
||||
|
||||
// missingRequired reports which required settings are still unset. mgsh refuses
|
||||
// to start while this is non-empty.
|
||||
func (c Config) missingRequired() []string {
|
||||
have := map[string]string{
|
||||
"base": c.Base, "githost": c.GitHost, "gitport": c.GitPort,
|
||||
"gituser": c.GitUser, "gitpath": c.GitPath,
|
||||
}
|
||||
var missing []string
|
||||
for _, k := range requiredKeys {
|
||||
if have[k] == "" {
|
||||
missing = append(missing, k)
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
// configFile returns the path of the user config file (~/.mgshrc).
|
||||
func configFile() string {
|
||||
home, _ := os.UserHomeDir()
|
||||
return filepath.Join(home, ".mgshrc")
|
||||
}
|
||||
|
||||
// loadConfig writes an empty ~/.mgshrc template on first run, then returns the
|
||||
// configuration parsed from it, overlaid with MGSH_* environment variables.
|
||||
// It applies no defaults; validation of required settings is the caller's job.
|
||||
func loadConfig() Config {
|
||||
path := configFile()
|
||||
if !fileExists(path) {
|
||||
writeConfigTemplate(path)
|
||||
}
|
||||
var c Config
|
||||
if data, err := os.ReadFile(path); err == nil {
|
||||
applyConfig(&c, parseConfig(string(data)))
|
||||
}
|
||||
applyEnv(&c)
|
||||
return c
|
||||
}
|
||||
|
||||
// writeConfigTemplate creates a blank, annotated ~/.mgshrc for the user to fill
|
||||
// in, migrating any aliases from the legacy ~/.mgsh_aliases file. It writes no
|
||||
// real values — mgsh has no built-in configuration.
|
||||
func writeConfigTemplate(path string) {
|
||||
var b strings.Builder
|
||||
b.WriteString("# mgsh configuration — fill in the required settings, then run mgsh again.\n")
|
||||
b.WriteString("# Format: 'key = value' (or 'key: value'); '#' starts a comment.\n")
|
||||
b.WriteString("# MGSH_* environment variables override these settings.\n\n")
|
||||
b.WriteString("# --- required ---\n")
|
||||
b.WriteString("base =\n")
|
||||
b.WriteString("githost =\n")
|
||||
b.WriteString("gitport =\n")
|
||||
b.WriteString("gituser =\n")
|
||||
b.WriteString("gitpath =\n\n")
|
||||
b.WriteString("# --- optional ---\n")
|
||||
b.WriteString("# gitkey =\n")
|
||||
b.WriteString("# gitname = Your Name\n")
|
||||
b.WriteString("# gitemail = you@example.com\n")
|
||||
b.WriteString("# pushdefault = matching\n")
|
||||
b.WriteString("# editor = code\n")
|
||||
|
||||
legacy := readLegacyAliases()
|
||||
if blk := aliasBlock(legacy); blk != "" {
|
||||
b.WriteString("\n" + blk)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(path, []byte(b.String()), 0644); err != nil {
|
||||
errorln("could not create " + path + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
if len(legacy) > 0 {
|
||||
os.Remove(legacyAliasFile()) // aliases now live in ~/.mgshrc
|
||||
}
|
||||
}
|
||||
|
||||
// parseConfig reads simple `key = value` (or `key: value`) lines, ignoring
|
||||
// blank lines, `#` comments and `alias` definitions. Keys are lower-cased;
|
||||
// values are unquoted.
|
||||
func parseConfig(s string) map[string]string {
|
||||
m := map[string]string{}
|
||||
sc := bufio.NewScanner(strings.NewReader(s))
|
||||
for sc.Scan() {
|
||||
line := strings.TrimSpace(sc.Text())
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
if f := strings.Fields(line); len(f) > 0 && f[0] == "alias" {
|
||||
continue // alias definitions are handled by the alias loader
|
||||
}
|
||||
i := strings.IndexAny(line, "=:")
|
||||
if i < 0 {
|
||||
continue
|
||||
}
|
||||
key := strings.ToLower(strings.TrimSpace(line[:i]))
|
||||
val := strings.Trim(strings.TrimSpace(line[i+1:]), "\"'")
|
||||
m[key] = val
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func applyConfig(c *Config, m map[string]string) {
|
||||
set := func(key string, dst *string) {
|
||||
if v, ok := m[key]; ok && v != "" {
|
||||
*dst = v
|
||||
}
|
||||
}
|
||||
set("base", &c.Base)
|
||||
set("githost", &c.GitHost)
|
||||
set("gitport", &c.GitPort)
|
||||
set("gituser", &c.GitUser)
|
||||
set("gitpath", &c.GitPath)
|
||||
set("gitkey", &c.GitKey)
|
||||
set("gitname", &c.GitName)
|
||||
set("gitemail", &c.GitEmail)
|
||||
set("pushdefault", &c.PushDefault)
|
||||
set("editor", &c.Editor)
|
||||
set("remoteurl", &c.RemoteURL)
|
||||
set("remotekey", &c.RemoteKey)
|
||||
set("remotetype", &c.RemoteType)
|
||||
set("remotevisibility", &c.RemoteVis)
|
||||
set("mirror", &c.Mirror)
|
||||
}
|
||||
|
||||
func applyEnv(c *Config) {
|
||||
env := func(key string, dst *string) {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
*dst = v
|
||||
}
|
||||
}
|
||||
env("MGSH_BASE", &c.Base)
|
||||
env("MGSH_GITHOST", &c.GitHost)
|
||||
env("MGSH_GITPORT", &c.GitPort)
|
||||
env("MGSH_GITUSER", &c.GitUser)
|
||||
env("MGSH_GITPATH", &c.GitPath)
|
||||
env("MGSH_GITKEY", &c.GitKey)
|
||||
env("MGSH_GITNAME", &c.GitName)
|
||||
env("MGSH_GITEMAIL", &c.GitEmail)
|
||||
env("MGSH_PUSHDEFAULT", &c.PushDefault)
|
||||
env("MGSH_EDITOR", &c.Editor)
|
||||
env("MGSH_REMOTEURL", &c.RemoteURL)
|
||||
env("MGSH_REMOTEKEY", &c.RemoteKey)
|
||||
env("MGSH_REMOTETYPE", &c.RemoteType)
|
||||
env("MGSH_REMOTEVISIBILITY", &c.RemoteVis)
|
||||
env("MGSH_MIRROR", &c.Mirror)
|
||||
}
|
||||
Reference in New Issue
Block a user