package gitpb // functions to import and export the protobuf // data to and from config files import ( "errors" "os" "path/filepath" "time" "go.wit.com/lib/config" "go.wit.com/lib/protobuf/bugpb" "go.wit.com/log" ) var globalChanged bool var globalReason string func (all *Repos) SmartSave() error { if !globalChanged { return nil } log.Info("saved because", globalReason) globalChanged = false globalReason = "" return all.Save() } // write the repos.pb file func (all *Repos) ConfigSave(fname string) error { if all == nil { log.Warn("gitpb repos == nil") return errors.New("gitpb.ConfigSave() repos == nil") } if _, s := filepath.Split(fname); s != "repos.pb" { log.Infof("ConfigSave() filename '%s' invalid\n", fname) return log.Errorf("ConfigSave() filename '%s' invalid\n", fname) } return all.SaveValidate(fname) } // bypass name check "repos.pb" func (pb *Repos) SaveValidate(fname string) error { if pb.Filename != fname { log.Printf("gitpb.Repos.Filename mismatch '%s' != '%s'\n", pb.Filename, fname) pb.Filename = fname time.Sleep(5 * time.Second) } data, err := pb.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 := pb.tryValidate(); err != nil { log.Info("gitpb.ConfigSave() STILL FAILEd", err) return err } else { // re-attempt Marshal() here data, err = pb.Marshal() if err == nil { // validate & sanitize strings worked configWrite(fname, data) return nil } log.Info("gitpb.ConfigSave() STILL FAILEd", err) } return err } err = config.Save(pb) return err } // 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. 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 log.Info("Repos ConfigLoad() failed", cfgname, err) 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) { // the file does not exist return nil, err } if err != nil { log.Info(fullname, "permission error? error =", 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 }