diff --git a/Makefile b/Makefile index 7bed3b0..5af5f94 100644 --- a/Makefile +++ b/Makefile @@ -36,10 +36,10 @@ clean: # refs.proto gitTag.pb.go: gitTag.proto - autogenpb --proto gitTag.proto --no-marshal + autogenpb --proto gitTag.proto --mutex goDep.pb.go: goDep.proto - autogenpb --proto goDep.proto --no-marshal + autogenpb --proto goDep.proto --mutex repo.pb.go: repo.proto - autogenpb --proto repo.proto + autogenpb --proto repo.proto --mutex diff --git a/config.go b/config.go new file mode 100644 index 0000000..3f4d976 --- /dev/null +++ b/config.go @@ -0,0 +1,103 @@ +package gitpb + +// functions to import and export the protobuf +// data to and from config files + +import ( + "errors" + "os" + "path/filepath" + + "go.wit.com/log" +) + +// write to ~/.config/forge/ unless ENV{FORGE_HOME} is set +func (all *Repos) ConfigSave() error { + if os.Getenv("FORGE_HOME") == "" { + homeDir, _ := os.UserHomeDir() + fullpath := filepath.Join(homeDir, ".config/forge") + os.Setenv("FORGE_HOME", fullpath) + } + if all == nil { + log.Warn("gitpb all == nil") + panic("why is this nil?") + } + + data, err := all.Marshal() + if err != nil { + log.Info("gitpb proto.Marshal() failed len", len(data), err) + return err + } + log.Info("gitpb.ConfigSave() repos.Marshal() worked len", len(data)) + configWrite("repos.pb", data) + return nil +} + +// load the ~/.config/forge/ files +func (all *Repos) ConfigLoad() error { + if os.Getenv("FORGE_HOME") == "" { + homeDir, _ := os.UserHomeDir() + fullpath := filepath.Join(homeDir, ".config/forge") + os.Setenv("FORGE_HOME", fullpath) + } + var data []byte + var err error + + if data, err = loadFile("repos.pb"); err != nil { + // something went wrong loading the file + return err + } + if data != nil { + // this means the forge.pb file exists and was read + if len(data) == 0 { + // todo: error out if the file is empty? + // try forge.text & forge.json? + } + if err = all.Unmarshal(data); err != nil { + log.Warn("broken forge.pb config file") + return err + } + log.Info("found", len(all.Repos), "repos in ~/.config/forge/repos.pb") + return nil + } + return nil +} + +func loadFile(filename string) ([]byte, error) { + fullname := filepath.Join(os.Getenv("FORGE_HOME"), 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_HOME"), filename) + + cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE, 0666) + defer cfgfile.Close() + if err != nil { + log.Warn("open config file :", err) + return err + } + if filename == "forge.text" { + // add header + cfgfile.Write([]byte("# this file is automatically re-generated from forge.pb, however,\n")) + cfgfile.Write([]byte("# if you want to edit it by hand, you can:\n")) + cfgfile.Write([]byte("# stop forge; remove forge.pb; edit forge.text; start forge\n")) + cfgfile.Write([]byte("# this will cause the default behavior to fallback to parsing this file for the config\n")) + 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(data) + return nil +} diff --git a/gitTag.proto b/gitTag.proto index 581a9b9..e69a25e 100644 --- a/gitTag.proto +++ b/gitTag.proto @@ -4,7 +4,7 @@ package gitpb; import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp -message GitTag { +message GitTag { // `autogenpb:marshal` string refname = 1; // `autogenpb:unique` // tag name. treated as unique google.protobuf.Timestamp creatordate = 2; // git creatordate google.protobuf.Timestamp authordate = 3; // git author date @@ -12,7 +12,7 @@ message GitTag { string subject = 5; // git tag subject } -message GitTags { +message GitTags { // `autogenpb:marshal` string uuid = 1; // I guess why not just have this on each file string version = 2; // maybe can be used for protobuf schema change violations repeated GitTag gitTags = 3; diff --git a/gitTag.update.go b/gitTag.update.go index ecf0d6b..47afb38 100644 --- a/gitTag.update.go +++ b/gitTag.update.go @@ -12,8 +12,7 @@ import ( // Update repo.Refs from .git/ func (repo *Repo) UpdateGitTags() error { - // delete the old hash - // r.DeleteByHash(hash) + // todo: look for changes in the tags? repo.Tags = new(GitTags) tags := []string{"%(objectname)", "%(creatordate)", "%(*authordate)", "%(refname)", "%(subject)"} diff --git a/goDep.proto b/goDep.proto index 1a15e90..41af614 100644 --- a/goDep.proto +++ b/goDep.proto @@ -6,7 +6,7 @@ package gitpb; import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp -message GoDep { +message GoDep { // `autogenpb:marshal` string hash = 1; // `autogenpb:unique` // md5sum/hash value from the go.sum file google.protobuf.Timestamp ctime = 2; // get the go date from 'go list' ? string version = 3; // v1.2.2 @@ -14,7 +14,7 @@ message GoDep { string goVersion = 5; // version of golang the developer used to make this package version } -message GoDeps { +message GoDeps { // `autogenpb:marshal` string uuid = 1; // I guess why not just have this on each file string version = 2; // maybe can be used for protobuf schema change violations repeated GoDep goDeps = 3; diff --git a/goDep.redoGoMod.go b/goDep.redoGoMod.go index ccf64ef..db64a1e 100644 --- a/goDep.redoGoMod.go +++ b/goDep.redoGoMod.go @@ -138,3 +138,67 @@ func (repo *Repo) RepoType() string { output = strings.Trim(output, "'") return output } + +// reads and parses the go.sum file +func (repo *Repo) UpdatePublished() (bool, error) { + // empty out what was there before + repo.Published = nil + tmp := filepath.Join(repo.FullPath, "go.sum") + gosum, err := os.Open(tmp) + if err != nil { + log.Warn("missing go.sum", repo.FullPath) + return false, err + } + defer gosum.Close() + + scanner := bufio.NewScanner(gosum) + log.Info("gosum:", tmp) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + parts := strings.Split(line, " ") + if len(parts) == 3 { + godep := strings.TrimSpace(parts[0]) + version := strings.TrimSpace(parts[1]) + if strings.HasSuffix(version, "/go.mod") { + version = strings.TrimSuffix(version, "/go.mod") + } + new1 := GoDep{ + GoPath: godep, + Version: version, + } + if repo.Published == nil { + repo.Published = new(GoDeps) + } + repo.Published.AppendUniqueGoPath(&new1) + /* + found := repo.FindGoDepByPath(godep) + if found == nil { + currentversion, ok := deps[godep] + if ok { + // only use the first value found in the file? + // this shouldn't have been possible. this function should + // only be called from MakeRedomod() + // todo: make go things a seperate package so this function + // isn't exported? + if version != currentversion { + log.Warn("\tgo.sum ", godep, "had both", version, currentversion) + } + } else { + deps[godep] = version + log.Info("\t", godep, "=", version) + } + */ + } else { + // I've never seen this happen yet + panic(errors.New("go.sum invalid: " + line)) + // return false, errors.New("go.sum invalid: " + line) + } + } + + if err := scanner.Err(); err != nil { + repo.Published = nil + return false, err + } + return true, nil +} diff --git a/repo.new.go b/repo.new.go index 60dd263..0246ddf 100644 --- a/repo.new.go +++ b/repo.new.go @@ -33,8 +33,18 @@ func (all *Repos) NewGoPath(basepath string, gopath string) (*Repo, error) { FullPath: filepath.Join(basepath, gopath), GoPath: gopath, } + newr.Tags = new(GitTags) // newr.UpdateGit() newr.UpdateGitTags() + newr.GoDeps = new(GoDeps) + // newr.RedoGoMod() + + switch newr.RepoType() { + case "library": + newr.GoLibrary = true + case "binary": + newr.GoBinary = true + } all.AppendUniqueGoPath(&newr) return &newr, nil diff --git a/repo.proto b/repo.proto index fff5579..62609d3 100644 --- a/repo.proto +++ b/repo.proto @@ -15,16 +15,16 @@ message Repo { // `autogenpb:marshal` string masterBranchName = 3; // git 'main' or 'master' branch name string develBranchName = 4; // whatever the git 'devel' branch name is string userBranchName = 5; // whatever your username branch is - GitTags tags = 6; - - // things specific to golang projects - string goPath = 7; // `autogenpb:unique` // the logical path as used by golang: 'go.wit.com/apps/helloworld' - bool goLibrary = 8; // if this is a golang library - bool goPrimitive = 9; // if this is a golang primitive - GoDeps goDeps = 10; - google.protobuf.Timestamp lastGoDep = 11; // last time go.sum was processed - - bool dirty = 12; // if git says things have been changed + GitTags tags = 6; // known tags + string goPath = 7; // `autogenpb:unique` // the logical path as used by golang: 'go.wit.com/apps/helloworld' + bool goLibrary = 8; // is this a golang library? + bool goBinary = 9; // is this a golang binary? + bool goPrimitive = 10; // if this is a golang primitive (only has go.mod) + bool goPlugin = 11; // is this a golang plugin? + GoDeps goDeps = 12; // what is in the go.sum file + google.protobuf.Timestamp lastGoDep = 13; // last time go.sum was processed + bool dirty = 14; // if git says things have been changed + GoDeps published = 15; // the last published go.mod/go.sum } message Repos { // `autogenpb:marshal`