summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--config.go10
-rw-r--r--forgeConfig.marshal.go50
-rw-r--r--forgeConfig.proto39
-rw-r--r--forgeConfig.sort.go151
-rw-r--r--forgeConfig/main.go4
-rw-r--r--human.go6
-rw-r--r--sampleConfig.go14
-rw-r--r--settings.go24
9 files changed, 276 insertions, 30 deletions
diff --git a/Makefile b/Makefile
index 9a32973..202004e 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@
# go install
-all: repo.pb.go
+all: repo.pb.go forgeConfig.pb.go
make -C forgeConfig
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 \
--go_opt=Mrepo.proto=go.wit.com/lib/protobuf/forgepb \
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
diff --git a/config.go b/config.go
index 672d974..0866ae3 100644
--- a/config.go
+++ b/config.go
@@ -12,7 +12,7 @@ import (
)
// 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") == "" {
homeDir, _ := os.UserHomeDir()
fullpath := filepath.Join(homeDir, ".config/forge")
@@ -39,7 +39,7 @@ func (m *Repos) ConfigSave() error {
}
// load the ~/.config/forge/ files
-func (c *Repos) ConfigLoad() error {
+func (c *ForgeConfigs) ConfigLoad() error {
if os.Getenv("FORGE_HOME") == "" {
homeDir, _ := os.UserHomeDir()
fullpath := filepath.Join(homeDir, ".config/forge")
@@ -66,7 +66,7 @@ func (c *Repos) ConfigLoad() error {
log.Warn("broken forge.pb config file")
return err
}
- log.Info("config load found", len(c.Repos), "repos")
+ log.Info("config load found", len(c.ForgeConfigs), "repos")
return nil
}
@@ -86,7 +86,7 @@ func (c *Repos) ConfigLoad() error {
log.Warn("broken forge.text config file")
return err
}
- log.Info("config load found", len(c.Repos), "repos")
+ log.Info("config load found", len(c.ForgeConfigs), "repos")
return nil
}
@@ -106,7 +106,7 @@ func (c *Repos) ConfigLoad() error {
log.Warn("broken forge.json config file")
return err
}
- log.Info("config load found", len(c.Repos), "repos")
+ log.Info("config load found", len(c.ForgeConfigs), "repos")
return nil
}
diff --git a/forgeConfig.marshal.go b/forgeConfig.marshal.go
new file mode 100644
index 0000000..63fa744
--- /dev/null
+++ b/forgeConfig.marshal.go
@@ -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)
+}
diff --git a/forgeConfig.proto b/forgeConfig.proto
new file mode 100644
index 0000000..ab4e033
--- /dev/null
+++ b/forgeConfig.proto
@@ -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;
+}
diff --git a/forgeConfig.sort.go b/forgeConfig.sort.go
new file mode 100644
index 0000000..4b0ff69
--- /dev/null
+++ b/forgeConfig.sort.go
@@ -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
+}
diff --git a/forgeConfig/main.go b/forgeConfig/main.go
index 1efa31e..fe53fe8 100644
--- a/forgeConfig/main.go
+++ b/forgeConfig/main.go
@@ -11,7 +11,7 @@ import (
var VERSION string
func main() {
- var repos forgepb.Repos
+ var repos forgepb.ForgeConfigs
if err := repos.ConfigLoad(); err != nil {
log.Warn("forgepb.ConfigLoad() failed", err)
os.Exit(-1)
@@ -55,7 +55,7 @@ func main() {
// try to add, then save config and exit
if argv.Add {
log.Info("going to add a new repo", argv.GoPath)
- new1 := forgepb.Repo{
+ new1 := forgepb.ForgeConfig{
GoPath: argv.GoPath,
Writable: argv.Writable,
ReadOnly: argv.ReadOnly,
diff --git a/human.go b/human.go
index 66d1211..294a789 100644
--- a/human.go
+++ b/human.go
@@ -18,7 +18,7 @@ func standardHeader() string {
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 readonly string
if all.IsPrivate(r.GoPath) {
@@ -36,7 +36,7 @@ func (all *Repos) standardHeader(r *Repo) string {
}
// print a human readable table to STDOUT
-func (all *Repos) PrintTable() {
+func (all *ForgeConfigs) PrintTable() {
if all == nil {
log.Info("WTF")
os.Exit(0)
@@ -44,7 +44,7 @@ func (all *Repos) PrintTable() {
log.Info(standardHeader())
loop := all.SortByPath()
for loop.Scan() {
- r := loop.Repo()
+ r := loop.Next()
log.Info(all.standardHeader(r))
}
}
diff --git a/sampleConfig.go b/sampleConfig.go
index fc83f6f..52038ab 100644
--- a/sampleConfig.go
+++ b/sampleConfig.go
@@ -6,8 +6,8 @@ import (
"go.wit.com/log"
)
-func (all *Repos) SampleConfig() {
- new1 := new(Repo)
+func (all *ForgeConfigs) SampleConfig() {
+ new1 := new(ForgeConfig)
new1.GoPath = "go.wit.com"
new1.Writable = true
new1.Directory = true
@@ -17,7 +17,7 @@ func (all *Repos) SampleConfig() {
log.Info("added", new1.GoPath, "failed")
}
- new1 = new(Repo)
+ new1 = new(ForgeConfig)
new1.GoPath = "go.wit.com/apps/zookeeper"
new1.DebName = "zookeeper-go"
if all.Append(new1) {
@@ -26,7 +26,7 @@ func (all *Repos) SampleConfig() {
log.Info("added", new1.GoPath, "failed")
}
- new1 = new(Repo)
+ new1 = new(ForgeConfig)
new1.GoPath = "go.wit.com/apps/wit-package"
new1.Private = true
if all.Append(new1) {
@@ -35,7 +35,7 @@ func (all *Repos) SampleConfig() {
log.Info("added", new1.GoPath, "failed")
}
- new1 = new(Repo)
+ new1 = new(ForgeConfig)
new1.GoPath = "go.wit.com/apps/networkQuality"
new1.DebName = "networkquality"
new1.ReadOnly = true
@@ -45,7 +45,7 @@ func (all *Repos) SampleConfig() {
log.Info("added", new1.GoPath, "failed")
}
- new2 := new(Repo)
+ new2 := new(ForgeConfig)
new2.GoPath = "go.wit.com/apps/go-clone"
if all.Append(new2) {
log.Info("added", new2.GoPath, "ok")
@@ -59,5 +59,5 @@ func (all *Repos) SampleConfig() {
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")
}
diff --git a/settings.go b/settings.go
index f40bdb4..61f0d98 100644
--- a/settings.go
+++ b/settings.go
@@ -14,7 +14,7 @@ import (
"strings"
)
-func (all *Repos) UpdateGoPath(name string, gopath string) bool {
+func (all *ForgeConfigs) UpdateGoPath(name string, gopath string) bool {
oldr := all.DeleteByPath(name)
if oldr == nil {
// nothing to update
@@ -28,12 +28,12 @@ func (all *Repos) UpdateGoPath(name string, gopath string) bool {
// returns true if gopath is readonly()
// will attempt to match IsWritable("foo") against anything ending in "foo"
-func (all *Repos) IsReadOnly(gopath string) bool {
- var match *Repo
+func (all *ForgeConfigs) IsReadOnly(gopath string) bool {
+ var match *ForgeConfig
loop := all.SortByPath() // get the list of repos
for loop.Scan() {
- r := loop.Repo()
+ r := loop.Next()
if r.GoPath == gopath {
// exact gopath match
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
// 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.
-func (all *Repos) DebName(gopath string) string {
+func (all *ForgeConfigs) DebName(gopath string) string {
// get "zookeeper" from "go.wit.com/apps/zookeeper"
normalBase := filepath.Base(gopath)
loop := all.SortByPath()
for loop.Scan() {
- r := loop.Repo()
+ r := loop.Next()
if r.GoPath == gopath {
// returns "zookeeper-go" for "go.wit.com/apps/zookeeper"
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("foo") also returns true if "go.bar.com/jcarr/foo" is private
-func (all *Repos) IsPrivate(thing string) bool {
- var match *Repo
+func (all *ForgeConfigs) IsPrivate(thing string) bool {
+ var match *ForgeConfig
// sort by path means the simple 'match' logic
// here works in the sense the last directory match
// is the one that is used
loop := all.SortByPath() // get the list of repos
for loop.Scan() {
- r := loop.Repo()
+ r := loop.Next()
if r.GoPath == thing {
// if private is set here, then ok, otherwise
// 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
// so you can just go-clone a bunch of common things
// on a new box or after you reset/delete your ~/go/src dir
-func (all *Repos) IsFavorite(thing string) bool {
- var match *Repo
+func (all *ForgeConfigs) IsFavorite(thing string) bool {
+ var match *ForgeConfig
loop := all.SortByPath() // get the list of repos
for loop.Scan() {
- r := loop.Repo()
+ r := loop.Next()
if r.GoPath == thing {
if r.Favorite {
return true