Compare commits
146 Commits
Author | SHA1 | Date |
---|---|---|
|
7e1804f6e3 | |
|
8ceec9210d | |
|
cf6db578a4 | |
|
da485dcc3f | |
|
5377a89d2c | |
|
f7d6dfa6a7 | |
|
281ffbc75b | |
|
4193f41847 | |
|
9d5bae8a14 | |
|
f540aab434 | |
|
a170250cb4 | |
|
2fc67512c8 | |
|
eaadfa21d3 | |
|
22ebf174c8 | |
|
9a87c93bad | |
|
b7b18626d8 | |
|
7900b1416e | |
|
5c84b9ab66 | |
|
c09e292a66 | |
|
3278f6400e | |
|
83ad663fc0 | |
|
f70f54615f | |
|
018772dbfb | |
|
9baa477990 | |
|
ab01c2cd60 | |
|
c89f101fb2 | |
|
23d7ad1581 | |
|
ec4acd425c | |
|
0614066fdb | |
|
b60279b19a | |
|
95fcacfde0 | |
|
b6a71a515f | |
|
47ee3f1493 | |
|
329710f9e7 | |
|
f7b5e1a83e | |
|
7c37e3841a | |
|
f146bf4ef0 | |
|
d9d90e9e12 | |
|
393b91c415 | |
|
b412e50df0 | |
|
aa06450042 | |
|
121e9f08da | |
|
df19b5b8f8 | |
|
0efc3c67ca | |
|
667257595d | |
|
76a0347fdf | |
|
66738e4300 | |
|
3e4b1ddc83 | |
|
9ec7b4394f | |
|
1191b9b65d | |
|
52b8a4e312 | |
|
b770759167 | |
|
0898c24f45 | |
|
58c64cd53b | |
|
f29f25b9b7 | |
|
4328692039 | |
|
244bf612f9 | |
|
f4ac491490 | |
|
bdf9d97cf9 | |
|
a822e1e4f0 | |
|
cee7e25f3d | |
|
9b8cb52b7b | |
|
538531f503 | |
|
e8f29e593d | |
|
b8d0864c37 | |
|
156af56859 | |
|
8d275ff054 | |
|
0f232fe342 | |
|
9e81be86da | |
|
2398e30048 | |
|
49a06843e9 | |
|
9b9c51d964 | |
|
23887a155e | |
|
0600f54488 | |
|
0ea93faef2 | |
|
48b19f1e70 | |
|
1e38cacfa7 | |
|
40c340c626 | |
|
f9dd82cdcc | |
|
4f84a4e584 | |
|
8aff3f13b2 | |
|
26cf5055a2 | |
|
14e5bf5fbd | |
|
e0c0d3d9e6 | |
|
bd3e924e2b | |
|
e6e70ccaa5 | |
|
67ae8d8773 | |
|
e333ca726b | |
|
6cbd7e67af | |
|
7e5db53e9d | |
|
b0662fb61a | |
|
ac57825c10 | |
|
338018376b | |
|
275a7db0e0 | |
|
f2ba4cab9c | |
|
c7066e1766 | |
|
f4837c807c | |
|
e9776796dd | |
|
934daa5a3b | |
|
a97b66e8f2 | |
|
4582a76081 | |
|
24c96ccaa7 | |
|
272f965eec | |
|
c4f9430e46 | |
|
7cdb2bf6a0 | |
|
23b2b26643 | |
|
7db8376213 | |
|
ee7e8d5b2b | |
|
b2e51ccb9e | |
|
1369d2df31 | |
|
075fce61e5 | |
|
a9286af8fd | |
|
6922059a0b | |
|
0b09db58dd | |
|
a31f675e12 | |
|
e3608b784e | |
|
340872788e | |
|
e796788e22 | |
|
b55548a58b | |
|
5673e2cc2e | |
|
b1d6923ca2 | |
|
d20ce6b0e8 | |
|
b715fdd11a | |
|
4a2568fea3 | |
|
9873bc3d57 | |
|
4e94089128 | |
|
4928804bf4 | |
|
6d25d1b1cb | |
|
b7e36449de | |
|
dae2653975 | |
|
0235d4d096 | |
|
10e1f545bb | |
|
7512463a57 | |
|
757fd9ba55 | |
|
c08079fc2f | |
|
7f1f8d4028 | |
|
e25095f2e7 | |
|
34a287a38e | |
|
f98179971e | |
|
4d221c5220 | |
|
a58234f82c | |
|
5d5f691570 | |
|
3600dbed8c | |
|
dfae92e3c3 | |
|
6342113c2a | |
|
cbdcfa0589 |
|
@ -1,5 +1,5 @@
|
||||||
go.*
|
go.*
|
||||||
|
*.swp
|
||||||
|
*.patch
|
||||||
|
*.mbox
|
||||||
*.pb.go
|
*.pb.go
|
||||||
|
|
||||||
forgeConfig/forgeConfig
|
|
||||||
|
|
18
Makefile
18
Makefile
|
@ -5,7 +5,11 @@
|
||||||
# go install
|
# go install
|
||||||
|
|
||||||
|
|
||||||
all: forgeConfig.pb.go vet
|
all: forgeConfig.pb.go patchset.pb.go goimports vet
|
||||||
|
|
||||||
|
generate: clean
|
||||||
|
autogenpb --proto patchset.proto
|
||||||
|
autogenpb --proto forgeConfig.proto
|
||||||
|
|
||||||
vet:
|
vet:
|
||||||
@GO111MODULE=off go vet
|
@GO111MODULE=off go vet
|
||||||
|
@ -15,15 +19,13 @@ vet:
|
||||||
goimports:
|
goimports:
|
||||||
goimports -w *.go
|
goimports -w *.go
|
||||||
|
|
||||||
redomod:
|
|
||||||
rm -f go.*
|
|
||||||
GO111MODULE= go mod init
|
|
||||||
GO111MODULE= go mod tidy
|
|
||||||
go mod edit -go=1.20
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.pb.go
|
rm -f *.pb.go *.patch
|
||||||
-rm -f go.*
|
-rm -f go.*
|
||||||
|
go-mod-clean --purge
|
||||||
|
|
||||||
forgeConfig.pb.go: forgeConfig.proto
|
forgeConfig.pb.go: forgeConfig.proto
|
||||||
autogenpb --proto forgeConfig.proto
|
autogenpb --proto forgeConfig.proto
|
||||||
|
|
||||||
|
patchset.pb.go: patchset.proto
|
||||||
|
autogenpb --proto patchset.proto
|
||||||
|
|
198
build.go
198
build.go
|
@ -1,14 +1,12 @@
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
// for golang repos, this is an attempt to build the package
|
// for golang repos, this is an attempt to build the package
|
||||||
// there might be some 'standard' ways to implement a build
|
//
|
||||||
// that make sense.
|
|
||||||
|
|
||||||
// Additions to 'go build' that are attempted here:
|
// Additions to 'go build' that are attempted here:
|
||||||
//
|
//
|
||||||
// * detect packages that are plugins
|
// * detect packages that are plugins
|
||||||
// * autogen packages that have .proto protobuf files
|
// * run autogenpb packages that have .proto protobuf files
|
||||||
// * define some 'standard' ldflags
|
// * use some 'standard' ldflags (VERISON, BUILDTIME)
|
||||||
//
|
//
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -19,6 +17,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/lib/gui/shell"
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
@ -31,51 +30,96 @@ func (f *Forge) Install(repo *gitpb.Repo, userFlags []string) error {
|
||||||
return f.doBuild(repo, userFlags, "install")
|
return f.doBuild(repo, userFlags, "install")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// userflags are intended for "-v" and "-x" right now
|
||||||
func (f *Forge) doBuild(repo *gitpb.Repo, userFlags []string, goWhat string) error {
|
func (f *Forge) doBuild(repo *gitpb.Repo, userFlags []string, goWhat string) error {
|
||||||
// always assume all sources have been downloaded
|
if repo == nil {
|
||||||
// todo: detect when in ~/go/src vs go.work mode
|
log.Warn("forge.doBuild repo == nil")
|
||||||
os.Setenv("GO111MODULE", "off")
|
return errors.New("forge.doBuild repo == nil")
|
||||||
defer os.Unsetenv("GO111MODULE")
|
}
|
||||||
|
if f.IsGoWork() {
|
||||||
|
// when building using a go.work file, never use GO111MODULE=off
|
||||||
|
os.Unsetenv("GO111MODULE")
|
||||||
|
} else {
|
||||||
|
// when building from ~/go/src, always use GO111MODULE=off
|
||||||
|
os.Setenv("GO111MODULE", "off")
|
||||||
|
defer os.Unsetenv("GO111MODULE")
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.IsGoWork() {
|
||||||
|
// there must be a valid go.mod file if compiling with go.work
|
||||||
|
if err := repo.ValidGoSum(); err != nil {
|
||||||
|
log.Warn("forge.doBuild() failed. run go-mod-clean here?")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.Exists(".forge") {
|
||||||
|
log.Info("custom build instructions")
|
||||||
|
data, _ := repo.ReadFile(".forge")
|
||||||
|
log.Info(".forge =", string(data))
|
||||||
|
log.Info("todo: do the custom build instructions")
|
||||||
|
basedir, filename := filepath.Split(repo.GetGoPath())
|
||||||
|
log.Info("touching filename", basedir, filename)
|
||||||
|
repo.RunVerbose([]string{"touch", filename})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not GoPrimitive, autogen each dependent git repo
|
||||||
|
if repo.GoDepsLen() != 0 {
|
||||||
|
// build the protobuf files in all protobuf repos
|
||||||
|
all := repo.GoDeps.SortByGoPath()
|
||||||
|
for all.Scan() {
|
||||||
|
t := all.Next()
|
||||||
|
found := f.FindByGoPath(t.GetGoPath())
|
||||||
|
if found.GetRepoType() == "protobuf" {
|
||||||
|
if err := f.runAutogenpb(found); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get the version
|
// get the version
|
||||||
version := repo.GetCurrentBranchVersion()
|
version := repo.GetCurrentBranchVersion()
|
||||||
|
if version == "" {
|
||||||
/*
|
version = "forgeErr"
|
||||||
loop := repo.Tags.SortByRefname()
|
|
||||||
for loop.Scan() {
|
|
||||||
t := loop.Next()
|
|
||||||
log.Info("Build() tag:", t.Refname)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if repo.GoDeps == nil {
|
|
||||||
repo.RedoGoMod()
|
|
||||||
}
|
|
||||||
if repo.GoDeps.Len() == 0 {
|
|
||||||
// eh, potentially runs it twice. don't care right now
|
|
||||||
log.Info("redo go.mod", repo.GetGoPath())
|
|
||||||
repo.RedoGoMod()
|
|
||||||
f.Repos.ConfigSave()
|
|
||||||
}
|
|
||||||
loop1 := repo.GoDeps.SortByGoPath()
|
|
||||||
for loop1.Scan() {
|
|
||||||
t := loop1.Next()
|
|
||||||
found := f.Repos.FindByGoPath(t.GetGoPath())
|
|
||||||
if found.RepoType() == "protobuf" {
|
|
||||||
if err := f.runAutogenpb(found); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if repo.CheckDirty() {
|
if repo.CheckDirty() {
|
||||||
version = version + "-dirty"
|
version = version + "-dirty"
|
||||||
}
|
}
|
||||||
cmd := []string{"go", goWhat, "-v"}
|
cmd := []string{"go"}
|
||||||
|
if repo.GetRepoType() == "plugin" {
|
||||||
|
if goWhat == "install" {
|
||||||
|
log.Info("Can not go install plugins yet. just building to ~/go/lib/")
|
||||||
|
}
|
||||||
|
cmd = append(cmd, "build")
|
||||||
|
} else {
|
||||||
|
cmd = append(cmd, goWhat)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, fname := filepath.Split(repo.FullPath)
|
||||||
|
homeDir, _ := os.UserHomeDir()
|
||||||
|
soname := fname + "." + version + ".so"
|
||||||
|
linkname := fname + ".so"
|
||||||
|
sopath := filepath.Join(homeDir, "go/lib/go-gui")
|
||||||
|
// if this is a plugin, use buildmode=plugin
|
||||||
|
if repo.GetRepoType() == "plugin" {
|
||||||
|
if goWhat == "install" {
|
||||||
|
fullname := filepath.Join(sopath, soname)
|
||||||
|
cmd = append(cmd, "-buildmode=plugin", "-o", fullname)
|
||||||
|
} else {
|
||||||
|
cmd = append(cmd, "-buildmode=plugin", "-o", soname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, flag := range userFlags {
|
||||||
|
cmd = append(cmd, flag)
|
||||||
|
}
|
||||||
|
|
||||||
// set standard ldflag options
|
// set standard ldflag options
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
datestamp := now.UTC().Format("2006/01/02_1504_UTC")
|
// datestamp := now.UTC().Format("2006/01/02_1504_UTC")
|
||||||
|
datestamp := now.UTC().Format("2006-01-02_15:04:05_UTC") // 2006-01-02 15:04:05 UTC
|
||||||
// log.Info("datestamp =", datestamp)
|
// log.Info("datestamp =", datestamp)
|
||||||
// add some standard golang flags
|
// add some standard golang flags
|
||||||
ldflags := "-X main.VERSION=" + version + " "
|
ldflags := "-X main.VERSION=" + version + " "
|
||||||
|
@ -83,21 +127,47 @@ func (f *Forge) doBuild(repo *gitpb.Repo, userFlags []string, goWhat string) err
|
||||||
ldflags += "-X main.GUIVERSION=" + version + "" // todo: git this from the filesystem
|
ldflags += "-X main.GUIVERSION=" + version + "" // todo: git this from the filesystem
|
||||||
cmd = append(cmd, "-ldflags", ldflags)
|
cmd = append(cmd, "-ldflags", ldflags)
|
||||||
|
|
||||||
// add any flags from the command line
|
testenv := os.Getenv("GO111MODULE")
|
||||||
// this might not actually work
|
if testenv == "off" {
|
||||||
// todo: test this
|
log.Info("GO111MODULE=off", "f.goWork =", f.IsGoWork(), "f.gosrc =", f.GetGoSrc())
|
||||||
for _, flag := range userFlags {
|
|
||||||
cmd = append(cmd, "-ldflags", "-X "+flag)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("running:", cmd)
|
|
||||||
if r := repo.RunRealtime(cmd); r.Error == nil {
|
|
||||||
// log.Warn("go build worked")
|
|
||||||
return nil
|
|
||||||
} else {
|
} else {
|
||||||
log.Warn("go build failed", cmd)
|
log.Info("GO111MODULE=", testenv, "f.goWork =", f.IsGoWork(), "f.gosrc =", f.GetGoSrc())
|
||||||
return errors.New("go build failed: " + fmt.Sprint(r.Error))
|
|
||||||
}
|
}
|
||||||
|
log.Info("running:", repo.FullPath)
|
||||||
|
log.Info("running:", cmd)
|
||||||
|
result := repo.RunRealtime(cmd)
|
||||||
|
if result.Exit != 0 {
|
||||||
|
// build failed
|
||||||
|
log.DaemonMode(true)
|
||||||
|
log.Info(strings.Join(result.Stdout, "\n"))
|
||||||
|
log.Info(strings.Join(result.Stderr, "\n"))
|
||||||
|
log.Info("result.Error =", result.Error)
|
||||||
|
log.Info("result.Exit =", result.Exit)
|
||||||
|
log.DaemonMode(false)
|
||||||
|
log.Warn("go build failed", cmd)
|
||||||
|
/*
|
||||||
|
pwd, _ := os.Getwd()
|
||||||
|
log.Warn("go build pwd", pwd)
|
||||||
|
res2 := shell.RunEcho(cmd)
|
||||||
|
if res2.Exit == 0 {
|
||||||
|
log.Info("again failed", res2.Exit)
|
||||||
|
log.Info("again failed cmd", strings.Join(cmd, "a"))
|
||||||
|
log.Info("again failed", strings.Join(res2.Stdout, "\n"))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return errors.New("go " + goWhat + " failed: " + fmt.Sprint(result.Error))
|
||||||
|
}
|
||||||
|
// make symlinks
|
||||||
|
if repo.GetRepoType() == "plugin" {
|
||||||
|
cmd := []string{"ln", "-sf", soname, linkname}
|
||||||
|
if goWhat == "install" {
|
||||||
|
shell.PathRun(sopath, cmd)
|
||||||
|
} else {
|
||||||
|
repo.Run(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info(strings.Join(result.Stdout, "\n"))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Forge) runAutogenpb(repo *gitpb.Repo) error {
|
func (f *Forge) runAutogenpb(repo *gitpb.Repo) error {
|
||||||
|
@ -131,3 +201,29 @@ func (f *Forge) runAutogenpb(repo *gitpb.Repo) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sortcut to find
|
||||||
|
func (f *Forge) FindWorkingDirRepo() *gitpb.Repo {
|
||||||
|
pwd, _ := os.Getwd()
|
||||||
|
basedir := strings.TrimPrefix(pwd, f.GetGoSrc())
|
||||||
|
basedir = strings.Trim(basedir, "/")
|
||||||
|
return f.FindByGoPath(basedir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Never do this. this is stupid. fix your repo
|
||||||
|
// Never try to version & release broken repos
|
||||||
|
// leave this code here as a reminder to never attempt this
|
||||||
|
func (f *Forge) forgeIgnoreGoMod(repo *gitpb.Repo) bool {
|
||||||
|
if !repo.Exists(".forge") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
log.Info("custom build instructions")
|
||||||
|
data, _ := repo.ReadFile(".forge")
|
||||||
|
log.Info(".forge =", string(data))
|
||||||
|
for _, line := range strings.Split(string(data), "\n") {
|
||||||
|
if strings.Contains(line, "forge:ignore:gomod") { // this is stupid
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This will recreate your go.sum and go.mod files
|
||||||
|
var cleanVerbose bool = false
|
||||||
|
|
||||||
|
// checks to see if every 'master' git branch version
|
||||||
|
// matches the go.sum file
|
||||||
|
func (f *Forge) CleanGoDepsCheckOk(check *gitpb.Repo) error {
|
||||||
|
var err error = nil
|
||||||
|
var fixes [][]string
|
||||||
|
log.Printf("current repo %s go dependancy count: %d", check.GetGoPath(), check.GoDepsLen())
|
||||||
|
if check.GoDeps == nil {
|
||||||
|
return errors.New("check.GoDeps == nil")
|
||||||
|
}
|
||||||
|
all := check.GoDeps.SortByGoPath()
|
||||||
|
for all.Scan() {
|
||||||
|
depRepo := all.Next()
|
||||||
|
found := f.FindByGoPath(depRepo.GetGoPath())
|
||||||
|
if found == nil {
|
||||||
|
if f.CheckOverride(depRepo.GetGoPath()) {
|
||||||
|
// skip this gopath because it's probably broken forever
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Info("not found:", depRepo.GetGoPath())
|
||||||
|
err = errors.New("not found: " + depRepo.GetGoPath())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// log.Info("found dep", depRepo.GetGoPath())
|
||||||
|
if depRepo.GetVersion() != found.GetMasterVersion() {
|
||||||
|
check := f.FindByGoPath(depRepo.GetGoPath())
|
||||||
|
var ends string
|
||||||
|
if check.CheckDirty() {
|
||||||
|
ends = "(dirty) "
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Config.IsReadOnly(check.GetGoPath()) {
|
||||||
|
ends += "(ignoring read-only) "
|
||||||
|
if cleanVerbose {
|
||||||
|
log.Printf("%-48s ok error .%s. vs .%s. %s", depRepo.GetGoPath(),
|
||||||
|
depRepo.GetVersion(), found.GetMasterVersion(), ends)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if f.CheckOverride(depRepo.GetGoPath()) {
|
||||||
|
ends += "(override) "
|
||||||
|
if cleanVerbose {
|
||||||
|
log.Printf("%-48s ok error .%s. vs .%s. %s", depRepo.GetGoPath(),
|
||||||
|
depRepo.GetVersion(), found.GetMasterVersion(), ends)
|
||||||
|
// skip this gopath because it's probably broken forever
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
log.Printf("%-48s error %10s vs %10s %s", depRepo.GetGoPath(),
|
||||||
|
depRepo.GetVersion(), found.GetMasterVersion(), ends)
|
||||||
|
errs := fmt.Sprintf("%s error %s vs %s %s", depRepo.GetGoPath(),
|
||||||
|
depRepo.GetVersion(), found.GetMasterVersion(), ends)
|
||||||
|
if ok, _ := ValidGoVersion(found.GetMasterVersion()); ok {
|
||||||
|
// can't go get invalid version numbers
|
||||||
|
cmd := []string{"go", "get", depRepo.GetGoPath() + "@" + found.GetMasterVersion()}
|
||||||
|
fixes = append(fixes, cmd)
|
||||||
|
}
|
||||||
|
err = errors.New(errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, cmd := range fixes {
|
||||||
|
log.Info("try cmd", i, cmd)
|
||||||
|
check.RunRealtime(cmd)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) TrimGoSum(check *gitpb.Repo) error {
|
||||||
|
var stuff map[string]string
|
||||||
|
stuff = make(map[string]string)
|
||||||
|
|
||||||
|
var modver map[string]string
|
||||||
|
modver = make(map[string]string)
|
||||||
|
|
||||||
|
var good map[string]bool
|
||||||
|
good = make(map[string]bool)
|
||||||
|
|
||||||
|
if check == nil {
|
||||||
|
log.Info("boo, check == nil")
|
||||||
|
return errors.New("*repo == nil")
|
||||||
|
}
|
||||||
|
filename := filepath.Join(filepath.Join(check.FullPath, "go.sum"))
|
||||||
|
data, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(string(data), "\n") {
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) < 3 {
|
||||||
|
log.Info("WIERD OR BAD:", line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
gopath := parts[0]
|
||||||
|
version := parts[1]
|
||||||
|
hash := parts[2]
|
||||||
|
|
||||||
|
if strings.HasSuffix(version, "/go.mod") {
|
||||||
|
if _, ok := stuff[gopath]; ok {
|
||||||
|
if cleanVerbose {
|
||||||
|
log.Info("MATCHED: gopath:", gopath, "version:", version)
|
||||||
|
}
|
||||||
|
modver[gopath] = version + " " + hash
|
||||||
|
good[gopath] = true
|
||||||
|
} else {
|
||||||
|
if cleanVerbose {
|
||||||
|
log.Info("GARBAGE: gopath:", gopath, "version:", version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if cleanVerbose {
|
||||||
|
log.Info("GOOD : gopath:", gopath, "version:", version)
|
||||||
|
}
|
||||||
|
stuff[gopath] = version + " " + hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, 0, len(stuff))
|
||||||
|
for k := range stuff {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewrite the go.sum file
|
||||||
|
newfilename := filepath.Join(filepath.Join(check.FullPath, "go.sum"))
|
||||||
|
newf, err := os.OpenFile(newfilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer newf.Close()
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, gopath := range keys {
|
||||||
|
if good[gopath] {
|
||||||
|
fmt.Fprintf(newf, "%s %s\n", gopath, stuff[gopath])
|
||||||
|
fmt.Fprintf(newf, "%s %s\n", gopath, modver[gopath])
|
||||||
|
check := f.FindByGoPath(gopath)
|
||||||
|
if check == nil {
|
||||||
|
log.Info("gopath does not really exist:", gopath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fmt.Fprintln(newf, "test")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// will not violate filesystem namespace
|
||||||
|
// always returns the path or a parent path
|
||||||
|
//
|
||||||
|
// attemps to exec git clone based off of a golang path
|
||||||
|
// will transferse parent directories in case the path
|
||||||
|
// is a child of a git repo
|
||||||
|
//
|
||||||
|
// returns *gitpb.Repo if already cloned
|
||||||
|
//
|
||||||
|
// example gopath = go.wit.com/apps/go-clone
|
||||||
|
// or "go.googlesource.com/go/src/cmd/internal/pkgpath/" returns repo for "go.googlesource.com/go"
|
||||||
|
func (f *Forge) GoClone(gopath string) (*gitpb.Repo, error) {
|
||||||
|
|
||||||
|
// will match /root/go/src/go.wit.com/apps/go-clone/something/inside
|
||||||
|
// and return the *gitpb.Repo for "go.wit.com/apps/go-clone"
|
||||||
|
fullpath := filepath.Join(f.goSrc, gopath)
|
||||||
|
if pb := f.FindAnyPath(fullpath); pb != nil {
|
||||||
|
// repo already exists
|
||||||
|
return pb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// try a direct git clone against the gopath
|
||||||
|
// if this doesn't work, probably the package is abandoned and you
|
||||||
|
// are probably using something old, broken, or wrong
|
||||||
|
if repo, err := f.urlClone(gopath, "https://"+gopath); repo != nil {
|
||||||
|
return repo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for parent git repos
|
||||||
|
if repo, err := f.goClonePop(gopath); repo != nil {
|
||||||
|
return repo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// query the golang package system for the last known location
|
||||||
|
// NOTE: take time to thank the go developers and google for designing this wonderful system
|
||||||
|
if pkgurl, err := runGoList(gopath); err == nil {
|
||||||
|
if repo, err := f.urlClone(gopath, pkgurl); repo != nil {
|
||||||
|
return repo, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: emit some sort of warning?
|
||||||
|
|
||||||
|
// hacks
|
||||||
|
if repo, err := f.clonePathHack(gopath); repo != nil {
|
||||||
|
return repo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("can not find git sources for gopath " + gopath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is obvious a experiemental hack
|
||||||
|
// todo: make a config file for this?
|
||||||
|
func overridePath(gopath string) string {
|
||||||
|
switch gopath {
|
||||||
|
case "golang.org/x/crypto":
|
||||||
|
return "https://" + "go.googlesource.com/crypto"
|
||||||
|
case "golang.org/x/mod":
|
||||||
|
return "https://" + "go.googlesource.com/mod"
|
||||||
|
case "golang.org/x/net":
|
||||||
|
return "https://" + "go.googlesource.com/net"
|
||||||
|
case "golang.org/x/sys":
|
||||||
|
return "https://" + "go.googlesource.com/sys"
|
||||||
|
case "golang.org/x/sync":
|
||||||
|
return "https://" + "go.googlesource.com/sync"
|
||||||
|
case "golang.org/x/term":
|
||||||
|
return "https://" + "go.googlesource.com/term"
|
||||||
|
case "golang.org/x/text":
|
||||||
|
return "https://" + "go.googlesource.com/text"
|
||||||
|
case "golang.org/x/tools":
|
||||||
|
return "https://" + "go.googlesource.com/tools"
|
||||||
|
case "golang.org/x/xerrors":
|
||||||
|
return "https://" + "go.googlesource.com/xerrors"
|
||||||
|
case "google.golang.org/protobuf":
|
||||||
|
return "https://" + "go.googlesource.com/protobuf"
|
||||||
|
case "google.golang.org/genproto":
|
||||||
|
return "https://" + "go.googlesource.com/genproto"
|
||||||
|
case "google.golang.org/api":
|
||||||
|
return "https://" + "go.googlesource.com/api"
|
||||||
|
case "google.golang.org/grpc":
|
||||||
|
return "https://" + "go.googlesource.com/grpc"
|
||||||
|
case "google.golang.org/appengine":
|
||||||
|
return "https://" + "go.googlesource.com/appengine"
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(gopath, "github.com/go-gl/glfw") {
|
||||||
|
return "https://github.com/go-gl/glfw"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make some config file for things like this
|
||||||
|
// can be used to work around temporary problems
|
||||||
|
func (f *Forge) clonePathHack(gopath string) (*gitpb.Repo, error) {
|
||||||
|
// newdir = helloworld
|
||||||
|
// basedir = /home/jcarr/go/src/go.wit.com/apps
|
||||||
|
// giturl = https://gitea.wit.com/gui/helloworld
|
||||||
|
|
||||||
|
url := overridePath(gopath)
|
||||||
|
if url == "" {
|
||||||
|
return nil, errors.New("no gopath override here")
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.urlClone(gopath, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for: github.com/gdamore/tcell/v2
|
||||||
|
// tries git clone github.com/gdamore/tcell/v2
|
||||||
|
// then git clone github.com/gdamore/tcell
|
||||||
|
// then git clone github.com/gdamore , etc
|
||||||
|
func (f *Forge) goClonePop(gopath string) (*gitpb.Repo, error) {
|
||||||
|
log.Info("forge.goClonePop() trying", gopath)
|
||||||
|
if gopath == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
newpath, newdir := filepath.Split(gopath)
|
||||||
|
if newdir == "" {
|
||||||
|
// nothing to chop
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if repo, _ := f.urlClone(newpath, "https://"+newpath); repo != nil {
|
||||||
|
return repo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo, err := f.goClonePop(newpath); repo != nil {
|
||||||
|
return repo, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("forge.goClonePop() did not work %s", gopath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone a URL directly, also try cloning if 'go-import' is sent
|
||||||
|
// newdir = helloworld
|
||||||
|
// basedir = /home/jcarr/go/src/go.wit.com/apps
|
||||||
|
// giturl = https://gitea.wit.com/gui/helloworld
|
||||||
|
func (f *Forge) urlClone(gopath, giturl string) (*gitpb.Repo, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
fullpath := filepath.Join(f.goSrc, gopath)
|
||||||
|
basedir, newdir := filepath.Split(fullpath)
|
||||||
|
|
||||||
|
// clone the URL directly
|
||||||
|
if err = RunGitClone(newdir, basedir, giturl); err == nil {
|
||||||
|
return f.NewGoRepo(gopath, giturl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if the URL has go-import for a new URL
|
||||||
|
if giturl, err = findGoImport(giturl); err == nil {
|
||||||
|
if err = RunGitClone(newdir, basedir, giturl); err == nil {
|
||||||
|
return f.NewGoRepo(gopath, giturl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// log.Info("git clone from 'go-import' info failed", url)
|
||||||
|
// yes, this misses the first error
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the server for the current go path to git url mapping
|
||||||
|
// for example:
|
||||||
|
// This will check go.wit.com for "go.wit.com/apps/go-clone"
|
||||||
|
// and return where the URL to do git clone should be
|
||||||
|
func findGoImport(url string) (string, error) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := string(bodyBytes)
|
||||||
|
parts := strings.Split(tmp, "go-import")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return "", errors.New("missing go-import")
|
||||||
|
}
|
||||||
|
// this is terrible, it doesn't even look for 'content='
|
||||||
|
// but again, this is just a hack for my own code to be
|
||||||
|
// usuable after the removal in go v1.22 of the go get clone behavior that was in v1.21
|
||||||
|
parts = strings.Split(parts[1], "\"")
|
||||||
|
var newurl string
|
||||||
|
for {
|
||||||
|
if len(parts) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tmp := strings.TrimSpace(parts[0])
|
||||||
|
fields := strings.Split(tmp, " ")
|
||||||
|
// log.Info("FIELDS:", fields)
|
||||||
|
if len(fields) == 3 {
|
||||||
|
newurl = fields[2]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
parts = parts[1:]
|
||||||
|
}
|
||||||
|
if newurl == "" {
|
||||||
|
return "", errors.New("missing git content string")
|
||||||
|
}
|
||||||
|
|
||||||
|
return newurl, nil
|
||||||
|
}
|
177
config.go
177
config.go
|
@ -1,3 +1,5 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
// functions to import and export the protobuf
|
// functions to import and export the protobuf
|
||||||
|
@ -5,115 +7,103 @@ package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// write to ~/.config/forge/ unless ENV{FORGE_HOME} is set
|
|
||||||
func (f *Forge) ConfigSave() error {
|
func (f *Forge) ConfigSave() error {
|
||||||
// f.Config.Lock()
|
var err error
|
||||||
// defer f.Config.UnLock()
|
// backup the current config files
|
||||||
if os.Getenv("FORGE_HOME") == "" {
|
if e := backupConfig(); e != nil {
|
||||||
homeDir, _ := os.UserHomeDir()
|
log.Info("forge.BackupConfig() error", e)
|
||||||
fullpath := filepath.Join(homeDir, ".config/forge")
|
err = e
|
||||||
os.Setenv("FORGE_HOME", fullpath)
|
// continue here? notsure. could be bad either way
|
||||||
|
// out of disk space?
|
||||||
}
|
}
|
||||||
// try to backup the current cluster config files
|
if f.Config != nil {
|
||||||
if err := backupConfig(); err != nil {
|
if e := f.Config.ConfigSave(); e != nil {
|
||||||
return err
|
log.Info("forge.Config.ConfigSave() error", e)
|
||||||
|
err = e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data, err := f.Config.Marshal()
|
if f.Repos != nil {
|
||||||
|
if f.HasFullScan() {
|
||||||
|
f.Repos.HasFullScan = true
|
||||||
|
t := time.Now()
|
||||||
|
f.Repos.FullScan = timestamppb.New(t)
|
||||||
|
}
|
||||||
|
if e := f.Repos.ConfigSave(); e != nil {
|
||||||
|
log.Info("forge.Repos.ConfigSave() error", e)
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// write to ~/.config/forge/ unless ENV{FORGE_CONFIG} is set
|
||||||
|
func (f *ForgeConfigs) ConfigSave() error {
|
||||||
|
data, err := f.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("proto.Marshal() failed len", len(data), err)
|
log.Info("proto.Marshal() failed len", len(data), err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("forgepb.ConfigSave() proto.Marshal() worked len", len(data))
|
// log.Info("forgepb.ConfigSave() proto.Marshal() worked len", len(data))
|
||||||
configWrite("forge.pb", data)
|
|
||||||
|
|
||||||
s := f.Config.FormatTEXT()
|
s := f.FormatTEXT()
|
||||||
configWrite("forge.text", []byte(s))
|
configWrite("forge.text", []byte(s))
|
||||||
|
|
||||||
s = f.Config.FormatJSON()
|
s = f.FormatJSON()
|
||||||
configWrite("forge.json", []byte(s))
|
configWrite("forge.json", []byte(s))
|
||||||
|
|
||||||
if f.Repos != nil {
|
|
||||||
f.Repos.ConfigSave()
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// load the ~/.config/forge/ files
|
// load the ~/.config/forge/ files
|
||||||
func (c *ForgeConfigs) ConfigLoad() error {
|
func (c *ForgeConfigs) ConfigLoad() error {
|
||||||
if os.Getenv("FORGE_HOME") == "" {
|
if os.Getenv("FORGE_CONFIG") == "" {
|
||||||
homeDir, _ := os.UserHomeDir()
|
homeDir, _ := os.UserHomeDir()
|
||||||
fullpath := filepath.Join(homeDir, ".config/forge")
|
fullpath := filepath.Join(homeDir, ".config/forge")
|
||||||
os.Setenv("FORGE_HOME", fullpath)
|
os.Setenv("FORGE_CONFIG", fullpath)
|
||||||
}
|
}
|
||||||
var data []byte
|
// var data []byte
|
||||||
var err error
|
// var err error
|
||||||
if c == nil {
|
if c == nil {
|
||||||
// can't safely do c = new(ForgeConfig) if c is in a struct from the caller. notsure why
|
// can't safely do c = new(ForgeConfig) if c is in a struct from the caller. notsure why
|
||||||
|
// TODO: recheck this. it might work now? It's probably still a bad idea(?)
|
||||||
return errors.New("It's not safe to run ConfigLoad() on a nil")
|
return errors.New("It's not safe to run ConfigLoad() on a nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if data, err = loadFile("forge.pb"); err != nil {
|
if err := c.loadText(); 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 = c.Unmarshal(data); err != nil {
|
|
||||||
log.Warn("broken forge.pb config file")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("found", len(c.ForgeConfigs), "entries in ~/.config/forge")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// forge.db doesn't exist. try forge.text
|
|
||||||
// this lets the user hand edit the config
|
|
||||||
if data, err = loadFile("forge.text"); err != nil {
|
|
||||||
// something went wrong loading the file
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if data != nil {
|
|
||||||
// this means the forge.text file exists and was read
|
|
||||||
if len(data) == 0 {
|
|
||||||
// todo: error out if the file is empty?
|
|
||||||
}
|
|
||||||
if err = c.UnmarshalTEXT(data); err != nil {
|
|
||||||
log.Warn("broken forge.text config file")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("found", len(c.ForgeConfigs), "entries in ~/.config/forge")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// forge.text doesn't exist. try forge.json
|
// forge.text doesn't exist. try forge.json
|
||||||
// this lets the user hand edit the config
|
// this lets the user hand edit the JSON config
|
||||||
if data, err = loadFile("forge.json"); err != nil {
|
// probably just deprecate this
|
||||||
// something went wrong loading the file
|
if data, err := loadFile("forge.json"); err != nil {
|
||||||
return err
|
if data != nil {
|
||||||
|
// this means the forge.json file exists and was read
|
||||||
|
if len(data) != 0 {
|
||||||
|
if err = c.UnmarshalJSON(data); err == nil {
|
||||||
|
log.Info("forge.ConfigLoad()", len(c.ForgeConfigs), "entries in ~/.config/forge")
|
||||||
|
// forge.text file was broken. save on load right away
|
||||||
|
log.Info("attempting forge.ConfigSave()")
|
||||||
|
c.ConfigSave()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if data != nil {
|
cpath := filepath.Join(os.Getenv("FORGE_CONFIG"), ".")
|
||||||
// this means the forge.text file exists and was read
|
if _, err := os.Stat(cpath); err == nil {
|
||||||
if len(data) == 0 {
|
log.Info("Something has gone wrong. Your", os.Getenv("FORGE_CONFIG"), "directory exists")
|
||||||
// todo: error out if the file is empty?
|
log.Info("However, the config files could not be loaded")
|
||||||
}
|
|
||||||
if err = c.UnmarshalJSON(data); err != nil {
|
|
||||||
log.Warn("broken forge.json config file")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("found", len(c.ForgeConfigs), "entries in ~/.config/forge")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// first time user. make a template config file
|
// first time user. make a template config file
|
||||||
|
@ -122,8 +112,29 @@ func (c *ForgeConfigs) ConfigLoad() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ForgeConfigs) loadText() error {
|
||||||
|
// this lets the user hand edit the config
|
||||||
|
data, err := loadFile("forge.text")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if data == nil {
|
||||||
|
return fmt.Errorf("forge.text data was nil")
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
return fmt.Errorf("forge.text was empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to marshal forge.text
|
||||||
|
if err := c.UnmarshalTEXT(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Log(INFO, "forge.ConfigLoad()", len(c.ForgeConfigs), "entries in ~/.config/forge")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func loadFile(filename string) ([]byte, error) {
|
func loadFile(filename string) ([]byte, error) {
|
||||||
fullname := filepath.Join(os.Getenv("FORGE_HOME"), filename)
|
fullname := filepath.Join(os.Getenv("FORGE_CONFIG"), filename)
|
||||||
data, err := os.ReadFile(fullname)
|
data, err := os.ReadFile(fullname)
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
// if file does not exist, just return nil. this
|
// if file does not exist, just return nil. this
|
||||||
|
@ -139,9 +150,9 @@ func loadFile(filename string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func configWrite(filename string, data []byte) error {
|
func configWrite(filename string, data []byte) error {
|
||||||
fullname := filepath.Join(os.Getenv("FORGE_HOME"), filename)
|
fullname := filepath.Join(os.Getenv("FORGE_CONFIG"), filename)
|
||||||
|
|
||||||
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE, 0666)
|
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
defer cfgfile.Close()
|
defer cfgfile.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("open config file :", err)
|
log.Warn("open config file :", err)
|
||||||
|
@ -149,13 +160,21 @@ func configWrite(filename string, data []byte) error {
|
||||||
}
|
}
|
||||||
if filename == "forge.text" {
|
if filename == "forge.text" {
|
||||||
// add header
|
// 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("\n"))
|
||||||
cfgfile.Write([]byte("# this file is intended to be used to customize settings on what\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([]byte("# git repos you have write access to. That is, where you can run 'git push'\n"))
|
||||||
|
cfgfile.Write([]byte("\n"))
|
||||||
|
}
|
||||||
|
if filename == "forge.json" {
|
||||||
|
// add header
|
||||||
|
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([]byte("\n"))
|
||||||
|
cfgfile.Write([]byte("# this file is parsed only if forge.text is missing\n"))
|
||||||
|
cfgfile.Write([]byte("# also, these comment lines don't work in json files and have to be removed for Marshal() to work\n"))
|
||||||
|
cfgfile.Write([]byte("# probably, JSON syntax for this is just going to be deprecated for the TEXT syntax\n"))
|
||||||
|
cfgfile.Write([]byte("\n"))
|
||||||
}
|
}
|
||||||
cfgfile.Write(data)
|
cfgfile.Write(data)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
@ -15,12 +14,8 @@ import (
|
||||||
|
|
||||||
func backupConfig() error {
|
func backupConfig() error {
|
||||||
// make a new dir to backup the files
|
// make a new dir to backup the files
|
||||||
now := time.Now()
|
srcDir := filepath.Join(os.Getenv("FORGE_CONFIG"))
|
||||||
// timestamp := now.Format("2022.07.18.190545") // 50yr shout out to K&R
|
destDir := filepath.Join(os.Getenv("FORGE_CONFIG"), "backup")
|
||||||
timestamp := now.Format("2006.01.02.150405") // bummer. other date doesn't work?
|
|
||||||
srcDir := filepath.Join(os.Getenv("FORGE_HOME"))
|
|
||||||
destDir := filepath.Join(os.Getenv("FORGE_HOME"), timestamp)
|
|
||||||
|
|
||||||
return backupFiles(srcDir, destDir)
|
return backupFiles(srcDir, destDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +39,7 @@ func backupFiles(srcDir string, destDir string) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("backing up file", entry.Name())
|
// log.Println("backing up file", entry.Name())
|
||||||
srcPath := filepath.Join(srcDir, entry.Name())
|
srcPath := filepath.Join(srcDir, entry.Name())
|
||||||
destPath := filepath.Join(destDir, entry.Name())
|
destPath := filepath.Join(destDir, entry.Name())
|
||||||
|
|
||||||
|
@ -64,6 +59,9 @@ func copyFile(src, dest string) error {
|
||||||
}
|
}
|
||||||
defer srcFile.Close()
|
defer srcFile.Close()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
timestamp := now.Format("2006.01.02.150405") // bummer. other date doesn't work?
|
||||||
|
dest = dest + timestamp
|
||||||
destFile, err := os.Create(dest)
|
destFile, err := os.Create(dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -2,8 +2,6 @@ package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (all *ForgeConfigs) sampleConfig() {
|
func (all *ForgeConfigs) sampleConfig() {
|
||||||
|
@ -11,53 +9,7 @@ func (all *ForgeConfigs) sampleConfig() {
|
||||||
new1.GoPath = "go.wit.com"
|
new1.GoPath = "go.wit.com"
|
||||||
new1.Writable = true
|
new1.Writable = true
|
||||||
new1.Directory = true
|
new1.Directory = true
|
||||||
if all.Append(new1) {
|
all.Append(new1)
|
||||||
log.Info("added", new1.GoPath, "ok")
|
|
||||||
} else {
|
|
||||||
log.Info("added", new1.GoPath, "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
new1 = new(ForgeConfig)
|
|
||||||
new1.GoPath = "go.wit.com/apps/zookeeper"
|
|
||||||
new1.DebName = "zookeeper-go"
|
|
||||||
if all.Append(new1) {
|
|
||||||
log.Info("added", new1.GoPath, "ok")
|
|
||||||
} else {
|
|
||||||
log.Info("added", new1.GoPath, "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
new1 = new(ForgeConfig)
|
|
||||||
new1.GoPath = "go.wit.com/apps/wit-package"
|
|
||||||
new1.Private = true
|
|
||||||
if all.Append(new1) {
|
|
||||||
log.Info("added", new1.GoPath, "ok")
|
|
||||||
} else {
|
|
||||||
log.Info("added", new1.GoPath, "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
new1 = new(ForgeConfig)
|
|
||||||
new1.GoPath = "go.wit.com/apps/networkQuality"
|
|
||||||
new1.DebName = "networkquality"
|
|
||||||
new1.ReadOnly = true
|
|
||||||
if all.Append(new1) {
|
|
||||||
log.Info("added", new1.GoPath, "ok")
|
|
||||||
} else {
|
|
||||||
log.Info("added", new1.GoPath, "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
new2 := new(ForgeConfig)
|
|
||||||
new2.GoPath = "go.wit.com/apps/go-clone"
|
|
||||||
if all.Append(new2) {
|
|
||||||
log.Info("added", new2.GoPath, "ok")
|
|
||||||
} else {
|
|
||||||
log.Info("added", new2.GoPath, "failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if all.Append(new2) {
|
|
||||||
log.Info("added", new2.GoPath, "ok (this is bad)")
|
|
||||||
} else {
|
|
||||||
log.Info("added", new2.GoPath, "failed (but ok)")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("first time user. adding an example config file with", len(all.ForgeConfigs), "repos")
|
fmt.Println("first time user. adding an example config file with", len(all.ForgeConfigs), "repos")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
/*
|
/*
|
||||||
check settings for a particular gopath
|
lookup settings for a particular *gitpb.Repo or gopath string
|
||||||
this provides checks for:
|
|
||||||
|
|
||||||
IsReadOnly() // user can't push commits
|
user settings are configured in ~/.config/forge/forge.text
|
||||||
IsPrivate() // repo can't be published to the pkg.go.dev system
|
|
||||||
DebName() // for 'zookeeper' returns 'zookeeper-go'
|
// searchs by string
|
||||||
|
Configs.IsReadOnly(path) // user can't push commits
|
||||||
|
Configs.IsWritable(path) // the opposite, but maybe different so I put both here
|
||||||
|
|
||||||
|
IsPrivate(repo) // repo can't be published to the pkg.go.dev system
|
||||||
|
DebName() // for 'zookeeper' returns 'zookeeper-go'
|
||||||
|
|
||||||
|
This code is practical, not perfect
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -14,12 +20,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
func (f *Forge) SortByGoPath() *ForgeConfigIterator {
|
|
||||||
return f.Config.SortByPath()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func (all *ForgeConfigs) UpdateGoPath(name string, gopath string) bool {
|
func (all *ForgeConfigs) UpdateGoPath(name string, gopath string) bool {
|
||||||
oldr := all.DeleteByGoPath(name)
|
oldr := all.DeleteByGoPath(name)
|
||||||
|
@ -36,12 +36,12 @@ func (all *ForgeConfigs) 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 (f *Forge) IsReadOnly(gopath string) bool {
|
func (fc *ForgeConfigs) IsReadOnly(gopath string) bool {
|
||||||
var match *ForgeConfig
|
var match *ForgeConfig
|
||||||
|
|
||||||
loop := f.Config.SortByGoPath() // get the list of repos
|
all := fc.SortByGoPath() // get the list of repos
|
||||||
for loop.Scan() {
|
for all.Scan() {
|
||||||
r := loop.Next()
|
r := all.Next()
|
||||||
if r.GoPath == gopath {
|
if r.GoPath == gopath {
|
||||||
// exact gopath match
|
// exact gopath match
|
||||||
if r.Writable {
|
if r.Writable {
|
||||||
|
@ -96,13 +96,13 @@ func (f *Forge) 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 (f *Forge) DebName(gopath string) string {
|
func (fc *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 := f.Config.SortByGoPath()
|
all := fc.SortByGoPath()
|
||||||
for loop.Scan() {
|
for all.Scan() {
|
||||||
r := loop.Next()
|
r := all.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 != "" {
|
||||||
|
@ -123,15 +123,15 @@ func (f *Forge) 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 (f *Forge) IsPrivate(thing string) bool {
|
func (fc *ForgeConfigs) IsPrivate(thing string) bool {
|
||||||
var match *ForgeConfig
|
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 := f.Config.SortByGoPath() // get the list of repos
|
all := fc.SortByGoPath() // get the list of repos
|
||||||
for loop.Scan() {
|
for all.Scan() {
|
||||||
r := loop.Next()
|
r := all.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
|
||||||
|
@ -167,12 +167,12 @@ func (f *Forge) 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 (f *Forge) IsFavorite(thing string) bool {
|
func (fc *ForgeConfigs) IsFavorite(thing string) bool {
|
||||||
var match *ForgeConfig
|
var match *ForgeConfig
|
||||||
|
|
||||||
loop := f.Config.SortByGoPath() // get the list of repos
|
all := fc.SortByGoPath() // get the list of repos
|
||||||
for loop.Scan() {
|
for all.Scan() {
|
||||||
r := loop.Next()
|
r := all.Next()
|
||||||
if r.GoPath == thing {
|
if r.GoPath == thing {
|
||||||
if r.Favorite {
|
if r.Favorite {
|
||||||
return true
|
return true
|
||||||
|
@ -196,3 +196,129 @@ func (f *Forge) IsFavorite(thing string) bool {
|
||||||
|
|
||||||
return match.Favorite
|
return match.Favorite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsWritable() checks your .config/forge/ settings
|
||||||
|
// looks for an exact match, then
|
||||||
|
// looks for a directory match
|
||||||
|
func (fc *ForgeConfigs) IsWritable(thing string) bool {
|
||||||
|
var match *ForgeConfig
|
||||||
|
|
||||||
|
all := fc.SortByGoPath() // get the list of repos
|
||||||
|
for all.Scan() {
|
||||||
|
r := all.Next()
|
||||||
|
if r.GoPath == thing {
|
||||||
|
if r.Writable {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base := filepath.Base(r.GoPath)
|
||||||
|
if base == thing {
|
||||||
|
if r.Writable {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.Directory {
|
||||||
|
if strings.HasPrefix(thing, r.GoPath) {
|
||||||
|
match = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return match.Writable
|
||||||
|
}
|
||||||
|
|
||||||
|
// allows custom user branch names in the forge config
|
||||||
|
func (fc *ForgeConfigs) FindUserBranch(thing string) string {
|
||||||
|
var match *ForgeConfig
|
||||||
|
|
||||||
|
all := fc.SortByGoPath() // get the list of repos
|
||||||
|
for all.Scan() {
|
||||||
|
r := all.Next()
|
||||||
|
if r.GoPath == thing {
|
||||||
|
if r.UserBranchName != "" {
|
||||||
|
return r.UserBranchName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base := filepath.Base(r.GoPath)
|
||||||
|
if base == thing {
|
||||||
|
if r.UserBranchName != "" {
|
||||||
|
return r.UserBranchName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.Directory {
|
||||||
|
if strings.HasPrefix(thing, r.GoPath) {
|
||||||
|
match = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return match.UserBranchName
|
||||||
|
}
|
||||||
|
|
||||||
|
// allows custom devel branch names in the forge config
|
||||||
|
func (fc *ForgeConfigs) FindDevelBranch(thing string) string {
|
||||||
|
var match *ForgeConfig
|
||||||
|
|
||||||
|
all := fc.SortByGoPath() // get the list of repos
|
||||||
|
for all.Scan() {
|
||||||
|
r := all.Next()
|
||||||
|
if r.GoPath == thing {
|
||||||
|
if r.DevelBranchName != "" {
|
||||||
|
return r.DevelBranchName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base := filepath.Base(r.GoPath)
|
||||||
|
if base == thing {
|
||||||
|
if r.DevelBranchName != "" {
|
||||||
|
return r.DevelBranchName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.Directory {
|
||||||
|
if strings.HasPrefix(thing, r.GoPath) {
|
||||||
|
match = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return match.DevelBranchName
|
||||||
|
}
|
||||||
|
|
||||||
|
// allows custom devel branch names in the forge config
|
||||||
|
func (fc *ForgeConfigs) FindMasterBranch(thing string) string {
|
||||||
|
var match *ForgeConfig
|
||||||
|
|
||||||
|
all := fc.SortByGoPath() // get the list of repos
|
||||||
|
for all.Scan() {
|
||||||
|
r := all.Next()
|
||||||
|
if r.GoPath == thing {
|
||||||
|
if r.MasterBranchName != "" {
|
||||||
|
return r.MasterBranchName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base := filepath.Base(r.GoPath)
|
||||||
|
if base == thing {
|
||||||
|
if r.MasterBranchName != "" {
|
||||||
|
return r.MasterBranchName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.Directory {
|
||||||
|
if strings.HasPrefix(thing, r.GoPath) {
|
||||||
|
match = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return match.MasterBranchName
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DOES NOT MODIFY ANYTHING
|
||||||
|
//
|
||||||
|
// this is a final check to make sure, before pushing
|
||||||
|
// a golang repo, that the go.sum file has the correct
|
||||||
|
// and current version of every package
|
||||||
|
//
|
||||||
|
// it re-scans the go.sum file. DOES NOT MODIFY ANYTHING
|
||||||
|
// this is the last thing to run to double check everything
|
||||||
|
// before 'git tag' or git push --tags
|
||||||
|
func (f *Forge) FinalGoDepsCheckOk(check *gitpb.Repo, verbose bool) error {
|
||||||
|
if check == nil {
|
||||||
|
return errors.New("FinalGoDepsCheckOk() boo, check == nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the go.mod and go.sum files
|
||||||
|
if !check.ParseGoSum() {
|
||||||
|
return fmt.Errorf("forge.ParseGoSum() failed. go.mod & go.sum are broken")
|
||||||
|
}
|
||||||
|
|
||||||
|
if check.GetGoPrimitive() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
deps := check.GoDeps.SortByGoPath()
|
||||||
|
for deps.Scan() {
|
||||||
|
depRepo := deps.Next()
|
||||||
|
found := f.FindByGoPath(depRepo.GetGoPath())
|
||||||
|
if found == nil {
|
||||||
|
if f.CheckOverride(depRepo.GetGoPath()) {
|
||||||
|
// skip this gopath because it's probably broken forever
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("dep not found: %s", depRepo.GetGoPath())
|
||||||
|
}
|
||||||
|
if depRepo.GetVersion() == found.GetMasterVersion() {
|
||||||
|
// log.Printf("%-48s error ?? %-10s vs %-10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// log.Info("found dep", depRepo.GetGoPath())
|
||||||
|
if depRepo.GetVersion() != found.GetTargetVersion() {
|
||||||
|
check := f.FindByGoPath(depRepo.GetGoPath())
|
||||||
|
if f.Config.IsReadOnly(check.GetGoPath()) {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%-48s ok error .%s. vs .%s. (ignoring read-only repo)\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if f.CheckOverride(depRepo.GetGoPath()) {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%-48s ok error .%s. vs .%s. (forge.CheckOverride())\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
|
||||||
|
}
|
||||||
|
// skip this gopath because it's probably broken forever
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// log.Printf("%-48s error ?? %-10s vs %-10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
|
||||||
|
// log.Printf("%-48s error %10s vs %10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
|
||||||
|
return fmt.Errorf("%-48s error %10s vs %10s", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) CheckOverride(gopath string) bool {
|
||||||
|
if gopath == "cloud.google.com/go" {
|
||||||
|
// log.Info("CheckOverride() is ignoring", gopath)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if gopath == "bou.ke/monkey" {
|
||||||
|
// log.Info("CheckOverride() is ignoring", gopath)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if gopath == "github.com/posener/complete/v2" {
|
||||||
|
// log.Info("CheckOverride() is ignoring", gopath)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(gopath, "github.com/go-gl") {
|
||||||
|
// log.Info("CheckOverride() is ignoring", gopath)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(gopath, "google.golang.org") {
|
||||||
|
// log.Info("CheckOverride() is ignoring", gopath)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(gopath, "go.opencensus.io") {
|
||||||
|
// log.Info("CheckOverride() is ignoring", gopath)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(gopath, "github.com/nicksnyder/go-i18n") {
|
||||||
|
// log.Info("CheckOverride() is ignoring", gopath)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// fuckit for now. just blacklist github.com
|
||||||
|
if strings.HasPrefix(gopath, "github.com/") {
|
||||||
|
// log.Info("CheckOverride() is ignoring", gopath)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) TestGoDepsCheckOk(godeps *gitpb.GoDeps, verbose bool) error {
|
||||||
|
if godeps == nil {
|
||||||
|
return errors.New("forge.TestGoDepsCheckOk() godeps == nil")
|
||||||
|
}
|
||||||
|
all := godeps.SortByGoPath()
|
||||||
|
for all.Scan() {
|
||||||
|
depRepo := all.Next()
|
||||||
|
found := f.FindByGoPath(depRepo.GetGoPath())
|
||||||
|
if found == nil {
|
||||||
|
if f.CheckOverride(depRepo.GetGoPath()) {
|
||||||
|
// skip this gopath because it's probably broken forever
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("dep not found: %s", depRepo.GetGoPath())
|
||||||
|
}
|
||||||
|
if depRepo.GetVersion() == found.GetMasterVersion() {
|
||||||
|
// log.Printf("%-48s error ?? %-10s vs %-10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// log.Info("found dep", depRepo.GetGoPath())
|
||||||
|
if depRepo.GetVersion() != found.GetTargetVersion() {
|
||||||
|
check := f.FindByGoPath(depRepo.GetGoPath())
|
||||||
|
if f.Config.IsReadOnly(check.GetGoPath()) {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%-48s ok error .%s. vs .%s. (ignoring read-only repo)\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if f.CheckOverride(depRepo.GetGoPath()) {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("%-48s ok error .%s. vs .%s. (forge.CheckOverride())\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
|
||||||
|
}
|
||||||
|
// skip this gopath because it's probably broken forever
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// log.Printf("%-48s error ?? %-10s vs %-10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
|
||||||
|
// log.Printf("%-48s error %10s vs %10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
|
||||||
|
return fmt.Errorf("%-48s error %10s vs %10s", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,39 +1,62 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
package forgepb;
|
package forgepb;
|
||||||
|
|
||||||
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
||||||
|
|
||||||
|
// `autogenpb:uuid:7267f5d5-954b-44b7-9f67-2eb808791355` // todo: add file support
|
||||||
|
|
||||||
// define 3 branches. that is all that is supported
|
// 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
|
// the term 'master' is used in the code because 'main' is a reserved word in golang already
|
||||||
// allow 'read only' and 'private' flags
|
// allow 'read only' and 'private' flags
|
||||||
// package names sometimes must be different than the binary name
|
// package names sometimes must be different than the binary name
|
||||||
// for example 'zookeeper' is packaged as 'zookeeper-go'
|
// for example 'zookeeper' is packaged as 'zookeeper-go'
|
||||||
// due to the prior apache foundation project. This happens and is ok!
|
// due to the prior apache foundation project. This happens and is ok!
|
||||||
message ForgeConfig {
|
message ForgeConfig { // `autogenpb:nomutex`
|
||||||
string goPath = 1; // `autogenpb:unique` // Examples: 'go.wit.com/apps/go-clone' or "~/mythings" or "/home/src/foo"
|
string goPath = 1; // `autogenpb:unique` `autogenpb:sort` // 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 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 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 private = 4; // if the repo can be published
|
||||||
bool directory = 5; // everything in this directory should use these writable & private values
|
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 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
|
bool interesting = 7; // this is something interesting you found and want to remember it
|
||||||
|
|
||||||
string masterBranchName = 8; // git 'main' or 'master' branch name
|
string masterBranchName = 8; // git 'main' or 'master' branch name
|
||||||
string develBranchName = 9; // whatever the git 'devel' branch name is
|
string develBranchName = 9; // whatever the git 'devel' branch name is
|
||||||
string userBranchName = 10; // whatever your username branch is
|
string userBranchName = 10; // whatever your username branch is
|
||||||
|
|
||||||
string debName = 11; // the actual name used with 'apt install' (or distro apt equivalent.
|
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'
|
// todo: appeal to everyone to alias 'apt' on fedora, gentoo, arch, etc to alias 'apt install'
|
||||||
// so we can make easier instructions for new linux users. KISS
|
// so we can make easier instructions for new linux users. KISS
|
||||||
|
|
||||||
google.protobuf.Timestamp verstamp = 12; // the git commit timestamp of the version
|
google.protobuf.Timestamp verstamp = 12; // the git commit timestamp of the version
|
||||||
|
string goSrc = 13; // is ~/go/src unless a go.work file is found
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: autogen 'sort', 'marshal'
|
// todo: fix autogenpb to look for enum
|
||||||
message ForgeConfigs { // `autogenpb:marshal`
|
enum ForgeMode {
|
||||||
string uuid = 1; // could be useful for /usr/share/file/magic someday?
|
MASTER = 0; // "release mode"
|
||||||
string version = 2; // could be used for protobuf schema change violations?
|
DEVEL = 1; // "patch mode"
|
||||||
repeated ForgeConfig ForgeConfigs = 3;
|
USER = 2; // "work mode"
|
||||||
|
}
|
||||||
|
|
||||||
|
message ForgeConfigs { // `autogenpb:marshal` `autogenpb:nomutex`
|
||||||
|
string uuid = 1; // `autogenpb:uuid:1941cd4f-1cfd-4bf6-aa75-c2c391907e81`
|
||||||
|
string version = 2; // `autogenpb:version:v0.0.47`
|
||||||
|
repeated ForgeConfig ForgeConfigs = 3;
|
||||||
|
string username = 4; // what to use for the user branch (default ENV{USER})
|
||||||
|
string xterm = 5; // what xterm the user wants as the default
|
||||||
|
repeated string xtermArgv = 6; // the argv line for xterm
|
||||||
|
string defaultGui = 7; // default GUI plugin to use
|
||||||
|
ForgeMode mode = 8; // what "mode" forge is in
|
||||||
|
}
|
||||||
|
|
||||||
|
// this generic message is used by autogen to identify and
|
||||||
|
// then dump the uuid and version from any arbitrary .pb file
|
||||||
|
message Identify { // `autogenpb:marshal`
|
||||||
|
string uuid = 1; //
|
||||||
|
string version = 2; //
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
package forgepb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// this is a final check to make sure, before pushing
|
|
||||||
// a golang repo, that the go.sum file has the correct
|
|
||||||
// and current version of every package
|
|
||||||
//
|
|
||||||
// it re-scans the go.sum file. DOES NOT MODIFY ANYTHING
|
|
||||||
// this is the last thing to run to double check everything
|
|
||||||
// before 'git tag' or git push --tags
|
|
||||||
func (f *Forge) FinalGoDepsCheckOk(check *gitpb.Repo) bool {
|
|
||||||
var good bool = true
|
|
||||||
if check == nil {
|
|
||||||
log.Info("boo, check == nil")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// clear out the protobuf and rescan from the file
|
|
||||||
check.GoDeps = nil
|
|
||||||
check.ParseGoSum()
|
|
||||||
|
|
||||||
if check.GoDepsLen() == 0 {
|
|
||||||
// this is a primitive
|
|
||||||
check.GoPrimitive = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("current repo %s go dependancy count: %d", check.GetGoPath(), check.GoDepsLen())
|
|
||||||
deps := check.GoDeps.SortByGoPath()
|
|
||||||
for deps.Scan() {
|
|
||||||
depRepo := deps.Next()
|
|
||||||
found := f.Repos.FindByGoPath(depRepo.GetGoPath())
|
|
||||||
if found == nil {
|
|
||||||
log.Info("not found:", depRepo.GetGoPath())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// log.Info("found dep", depRepo.GetGoPath())
|
|
||||||
if depRepo.GetVersion() != found.GetTargetVersion() {
|
|
||||||
if f.IsReadOnly(depRepo.GetGoPath()) {
|
|
||||||
log.Printf("%-48s ok error %10s vs %10s (ignoring read-only repo)", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
|
|
||||||
} else {
|
|
||||||
log.Printf("%-48s error %10s vs %10s", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return good
|
|
||||||
}
|
|
35
goSrcFind.go
35
goSrcFind.go
|
@ -9,28 +9,37 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (f *Forge) GetHome() string {
|
||||||
|
return f.goSrc
|
||||||
|
}
|
||||||
|
|
||||||
// look for a go.work file
|
// look for a go.work file
|
||||||
// otherwise use ~/go/src
|
// otherwise use ~/go/src
|
||||||
func (f *Forge) findGoSrc() (string, error) {
|
func (f *Forge) findGoSrc() (string, error) {
|
||||||
pwd, err := os.Getwd()
|
pwd, err := os.Getwd()
|
||||||
|
// startpwd, _ := os.Getwd()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Check for go.work in the current directory and then move up until root
|
// Check for go.work in the current directory and then move up until root
|
||||||
if pwd, err := digup(pwd); err == nil {
|
if pwd, err := digup(pwd); err == nil {
|
||||||
log.Info("using go.work file in directory", pwd)
|
|
||||||
f.goWork = true
|
f.goWork = true
|
||||||
// found an existing go.work file
|
// found an existing go.work file
|
||||||
// os.Chdir(pwd)
|
// os.Chdir(pwd)
|
||||||
return pwd, nil
|
return pwd, nil
|
||||||
|
} else {
|
||||||
|
// if there is an error looking for the go.work file
|
||||||
|
// default to using ~/go/src
|
||||||
|
// log.Info("forge.digup() err", pwd, err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// this shouldn't really happen. maybe your working directory got deleted
|
||||||
|
log.Info("forge.findGoSrc() os.Getwd() was probably deleted", pwd, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// there are no go.work files, resume the ~/go/src behavior from prior to golang 1.22
|
// there are no go.work files, resume the ~/go/src behavior from prior to golang 1.22
|
||||||
pwd, err = useGoSrc()
|
pwd, err = useGoSrc()
|
||||||
log.Info("using ~/go/src directory", pwd)
|
|
||||||
return pwd, err
|
return pwd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,9 +51,23 @@ func useGoSrc() (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
pwd := filepath.Join(homeDir, "go/src")
|
pwd := filepath.Join(homeDir, "go/src")
|
||||||
shell.Mkdir(pwd)
|
err = os.MkdirAll(pwd, os.ModePerm)
|
||||||
// os.Chdir(pwd)
|
return pwd, err
|
||||||
return pwd, nil
|
}
|
||||||
|
|
||||||
|
func (f *Forge) goWorkExists() bool {
|
||||||
|
var err error
|
||||||
|
workFilePath := filepath.Join(f.GetGoSrc(), "go.work")
|
||||||
|
if _, err = os.Stat(workFilePath); err == nil {
|
||||||
|
// log.Info("f.goWorkExists() found", workFilePath)
|
||||||
|
return true
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
// log.Info("f.goWorkExists() missing", workFilePath)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// probably false, but some other error
|
||||||
|
// log.Info("f.goWorkExists() os.Stat() error", err, workFilePath)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func digup(path string) (string, error) {
|
func digup(path string) (string, error) {
|
||||||
|
|
111
goSrcScan.go
111
goSrcScan.go
|
@ -13,33 +13,51 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (f *Forge) ScanGoSrc() (bool, error) {
|
func (f *Forge) ScanGoSrc() (bool, error) {
|
||||||
log.Info("pre dir walk")
|
|
||||||
dirs, err := gitDirectoriesNew(f.goSrc)
|
dirs, err := gitDirectoriesNew(f.goSrc)
|
||||||
log.Info("post dir walk", len(dirs))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var gopaths []string
|
var gopaths []string
|
||||||
for _, dir := range dirs {
|
for _, dir := range dirs {
|
||||||
|
// log.Info("forge.ScanGoSrc()", dir)
|
||||||
if strings.HasPrefix(dir, f.goSrc) {
|
if strings.HasPrefix(dir, f.goSrc) {
|
||||||
gopath := strings.TrimPrefix(dir, f.goSrc)
|
gopath := strings.TrimPrefix(dir, f.goSrc)
|
||||||
gopath = strings.Trim(gopath, "/")
|
gopath = strings.Trim(gopath, "/")
|
||||||
|
if r := f.FindByGoPath(gopath); r != nil {
|
||||||
|
// log.Info("already have", gopath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
gopaths = append(gopaths, gopath)
|
gopaths = append(gopaths, gopath)
|
||||||
} else {
|
} else {
|
||||||
log.Log(FORGEPBWARN, "ScanGoSrc() bad:", dir)
|
log.Log(WARN, "ScanGoSrc() bad:", dir)
|
||||||
return false, errors.New("forgepb.ScanGoSrc() bad dir: " + dir)
|
return false, errors.New("forgepb.ScanGoSrc() bad dir: " + dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Info("pre rill")
|
newcount, err := f.rillScanDirs(gopaths)
|
||||||
f.rillScanDirs(gopaths)
|
if err != nil {
|
||||||
|
log.Info("go src dir problem. exit for now?", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if newcount != 0 {
|
||||||
|
log.Info("forge go src scan found", newcount, "repos")
|
||||||
|
f.SetConfigSave(true)
|
||||||
|
}
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Forge) ScanDir(dir string) *gitpb.Repo {
|
||||||
|
// repo, err := f.NewGoRepo(gopath, "")
|
||||||
|
repo, err := f.Repos.NewGoRepo(dir, "")
|
||||||
|
log.Info("need to implement ScanDir()", dir, err)
|
||||||
|
return repo
|
||||||
|
}
|
||||||
|
|
||||||
// doesn't enter the directory any further when it finds a .git/
|
// doesn't enter the directory any further when it finds a .git/
|
||||||
// not stupid like my old version
|
// not stupid like my old version
|
||||||
func gitDirectoriesNew(srcDir string) ([]string, error) {
|
func gitDirectoriesNew(srcDir string) ([]string, error) {
|
||||||
var all []string
|
var all []string
|
||||||
|
var trip bool
|
||||||
err := filepath.WalkDir(srcDir, func(path string, d os.DirEntry, err error) error {
|
err := filepath.WalkDir(srcDir, func(path string, d os.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Handle possible errors, like permission issues
|
// Handle possible errors, like permission issues
|
||||||
|
@ -47,6 +65,25 @@ func gitDirectoriesNew(srcDir string) ([]string, error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.IsDir() {
|
||||||
|
// log.Info("path is dir", path)
|
||||||
|
} else {
|
||||||
|
_, fname := filepath.Split(path)
|
||||||
|
switch fname {
|
||||||
|
case "repos.pb":
|
||||||
|
case "go.work":
|
||||||
|
case "go.work.last":
|
||||||
|
default:
|
||||||
|
// todo: figure out a way to do padding for init()
|
||||||
|
if trip == false {
|
||||||
|
log.Info("WARNING:")
|
||||||
|
}
|
||||||
|
log.Info("WARNING: you have an untracked file outside of any .git repository:", path)
|
||||||
|
trip = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
gitdir := filepath.Join(path, ".git")
|
gitdir := filepath.Join(path, ".git")
|
||||||
_, err2 := os.Stat(gitdir)
|
_, err2 := os.Stat(gitdir)
|
||||||
if !os.IsNotExist(err2) {
|
if !os.IsNotExist(err2) {
|
||||||
|
@ -55,6 +92,20 @@ func gitDirectoriesNew(srcDir string) ([]string, error) {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
//
|
||||||
|
// probably always leave this here forever
|
||||||
|
// this check, along with CheckDirty() makes sure you can safely delete ~/go/src or the go.work directory
|
||||||
|
// because everything is either checked in or deleted. An important thing to know!
|
||||||
|
if trip {
|
||||||
|
log.Info("WARNING:")
|
||||||
|
log.Info("WARNING: there isn't a way to disable this warning yet")
|
||||||
|
log.Info("WARNING: probably this is a good thing however. you don't want to leave files outside of git repos here")
|
||||||
|
log.Info("WARNING: so this warning should probably stay")
|
||||||
|
log.Info("WARNING:")
|
||||||
|
log.Info("WARNING: this also might mean you put these files here because you are actively working on them")
|
||||||
|
log.Info("WARNING: and you don't want to forget about them")
|
||||||
|
log.Info("WARNING:")
|
||||||
|
}
|
||||||
return all, err
|
return all, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +113,7 @@ func gitDirectoriesOld(srcDir string) ([]string, error) {
|
||||||
var all []string
|
var all []string
|
||||||
err := filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(FORGEPBWARN, "Error accessing path:", path, err)
|
log.Log(WARN, "Error accessing path:", path, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +126,7 @@ func gitDirectoriesOld(srcDir string) ([]string, error) {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(FORGEPBWARN, "Error walking the path:", srcDir, err)
|
log.Log(WARN, "Error walking the path:", srcDir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return all, err
|
return all, err
|
||||||
|
@ -91,51 +142,35 @@ func IsGitDir(dir string) bool {
|
||||||
return info.IsDir()
|
return info.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// rill is awesome. long live rill
|
|
||||||
func rillAddDirs(gopaths []string) {
|
|
||||||
// Convert a slice of user IDs into a channel
|
|
||||||
ids := rill.FromSlice(gopaths, nil)
|
|
||||||
|
|
||||||
// Read users from the API.
|
|
||||||
// Concurrency = 20
|
|
||||||
dirs := rill.Map(ids, 20, func(id string) (*repolist.RepoRow, error) {
|
|
||||||
return me.repos.View.FindByName(id), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// Activate users.
|
|
||||||
// Concurrency = 10
|
|
||||||
err := rill.ForEach(dirs, 10, func(repo *repolist.RepoRow) error {
|
|
||||||
fmt.Printf("Repo found : %s\n", repo.GoPath())
|
|
||||||
repo.Run([]string{"git", "pull"})
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// Handle errors
|
|
||||||
fmt.Println("Error:", err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// rill is awesome. long live rill
|
// rill is awesome. long live rill
|
||||||
// attempt scan with rill
|
// attempt scan with rill
|
||||||
func (f *Forge) rillScanDirs(gopaths []string) error {
|
func (f *Forge) rillScanDirs(gopaths []string) (int, error) {
|
||||||
// Convert a slice of user IDs into a channel
|
// Convert a slice of user IDs into a channel
|
||||||
ids := rill.FromSlice(gopaths, nil)
|
ids := rill.FromSlice(gopaths, nil)
|
||||||
|
|
||||||
// Read users from the API.
|
// Read users from the API.
|
||||||
// Concurrency = 20
|
// Concurrency = 20
|
||||||
dirs := rill.Map(ids, 20, func(id string) (*gitpb.Repo, error) {
|
dirs := rill.Map(ids, 20, func(id string) (*gitpb.Repo, error) {
|
||||||
return f.Repos.NewGoPath(f.goSrc, id)
|
return f.checkpath(id, "")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var counter int
|
||||||
// Activate users.
|
// Activate users.
|
||||||
// Concurrency = 10
|
// Concurrency = 10
|
||||||
err := rill.ForEach(dirs, 10, func(repo *gitpb.Repo) error {
|
err := rill.ForEach(dirs, 10, func(repo *gitpb.Repo) error {
|
||||||
// could do something here
|
counter += 1
|
||||||
// fmt.Printf("Repo found : %s\n", repo.GoPath)
|
|
||||||
// repo.Run([]string{"git", "pull"})
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return err
|
return counter, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) checkpath(gopath string, url string) (*gitpb.Repo, error) {
|
||||||
|
fullpath := filepath.Join(f.GetGoSrc(), gopath)
|
||||||
|
log.Info("forge creating protobuf for", fullpath)
|
||||||
|
repo, err := f.NewGoRepo(gopath, "")
|
||||||
|
if err != nil {
|
||||||
|
log.Info("\tprotobuf error", gopath, err)
|
||||||
|
}
|
||||||
|
return repo, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// very much a hack job
|
||||||
|
func (f *Forge) MakeGoWork() error {
|
||||||
|
if f.IsGoWork() {
|
||||||
|
// a go.work file was found
|
||||||
|
} else {
|
||||||
|
// you can use a go.work file in ~/go/src , but you probably shouldn't unless something
|
||||||
|
// has gone terribly wrong
|
||||||
|
return errors.New("if you want a go.work file in ~/go/src/, touch it first")
|
||||||
|
}
|
||||||
|
filename := filepath.Join(f.GetGoSrc(), "go.work")
|
||||||
|
workf, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer workf.Close()
|
||||||
|
|
||||||
|
fmt.Fprintln(workf, "go 1.21") // fix this
|
||||||
|
fmt.Fprintln(workf, "")
|
||||||
|
fmt.Fprintln(workf, "use (")
|
||||||
|
|
||||||
|
all := f.Repos.SortByFullPath()
|
||||||
|
for all.Scan() {
|
||||||
|
repo := all.Next()
|
||||||
|
/*
|
||||||
|
if !repo.IsGoLang() == "" {
|
||||||
|
// skip repos that aren't go
|
||||||
|
// todo: handle non-flat repos?
|
||||||
|
log.Info("skip non-go", repo.GetGoPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if repo.GetGoPath() == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintln(workf, "\t"+repo.GetGoPath())
|
||||||
|
/*
|
||||||
|
if repo.pb.Exists("go.mod") {
|
||||||
|
// log.Info("ADDING REPO", goSrcDir, repo.GetGoPath())
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(workf, "\t"+repo.GetGoPath)
|
||||||
|
log.Log(REPO, "missing go.mod for", repo.GetGoPath())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
fmt.Fprintln(workf, ")")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright 1994-2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os/user"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (f *Forge) HttpPostMachine(url string) ([]byte, error) {
|
||||||
|
if f.Machine == nil {
|
||||||
|
// run f.InitMachine() here?
|
||||||
|
log.Info("you must run f.InitMachine()")
|
||||||
|
return nil, fmt.Errorf("you must run f.InitMachine()")
|
||||||
|
}
|
||||||
|
if f.Machine.Hostname == "" {
|
||||||
|
log.Info("WTF. hostname is blank")
|
||||||
|
} else {
|
||||||
|
log.Info("GOOD. hostname is set to", f.Machine.Hostname)
|
||||||
|
}
|
||||||
|
log.Info("GOOD2. hostname is set to", f.Machine.Hostname)
|
||||||
|
msg, err := f.Machine.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
log.Info("proto.Marshal() failed:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Info("GOOD3. hostname is set to", f.Machine.Hostname)
|
||||||
|
|
||||||
|
check := new(zoopb.Machine)
|
||||||
|
check.Unmarshal(msg)
|
||||||
|
if check == nil {
|
||||||
|
log.Info("WTF. check == nil")
|
||||||
|
}
|
||||||
|
log.Info("good? check.hostname =", check.Hostname)
|
||||||
|
return f.HttpPost(url, msg)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (f *Forge) HttpPost(url string, data []byte) ([]byte, error) {
|
||||||
|
var err error
|
||||||
|
var req *http.Request
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data))
|
||||||
|
// log.Info("httpPost() with len", len(data), "url", url)
|
||||||
|
|
||||||
|
usr, _ := user.Current()
|
||||||
|
req.Header.Set("author", usr.Username)
|
||||||
|
/*
|
||||||
|
if f.Machine == nil {
|
||||||
|
// run f.InitMachine() here?
|
||||||
|
log.Info("you must run f.InitMachine()")
|
||||||
|
return nil, fmt.Errorf("you must run f.InitMachine()")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
req.Header.Set("hostname", "fixme:hostname")
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return []byte("client.Do(req) error"), err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
// log.Info("httpPost() with len", len(data))
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return body, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return body, nil
|
||||||
|
}
|
69
human.go
69
human.go
|
@ -1,8 +1,9 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/lib/gui/shell"
|
"go.wit.com/lib/gui/shell"
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
@ -23,17 +24,29 @@ func standardHeader() string {
|
||||||
func (f *Forge) standardHeader(r *ForgeConfig) string {
|
func (f *Forge) standardHeader(r *ForgeConfig) string {
|
||||||
var flags string
|
var flags string
|
||||||
var readonly string
|
var readonly string
|
||||||
if f.IsPrivate(r.GoPath) {
|
if f.Config.IsPrivate(r.GoPath) {
|
||||||
flags += "(private) "
|
flags += "(private) "
|
||||||
}
|
}
|
||||||
if f.IsFavorite(r.GoPath) {
|
if f.Config.IsFavorite(r.GoPath) {
|
||||||
flags += "(favorite) "
|
flags += "(favorite) "
|
||||||
}
|
}
|
||||||
if f.IsReadOnly(r.GoPath) {
|
if f.Config.IsReadOnly(r.GoPath) {
|
||||||
readonly = ""
|
readonly = ""
|
||||||
} else {
|
} else {
|
||||||
readonly = "r/w"
|
readonly = "r/w"
|
||||||
}
|
}
|
||||||
|
if r.MasterBranchName != "" {
|
||||||
|
flags += "(master=" + r.MasterBranchName + ") "
|
||||||
|
}
|
||||||
|
if r.DevelBranchName != "" {
|
||||||
|
flags += "(devel=" + r.DevelBranchName + ") "
|
||||||
|
}
|
||||||
|
if r.UserBranchName != "" {
|
||||||
|
flags += "(user=" + r.UserBranchName + ") "
|
||||||
|
}
|
||||||
|
if r.DebName != "" {
|
||||||
|
flags += "(deb=" + r.DebName + ") "
|
||||||
|
}
|
||||||
return fmt.Sprintf("%-4s %-40s %s", readonly, r.GoPath, flags)
|
return fmt.Sprintf("%-4s %-40s %s", readonly, r.GoPath, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,38 +56,29 @@ func (f *Forge) ConfigPrintTable() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Info(standardHeader())
|
log.Info(standardHeader())
|
||||||
loop := f.Config.SortByGoPath()
|
all := f.Config.SortByGoPath()
|
||||||
for loop.Scan() {
|
for all.Scan() {
|
||||||
r := loop.Next()
|
r := all.Next()
|
||||||
log.Info(f.standardHeader(r))
|
log.Info(f.standardHeader(r))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Forge) newestAge(repo *gitpb.Repo) time.Duration {
|
|
||||||
loop := repo.Tags.SortByAge()
|
|
||||||
for loop.Scan() {
|
|
||||||
r := loop.Next()
|
|
||||||
return time.Since(r.GetAuthordate().AsTime())
|
|
||||||
}
|
|
||||||
return time.Since(time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
// show information while doing golang releases
|
// show information while doing golang releases
|
||||||
func (f *Forge) StandardReleaseHeader(repo *gitpb.Repo, state string) string {
|
func (f *Forge) StandardReleaseHeader(repo *gitpb.Repo, state string) string {
|
||||||
lastTag := repo.GetLastTag()
|
|
||||||
// tag := repo.NewestTag()
|
// tag := repo.NewestTag()
|
||||||
// gitAge, _ := tag.GetDate()
|
// gitAge, _ := tag.GetDate()
|
||||||
dur := f.newestAge(repo)
|
dur := repo.NewestAge()
|
||||||
|
|
||||||
curname := repo.GetCurrentBranchName()
|
curname := repo.GetCurrentBranchName()
|
||||||
|
|
||||||
|
lastTag := repo.GetLastTag()
|
||||||
|
target := repo.GetTargetVersion()
|
||||||
master := repo.GetMasterVersion()
|
master := repo.GetMasterVersion()
|
||||||
user := repo.GetUserVersion()
|
user := repo.GetUserVersion()
|
||||||
target := repo.GetTargetVersion()
|
|
||||||
|
|
||||||
header := fmt.Sprintf("%-35s %5s %-10s %-10s %-10s %-20s %-20s %-15s",
|
header := fmt.Sprintf("%-35s %5s %-10s %-10s %-10s %-20s %-20s %-15s",
|
||||||
repo.GetGoPath(), shell.FormatDuration(dur), curname,
|
repo.GetGoPath(), shell.FormatDuration(dur), curname,
|
||||||
lastTag, target,
|
lastTag, target, master, user,
|
||||||
master, user,
|
|
||||||
state)
|
state)
|
||||||
return header
|
return header
|
||||||
}
|
}
|
||||||
|
@ -86,3 +90,26 @@ func ReleaseReportHeader() string {
|
||||||
"MASTER", "USER",
|
"MASTER", "USER",
|
||||||
"STATE")
|
"STATE")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Forge) PrintReleaseReport(repos *gitpb.Repos) int {
|
||||||
|
var count int
|
||||||
|
|
||||||
|
log.Info(ReleaseReportHeader())
|
||||||
|
|
||||||
|
loop := repos.SortByFullPath()
|
||||||
|
for loop.Scan() {
|
||||||
|
check := loop.Next()
|
||||||
|
count += 1
|
||||||
|
if check == nil {
|
||||||
|
// wtf
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var state string
|
||||||
|
if check.CheckDirty() {
|
||||||
|
state = "(dirty)"
|
||||||
|
}
|
||||||
|
log.Info(f.StandardReleaseHeader(check, state))
|
||||||
|
}
|
||||||
|
log.Info(fmt.Sprintf("total repo count = %d", count))
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/lib/gui/shell"
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Forge) HumanPrintRepo(check *gitpb.Repo) {
|
||||||
|
if check == nil {
|
||||||
|
log.Info("forge: you sent me nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if check.GetTargetVersion() == "" {
|
||||||
|
log.Info("TargetVersion == blank")
|
||||||
|
}
|
||||||
|
if check.GetTargetVersion() == check.GetCurrentVersion() {
|
||||||
|
log.Info("IsReleased() == true. do not release this a second time")
|
||||||
|
} else {
|
||||||
|
log.Info("IsReleased() == false")
|
||||||
|
}
|
||||||
|
if check.CheckDirty() {
|
||||||
|
log.Info("CheckDirty() == true. do not release dirty repos")
|
||||||
|
} else {
|
||||||
|
log.Info("CheckDirty() == false")
|
||||||
|
}
|
||||||
|
if check.GetGoPrimitive() {
|
||||||
|
log.Info("IsPrimitive() == true")
|
||||||
|
} else {
|
||||||
|
log.Info("IsPrimitive() == false")
|
||||||
|
}
|
||||||
|
if f.Config.IsPrivate(check.GetGoPath()) {
|
||||||
|
log.Info("IsPrivate() == true")
|
||||||
|
} else {
|
||||||
|
log.Info("IsPrivate() == false")
|
||||||
|
}
|
||||||
|
if ok, compiled, err := check.IsProtobuf(); ok {
|
||||||
|
log.Info(log.Sprint("IsProtobuf() == true compiled protobuf files = ", compiled))
|
||||||
|
if err != nil {
|
||||||
|
log.Info("IsProtobuf() ERROR = ", err)
|
||||||
|
}
|
||||||
|
for _, s := range compiled {
|
||||||
|
log.Info("\tcompiled file found:", s)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Info("IsProtobuf() == false")
|
||||||
|
if err != nil {
|
||||||
|
log.Info("IsProtobuf() ERROR = ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info("git master name ==", check.GetMasterBranchName())
|
||||||
|
log.Info("git devel name ==", check.GetDevelBranchName())
|
||||||
|
log.Info("git user name ==", check.GetUserBranchName())
|
||||||
|
log.Info("git current name ==", check.GetCurrentBranchName())
|
||||||
|
|
||||||
|
// testNext(check)
|
||||||
|
|
||||||
|
found := new(gitpb.Repos)
|
||||||
|
if !found.AppendByGoPath(check) {
|
||||||
|
log.Info("forgepb check. repo already existed", check.FullPath, check.GetGoPath())
|
||||||
|
} else {
|
||||||
|
log.Info("forgepb check. repo was new", check.FullPath, check.GetGoPath())
|
||||||
|
}
|
||||||
|
f.PrintHumanTable(found)
|
||||||
|
|
||||||
|
printTime("Last Pull", check.Times.LastPull.AsTime())
|
||||||
|
printTime("Last Dirty", check.Times.LastDirty.AsTime())
|
||||||
|
printTime("dir mtime", check.Times.MtimeDir.AsTime())
|
||||||
|
printTime("HEAD mtime", check.Times.MtimeHead.AsTime())
|
||||||
|
printTime("Index mtime", check.Times.MtimeIndex.AsTime())
|
||||||
|
printTime("fetch", check.Times.MtimeFetch.AsTime())
|
||||||
|
printTime("last go.sum", check.Times.LastGoDep.AsTime())
|
||||||
|
printTime("last commit", check.Times.NewestCommit.AsTime())
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
dur := now.Sub(check.Times.LastUpdate.AsTime())
|
||||||
|
log.Printf("Repo Last Reload: %s\n", shell.FormatDuration(dur))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) testGoRepo(check *gitpb.Repo) {
|
||||||
|
data, _ := os.ReadFile(filepath.Join(check.FullPath, "go.mod"))
|
||||||
|
log.Info(string(data))
|
||||||
|
|
||||||
|
if err := f.FinalGoDepsCheckOk(check, true); err == nil {
|
||||||
|
log.Info("forge.FinalGoDepsCheck(check) worked!")
|
||||||
|
} else {
|
||||||
|
log.Info("forge.FinalGoDepsCheck(check) failed. boo.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTime(s string, t time.Time) {
|
||||||
|
now := time.Now()
|
||||||
|
dur := now.Sub(t)
|
||||||
|
if dur < (time.Hour * 24) {
|
||||||
|
log.Printf("%s mtime last changed %s\n", s, shell.FormatDuration(dur))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,245 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"go.wit.com/lib/gui/shell"
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// you can replace all of COBOL with this amount of GO
|
||||||
|
|
||||||
|
// ah yes, COBOL. what an ancient throwback. for those that know
|
||||||
|
// then you know exactly what is in this file. For those that don't, here it is:
|
||||||
|
|
||||||
|
// All this does is output human readable text formatted to be viewable on
|
||||||
|
// a console with a fixed with font. AKA: a typerwriter. Which is exactly
|
||||||
|
// what COBOL did in the 1970's (60s? notsure) And the 80s.
|
||||||
|
|
||||||
|
// So, you want to dump out stuff on the console. Let's see. Something like
|
||||||
|
|
||||||
|
/*
|
||||||
|
forge --favorites
|
||||||
|
|
||||||
|
go.wit.com/apps/myapp v0.2.0 (installed)
|
||||||
|
go.wit.com/lib/somethingfun v0.0.7 (not downloaded)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// anyway, you get the idea. This is also called COBOL because it does on
|
||||||
|
// thing and truncates every line output to the columns you see with stty -a
|
||||||
|
// my monitor is huge, so it's not going to work at 80x24. 160x48 is better
|
||||||
|
// actually, I'd predict some of these will probably end up 240 wide
|
||||||
|
// long live good eyesight and 4K monitors!
|
||||||
|
|
||||||
|
func (f *Forge) PrintHumanTable(allr *gitpb.Repos) {
|
||||||
|
log.DaemonMode(true)
|
||||||
|
|
||||||
|
var count int
|
||||||
|
// log.Info(standardStart5("gopath", "cur name", "master", "user", "repo type"))
|
||||||
|
log.Info(standardTable10("repopath", "cur br", "age", "master", "devel", "user", "curver", "lasttag", "next", "repo type"))
|
||||||
|
all := allr.SortByFullPath()
|
||||||
|
for all.Scan() {
|
||||||
|
repo := all.Next()
|
||||||
|
f.printRepoToTable(repo)
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
log.Info("Total git repositories:", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// also shows which files are dirty
|
||||||
|
func (f *Forge) PrintHumanTableDirty(allr *gitpb.Repos) {
|
||||||
|
log.DaemonMode(true)
|
||||||
|
|
||||||
|
var count int
|
||||||
|
// log.Info(standardStart5("gopath", "cur name", "master", "user", "repo type"))
|
||||||
|
log.Info(standardTable10("repopath", "cur br", "age", "master", "devel", "user", "curver", "lasttag", "next", "repo type"))
|
||||||
|
// all := allr.SortByFullPath()
|
||||||
|
all := allr.All()
|
||||||
|
for all.Scan() {
|
||||||
|
repo := all.Next()
|
||||||
|
f.printRepoToTable(repo)
|
||||||
|
if len(repo.DirtyList) != 0 {
|
||||||
|
for _, line := range repo.DirtyList {
|
||||||
|
log.Info("\t", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var mver string = repo.GetMasterVersion()
|
||||||
|
repo.Tags.GetAge(mver)
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
log.Info("Total git repositories:", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardTable5(arg1, arg2, arg3, arg4, arg5 string) string {
|
||||||
|
len1 := 40
|
||||||
|
len2 := 12
|
||||||
|
len3 := 12
|
||||||
|
len4 := 16
|
||||||
|
len5 := 8
|
||||||
|
var s string
|
||||||
|
if len(arg1) > len1 {
|
||||||
|
arg1 = arg1[:len1]
|
||||||
|
}
|
||||||
|
s = "%-" + fmt.Sprintf("%d", len1) + "s "
|
||||||
|
if len(arg2) > len2 {
|
||||||
|
arg2 = arg2[:len2]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len2) + "s "
|
||||||
|
if len(arg3) > len3 {
|
||||||
|
arg3 = arg3[:len3]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len3) + "s "
|
||||||
|
if len(arg4) > len4 {
|
||||||
|
arg4 = arg4[:len4]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len4) + "s "
|
||||||
|
if len(arg5) > len5 {
|
||||||
|
arg5 = arg5[:len5]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len5) + "s"
|
||||||
|
return fmt.Sprintf(s, arg1, arg2, arg3, arg4, arg5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardTable10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 string) string {
|
||||||
|
len1 := 40
|
||||||
|
len2 := 12
|
||||||
|
len3 := 6
|
||||||
|
len4 := 12
|
||||||
|
len5 := 16
|
||||||
|
len6 := 16
|
||||||
|
len7 := 16
|
||||||
|
len8 := 12
|
||||||
|
len9 := 12
|
||||||
|
len10 := 8
|
||||||
|
var s string
|
||||||
|
if len(arg1) > len1 {
|
||||||
|
arg1 = arg1[:len1]
|
||||||
|
}
|
||||||
|
s = "%-" + fmt.Sprintf("%d", len1) + "s "
|
||||||
|
if len(arg2) > len2 {
|
||||||
|
arg2 = arg2[:len2]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len2) + "s "
|
||||||
|
if len(arg3) > len3 {
|
||||||
|
arg3 = arg3[:len3]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len3) + "s "
|
||||||
|
if len(arg4) > len4 {
|
||||||
|
arg4 = arg4[:len4]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len4) + "s "
|
||||||
|
|
||||||
|
if len(arg5) > len5 {
|
||||||
|
arg5 = arg5[:len5]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len5) + "s "
|
||||||
|
|
||||||
|
if len(arg6) > len6 {
|
||||||
|
arg6 = arg6[:len6]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len6) + "s "
|
||||||
|
|
||||||
|
if len(arg7) > len7 {
|
||||||
|
arg7 = arg7[:len7]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len7) + "s "
|
||||||
|
|
||||||
|
if len(arg8) > len8 {
|
||||||
|
arg8 = arg8[:len8]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len8) + "s "
|
||||||
|
|
||||||
|
if len(arg9) > len9 {
|
||||||
|
arg9 = arg9[:len9]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len9) + "s "
|
||||||
|
|
||||||
|
if len(arg10) > len10 {
|
||||||
|
arg10 = arg10[:len10]
|
||||||
|
}
|
||||||
|
s += "%-" + fmt.Sprintf("%d", len10) + "s "
|
||||||
|
|
||||||
|
return fmt.Sprintf(s, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) printRepoToTable(repo *gitpb.Repo) {
|
||||||
|
var end string
|
||||||
|
|
||||||
|
// shortened version numbers
|
||||||
|
var mhort string = repo.GetMasterVersion()
|
||||||
|
var dhort string = repo.GetDevelVersion()
|
||||||
|
var uhort string = repo.GetUserVersion()
|
||||||
|
if uhort == "uerr" {
|
||||||
|
// blank these out
|
||||||
|
uhort = ""
|
||||||
|
}
|
||||||
|
var lasttag string = repo.GetLastTag()
|
||||||
|
var thort string = repo.GetTargetVersion()
|
||||||
|
var chort string = repo.GetCurrentBranchVersion()
|
||||||
|
var cname string = repo.GetCurrentBranchName()
|
||||||
|
|
||||||
|
var gopath string = repo.GetGoPath()
|
||||||
|
var rtype string = repo.GetRepoType()
|
||||||
|
|
||||||
|
// ctime := repo.Tags.GetAge(mhort)
|
||||||
|
// age := shell.FormatDuration(time.Since(ctime))
|
||||||
|
age := shell.FormatDuration(repo.BranchAge(cname))
|
||||||
|
|
||||||
|
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||||
|
// end += "(readonly) "
|
||||||
|
} else {
|
||||||
|
end += "(rw) "
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.IsDirty() {
|
||||||
|
age = ""
|
||||||
|
end += "(dirty) "
|
||||||
|
}
|
||||||
|
|
||||||
|
start := standardTable10(gopath, cname, age, mhort, dhort, uhort, chort, lasttag, thort, rtype)
|
||||||
|
|
||||||
|
if rtype == "protobuf" {
|
||||||
|
if repo.GoInfo.GoBinary {
|
||||||
|
end += "(binary) "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.GetMasterBranchName() != "master" && repo.GetMasterBranchName() != "main" {
|
||||||
|
end += "(m:" + repo.GetMasterBranchName() + ") "
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.GetDevelBranchName() != "devel" {
|
||||||
|
end += "(d:" + repo.GetDevelBranchName() + ") "
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.GetUserBranchName() != f.Config.Username {
|
||||||
|
end += "(u:" + repo.GetUserBranchName() + ") "
|
||||||
|
}
|
||||||
|
|
||||||
|
debname := f.Config.DebName(repo.GetGoPath())
|
||||||
|
if debname != filepath.Base(gopath) {
|
||||||
|
end += "(deb:" + debname + ") "
|
||||||
|
}
|
||||||
|
|
||||||
|
switch repo.GetState() {
|
||||||
|
case "PERFECT":
|
||||||
|
case "unchanged":
|
||||||
|
case "dirty":
|
||||||
|
case "unknown branches":
|
||||||
|
if repo.CurrentTag == nil {
|
||||||
|
end += "(" + repo.GetState() + ") "
|
||||||
|
} else {
|
||||||
|
end += "(unknown branch " + repo.CurrentTag.Refname + ") "
|
||||||
|
}
|
||||||
|
// end += "(invalid tag) "
|
||||||
|
default:
|
||||||
|
end += "(" + repo.GetState() + ") "
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info(start, end)
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// print the protobuf in human form
|
||||||
|
func IdentifyProtobuf(data []byte) error {
|
||||||
|
var pb *Identify
|
||||||
|
pb = new(Identify)
|
||||||
|
if err := pb.Unmarshal(data); err != nil {
|
||||||
|
log.Info("data can't be identified as a standard protobuf. len =", len(data))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("Identify protobuf file uuid =", pb.Uuid, "version =", pb.Version)
|
||||||
|
return nil
|
||||||
|
}
|
174
init.go
174
init.go
|
@ -1,51 +1,181 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/lib/gui/shell"
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
"go.wit.com/lib/protobuf/zoopb"
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// todo: use initOnce
|
||||||
|
// cache.go has Do()
|
||||||
|
// f.initOnce.Do(f.initWork)
|
||||||
func Init() *Forge {
|
func Init() *Forge {
|
||||||
|
f := InitPB()
|
||||||
|
|
||||||
|
/*
|
||||||
|
f.Machine = new(zoopb.Machine)
|
||||||
|
if err := f.Machine.ConfigLoad(); err != nil {
|
||||||
|
log.Log(WARN, "zoopb.ConfigLoad() failed", err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if f.Config.Username == "" {
|
||||||
|
usr, _ := user.Current()
|
||||||
|
f.Config.Username = usr.Username
|
||||||
|
f.SetConfigSave(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Config.Xterm == "" {
|
||||||
|
f.Config.Xterm = "xterm"
|
||||||
|
f.Config.XtermArgv = append(f.Config.XtermArgv, "-bg")
|
||||||
|
f.Config.XtermArgv = append(f.Config.XtermArgv, "black")
|
||||||
|
f.Config.XtermArgv = append(f.Config.XtermArgv, "-fg")
|
||||||
|
f.Config.XtermArgv = append(f.Config.XtermArgv, "white")
|
||||||
|
f.SetConfigSave(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// f.Machine.InitWit()
|
||||||
|
|
||||||
|
if f.hasFullScan {
|
||||||
|
// duplicate time checking below. which one to keep?
|
||||||
|
if f.FullScanAge() > time.Minute {
|
||||||
|
log.Log(INFO, "forgepb.Scan() skipping scan. been run a minute ago", f.FullScanAge())
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
start := f.Repos.Len()
|
||||||
|
f.ScanGoSrc()
|
||||||
|
f.FullScanRan()
|
||||||
|
end := f.Repos.Len()
|
||||||
|
if (end - start) == 0 {
|
||||||
|
log.Log(INFO, "forgepb.Scan() Scan did not find new git repositories. Total =", end)
|
||||||
|
if f.FullScanAge() > time.Minute {
|
||||||
|
f.rillUpdate(20, 10)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Log(INFO, "forgepb.Scan() Scan found", end-start, "new git repositories. Total =", end)
|
||||||
|
f.rillUpdate(20, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.configSave {
|
||||||
|
// taking this out to debug Marshal() panic
|
||||||
|
// os.Exit(-1)
|
||||||
|
f.ConfigSave()
|
||||||
|
f.configSave = false
|
||||||
|
}
|
||||||
|
log.Log(INFO, "update() check took", shell.FormatDuration(time.Since(now)))
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func DetermineGoPath() *Forge {
|
||||||
f := new(Forge)
|
f := new(Forge)
|
||||||
|
|
||||||
// TODO: rethink this but it works for now
|
// TODO: rethink this but it works for now
|
||||||
gosrc := os.Getenv("FORGE_GOSRC")
|
gosrc := os.Getenv("FORGE_GOSRC")
|
||||||
if gosrc == "" {
|
if gosrc == "" {
|
||||||
// already set. ignore init()
|
|
||||||
goSrcDir, err := f.findGoSrc()
|
goSrcDir, err := f.findGoSrc()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("forge init() findGoSrc()", err)
|
log.Log(WARN, "forge init() findGoSrc()", err)
|
||||||
}
|
}
|
||||||
os.Setenv("FORGE_GOSRC", goSrcDir)
|
os.Setenv("FORGE_GOSRC", goSrcDir)
|
||||||
}
|
}
|
||||||
f.goSrc = os.Getenv("FORGE_GOSRC")
|
f.goSrc = os.Getenv("FORGE_GOSRC")
|
||||||
|
|
||||||
// cache.go has Do()
|
// also rethink this, but maybe this is the right thing to do
|
||||||
// f.initOnce.Do(f.initWork)
|
if os.Getenv("FORGE_CONFIG") == "" {
|
||||||
|
homeDir, _ := os.UserHomeDir()
|
||||||
f.Config = new(ForgeConfigs)
|
fullpath := filepath.Join(homeDir, ".config/forge")
|
||||||
|
os.Setenv("FORGE_CONFIG", fullpath)
|
||||||
// load the ~/.config/forge/ config
|
|
||||||
if err := f.Config.ConfigLoad(); err != nil {
|
|
||||||
log.Warn("forgepb.ConfigLoad() failed", err)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Repos = new(gitpb.Repos)
|
f.configDir = os.Getenv("FORGE_CONFIG")
|
||||||
f.Repos.ConfigLoad()
|
|
||||||
f.Machine = new(zoopb.Machine)
|
|
||||||
|
|
||||||
if err := f.Machine.ConfigLoad(); err != nil {
|
// check again for go.work // user could have a go.work file in ~/go/src
|
||||||
log.Warn("zoopb.ConfigLoad() failed", err)
|
if f.goWorkExists() {
|
||||||
os.Exit(-1)
|
f.goWork = true
|
||||||
}
|
}
|
||||||
f.Machine.InitWit()
|
|
||||||
|
|
||||||
log.Info("forge pre scan ", f.Repos.Len(), "repos in", f.goSrc)
|
// print out the settings that will be used
|
||||||
f.ScanGoSrc()
|
log.Log(INFO, "forgepb.Init() FORGE_CONFIG", os.Getenv("FORGE_CONFIG"))
|
||||||
log.Info("forge.Init() found", f.Repos.Len(), "repos in", f.goSrc)
|
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Forge) InitPB() {
|
||||||
|
// load the ~/.config/forge/ config
|
||||||
|
f.Config = new(ForgeConfigs)
|
||||||
|
if err := f.Config.ConfigLoad(); err != nil {
|
||||||
|
log.Log(WARN, "forgepb.ConfigLoad() failed", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.IsGoWork() {
|
||||||
|
log.Log(INFO, "forgepb.Init() FORGE_GOSRC ", os.Getenv("FORGE_GOSRC"), "(go.work = true)")
|
||||||
|
} else {
|
||||||
|
log.Log(INFO, "forgepb.Init() FORGE_GOSRC ", os.Getenv("FORGE_GOSRC"), "(go.work = false)")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Repos = gitpb.NewRepos()
|
||||||
|
f.Repos.ConfigLoad()
|
||||||
|
if f.Repos.HasFullScan {
|
||||||
|
f.hasFullScan = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("FORGE_URL") != "" {
|
||||||
|
forgeURL = os.Getenv("FORGE_URL")
|
||||||
|
log.Info("got forge url", forgeURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) InitMachine() {
|
||||||
|
/*
|
||||||
|
f.Machine = new(zoopb.Machine)
|
||||||
|
if err := f.Machine.ConfigLoad(); err != nil {
|
||||||
|
log.Log(WARN, "zoopb.ConfigLoad() failed", err)
|
||||||
|
f.Machine.InitWit()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if f.Config.Username == "" {
|
||||||
|
usr, _ := user.Current()
|
||||||
|
f.Config.Username = usr.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if f.Machine.Hostname == "" {
|
||||||
|
r, err := shell.RunVerbose([]string{"hostname", "-f"})
|
||||||
|
if err == nil {
|
||||||
|
tmp := strings.Join(r.Stdout, "\n")
|
||||||
|
f.Machine.Hostname = strings.TrimSpace(tmp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// only init's the protobuf. intended to not scan or change anything
|
||||||
|
func InitPB() *Forge {
|
||||||
|
f := DetermineGoPath()
|
||||||
|
f.InitPB()
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) SetConfigSave(b bool) {
|
||||||
|
f.configSave = b
|
||||||
|
}
|
||||||
|
|
||||||
|
// saves the config if there have been changes
|
||||||
|
func (f *Forge) Exit() {
|
||||||
|
// log.Info("forge.configSave =", f.configSave)
|
||||||
|
if f.configSave {
|
||||||
|
f.ConfigSave()
|
||||||
|
}
|
||||||
|
// log.Info("forge.Exit() ok")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
8
log.go
8
log.go
|
@ -4,13 +4,13 @@ import (
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var FORGEPB *log.LogFlag
|
var INFO *log.LogFlag
|
||||||
var FORGEPBWARN *log.LogFlag
|
var WARN *log.LogFlag
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
full := "go.wit.com/lib/protobuf/forgepb"
|
full := "go.wit.com/lib/protobuf/forgepb"
|
||||||
short := "forgepb"
|
short := "forgepb"
|
||||||
|
|
||||||
FORGEPB = log.NewFlag("FORGEPB", false, full, short, "general forgepb things")
|
INFO = log.NewFlag("INFO", false, full, short, "general forgepb things")
|
||||||
FORGEPBWARN = log.NewFlag("FORGEPBWARN", true, full, short, "forgepb warnings")
|
WARN = log.NewFlag("WARN", true, full, short, "forgepb warnings")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 1994-2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
// TODO: implement i18n with the protobuf's
|
||||||
|
func (f *Forge) GetMode() string {
|
||||||
|
switch f.Config.Mode {
|
||||||
|
case ForgeMode_MASTER:
|
||||||
|
return "Release Mode (master branch)"
|
||||||
|
case ForgeMode_DEVEL:
|
||||||
|
return "Patch Mode (devel branch)"
|
||||||
|
case ForgeMode_USER:
|
||||||
|
return "Hack Mode (user branch)"
|
||||||
|
default:
|
||||||
|
return f.Config.Mode.String()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 1994-2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var forgeURL string = "https://go.wit.com/"
|
||||||
|
|
||||||
|
func (f *Forge) GetPatchesets() (*Patchsets, error) {
|
||||||
|
url := forgeURL + "GetPatchsets"
|
||||||
|
log.Info("GetPatchsets() url", url)
|
||||||
|
body, err := f.HttpPost(url, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("httpPost() failed:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Info("GetPatchets() len(body)", len(body))
|
||||||
|
var psets *Patchsets
|
||||||
|
psets = new(Patchsets)
|
||||||
|
err = psets.Unmarshal(body)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Unmarshal failed", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
filename := filepath.Join("/tmp", pbfile)
|
||||||
|
f, _ := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
f.Write(body)
|
||||||
|
f.Close()
|
||||||
|
*/
|
||||||
|
return psets, nil
|
||||||
|
}
|
|
@ -0,0 +1,272 @@
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// creates a patchset
|
||||||
|
// works from the user branches against the devel branches
|
||||||
|
func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
|
||||||
|
pset := new(Patchset)
|
||||||
|
pset.Name = name
|
||||||
|
pset.Ctime = timestamppb.New(time.Now())
|
||||||
|
pset.Uuid = uuid.New().String()
|
||||||
|
if os.Getenv("GIT_AUTHOR_NAME") == "" {
|
||||||
|
return nil, fmt.Errorf("GIT_AUTHOR_NAME not set")
|
||||||
|
} else {
|
||||||
|
pset.GitAuthorName = os.Getenv("GIT_AUTHOR_NAME")
|
||||||
|
}
|
||||||
|
if os.Getenv("GIT_AUTHOR_EMAIL") == "" {
|
||||||
|
return nil, fmt.Errorf("GIT_AUTHOR_EMAIL not set")
|
||||||
|
} else {
|
||||||
|
pset.GitAuthorEmail = os.Getenv("GIT_AUTHOR_EMAIL")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := os.MkdirTemp("", "forge")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir) // clean up
|
||||||
|
pset.TmpDir = dir
|
||||||
|
|
||||||
|
all := f.Repos.SortByFullPath()
|
||||||
|
for all.Scan() {
|
||||||
|
repo := all.Next()
|
||||||
|
|
||||||
|
if !repo.IsLocalBranch(repo.GetUserBranchName()) {
|
||||||
|
// log.Info("repo doesn't have user branch", repo.GetGoPath())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !repo.IsLocalBranch(repo.GetDevelBranchName()) {
|
||||||
|
// log.Info("repo doesn't have devel branch", repo.GetGoPath())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a patchset from user to devel
|
||||||
|
// TODO: verify branches are otherwise exact
|
||||||
|
pset.StartBranchName = repo.GetDevelBranchName()
|
||||||
|
pset.EndBranchName = repo.GetUserBranchName()
|
||||||
|
|
||||||
|
err := pset.makePatchSetNew(repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) SubmitDevelPatchSet(name string) (*Patchset, error) {
|
||||||
|
pset, err := f.MakeDevelPatchSet(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := f.submitPatchset(pset); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) MakeMasterPatchSet() (*Patchset, error) {
|
||||||
|
pset := new(Patchset)
|
||||||
|
dir, err := os.MkdirTemp("", "forge")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir) // clean up
|
||||||
|
pset.TmpDir = dir
|
||||||
|
|
||||||
|
all := f.Repos.SortByFullPath()
|
||||||
|
for all.Scan() {
|
||||||
|
repo := all.Next()
|
||||||
|
startb := repo.GetMasterBranchName()
|
||||||
|
endb := repo.GetUserBranchName()
|
||||||
|
|
||||||
|
if startb == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if endb == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// log.Info("repo", repo.GetGoPath(), startb, "..", endb)
|
||||||
|
pset.StartBranchName = startb
|
||||||
|
pset.EndBranchName = endb
|
||||||
|
err := pset.makePatchSetNew(repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
|
||||||
|
startBranch := pset.StartBranchName
|
||||||
|
endBranch := pset.EndBranchName
|
||||||
|
repoDir := filepath.Join(pset.TmpDir, repo.GetGoPath())
|
||||||
|
err := os.MkdirAll(repoDir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe better? maybe worse?
|
||||||
|
// git format-patch -o patches --stdout <commit-range> > my-patch.mbox
|
||||||
|
// git format-patch --stdout -5 > my-patch.mbox # last 5 patches
|
||||||
|
// git am < my-patch.mbox
|
||||||
|
// git format-patch branch1..branch2
|
||||||
|
// export GIT_COMMITTER_DATE="2024-01-01T12:00:00"
|
||||||
|
// export GIT_AUTHOR_DATE="2024-01-01T12:00:00"
|
||||||
|
// export GIT_COMMITTER_NAME="Your Name"
|
||||||
|
// export GIT_COMMITTER_EMAIL="your.email@example.com"
|
||||||
|
// export GIT_AUTHOR_NAME="Your Name"
|
||||||
|
// export GIT_AUTHOR_EMAIL="your.email@example.com"
|
||||||
|
// git am < patch.mbox
|
||||||
|
|
||||||
|
cmd := []string{"git", "format-patch", "-o", repoDir, startBranch + ".." + endBranch}
|
||||||
|
r := repo.Run(cmd)
|
||||||
|
if r.Error != nil {
|
||||||
|
log.Info("git format-patch", repo.FullPath)
|
||||||
|
log.Info("git format-patch", cmd)
|
||||||
|
log.Info("git format-patch error", r.Error)
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
if r.Exit != 0 {
|
||||||
|
log.Info("git format-patch", repo.FullPath)
|
||||||
|
log.Info("git format-patch", cmd)
|
||||||
|
log.Info("git format-patch exit", r.Exit)
|
||||||
|
return errors.New(fmt.Sprintf("git returned %d", r.Exit))
|
||||||
|
}
|
||||||
|
if len(r.Stdout) == 0 {
|
||||||
|
// git created no files to add
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pset.addPatchFiles(repo)
|
||||||
|
pset.Ctime = timestamppb.New(time.Now())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// process each file in pDir/
|
||||||
|
func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
||||||
|
psetDir := repo.GetGoPath()
|
||||||
|
tmpDir := p.TmpDir
|
||||||
|
// log.Info("ADD PATCH FILES ADDED DIR", tmpDir)
|
||||||
|
fullDir := filepath.Join(tmpDir, psetDir)
|
||||||
|
var baderr error
|
||||||
|
filepath.Walk(fullDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
// Handle possible errors, like permission issues
|
||||||
|
fmt.Fprintf(os.Stderr, "error accessing path %q: %v\n", path, err)
|
||||||
|
baderr = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// log.Info("IS THIS A FULL PATH ?", path)
|
||||||
|
// log.Info("trim this from path ?", fullDir)
|
||||||
|
// log.Info("trim this from path ?", psetDir)
|
||||||
|
// log.Info("trim this from path ?", tmpDir)
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("addPatchFile() failed", path)
|
||||||
|
baderr = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
patch := new(Patch)
|
||||||
|
patch.Filename, _ = filepath.Rel(p.TmpDir, path)
|
||||||
|
patch.Data = data
|
||||||
|
patch.parseData()
|
||||||
|
patch.StartHash = repo.DevelHash()
|
||||||
|
patch.NewHash = "na"
|
||||||
|
patch.RepoNamespace = repo.GetGoPath()
|
||||||
|
if p.Patches == nil {
|
||||||
|
p.Patches = new(Patches)
|
||||||
|
}
|
||||||
|
p.Patches.Append(patch)
|
||||||
|
p.Patches.Uuid = uuid.New().String()
|
||||||
|
// log.Info("ADDED PATCH FILE", path)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return baderr
|
||||||
|
}
|
||||||
|
|
||||||
|
// looks at the git format-patch output
|
||||||
|
// saves the commit Hash
|
||||||
|
// saves the diff lines
|
||||||
|
func (p *Patch) parseData() string {
|
||||||
|
lines := strings.Split(string(p.Data), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch fields[0] {
|
||||||
|
case "From":
|
||||||
|
p.CommitHash = fields[1]
|
||||||
|
case "Subject:":
|
||||||
|
p.Comment = line
|
||||||
|
case "diff":
|
||||||
|
p.Files = append(p.Files, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// just an example of how to walk only directories
|
||||||
|
func onlyWalkDirs(pDir string) error {
|
||||||
|
log.Info("DIR", pDir)
|
||||||
|
// var all []string
|
||||||
|
var baderr error
|
||||||
|
filepath.WalkDir(pDir, func(path string, d os.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
// Handle possible errors, like permission issues
|
||||||
|
fmt.Fprintf(os.Stderr, "error accessing path %q: %v\n", path, err)
|
||||||
|
baderr = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("TESTING DIR", path)
|
||||||
|
if d.IsDir() {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
log.Info("NEVER GETS HERE? WHAT IS THIS?", path)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return baderr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) submitPatchset(pset *Patchset) error {
|
||||||
|
var url string
|
||||||
|
url = forgeURL + "patchset"
|
||||||
|
msg, err := pset.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
log.Info("proto.Marshal() failed:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("proto.Marshal() msg len", len(msg))
|
||||||
|
body, err := f.HttpPost(url, msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("httpPost() failed:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
test := strings.TrimSpace(string(body))
|
||||||
|
lines := strings.Split(test, "\n")
|
||||||
|
count := 0
|
||||||
|
for _, line := range lines {
|
||||||
|
log.Info("got back:", line)
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
log.Info("Total patches:", count)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
// functions to import and export the protobuf
|
||||||
|
// data to and from config files
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Forge) SendPatchSet(pset *Patchset) error {
|
||||||
|
var err error
|
||||||
|
data, err := pset.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
log.Info("proto.Marshal() pset(len) error", len(data), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
timestamp := now.Format("2006.01.02.150405") // bummer. other date doesn't work?
|
||||||
|
cfgfile := "patchset/patchset." + timestamp + ".pb"
|
||||||
|
log.Info("proto.Marshal() pset(len)", len(data))
|
||||||
|
configWrite(cfgfile, data)
|
||||||
|
|
||||||
|
return errors.New("don't know how to send yet")
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package forgepb;
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
||||||
|
|
||||||
|
// Forge doesn't need this kind of specificity
|
||||||
|
// but this is what the patch files contain and how git sees them
|
||||||
|
// message Blob {
|
||||||
|
// string hunkLine = 1;
|
||||||
|
// bytes data = 2;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// message File {
|
||||||
|
// string filename = 1;
|
||||||
|
// string hashLine = 2;
|
||||||
|
// repeated Blob Blobs = 3;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// message Patch {
|
||||||
|
// repeated File Files = 1;
|
||||||
|
// string repoNamespace = 2;
|
||||||
|
// string gH = 3;
|
||||||
|
// string gaI = 4;
|
||||||
|
// string gcI = 5;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// git log -1 --format="%H %aI %cI %an %ae %cn %ce"
|
||||||
|
message Patch {
|
||||||
|
string repoNamespace = 1; // the base repo git URL
|
||||||
|
bytes data = 2; // the raw data of the whole patch
|
||||||
|
string gH = 3; // after some deliberation, I think I'll just try variable names
|
||||||
|
string gT = 4;
|
||||||
|
string gP = 5;
|
||||||
|
string gs = 6;
|
||||||
|
string gaI = 7; // that exactly match what git uses.
|
||||||
|
string gan = 8;
|
||||||
|
string gae = 9;
|
||||||
|
string gcI = 10;
|
||||||
|
string gcn = 11;
|
||||||
|
string gce = 12;
|
||||||
|
string gN = 13;
|
||||||
|
string gGG = 14;
|
||||||
|
string gGS = 15;
|
||||||
|
string gGK = 16;
|
||||||
|
string newHash = 17; // new hash
|
||||||
|
string state = 18; // the 'state' of the patch
|
||||||
|
string filename = 19; // `autogenpb:unique` `autogenpb:sort`
|
||||||
|
string startHash = 20; // the start commit hash
|
||||||
|
string commitHash = 21; // the git commit hash of this patch
|
||||||
|
string comment = 22; // the git commit message (in patch form)
|
||||||
|
repeated string Files = 23; // the filenames this patch changes
|
||||||
|
google.protobuf.Timestamp ctime = 24; // create time of the patch
|
||||||
|
bool applied = 25; // have you applied this patch?
|
||||||
|
bool upstream = 26; // has this patch been applied upstream?
|
||||||
|
}
|
||||||
|
|
||||||
|
message Patches { // this is a "PATCH: [1/x]" series `autogenpb:gui:Patch`
|
||||||
|
string uuid = 1; // `autogenpb:uuid:be926ad9-1111-484c-adf2-d96eeabf3079`
|
||||||
|
string version = 2; // `autogenpb:version:v0.0.45`
|
||||||
|
repeated Patch Patches = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Patchset { // `autogenpb:marshal`
|
||||||
|
Patches patches = 1; //
|
||||||
|
string name = 2; // `autogenpb:sort`
|
||||||
|
string comment = 3; //
|
||||||
|
string gitAuthorName = 4; // `autogenpb:sort`
|
||||||
|
string gitAuthorEmail = 5; //
|
||||||
|
google.protobuf.Timestamp ctime = 6; // create time of the patchset
|
||||||
|
string tmpDir = 7; // temp dir
|
||||||
|
string startBranchName = 8; //
|
||||||
|
string endBranchName = 9; //
|
||||||
|
string startBranchHash = 10; //
|
||||||
|
string endBranchHash = 11; //
|
||||||
|
string state = 12; // the state of the patch
|
||||||
|
string uuid = 13; // `autogenpb:sort` `autogenpb:unique`
|
||||||
|
}
|
||||||
|
|
||||||
|
message Patchsets { // `autogenpb:marshal` `autogenpb:gui`
|
||||||
|
string uuid = 1; // `autogenpb:uuid:be926ad9-f07f-484c-adf2-d96eeabf3079`
|
||||||
|
string version = 2; // `autogenpb:version:v0.0.45`
|
||||||
|
repeated Patchset Patchsets = 3;
|
||||||
|
}
|
278
repoClone.go
278
repoClone.go
|
@ -1,278 +0,0 @@
|
||||||
package forgepb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
// guess the paths. returns
|
|
||||||
|
|
||||||
// realpath : the actual path on the filesystem
|
|
||||||
// goSrcPath : this could be ~/go/src, or where the go.work file is
|
|
||||||
|
|
||||||
// goPath : go.wit.com/lib/gui/repostatus (for example)
|
|
||||||
// true/false if the repo is a golang repo
|
|
||||||
func (f *Forge) guessPaths(path string) (string, string, string, bool, error) {
|
|
||||||
var realpath, goSrcDir string
|
|
||||||
var isGoLang bool = false
|
|
||||||
|
|
||||||
homeDir, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
log.Log(FORGEPBWARN, "Error getting home directory:", err)
|
|
||||||
return path, realpath, goSrcDir, false, err
|
|
||||||
}
|
|
||||||
goSrcDir = filepath.Join(homeDir, "go/src")
|
|
||||||
|
|
||||||
// allow arbitrary paths, otherwise, assume the repo is in ~/go/src
|
|
||||||
// unless REPO_WORK_PATH was set. to over-ride ~/go/src
|
|
||||||
// todo, look for go.work
|
|
||||||
if os.Getenv("REPO_WORK_PATH") == "" {
|
|
||||||
os.Setenv("REPO_WORK_PATH", goSrcDir)
|
|
||||||
} else {
|
|
||||||
goSrcDir = os.Getenv("REPO_WORK_PATH")
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: this is dumb
|
|
||||||
if strings.HasPrefix(path, "/") {
|
|
||||||
realpath = path
|
|
||||||
} else if strings.HasPrefix(path, "~") {
|
|
||||||
tmp := strings.TrimPrefix(path, "~")
|
|
||||||
realpath = filepath.Join(homeDir, tmp)
|
|
||||||
} else {
|
|
||||||
realpath = filepath.Join(goSrcDir, path)
|
|
||||||
isGoLang = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.Getenv("REPO_AUTO_CLONE") == "true" {
|
|
||||||
err := f.Clone(goSrcDir, path)
|
|
||||||
if err != nil {
|
|
||||||
// directory doesn't exist. exit with nil and error nil
|
|
||||||
return path, realpath, goSrcDir, false, errors.New("git clone")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !IsDirectory(realpath) {
|
|
||||||
log.Log(FORGEPBWARN, "directory doesn't exist", realpath)
|
|
||||||
// directory doesn't exist. exit with nil and error nil
|
|
||||||
return path, realpath, goSrcDir, false, errors.New(realpath + " does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := filepath.Join(realpath, ".git/config")
|
|
||||||
|
|
||||||
_, err = os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
// log.Log(WARN, "Error reading .git/config:", filename, err)
|
|
||||||
// log.Log(WARN, "TODO: find .git/config in parent directory")
|
|
||||||
return path, realpath, goSrcDir, false, err
|
|
||||||
}
|
|
||||||
return path, realpath, goSrcDir, isGoLang, nil
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: make some config file for things like this
|
|
||||||
// can be used to work around temporary problems
|
|
||||||
func clonePathHack(dirname string, basedir string, gopath string) error {
|
|
||||||
// newdir = helloworld
|
|
||||||
// basedir = /home/jcarr/go/src/go.wit.com/apps
|
|
||||||
// giturl = https://gitea.wit.com/gui/helloworld
|
|
||||||
// func cloneActual(newdir, basedir, giturl string) error {
|
|
||||||
|
|
||||||
switch gopath {
|
|
||||||
case "golang.org/x/crypto":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/crypto")
|
|
||||||
case "golang.org/x/mod":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/mod")
|
|
||||||
case "golang.org/x/net":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/net")
|
|
||||||
case "golang.org/x/sys":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/sys")
|
|
||||||
case "golang.org/x/sync":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/sync")
|
|
||||||
case "golang.org/x/term":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/term")
|
|
||||||
case "golang.org/x/text":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/text")
|
|
||||||
case "golang.org/x/tools":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/tools")
|
|
||||||
case "golang.org/x/xerrors":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/xerrors")
|
|
||||||
case "google.golang.org/protobuf":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/protobuf")
|
|
||||||
case "google.golang.org/genproto":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/genproto")
|
|
||||||
case "google.golang.org/api":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/api")
|
|
||||||
case "google.golang.org/grpc":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/grpc")
|
|
||||||
case "google.golang.org/appengine":
|
|
||||||
return cloneActual(dirname, basedir, "https://"+"go.googlesource.com/appengine")
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("no gopath override here")
|
|
||||||
}
|
|
||||||
|
|
||||||
// attempt to git clone if the go path doesn't exist
|
|
||||||
// does a git clone, if it works, returns true
|
|
||||||
// workdir = /home/jcarr/go/src/
|
|
||||||
// gopath = go.wit.com/apps/helloworld
|
|
||||||
func (f *Forge) Clone(gopath string) (*gitpb.Repo, error) {
|
|
||||||
var err error
|
|
||||||
pb, err := f.Repos.NewGoPath(f.goSrc, gopath)
|
|
||||||
if err == nil {
|
|
||||||
return pb, err
|
|
||||||
}
|
|
||||||
workdir := f.goSrc
|
|
||||||
fullpath := filepath.Join(workdir, gopath)
|
|
||||||
dirname := filepath.Base(fullpath)
|
|
||||||
|
|
||||||
basedir := strings.TrimSuffix(fullpath, dirname)
|
|
||||||
|
|
||||||
url := "https://" + gopath
|
|
||||||
log.Info("trying git clone")
|
|
||||||
log.Info("gopath =", gopath)
|
|
||||||
|
|
||||||
// try a direct git clone against the gopath
|
|
||||||
// cloneActual("helloworld", "/home/jcarr/go/src/go.wit.com/apps", "https://go.wit.com/apps/helloworld")
|
|
||||||
if err = cloneActual(dirname, basedir, url); err == nil {
|
|
||||||
// git clone worked!
|
|
||||||
return f.Repos.NewGoPath(f.goSrc, gopath)
|
|
||||||
}
|
|
||||||
log.Info("direct attempt at git clone failed", url)
|
|
||||||
|
|
||||||
// if direct git clone doesn't work, look for a redirect
|
|
||||||
// go directly to the URL as that is autoritive. If that failes
|
|
||||||
// try the go package system as maybe the git site no longer exists
|
|
||||||
if url, err = findGoImport(url); err != nil {
|
|
||||||
log.Info("findGoImport() DID NOT WORK", url)
|
|
||||||
log.Info("findGoImport() DID NOT WORK", err)
|
|
||||||
} else {
|
|
||||||
if err := cloneActual(dirname, basedir, url); err == nil {
|
|
||||||
// git clone worked!
|
|
||||||
return f.Repos.NewGoPath(f.goSrc, gopath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Info("git clone from 'go-import' info failed", url)
|
|
||||||
|
|
||||||
// query the golang package system for the last known location
|
|
||||||
// NOTE: take time to thank the go developers and google for designing this wonderful system
|
|
||||||
if url, err = runGoList(gopath); err != nil {
|
|
||||||
log.Info("go list failed", err)
|
|
||||||
} else {
|
|
||||||
if err := cloneActual(dirname, basedir, url); err == nil {
|
|
||||||
// git clone worked!
|
|
||||||
return f.Repos.NewGoPath(f.goSrc, gopath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Info("git clone from 'git list' info failed", url)
|
|
||||||
|
|
||||||
// try to parse a redirect
|
|
||||||
|
|
||||||
if err = clonePathHack(dirname, basedir, gopath); err == nil {
|
|
||||||
// WTF didn't go-import or go list work?
|
|
||||||
return f.Repos.NewGoPath(f.goSrc, gopath)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("can not find git sources for gopath " + gopath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newdir = helloworld
|
|
||||||
// basedir = /home/jcarr/go/src/go.wit.com/apps
|
|
||||||
// giturl = https://gitea.wit.com/gui/helloworld
|
|
||||||
func cloneActual(newdir, basedir, giturl string) error {
|
|
||||||
log.Info("cloneActual() newdir =", newdir)
|
|
||||||
log.Info("cloneActual() basedir =", basedir)
|
|
||||||
log.Info("cloneActual() giturl =", giturl)
|
|
||||||
if !IsDirectory(basedir) {
|
|
||||||
os.MkdirAll(basedir, 0750)
|
|
||||||
}
|
|
||||||
err := os.Chdir(basedir)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("chdir failed", basedir, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := []string{"git", "clone", "--verbose", "--progress", giturl, newdir}
|
|
||||||
log.Info("Running:", cmd)
|
|
||||||
r := shell.PathRunRealtime(basedir, cmd)
|
|
||||||
if r.Error != nil {
|
|
||||||
log.Warn("git clone error", r.Error)
|
|
||||||
return r.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
fullpath := filepath.Join(basedir, newdir)
|
|
||||||
if !IsDirectory(fullpath) {
|
|
||||||
log.Info("git clone failed", giturl)
|
|
||||||
return errors.New("git clone failed " + giturl)
|
|
||||||
}
|
|
||||||
gitdir := filepath.Join(fullpath, ".git")
|
|
||||||
if IsDirectory(gitdir) {
|
|
||||||
log.Info("git cloned worked to", fullpath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// git clone didn't really work but did make a directory
|
|
||||||
log.Info("fullpath is probably empty", fullpath)
|
|
||||||
return errors.New("crapnuts. rmdir fullpath here? " + fullpath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the server for the current go path to git url mapping
|
|
||||||
// for example:
|
|
||||||
// This will check go.wit.com for "go.wit.com/apps/go-clone"
|
|
||||||
// and return where the URL to do git clone should be
|
|
||||||
func findGoImport(url string) (string, error) {
|
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp := string(bodyBytes)
|
|
||||||
parts := strings.Split(tmp, "go-import")
|
|
||||||
if len(parts) < 2 {
|
|
||||||
return "", errors.New("missing go-import")
|
|
||||||
}
|
|
||||||
// this is terrible, it doesn't even look for 'content='
|
|
||||||
// but again, this is just a hack for my own code to be
|
|
||||||
// usuable after the removal in go v1.22 of the go get clone behavior that was in v1.21
|
|
||||||
parts = strings.Split(parts[1], "\"")
|
|
||||||
var newurl string
|
|
||||||
for {
|
|
||||||
if len(parts) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
tmp := strings.TrimSpace(parts[0])
|
|
||||||
fields := strings.Split(tmp, " ")
|
|
||||||
// log.Info("FIELDS:", fields)
|
|
||||||
if len(fields) == 3 {
|
|
||||||
newurl = fields[2]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
parts = parts[1:]
|
|
||||||
}
|
|
||||||
if newurl == "" {
|
|
||||||
return "", errors.New("missing git content string")
|
|
||||||
}
|
|
||||||
|
|
||||||
return newurl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsDirectory(path string) bool {
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return info.IsDir()
|
|
||||||
}
|
|
274
repoNew.go
274
repoNew.go
|
@ -1,87 +1,241 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os/user"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
func (f *Forge) NewGoRepo(gopath string, url string) (*gitpb.Repo, error) {
|
||||||
func (f *Forge) NewGoPath(gopath string) (*gitpb.Repo, error) {
|
fullpath := filepath.Join(f.GetGoSrc(), gopath)
|
||||||
newr, err := f.Repos.NewGoPath(f.goSrc, gopath)
|
test := f.Repos.FindByFullPath(fullpath)
|
||||||
|
if test != nil {
|
||||||
|
return test, nil
|
||||||
|
}
|
||||||
|
repo, err := f.Repos.NewGoRepo(fullpath, gopath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(FORGEPBWARN, "init failed", err)
|
log.Info("WARNING. NEW FAILED", fullpath)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Info("init worked for", newr.GoPath)
|
// slices.Reverse(f.Repos.Repos)
|
||||||
// try to guess what the 'master' branch is
|
|
||||||
if newr.IsBranch("guimaster") {
|
|
||||||
newr.SetMasterBranchName("guimaster")
|
|
||||||
} else if newr.IsBranch("master") {
|
|
||||||
newr.SetMasterBranchName("master")
|
|
||||||
} else if newr.IsBranch("main") {
|
|
||||||
newr.SetMasterBranchName("main")
|
|
||||||
} else {
|
|
||||||
newr.SetMasterBranchName("masterFIXME")
|
|
||||||
}
|
|
||||||
|
|
||||||
if newr.IsBranch("guidevel") {
|
repo.URL = url
|
||||||
newr.SetDevelBranchName("guidevel")
|
f.VerifyBranchNames(repo)
|
||||||
} else if newr.IsBranch("devel") {
|
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||||
newr.SetDevelBranchName("devel")
|
repo.ReadOnly = true
|
||||||
} else {
|
|
||||||
newr.SetDevelBranchName("develFIXME")
|
|
||||||
}
|
}
|
||||||
|
repo.Reload()
|
||||||
usr, _ := user.Current()
|
return repo, nil
|
||||||
uname := usr.Username
|
|
||||||
if newr.IsBranch(uname) {
|
|
||||||
newr.SetUserBranchName(uname)
|
|
||||||
} else {
|
|
||||||
newr.SetUserBranchName(uname + "FIXME")
|
|
||||||
}
|
|
||||||
f.Repos.ConfigSave()
|
|
||||||
return newr, err
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
func (f *Forge) VerifyBranchNames(newr *gitpb.Repo) {
|
func isValidSemVer(version string) bool {
|
||||||
// log.Info("init worked for", newr.GoPath)
|
// Regular expression for semantic versioning
|
||||||
|
regex := `^v(\d+)\.(\d+)\.(\d+)$`
|
||||||
|
matched, _ := regexp.MatchString(regex, version)
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
|
||||||
if newr.GetMasterBranchName() == "" {
|
// golang versions MUST be vX.X.X
|
||||||
// try to guess what the 'master' branch is
|
// it can not be vX.X and it also can not be v0.0.0
|
||||||
if newr.IsBranch("guimaster") {
|
// verifies the version is format v3.2.1
|
||||||
newr.SetMasterBranchName("guimaster")
|
// this is retardedly wrong. it needs to be done right
|
||||||
} else if newr.IsBranch("master") {
|
func (f *Forge) ValidGoVersion(ver string) (bool, error) {
|
||||||
newr.SetMasterBranchName("master")
|
return ValidGoVersion(ver)
|
||||||
} else if newr.IsBranch("main") {
|
}
|
||||||
newr.SetMasterBranchName("main")
|
|
||||||
} else {
|
func ValidGoVersion(ver string) (bool, error) {
|
||||||
newr.SetMasterBranchName("masterFIXME")
|
if ver == "v0.0.0" {
|
||||||
|
return false, fmt.Errorf("golang does not allow version v0.0.0")
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(ver, "v") {
|
||||||
|
return false, fmt.Errorf("(%s) invalid. golang versions must start with a 'v'", ver)
|
||||||
|
}
|
||||||
|
xver := ver[1:]
|
||||||
|
parts := strings.Split(xver, ".")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return false, fmt.Errorf("(%s) invalid. golang versions must have exactly 3 numbers (v1.2.3)", ver)
|
||||||
|
}
|
||||||
|
if isValidSemVer(ver) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out what the name of the git master branch is
|
||||||
|
// also check the forge config
|
||||||
|
func (f *Forge) findMasterBranch(repo *gitpb.Repo) {
|
||||||
|
// check the forge config first
|
||||||
|
if bname := f.Config.FindMasterBranch(repo.GetGoPath()); bname != "" {
|
||||||
|
log.Info("Using master branch name from forge config:", bname)
|
||||||
|
repo.SetMasterBranchName(bname)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// todo: fix this after .git parsing is better or libgit2 is being used
|
||||||
|
headfile := filepath.Join(repo.GetFullPath(), ".git/refs/remotes/origin/HEAD")
|
||||||
|
if data, err := os.ReadFile(headfile); err == nil {
|
||||||
|
s := string(data)
|
||||||
|
if strings.HasPrefix(s, "ref: ") {
|
||||||
|
fields := strings.Fields(s)
|
||||||
|
_, bname := filepath.Split(fields[1])
|
||||||
|
// log.Info("Using master branch name from .git/ HEAD:", bname)
|
||||||
|
repo.SetMasterBranchName(bname)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.IsReadOnly(newr.GoPath) {
|
// try to guess what the 'master' branch is
|
||||||
|
if repo.IsBranch("master") {
|
||||||
|
repo.SetMasterBranchName("master")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if newr.GetDevelBranchName() == "" {
|
if repo.IsBranch("main") {
|
||||||
if newr.IsBranch("guidevel") {
|
repo.SetMasterBranchName("main")
|
||||||
newr.SetDevelBranchName("guidevel")
|
return
|
||||||
} else if newr.IsBranch("devel") {
|
|
||||||
newr.SetDevelBranchName("devel")
|
|
||||||
} else {
|
|
||||||
newr.SetDevelBranchName("develFIXME")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if newr.GetUserBranchName() == "" {
|
// TODO: figure out the name from git
|
||||||
usr, _ := user.Current()
|
repo.SetMasterBranchName("master")
|
||||||
uname := usr.Username
|
/* no longer checkout on Init()
|
||||||
if newr.IsBranch(uname) {
|
if repo.CheckoutMaster() {
|
||||||
newr.SetUserBranchName(uname)
|
} else {
|
||||||
|
cmd := []string{"git", "branch", "master"}
|
||||||
|
repo.Run(cmd)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out what the name of the git devel branch is
|
||||||
|
// also check the forge config
|
||||||
|
func (f *Forge) findDevelBranch(repo *gitpb.Repo) {
|
||||||
|
// check the forge config first
|
||||||
|
if bname := f.Config.FindDevelBranch(repo.GetGoPath()); bname != "" {
|
||||||
|
repo.SetDevelBranchName(bname)
|
||||||
|
/* no longer checkout on Init()
|
||||||
|
if repo.CheckoutDevel() {
|
||||||
} else {
|
} else {
|
||||||
newr.SetUserBranchName(uname + "FIXME")
|
cmd := []string{"git", "branch", bname}
|
||||||
|
repo.Run(cmd)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.IsBranch("devel") {
|
||||||
|
repo.SetDevelBranchName("devel")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.SetDevelBranchName("devel")
|
||||||
|
/* no longer checkout on Init()
|
||||||
|
if repo.CheckoutDevel() {
|
||||||
|
} else {
|
||||||
|
cmd := []string{"git", "branch", "devel"}
|
||||||
|
repo.Run(cmd)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is still in flux
|
||||||
|
func (f *Forge) VerifyBranchNames(repo *gitpb.Repo) {
|
||||||
|
// log.Info("init worked for", repo.GoPath)
|
||||||
|
|
||||||
|
if repo.GetMasterBranchName() == "" {
|
||||||
|
f.findMasterBranch(repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.GetDevelBranchName() == "" {
|
||||||
|
f.findDevelBranch(repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.GetUserBranchName() == "" {
|
||||||
|
uname := f.configUserBranchName(repo)
|
||||||
|
if repo.IsBranch(uname) {
|
||||||
|
repo.SetUserBranchName(uname)
|
||||||
|
} else {
|
||||||
|
// forcing for now. todo: warn users
|
||||||
|
repo.SetUserBranchName(uname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// what name should be used for the user branch?
|
||||||
|
func (f *Forge) configUserBranchName(repo *gitpb.Repo) string {
|
||||||
|
// look in the repo record for the username
|
||||||
|
uname := repo.GetUserBranchName()
|
||||||
|
if uname != "" {
|
||||||
|
return uname
|
||||||
|
}
|
||||||
|
|
||||||
|
// query the the forge config for a custom User Branch Name
|
||||||
|
uname = f.Config.FindUserBranch(repo.GetGoPath())
|
||||||
|
if uname != "" {
|
||||||
|
return uname
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the os.Username
|
||||||
|
uname = f.Config.Username
|
||||||
|
return uname
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: check the forge config
|
||||||
|
func (f *Forge) configDevelBranchName(repo *gitpb.Repo) string {
|
||||||
|
if repo.GetDevelBranchName() != "" {
|
||||||
|
return repo.GetDevelBranchName()
|
||||||
|
}
|
||||||
|
if repo.IsBranch("guidevel") {
|
||||||
|
return "guidevel"
|
||||||
|
}
|
||||||
|
if repo.IsBranch("devel") {
|
||||||
|
return "devel"
|
||||||
|
}
|
||||||
|
return "devel"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) AddFullPath(fulldir string) *gitpb.Repo {
|
||||||
|
repo := f.Repos.FindByFullPath(fulldir)
|
||||||
|
if repo != nil {
|
||||||
|
return repo
|
||||||
|
}
|
||||||
|
log.Info("don't know how to add full paths yet", fulldir)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) FindByGoPath(gopath string) *gitpb.Repo {
|
||||||
|
fullpath := filepath.Join(f.GetGoSrc(), gopath)
|
||||||
|
return f.Repos.FindByFullPath(fullpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tries any parent
|
||||||
|
func (f *Forge) FindAnyPath(dir string) *gitpb.Repo {
|
||||||
|
dir = filepath.Clean(dir)
|
||||||
|
repo := f.Repos.FindByFullPath(dir)
|
||||||
|
if repo != nil {
|
||||||
|
log.Info("FindAnyPath() worked = ", dir)
|
||||||
|
return repo
|
||||||
|
}
|
||||||
|
if dir == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if dir == "/" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
basedir, newdir := filepath.Split(dir)
|
||||||
|
if newdir == "" {
|
||||||
|
// is this correct? not sure what stupid windows does
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f.FindAnyPath(basedir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) DeleteByGoPath(gopath string) bool {
|
||||||
|
fullpath := filepath.Join(f.GetGoSrc(), gopath)
|
||||||
|
return f.Repos.DeleteByFullPath(fullpath)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/destel/rill"
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rill is awesome. long live rill
|
||||||
|
// attempt scan with rill
|
||||||
|
func (f *Forge) rillUpdate(pool1 int, pool2 int) (int, error) {
|
||||||
|
var repos []*gitpb.Repo
|
||||||
|
all := f.Repos.SortByFullPath()
|
||||||
|
for all.Scan() {
|
||||||
|
repo := all.Next()
|
||||||
|
repos = append(repos, repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a slice of user IDs into a channel
|
||||||
|
ids := rill.FromSlice(repos, nil)
|
||||||
|
|
||||||
|
// Read users from the API.
|
||||||
|
// Concurrency = 20
|
||||||
|
rills := rill.Map(ids, pool1, func(repo *gitpb.Repo) (*gitpb.Repo, error) {
|
||||||
|
return repo, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
var counter int
|
||||||
|
// Activate users.
|
||||||
|
// Concurrency = 10
|
||||||
|
err := rill.ForEach(rills, pool2, func(repo *gitpb.Repo) error {
|
||||||
|
counter += 1
|
||||||
|
// log.Info("rill.ForEach() gopath=", repo.GetGoPath())
|
||||||
|
return f.updateRepo(repo)
|
||||||
|
})
|
||||||
|
|
||||||
|
return counter, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) updateRepo(repo *gitpb.Repo) error {
|
||||||
|
if !repo.IsValidDir() {
|
||||||
|
log.Printf("%10s %-50s gopath=%s\n", "git dir is missing:", repo.FullPath, repo.GetGoPath())
|
||||||
|
f.Repos.DeleteByFullPath(repo.FullPath)
|
||||||
|
f.configSave = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.DidRepoChange() {
|
||||||
|
f.configSave = true
|
||||||
|
log.Info("repo changed ", repo.FullPath, repo.StateChange)
|
||||||
|
if err := repo.Reload(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// log.Info("repo did not change", repo.FullPath, repo.StateChange)
|
||||||
|
}
|
||||||
|
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||||
|
if repo.ReadOnly {
|
||||||
|
} else {
|
||||||
|
log.Info("readonly flag on repo is wrong", repo.GetGoPath())
|
||||||
|
repo.ReadOnly = true
|
||||||
|
f.configSave = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var RillX int = 10
|
||||||
|
var RillY int = 10
|
||||||
|
|
||||||
|
// x is the size of the queued up pool (shouldn't matter here for this I think)
|
||||||
|
// y is how many simultanous functions will run
|
||||||
|
// todo: tune and compute x,y by # of CPUs and disk io
|
||||||
|
// todo: store x,y in forge config ? (or compute them. notsure)
|
||||||
|
func (f *Forge) RillReload() int {
|
||||||
|
var all []*gitpb.Repo
|
||||||
|
tmp := f.Repos.All()
|
||||||
|
for tmp.Scan() {
|
||||||
|
repo := tmp.Next()
|
||||||
|
if !repo.IsValidDir() {
|
||||||
|
log.Printf("%s %-50s", "got an invalid repo in forgepb.RillFuncError()", repo.GetGoPath())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
all = append(all, repo)
|
||||||
|
}
|
||||||
|
// Convert a slice of user IDs into a channel
|
||||||
|
ids := rill.FromSlice(all, nil)
|
||||||
|
|
||||||
|
var counter int
|
||||||
|
// Read users from the API.
|
||||||
|
// Concurrency = 20
|
||||||
|
dirs := rill.Map(ids, RillX, func(repo *gitpb.Repo) (*gitpb.Repo, error) {
|
||||||
|
return repo, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
rill.ForEach(dirs, RillY, func(repo *gitpb.Repo) error {
|
||||||
|
if !repo.DidRepoChange() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
f.configSave = true
|
||||||
|
repo.Reload()
|
||||||
|
counter += 1
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return counter
|
||||||
|
}
|
||||||
|
|
||||||
|
// x is the size of the queued up pool (shouldn't matter here for this I think)
|
||||||
|
// y is how many simultanous functions will run
|
||||||
|
// todo: tune and compute x,y by # of CPUs and disk io
|
||||||
|
// todo: store x,y in forge config ? (or compute them. notsure)
|
||||||
|
func (f *Forge) RillFuncError(rillf func(*gitpb.Repo) error) int {
|
||||||
|
var all []*gitpb.Repo
|
||||||
|
tmp := f.Repos.All()
|
||||||
|
for tmp.Scan() {
|
||||||
|
repo := tmp.Next()
|
||||||
|
if !repo.IsValidDir() {
|
||||||
|
log.Printf("%s %-50s", "got an invalid repo in forgepb.RillFuncError()", repo.GetGoPath())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
all = append(all, repo)
|
||||||
|
}
|
||||||
|
// Convert a slice of user IDs into a channel
|
||||||
|
ids := rill.FromSlice(all, nil)
|
||||||
|
|
||||||
|
var counter int
|
||||||
|
var watch int = 10
|
||||||
|
var meMu sync.Mutex
|
||||||
|
|
||||||
|
// Read users from the API.
|
||||||
|
// Concurrency = 20
|
||||||
|
dirs := rill.Map(ids, RillX, func(id *gitpb.Repo) (*gitpb.Repo, error) {
|
||||||
|
return id, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
err := rill.ForEach(dirs, RillY, func(repo *gitpb.Repo) error {
|
||||||
|
meMu.Lock()
|
||||||
|
counter += 1
|
||||||
|
if counter > watch {
|
||||||
|
// log.Info("Processed", watch, "repos") // this doesn't work
|
||||||
|
watch += 50
|
||||||
|
}
|
||||||
|
meMu.Unlock()
|
||||||
|
return rillf(repo)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Info("rill.ForEach() error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"go.wit.com/lib/gui/shell"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// git clone (also downloads git notes)
|
||||||
|
// newdir = helloworld
|
||||||
|
// basedir = /home/jcarr/go/src/go.wit.com/apps
|
||||||
|
// giturl = https://gitea.wit.com/gui/helloworld
|
||||||
|
func RunGitClone(newdir, basedir, giturl string) error {
|
||||||
|
log.Info("runGitClone() newdir =", newdir)
|
||||||
|
log.Info("runGitClone() basedir =", basedir)
|
||||||
|
log.Info("runGitClone() giturl =", giturl)
|
||||||
|
if !shell.IsDir(basedir) {
|
||||||
|
os.MkdirAll(basedir, os.ModePerm)
|
||||||
|
}
|
||||||
|
err := os.Chdir(basedir)
|
||||||
|
if err != nil {
|
||||||
|
// log.Warn("chdir failed", basedir, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := []string{"git", "clone", "--verbose", "--progress", giturl, newdir}
|
||||||
|
log.Info("Running:", basedir, cmd)
|
||||||
|
r := shell.PathRunRealtime(basedir, cmd)
|
||||||
|
if r.Error != nil {
|
||||||
|
// log.Warn("git clone error", r.Error)
|
||||||
|
return r.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
fullpath := filepath.Join(basedir, newdir)
|
||||||
|
if !shell.IsDir(fullpath) {
|
||||||
|
// log.Info("git clone failed", giturl)
|
||||||
|
return fmt.Errorf("git clone %s failed to create a directory", giturl)
|
||||||
|
}
|
||||||
|
gitdir := filepath.Join(fullpath, ".git")
|
||||||
|
if shell.IsDir(gitdir) {
|
||||||
|
// log.Info("git cloned worked to", fullpath)
|
||||||
|
// also clone notes -- this can store the go.mod and go.sum files
|
||||||
|
cmd := []string{"git", "fetch", "origin", "refs/notes/*:refs/notes/*"}
|
||||||
|
shell.PathRunRealtime(fullpath, cmd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// git clone didn't really work but did make a directory
|
||||||
|
log.Info("fullpath is probably empty", fullpath)
|
||||||
|
return errors.New("crapnuts. rmdir fullpath here? " + fullpath)
|
||||||
|
}
|
42
structs.go
42
structs.go
|
@ -2,24 +2,48 @@ package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
sync "sync"
|
sync "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
"go.wit.com/lib/protobuf/zoopb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// maybe an interface someday?
|
// maybe an interface someday?
|
||||||
type Forge struct {
|
type Forge struct {
|
||||||
// one-time initialized data
|
// one-time initialized data
|
||||||
initOnce sync.Once
|
initOnce sync.Once
|
||||||
initErr error // init error, if any
|
initErr error // init error, if any
|
||||||
|
goSrc string // the path to go/src
|
||||||
goSrc string // the path to go/src
|
configDir string // normally ~/.config/forge
|
||||||
goWork bool // means the user is currently using a go.work file
|
goWork bool // means the user is currently using a go.work file
|
||||||
Config *ForgeConfigs // config repos for readonly, private, etc
|
Config *ForgeConfigs // config repos for readonly, private, etc
|
||||||
Repos *gitpb.Repos
|
Repos *gitpb.Repos // the repo protobufs
|
||||||
Machine *zoopb.Machine
|
// Machine *zoopb.Machine // things for virtigo to track vm's
|
||||||
|
configSave bool // if you need to save the config because things changed
|
||||||
|
hasFullScan bool // track last scan so it can be throttled
|
||||||
|
fullscan time.Time // time of the last scan so it can be throttled
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Forge) GetGoSrc() string {
|
func (f *Forge) GetGoSrc() string {
|
||||||
return f.goSrc
|
return f.goSrc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Forge) GetConfigDir() string {
|
||||||
|
return f.configDir
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) IsGoWork() bool {
|
||||||
|
return f.goWork
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) HasFullScan() bool {
|
||||||
|
return f.Repos.HasFullScan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) FullScanRan() {
|
||||||
|
f.fullscan = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Forge) FullScanAge() time.Duration {
|
||||||
|
fs := f.Repos.FullScan.AsTime()
|
||||||
|
return time.Since(fs)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue