diff --git a/Makefile b/Makefile index d3630e7..4c97719 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # go install -all: repository.pb.go +all: package.pb.go make -C example vet: lint @@ -29,10 +29,10 @@ clean: -rm -f go.* make -C example clean -repository.pb.go: repository.proto +package.pb.go: package.proto # protoc --go_out=. droplet.proto # This is switched over to use the new protoc-gen-go from google.golang.org/protobuf/cmd/protoc-gen-go # the debian one (2024/10/21) seems to be the older/original one from github.com/golang/protobuf/protoc-gen-go - cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/gitpb \ - --go_opt=Mrepository.proto=go.wit.com/lib/protobuf/gitpb \ - repository.proto + cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/zoopb \ + --go_opt=Mpackage.proto=go.wit.com/lib/protobuf/zoopb \ + package.proto diff --git a/README.md b/README.md index aecb981..f85d699 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -simple protobuf definition files for git repos +protobuf definition files for zookeeper diff --git a/example/main.go b/example/main.go index bbf6729..8187d57 100644 --- a/example/main.go +++ b/example/main.go @@ -3,12 +3,18 @@ package main import ( "fmt" - "go.wit.com/lib/protobuf/gitpb" + "go.wit.com/lib/protobuf/zoopb" ) func main() { - var r *gitpb.Repository - r = gitpb.Load("go.wit.com/lib/protobuf/gitpb") + var all *zoopb.Packages + all = new(zoopb.Packages) + // r = zoopb.LoadJSON("go.wit.com/lib/protobuf/zoopb") - fmt.Println("repo has branches:", len(r.Branches)) + newP := new(zoopb.Package) + newP.Name = "bash" + newP.Version = "5.2.21" + all.Append(newP) + + fmt.Println("packages are:", len(all.Packages)) } diff --git a/goList.go b/goList.go deleted file mode 100644 index 4330331..0000000 --- a/goList.go +++ /dev/null @@ -1,114 +0,0 @@ -package gitpb - -import ( - "encoding/json" - "time" - - "go.wit.com/lib/gui/shell" - "go.wit.com/log" -) - -// go list -json -m go.wit.com/apps/go-clone@latest - -// go list -json -m go.wit.com/apps/go-clone@latest -// { -// "Path": "go.wit.com/apps/go-clone", -// "Version": "v0.0.6", -// "Query": "latest", -// "Time": "2024-03-10T04:12:15Z", -// "GoMod": "/home/jcarr/go/pkg/mod/cache/download/go.wit.com/apps/go-clone/@v/v0.0.6.mod", -// "GoVersion": "1.22.0" -// } - -type Module struct { - Path string // module path - Query string // version query corresponding to this version - Version string // module version - Versions []string // available module versions - Replace *Module // replaced by this module - Time *time.Time // time version was created - Update *Module // available update (with -u) - Main bool // is this the main module? - Indirect bool // module is only indirectly needed by main module - Dir string // directory holding local copy of files, if any - GoMod string // path to go.mod file describing module, if any - GoVersion string // go version used in module - Retracted []string // retraction information, if any (with -retracted or -u) - Deprecated string // deprecation message, if any (with -u) - Error *ModuleError // error loading module - Sum string // checksum for path, version (as in go.sum) - GoModSum string // checksum for go.mod (as in go.sum) - Reuse bool // reuse of old module info is safe - Origin Origin -} - -type ModuleError struct { - Err string // the error itself -} - -// An Origin describes the provenance of a given repo method result. -// It can be passed to CheckReuse (usually in a different go command invocation) -// to see whether the result remains up-to-date. -type Origin struct { - VCS string `json:",omitempty"` // "git" etc - URL string `json:",omitempty"` // URL of repository - Subdir string `json:",omitempty"` // subdirectory in repo - - Hash string `json:",omitempty"` // commit hash or ID - - // If TagSum is non-empty, then the resolution of this module version - // depends on the set of tags present in the repo, specifically the tags - // of the form TagPrefix + a valid semver version. - // If the matching repo tags and their commit hashes still hash to TagSum, - // the Origin is still valid (at least as far as the tags are concerned). - // The exact checksum is up to the Repo implementation; see (*gitRepo).Tags. - TagPrefix string `json:",omitempty"` - TagSum string `json:",omitempty"` - - // If Ref is non-empty, then the resolution of this module version - // depends on Ref resolving to the revision identified by Hash. - // If Ref still resolves to Hash, the Origin is still valid (at least as far as Ref is concerned). - // For Git, the Ref is a full ref like "refs/heads/main" or "refs/tags/v1.2.3", - // and the Hash is the Git object hash the ref maps to. - // Other VCS might choose differently, but the idea is that Ref is the name - // with a mutable meaning while Hash is a name with an immutable meaning. - Ref string `json:",omitempty"` - - // If RepoSum is non-empty, then the resolution of this module version - // failed due to the repo being available but the version not being present. - // This depends on the entire state of the repo, which RepoSum summarizes. - // For Git, this is a hash of all the refs and their hashes. - RepoSum string `json:",omitempty"` -} - -// A Tags describes the available tags in a code repository. - -func runGoList(url string) (string, error) { - ver, err := getLatestVersion(url) - if err != nil { - return "", err - } - r := shell.Output("", []string{"go", "list", "-json", "-m", url + "@" + ver}) - var modInfo Module - err = json.Unmarshal(r.Output, &modInfo) - if err != nil { - log.Info("runGoList() r.Output =", string(r.Output)) - log.Info("runGoList() json.Unmarshal() error =", err) - return "", err - } - log.Info("runGoList() Output.Origin.URL =", modInfo.Origin.URL) - return modInfo.Origin.URL, err -} - -func getLatestVersion(url string) (string, error) { - r := shell.Output("", []string{"go", "list", "-json", "-m", url + "@latest"}) - var modInfo Module - err := json.Unmarshal(r.Output, &modInfo) - if err != nil { - log.Info("runGoList() r.Output =", string(r.Output)) - log.Info("runGoList() json.Unmarshal() error =", err) - return "", err - } - log.Info("runGoList() Output.Version =", modInfo.Version) - return modInfo.Version, err -} diff --git a/helpers.go b/helpers.go new file mode 100644 index 0000000..93d0a73 --- /dev/null +++ b/helpers.go @@ -0,0 +1,32 @@ +package zoopb + +// functions to import and export the protobuf +// data to and from config files + +import ( + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/encoding/prototext" + // "google.golang.org/protobuf/proto" +) + +// human readable JSON +func (p *Packages) FormatJSON() string { + return protojson.Format(p) +} + +// apparently this isn't supposed to be used? +// https://protobuf.dev/reference/go/faq/#unstable-text +// this is a shame because this is much nicer output than JSON Format() +func (p *Packages) FormatTEXT() string { + return prototext.Format(p) +} + +// marshal +func (p *Packages) MarshalJSON() ([]byte, error) { + return protojson.Marshal(p) +} + +// unmarshal +func (p *Packages) UnmarshalJSON(data []byte) error { + return protojson.Unmarshal(data, p) +} diff --git a/package.proto b/package.proto new file mode 100644 index 0000000..53859a8 --- /dev/null +++ b/package.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package gitpb; + +// import "google/protobuf/duration.proto"; // Import the well-known type for Timestamp +import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp + +message Package { + string name = 1; + string version = 2; + google.protobuf.Timestamp laststamp = 4; // the last time we heard anything from this droplet +} + +message Packages { + 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 Package packages = 3; +} diff --git a/packages.go b/packages.go new file mode 100644 index 0000000..1436c45 --- /dev/null +++ b/packages.go @@ -0,0 +1,131 @@ +package zoopb + +// this is becoming a standard format +// todo: autogenerate this from the .proto file? + +import ( + "fmt" + "os" + "sort" + sync "sync" +) + +// bad global lock until I figure out some other plan +var lock sync.RWMutex + +type PackageIterator struct { + sync.RWMutex + + packs []*Package + index int +} + +// NewPackageIterator initializes a new iterator. +func NewPackageIterator(packs []*Package) *PackageIterator { + return &PackageIterator{packs: packs} +} + +// Scan moves to the next element and returns false if there are no more packs. +func (it *PackageIterator) Scan() bool { + if it.index >= len(it.packs) { + return false + } + it.index++ + return true +} + +// Package returns the current repo. +func (it *PackageIterator) Package() *Package { + if it.packs[it.index-1] == nil { + for i, d := range it.packs { + fmt.Println("i =", i, d) + } + fmt.Println("len =", len(it.packs)) + fmt.Println("repo == nil", it.index, it.index-1) + os.Exit(-1) + } + return it.packs[it.index-1] +} + +// Use Scan() in a loop, similar to a while loop +// +// for iterator.Scan() { +// d := iterator.Package() +// fmt.Println("Package UUID:", d.Uuid) +// } + +func (r *Packages) All() *PackageIterator { + repoPointers := r.selectAllPackages() + + iterator := NewPackageIterator(repoPointers) + return iterator +} + +func (r *Packages) SortByName() *PackageIterator { + packs := r.selectAllPackages() + + sort.Sort(ByName(packs)) + + iterator := NewPackageIterator(packs) + return iterator +} + +// should this be a pointer? what really happens here? +func (r *Packages) Append(newP *Package) { + lock.Lock() + defer lock.Unlock() + + r.Packages = append(r.Packages, newP) +} + +type ByName []*Package + +func (a ByName) Len() int { return len(a) } +func (a ByName) Less(i, j int) bool { return a[i].Name < a[j].Name } +func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// safely returns a slice of pointers to the Package protobufs +func (r *Packages) selectAllPackages() []*Package { + lock.RLock() + defer lock.RUnlock() + + // Create a new slice to hold pointers to each Package + var allPacks []*Package + allPacks = make([]*Package, len(r.Packages)) + for i, p := range r.Packages { + allPacks[i] = p // Copy pointers for safe iteration + } + + return allPacks +} + +/* +func (r *Packages) UnmergedPackageRepos() *PackageRepoIterator { + repoPointers := r.selectUnmergedPackageRepos() + + sort.Sort(ByName(repoPointers)) + + iterator := NewPackageRepoIterator(repoPointers) + + return iterator +} +*/ + +/* +// this sort doesn't really work. I think it forgets to sort the last two +// todo: sort this out. literally +// SelectPackagePointers safely returns a slice of pointers to Package records. +func (r *Packages) selectUnmergedPackages() []*PackageRow { + r.RLock() + defer r.RUnlock() + + // Create a new slice to hold pointers to each Package + // repoPointers := make([]*Package, len(c.E.Packages)) + var repoPointers []*PackageRow + for _, repo := range me.allrepos { + repoPointers = append(repoPointers, repo) // Copy pointers for safe iteration + } + + return repoPointers +} +*/ diff --git a/repository.proto b/repository.proto deleted file mode 100644 index c415075..0000000 --- a/repository.proto +++ /dev/null @@ -1,29 +0,0 @@ -syntax = "proto3"; - -package gitpb; - -// import "google/protobuf/duration.proto"; // Import the well-known type for Timestamp -import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp - -message Commit { - string id = 1; - string message = 2; - string author = 3; - google.protobuf.Timestamp timestamp = 4; // the last time we heard anything from this droplet -} - -message Tag { - string name = 1; - Commit commit = 2; -} - -message Branch { - string name = 1; - Commit head = 2; -} - -message Repository { - string name = 1; - repeated Branch branches = 2; - repeated Tag tags = 3; -}