package forgepb // functions to import and export the protobuf // data to and from config files import ( "errors" "fmt" "os" "path/filepath" "go.wit.com/log" ) func (f *Forge) ConfigSave() error { var err error // backup the current config files if e := backupConfig(); e != nil { log.Info("forge.BackupConfig() error", e) err = e // continue here? notsure. could be bad either way // out of disk space? } if f.Config != nil { if e := f.Config.ConfigSave(); e != nil { log.Info("forge.Config.ConfigSave() error", e) err = e } } if f.Repos != nil { if e := f.Repos.ConfigSave(); e != nil { log.Info("forge.Repos.ConfigSave() error", e) err = e } } return err } // write to ~/.config/forge/ unless ENV{FORGE_CONFIG} is set func (f *ForgeConfigs) ConfigSave() error { data, err := f.Marshal() if err != nil { log.Info("proto.Marshal() failed len", len(data), err) return err } log.Info("forgepb.ConfigSave() proto.Marshal() worked len", len(data)) s := f.FormatTEXT() configWrite("forge.text", []byte(s)) s = f.FormatJSON() configWrite("forge.json", []byte(s)) return nil } // load the ~/.config/forge/ files func (c *ForgeConfigs) ConfigLoad() error { if os.Getenv("FORGE_CONFIG") == "" { homeDir, _ := os.UserHomeDir() fullpath := filepath.Join(homeDir, ".config/forge") os.Setenv("FORGE_CONFIG", fullpath) } // var data []byte // var err error if c == nil { // can't safely do c = new(ForgeConfig) if c is in a struct from the caller. notsure why // TODO: recheck this. it might work now? It's probably still a bad idea(?) return errors.New("It's not safe to run ConfigLoad() on a nil") } if err := c.loadText(); err == nil { return nil } // forge.text doesn't exist. try forge.json // this lets the user hand edit the JSON config // probably just deprecate this if data, err := loadFile("forge.json"); err != nil { if data != nil { // this means the forge.json file exists and was read if len(data) != 0 { if err = c.UnmarshalJSON(data); err == nil { log.Info("forge.ConfigLoad()", len(c.ForgeConfigs), "entries in ~/.config/forge") // forge.text file was broken. save on load right away log.Info("attempting forge.ConfigSave()") c.ConfigSave() return nil } } } } cpath := filepath.Join(os.Getenv("FORGE_CONFIG"), ".") if _, err := os.Stat(cpath); err == nil { log.Info("Something has gone wrong. Your", os.Getenv("FORGE_CONFIG"), "directory exists") log.Info("However, the config files could not be loaded") } // first time user. make a template config file log.Info("crap") // c.sampleConfig() return nil } func (c *ForgeConfigs) loadText() error { // this lets the user hand edit the config data, err := loadFile("forge.text") if err != nil { return err } if data == nil { return fmt.Errorf("forge.text data was nil") } if len(data) == 0 { return fmt.Errorf("forge.text was empty") } // attempt to marshal forge.text if err := c.UnmarshalTEXT(data); err != nil { return err } log.Info("forge.ConfigLoad()", len(c.ForgeConfigs), "entries in ~/.config/forge") return nil } func loadFile(filename string) ([]byte, error) { fullname := filepath.Join(os.Getenv("FORGE_CONFIG"), filename) data, err := os.ReadFile(fullname) if errors.Is(err, os.ErrNotExist) { // if file does not exist, just return nil. this // will cause ConfigLoad() to try the next config file like "forge.text" // because the user might want to edit the .config by hand return nil, nil } if err != nil { // log.Info("open config file :", err) return nil, err } return data, nil } func configWrite(filename string, data []byte) error { fullname := filepath.Join(os.Getenv("FORGE_CONFIG"), filename) cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) defer cfgfile.Close() if err != nil { log.Warn("open config file :", err) return err } if filename == "forge.text" { // add header cfgfile.Write([]byte("\n")) cfgfile.Write([]byte("# this file is intended to be used to customize settings on what\n")) cfgfile.Write([]byte("# git repos you have write access to. That is, where you can run 'git push'\n")) cfgfile.Write([]byte("\n")) } if filename == "forge.json" { // add header cfgfile.Write([]byte("\n")) cfgfile.Write([]byte("# this file is intended to be used to customize settings on what\n")) cfgfile.Write([]byte("# git repos you have write access to. That is, where you can run 'git push'\n")) cfgfile.Write([]byte("\n")) cfgfile.Write([]byte("# this file is parsed only if forge.text is missing\n")) cfgfile.Write([]byte("# also, these comment lines don't work in json files and have to be removed for Marshal() to work\n")) cfgfile.Write([]byte("# probably, JSON syntax for this is just going to be deprecated for the TEXT syntax\n")) cfgfile.Write([]byte("\n")) } cfgfile.Write(data) return nil }