boo. big mistake on naming protobufs

It's important to really choose good names from the
	start. do not think you can rename .proto files later
	Good software engineering practices enforced here!
	no bullshit. you really want to know what you are planning.
This commit is contained in:
Jeff Carr 2024-11-27 21:05:12 -06:00
parent 5d03131047
commit 0fc7c2d753
9 changed files with 276 additions and 30 deletions

View File

@ -5,7 +5,7 @@
# go install # go install
all: repo.pb.go all: repo.pb.go forgeConfig.pb.go
make -C forgeConfig make -C forgeConfig
vet: lint vet: lint
@ -34,3 +34,9 @@ repo.pb.go: repo.proto
cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/forgepb \ cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/forgepb \
--go_opt=Mrepo.proto=go.wit.com/lib/protobuf/forgepb \ --go_opt=Mrepo.proto=go.wit.com/lib/protobuf/forgepb \
repo.proto repo.proto
forgeConfig.pb.go: forgeConfig.proto
# I'm using version v1.35.x from google.golang.org/protobuf/cmd/protoc-gen-go
cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/forgepb \
--go_opt=MforgeConfig.proto=go.wit.com/lib/protobuf/forgepb \
forgeConfig.proto

View File

@ -12,7 +12,7 @@ import (
) )
// write to ~/.config/forge/ unless ENV{FORGE_HOME} is set // write to ~/.config/forge/ unless ENV{FORGE_HOME} is set
func (m *Repos) ConfigSave() error { func (m *ForgeConfigs) ConfigSave() error {
if os.Getenv("FORGE_HOME") == "" { if os.Getenv("FORGE_HOME") == "" {
homeDir, _ := os.UserHomeDir() homeDir, _ := os.UserHomeDir()
fullpath := filepath.Join(homeDir, ".config/forge") fullpath := filepath.Join(homeDir, ".config/forge")
@ -39,7 +39,7 @@ func (m *Repos) ConfigSave() error {
} }
// load the ~/.config/forge/ files // load the ~/.config/forge/ files
func (c *Repos) ConfigLoad() error { func (c *ForgeConfigs) ConfigLoad() error {
if os.Getenv("FORGE_HOME") == "" { if os.Getenv("FORGE_HOME") == "" {
homeDir, _ := os.UserHomeDir() homeDir, _ := os.UserHomeDir()
fullpath := filepath.Join(homeDir, ".config/forge") fullpath := filepath.Join(homeDir, ".config/forge")
@ -66,7 +66,7 @@ func (c *Repos) ConfigLoad() error {
log.Warn("broken forge.pb config file") log.Warn("broken forge.pb config file")
return err return err
} }
log.Info("config load found", len(c.Repos), "repos") log.Info("config load found", len(c.ForgeConfigs), "repos")
return nil return nil
} }
@ -86,7 +86,7 @@ func (c *Repos) ConfigLoad() error {
log.Warn("broken forge.text config file") log.Warn("broken forge.text config file")
return err return err
} }
log.Info("config load found", len(c.Repos), "repos") log.Info("config load found", len(c.ForgeConfigs), "repos")
return nil return nil
} }
@ -106,7 +106,7 @@ func (c *Repos) ConfigLoad() error {
log.Warn("broken forge.json config file") log.Warn("broken forge.json config file")
return err return err
} }
log.Info("config load found", len(c.Repos), "repos") log.Info("config load found", len(c.ForgeConfigs), "repos")
return nil return nil
} }

50
forgeConfig.marshal.go Normal file
View File

@ -0,0 +1,50 @@
package forgepb
// TODO: autogen this
// 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"
// "google.golang.org/protobuf/proto"
)
// human readable JSON
func (p *ForgeConfigs) 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()
// TODO: fix things so this is the default
func (p *ForgeConfigs) FormatTEXT() string {
return prototext.Format(p)
}
// unmarshalTEXT
func (p *ForgeConfigs) UnmarshalTEXT(data []byte) error {
return prototext.Unmarshal(data, p)
}
// marshal json
func (p *ForgeConfigs) MarshalJSON() ([]byte, error) {
return protojson.Marshal(p)
}
// unmarshal
func (p *ForgeConfigs) UnmarshalJSON(data []byte) error {
return protojson.Unmarshal(data, p)
}
// marshal to wire
func (m *ForgeConfigs) Marshal() ([]byte, error) {
return proto.Marshal(m)
}
// unmarshal from wire
func (m *ForgeConfigs) Unmarshal(data []byte) error {
return proto.Unmarshal(data, m)
}

39
forgeConfig.proto Normal file
View File

@ -0,0 +1,39 @@
syntax = "proto3";
package forgepb;
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
// define 3 branches. that is all that is supported
// the term 'master' is used in the code because 'main' is a reserved word in golang already
// allow 'read only' and 'private' flags
// package names sometimes must be different than the binary name
// for example 'zookeeper' is packaged as 'zookeeper-go'
// due to the prior apache foundation project. This happens and is ok!
message ForgeConfig {
string goPath = 1; // Examples: 'go.wit.com/apps/go-clone' or "~/mythings" or "/home/src/foo"
bool writable = 2; // if you have write access to the repo
bool readOnly = 3; // the opposite, but needed for now because I don't know what I'm doing
bool private = 4; // if the repo can be published
bool directory = 5; // everything in this directory should use these writable & private values
bool favorite = 6; // you like this. always git clone/go clone this repo
bool interesting = 7; // this is something interesting you found and want to remember it
string masterBranch = 8; // git 'main' or 'master' branch name
string develBranch = 9; // whatever the git 'devel' branch name is
string userBranch = 10; // whatever your username branch is
string debName = 11; // the actual name used with 'apt install' (or distro apt equivalent.
// todo: appeal to everyone to alias 'apt' on rhat, gentoo, arch, etc to alias 'apt install'
// so we can make easier instructions for new linux users. KISS
google.protobuf.Timestamp verstamp = 12; // the git commit timestamp of the version
}
// TODO: autogen 'Repos'
message ForgeConfigs {
string uuid = 1; // could be useful for /usr/share/file/magic someday?
string version = 2; // could be used for protobuf schema change violations?
repeated ForgeConfig ForgeConfigs = 3;
}

151
forgeConfig.sort.go Normal file
View File

@ -0,0 +1,151 @@
package forgepb
// TODO: autogen this? (probably not feasible. need go-arglike tricks in proto)
import (
"fmt"
"os"
"sort"
sync "sync"
"time"
)
// bad global lock until I figure out some other plan
var forgeConfigsLock sync.RWMutex
type ForgeConfigIterator struct {
sync.RWMutex
packs []*ForgeConfig
index int
}
// NewForgeConfigIterator initializes a new iterator.
func NewForgeConfigIterator(packs []*ForgeConfig) *ForgeConfigIterator {
return &ForgeConfigIterator{packs: packs}
}
// Scan moves to the next element and returns false if there are no more packs.
func (it *ForgeConfigIterator) Scan() bool {
if it.index >= len(it.packs) {
return false
}
it.index++
return true
}
// ForgeConfig returns the current forgeConfig.
func (it *ForgeConfigIterator) Next() *ForgeConfig {
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("forgeConfig == 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.ForgeConfig()
// fmt.Println("ForgeConfig UUID:", d.Uuid)
// }
func (r *ForgeConfigs) All() *ForgeConfigIterator {
forgeConfigPointers := r.selectAllForgeConfigs()
iterator := NewForgeConfigIterator(forgeConfigPointers)
return iterator
}
func (r *ForgeConfigs) SortByPath() *ForgeConfigIterator {
packs := r.selectAllForgeConfigs()
sort.Sort(ByForgeConfigPath(packs))
iterator := NewForgeConfigIterator(packs)
return iterator
}
// enforces no duplicate forgeConfig paths
func (r *ForgeConfigs) Append(newP *ForgeConfig) bool {
forgeConfigsLock.Lock()
defer forgeConfigsLock.Unlock()
for _, p := range r.ForgeConfigs {
if p.GoPath == newP.GoPath {
return false
}
}
r.ForgeConfigs = append(r.ForgeConfigs, newP)
return true
}
// returns time.Duration since last Update()
func (r *ForgeConfig) Age(newP *ForgeConfig) time.Duration {
t := time.Since(r.Verstamp.AsTime())
return t
}
// find a forgeConfig by path
func (r *ForgeConfigs) FindByPath(gopath string) *ForgeConfig {
forgeConfigsLock.RLock()
defer forgeConfigsLock.RUnlock()
for _, p := range r.ForgeConfigs {
if p.GoPath == gopath {
return p
}
}
return nil
}
func (r *ForgeConfigs) Len() int {
forgeConfigsLock.RLock()
defer forgeConfigsLock.RUnlock()
return len(r.ForgeConfigs)
}
type ByForgeConfigPath []*ForgeConfig
func (a ByForgeConfigPath) Len() int { return len(a) }
func (a ByForgeConfigPath) Less(i, j int) bool { return a[i].GoPath < a[j].GoPath }
func (a ByForgeConfigPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (all *ForgeConfigs) DeleteByPath(gopath string) *ForgeConfig {
forgeConfigsLock.Lock()
defer forgeConfigsLock.Unlock()
var newr ForgeConfig
for i, _ := range all.ForgeConfigs {
if all.ForgeConfigs[i].GoPath == gopath {
newr = *all.ForgeConfigs[i]
all.ForgeConfigs[i] = all.ForgeConfigs[len(all.ForgeConfigs)-1]
all.ForgeConfigs = all.ForgeConfigs[:len(all.ForgeConfigs)-1]
return &newr
}
}
return nil
}
// safely returns a slice of pointers to the ForgeConfig protobufs
func (r *ForgeConfigs) selectAllForgeConfigs() []*ForgeConfig {
forgeConfigsLock.RLock()
defer forgeConfigsLock.RUnlock()
// Create a new slice to hold pointers to each ForgeConfig
var allPacks []*ForgeConfig
allPacks = make([]*ForgeConfig, len(r.ForgeConfigs))
for i, p := range r.ForgeConfigs {
allPacks[i] = p // Copy pointers for safe iteration
}
return allPacks
}

View File

@ -11,7 +11,7 @@ import (
var VERSION string var VERSION string
func main() { func main() {
var repos forgepb.Repos var repos forgepb.ForgeConfigs
if err := repos.ConfigLoad(); err != nil { if err := repos.ConfigLoad(); err != nil {
log.Warn("forgepb.ConfigLoad() failed", err) log.Warn("forgepb.ConfigLoad() failed", err)
os.Exit(-1) os.Exit(-1)
@ -55,7 +55,7 @@ func main() {
// try to add, then save config and exit // try to add, then save config and exit
if argv.Add { if argv.Add {
log.Info("going to add a new repo", argv.GoPath) log.Info("going to add a new repo", argv.GoPath)
new1 := forgepb.Repo{ new1 := forgepb.ForgeConfig{
GoPath: argv.GoPath, GoPath: argv.GoPath,
Writable: argv.Writable, Writable: argv.Writable,
ReadOnly: argv.ReadOnly, ReadOnly: argv.ReadOnly,

View File

@ -18,7 +18,7 @@ func standardHeader() string {
return fmt.Sprintf("%-4s %-40s %s", "", "Path", "flags") return fmt.Sprintf("%-4s %-40s %s", "", "Path", "flags")
} }
func (all *Repos) standardHeader(r *Repo) string { func (all *ForgeConfigs) standardHeader(r *ForgeConfig) string {
var flags string var flags string
var readonly string var readonly string
if all.IsPrivate(r.GoPath) { if all.IsPrivate(r.GoPath) {
@ -36,7 +36,7 @@ func (all *Repos) standardHeader(r *Repo) string {
} }
// print a human readable table to STDOUT // print a human readable table to STDOUT
func (all *Repos) PrintTable() { func (all *ForgeConfigs) PrintTable() {
if all == nil { if all == nil {
log.Info("WTF") log.Info("WTF")
os.Exit(0) os.Exit(0)
@ -44,7 +44,7 @@ func (all *Repos) PrintTable() {
log.Info(standardHeader()) log.Info(standardHeader())
loop := all.SortByPath() loop := all.SortByPath()
for loop.Scan() { for loop.Scan() {
r := loop.Repo() r := loop.Next()
log.Info(all.standardHeader(r)) log.Info(all.standardHeader(r))
} }
} }

View File

@ -6,8 +6,8 @@ import (
"go.wit.com/log" "go.wit.com/log"
) )
func (all *Repos) SampleConfig() { func (all *ForgeConfigs) SampleConfig() {
new1 := new(Repo) new1 := new(ForgeConfig)
new1.GoPath = "go.wit.com" new1.GoPath = "go.wit.com"
new1.Writable = true new1.Writable = true
new1.Directory = true new1.Directory = true
@ -17,7 +17,7 @@ func (all *Repos) SampleConfig() {
log.Info("added", new1.GoPath, "failed") log.Info("added", new1.GoPath, "failed")
} }
new1 = new(Repo) new1 = new(ForgeConfig)
new1.GoPath = "go.wit.com/apps/zookeeper" new1.GoPath = "go.wit.com/apps/zookeeper"
new1.DebName = "zookeeper-go" new1.DebName = "zookeeper-go"
if all.Append(new1) { if all.Append(new1) {
@ -26,7 +26,7 @@ func (all *Repos) SampleConfig() {
log.Info("added", new1.GoPath, "failed") log.Info("added", new1.GoPath, "failed")
} }
new1 = new(Repo) new1 = new(ForgeConfig)
new1.GoPath = "go.wit.com/apps/wit-package" new1.GoPath = "go.wit.com/apps/wit-package"
new1.Private = true new1.Private = true
if all.Append(new1) { if all.Append(new1) {
@ -35,7 +35,7 @@ func (all *Repos) SampleConfig() {
log.Info("added", new1.GoPath, "failed") log.Info("added", new1.GoPath, "failed")
} }
new1 = new(Repo) new1 = new(ForgeConfig)
new1.GoPath = "go.wit.com/apps/networkQuality" new1.GoPath = "go.wit.com/apps/networkQuality"
new1.DebName = "networkquality" new1.DebName = "networkquality"
new1.ReadOnly = true new1.ReadOnly = true
@ -45,7 +45,7 @@ func (all *Repos) SampleConfig() {
log.Info("added", new1.GoPath, "failed") log.Info("added", new1.GoPath, "failed")
} }
new2 := new(Repo) new2 := new(ForgeConfig)
new2.GoPath = "go.wit.com/apps/go-clone" new2.GoPath = "go.wit.com/apps/go-clone"
if all.Append(new2) { if all.Append(new2) {
log.Info("added", new2.GoPath, "ok") log.Info("added", new2.GoPath, "ok")
@ -59,5 +59,5 @@ func (all *Repos) SampleConfig() {
log.Info("added", new2.GoPath, "failed (but ok)") log.Info("added", new2.GoPath, "failed (but ok)")
} }
fmt.Println("first time user. adding an example config file with", len(all.Repos), "repos") fmt.Println("first time user. adding an example config file with", len(all.ForgeConfigs), "repos")
} }

View File

@ -14,7 +14,7 @@ import (
"strings" "strings"
) )
func (all *Repos) UpdateGoPath(name string, gopath string) bool { func (all *ForgeConfigs) UpdateGoPath(name string, gopath string) bool {
oldr := all.DeleteByPath(name) oldr := all.DeleteByPath(name)
if oldr == nil { if oldr == nil {
// nothing to update // nothing to update
@ -28,12 +28,12 @@ func (all *Repos) UpdateGoPath(name string, gopath string) bool {
// returns true if gopath is readonly() // returns true if gopath is readonly()
// will attempt to match IsWritable("foo") against anything ending in "foo" // will attempt to match IsWritable("foo") against anything ending in "foo"
func (all *Repos) IsReadOnly(gopath string) bool { func (all *ForgeConfigs) IsReadOnly(gopath string) bool {
var match *Repo var match *ForgeConfig
loop := all.SortByPath() // get the list of repos loop := all.SortByPath() // get the list of repos
for loop.Scan() { for loop.Scan() {
r := loop.Repo() r := loop.Next()
if r.GoPath == gopath { if r.GoPath == gopath {
// exact gopath match // exact gopath match
if r.Writable { if r.Writable {
@ -88,13 +88,13 @@ func (all *Repos) IsReadOnly(gopath string) bool {
// this let's you check a git tag version against a package .deb version // this let's you check a git tag version against a package .deb version
// allows gopath's to not need to match the .deb name // allows gopath's to not need to match the .deb name
// this is important in lots of cases! It is normal and happens often enough. // this is important in lots of cases! It is normal and happens often enough.
func (all *Repos) DebName(gopath string) string { func (all *ForgeConfigs) DebName(gopath string) string {
// get "zookeeper" from "go.wit.com/apps/zookeeper" // get "zookeeper" from "go.wit.com/apps/zookeeper"
normalBase := filepath.Base(gopath) normalBase := filepath.Base(gopath)
loop := all.SortByPath() loop := all.SortByPath()
for loop.Scan() { for loop.Scan() {
r := loop.Repo() r := loop.Next()
if r.GoPath == gopath { if r.GoPath == gopath {
// returns "zookeeper-go" for "go.wit.com/apps/zookeeper" // returns "zookeeper-go" for "go.wit.com/apps/zookeeper"
if r.DebName != "" { if r.DebName != "" {
@ -115,15 +115,15 @@ func (all *Repos) DebName(gopath string) string {
// //
// IsPrivate("go.foo.com/jcarr/foo") returns true if private // IsPrivate("go.foo.com/jcarr/foo") returns true if private
// IsPrivate("foo") also returns true if "go.bar.com/jcarr/foo" is private // IsPrivate("foo") also returns true if "go.bar.com/jcarr/foo" is private
func (all *Repos) IsPrivate(thing string) bool { func (all *ForgeConfigs) IsPrivate(thing string) bool {
var match *Repo var match *ForgeConfig
// sort by path means the simple 'match' logic // sort by path means the simple 'match' logic
// here works in the sense the last directory match // here works in the sense the last directory match
// is the one that is used // is the one that is used
loop := all.SortByPath() // get the list of repos loop := all.SortByPath() // get the list of repos
for loop.Scan() { for loop.Scan() {
r := loop.Repo() r := loop.Next()
if r.GoPath == thing { if r.GoPath == thing {
// if private is set here, then ok, otherwise // if private is set here, then ok, otherwise
// still check if a Directory match exists // still check if a Directory match exists
@ -159,12 +159,12 @@ func (all *Repos) IsPrivate(thing string) bool {
// file that lets you set things as favorites // file that lets you set things as favorites
// so you can just go-clone a bunch of common things // so you can just go-clone a bunch of common things
// on a new box or after you reset/delete your ~/go/src dir // on a new box or after you reset/delete your ~/go/src dir
func (all *Repos) IsFavorite(thing string) bool { func (all *ForgeConfigs) IsFavorite(thing string) bool {
var match *Repo var match *ForgeConfig
loop := all.SortByPath() // get the list of repos loop := all.SortByPath() // get the list of repos
for loop.Scan() { for loop.Scan() {
r := loop.Repo() r := loop.Next()
if r.GoPath == thing { if r.GoPath == thing {
if r.Favorite { if r.Favorite {
return true return true