gitpb/config.go

185 lines
5.2 KiB
Go

package gitpb
// functions to import and export the protobuf
// data to and from config files
import (
"errors"
"os"
"path/filepath"
"go.wit.com/lib/protobuf/bugpb"
"go.wit.com/log"
)
// write to ~/.config/forge/ unless ENV{FORGE_REPOSDIR} is set
func (all *Repos) ConfigSave(fname string) error {
if all == nil {
log.Warn("gitpb repos == nil")
return errors.New("gitpb.ConfigSave() repos == nil")
}
data, err := all.Marshal()
if err != nil {
log.Info("gitpb proto.Marshal() failed len", len(data), err)
// often this is because strings have invalid UTF-8. This should probably be fixed in the protobuf code
// this might be fixed in the create code, but it can't hurt to try this as a last ditch effort here
log.Info("gitpb.ConfigSave() ATTEMPTING TO VALIDATE UTF-8 strings in the protobuf file")
if err := all.tryValidate(); err != nil {
log.Info("gitpb.ConfigSave() STILL FAILEd", err)
return err
} else {
// re-attempt Marshal() here
data, err = all.Marshal()
if err == nil {
// validate & sanitize strings worked
configWrite(fname, data)
return nil
}
log.Info("gitpb.ConfigSave() STILL FAILEd", err)
}
return err
}
configWrite(fname, data)
return nil
}
// todo: move this to Marshal() functions automatically in autogenpb?
func (repo *Repo) ValidateUTF8() error {
if _, err := repo.Marshal(); err == nil {
// exit if Marshal() works
return nil
} else {
// log.Printf("%s repo.Marshal() failed: %v\n", repo.GetFullPath(), err)
}
// you only need to do this if Marshal() fails
err := bugpb.ValidateProtoUTF8(repo)
if err != nil {
// log.Printf("Protobuf UTF-8 validation failed: %v\n", err)
}
if err := bugpb.SanitizeProtoUTF8(repo); err != nil {
log.Warn("gitpb.ValidateUTF8()( failed:", err)
return err
}
return nil
}
func (all *Repos) tryValidate() error {
err := bugpb.ValidateProtoUTF8(all)
if err != nil {
log.Printf("Protobuf UTF-8 validation failed: %v\n", err)
}
if err := bugpb.SanitizeProtoUTF8(all); err != nil {
log.Warn("Sanitation failed:", err)
// log.Fatalf("Sanitization failed: %v", err)
return err
}
return nil
}
// load the repos.pb file. I shouldn't really matter if this
// fails. the file should be autogenerated. This is used
// locally just for speed
func (all *Repos) ConfigLoadOld() error {
if os.Getenv("FORGE_REPOSDIR") == "" {
homeDir, _ := os.UserHomeDir()
fullpath := filepath.Join(homeDir, ".config/forge")
os.Setenv("FORGE_REPOSDIR", fullpath)
}
var data []byte
var err error
cfgname := filepath.Join(os.Getenv("FORGE_REPOSDIR"), "repos.pb")
if data, err = loadFile(cfgname); err != nil {
// something went wrong loading the file
// all.sampleConfig() // causes nil panic
return err
}
// this means the forge.pb file exists and was read
if len(data) == 0 {
return errors.New("gitpb.ConfigLoad() repos.pb is empty")
}
err = all.Unmarshal(data)
test := NewRepos()
if test.Uuid != all.Uuid {
log.Log(WARN, "uuids do not match", test.Uuid, all.Uuid)
deleteProtobufFile(cfgname)
}
if test.Version != all.Version {
log.Log(WARN, "versions do not match", test.Version, all.Version)
deleteProtobufFile(cfgname)
}
log.Log(INFO, cfgname, "protobuf versions and uuid match", all.Uuid, all.Version)
return err
}
func (all *Repos) ConfigLoad(cfgname string) error {
var data []byte
var err error
if data, err = loadFile(cfgname); err != nil {
// something went wrong loading the file
// all.sampleConfig() // causes nil panic
return err
}
// this means the forge.pb file exists and was read
if len(data) == 0 {
return errors.New("gitpb.ConfigLoad() repos.pb is empty")
}
err = all.Unmarshal(data)
test := NewRepos()
if test.Uuid != all.Uuid {
log.Log(WARN, "uuids do not match", test.Uuid, all.Uuid)
deleteProtobufFile(cfgname)
}
if test.Version != all.Version {
log.Log(WARN, "versions do not match", test.Version, all.Version)
deleteProtobufFile(cfgname)
}
log.Log(INFO, cfgname, "protobuf versions and uuid match", all.Uuid, all.Version)
return err
}
func deleteProtobufFile(filename string) {
log.Log(WARN, "The protobuf file format has changed for", filename)
log.Log(WARN, "Deleting old file:", filename)
log.Log(WARN, "This file will be recreated on the next run.")
err := os.Remove(filename)
if err != nil {
log.Log(WARN, "failed to remove old protobuf file", "err", err)
}
}
func (all *Repos) sampleConfig() {
newr := new(Repo)
newr.FullPath = "/opt/forge/dummyentry"
all.Append(newr)
}
func loadFile(fullname string) ([]byte, error) {
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(fullname string, data []byte) error {
log.Infof("%s your repos have changed state. cached state. (%d) bytes\n", fullname, len(data))
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
}
cfgfile.Write(data)
return nil
}