Compare commits
191 Commits
Author | SHA1 | Date |
---|---|---|
|
98ecc249c0 | |
|
cb6394f34a | |
|
2fccf60335 | |
|
2b3bd66ef2 | |
|
27a539b879 | |
|
0f895e83e6 | |
|
ce4af38e8b | |
|
d406ee5f21 | |
|
bc9509e43b | |
|
b21a47434e | |
|
07b0fcf3b6 | |
|
be1cd7372c | |
|
c99f9faf3c | |
|
692264d1f1 | |
|
7431308823 | |
|
b77555e9fa | |
|
e12a1fb496 | |
|
eda91e3af6 | |
|
6c3162f7ce | |
|
ef00352a0b | |
|
235fe57beb | |
|
81cac13284 | |
|
98407ed8b7 | |
|
8b41d89ab2 | |
|
f2ec2e74ee | |
|
74313bd867 | |
|
15d545f389 | |
|
c62f48ec16 | |
|
de30c30005 | |
|
b89a5571e9 | |
|
b231a2144d | |
|
98467dec72 | |
|
dbde2f51b8 | |
|
ac107331fc | |
|
e2e30d02d1 | |
|
556c549265 | |
|
18796d1a47 | |
|
da9221ce34 | |
|
a3007ba1b5 | |
|
43281eea6f | |
|
16b0cad836 | |
|
ecf4049947 | |
|
e3c8669be4 | |
|
964aaf823d | |
|
9dbc7c6615 | |
|
867a4f973e | |
|
3729df67a5 | |
|
bf031595e3 | |
|
c0b9518c0d | |
|
a9dbfb9201 | |
|
2015f84fb4 | |
|
822fe38eee | |
|
817107dc16 | |
|
4a27e7702b | |
|
0e05b773cf | |
|
625dfe2f68 | |
|
f54f189bcd | |
|
dbb0d9867d | |
|
8202a75eea | |
|
4cac4386f5 | |
|
89e6fd0805 | |
|
076bdfb9c6 | |
|
72e8d3659f | |
|
18aad5f1b5 | |
|
d44e359d70 | |
|
15fbb15da8 | |
|
e506a73349 | |
|
111c67e975 | |
|
961ca8f41e | |
|
d6cd7fc88f | |
|
50351298b6 | |
|
3ec4b29886 | |
|
d981b93728 | |
|
98cc06ab8f | |
|
888442708c | |
|
f82c9ee088 | |
|
7e2caa9290 | |
|
5147aa73de | |
|
46c11e7d33 | |
|
eee996ed59 | |
|
4fcc1e0349 | |
|
a1bab32121 | |
|
c8339bec60 | |
|
f7fe365ed1 | |
|
7fdb7ee788 | |
|
ba3846b5f2 | |
|
c4146f9fbe | |
|
0539e2feee | |
|
473cf6f9ad | |
|
f020b6f828 | |
|
bab3fcd479 | |
|
624fd71c3d | |
|
2c74e3cde8 | |
|
5c0ed4ef8c | |
|
6647a3db23 | |
|
e1eafe87c4 | |
|
6a1541c132 | |
|
6db458b2df | |
|
fd4b2aa6c9 | |
|
9392657f76 | |
|
a433ef6309 | |
|
04983dc7ce | |
|
9027d88e50 | |
|
554adc9f0f | |
|
25cee2b013 | |
|
0de26aee43 | |
|
8b3f289eae | |
|
8e8d9e9a69 | |
|
b6a7af773d | |
|
bd05270a83 | |
|
b60226c818 | |
|
34a10367c5 | |
|
dea10e2150 | |
|
90ee9f6f63 | |
|
dbb8f23f18 | |
|
a0525067a0 | |
|
f9331ea548 | |
|
521857cd53 | |
|
050d93d401 | |
|
d27ad541f1 | |
|
4b6ec6443e | |
|
88e87f5c1a | |
|
b2ed410276 | |
|
68127e4356 | |
|
b4a15ef947 | |
|
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 |
|
@ -1,5 +1,5 @@
|
|||
go.*
|
||||
|
||||
*.swp
|
||||
*.patch
|
||||
*.mbox
|
||||
*.pb.go
|
||||
|
||||
forgeConfig/forgeConfig
|
||||
|
|
12
Makefile
12
Makefile
|
@ -20,12 +20,20 @@ goimports:
|
|||
goimports -w *.go
|
||||
|
||||
clean:
|
||||
rm -f *.pb.go
|
||||
rm -f *.pb.go *.patch
|
||||
-rm -f go.*
|
||||
go-mod-clean --purge
|
||||
go-mod-clean purge
|
||||
|
||||
forgeConfig.pb.go: forgeConfig.proto
|
||||
autogenpb --proto forgeConfig.proto
|
||||
|
||||
patchset.pb.go: patchset.proto
|
||||
autogenpb --proto patchset.proto
|
||||
|
||||
protoc-test:
|
||||
cd ~/go/src && protoc \
|
||||
--proto_path=. \
|
||||
--go_out=. \
|
||||
--go_opt=Mgo.wit.com/lib/protobuf/forgepb/patchset.proto=go.wit.com/lib/protobuf/forgepb \
|
||||
--go_opt=Mgo.wit.com/lib/protobuf/httppb/httpRequest.proto=go.wit.com/lib/protobuf/httppb \
|
||||
go.wit.com/lib/protobuf/forgepb/patchset.proto
|
||||
|
|
|
@ -0,0 +1,316 @@
|
|||
package forgepb
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
var ErrorNotAllReposOnMaster error = log.Errorf("not all repos on are on the master branch")
|
||||
var ErrorNotAllReposOnDevel error = log.Errorf("not all repos on are on the devel branch")
|
||||
var ErrorNotAllReposOnUser error = log.Errorf("not all repos on are on the user branch")
|
||||
|
||||
func (f *Forge) IsEverythingOnMaster() (int, int, int, error) {
|
||||
var total int
|
||||
var count int
|
||||
var nope int
|
||||
|
||||
// first make sure every repo is on the master branch
|
||||
for repo := range f.Repos.IterAll() {
|
||||
total += 1
|
||||
if repo.GetMasterBranchName() == repo.GetCurrentBranchName() {
|
||||
count += 1
|
||||
} else {
|
||||
nope += 1
|
||||
}
|
||||
}
|
||||
if total != count {
|
||||
// log.Info(ErrorNotAllReposOnMaster)
|
||||
return total, count, nope, ErrorNotAllReposOnMaster
|
||||
}
|
||||
return total, count, nope, nil
|
||||
}
|
||||
|
||||
func (f *Forge) IsEverythingOnDevel() (int, int, int, error) {
|
||||
var total int
|
||||
var count int
|
||||
var nope int
|
||||
|
||||
// first make sure every repo is on the master branch
|
||||
for repo := range f.Repos.IterAll() {
|
||||
total += 1
|
||||
if repo.GetDevelBranchName() == repo.GetCurrentBranchName() {
|
||||
count += 1
|
||||
} else {
|
||||
nope += 1
|
||||
}
|
||||
}
|
||||
if total != count {
|
||||
return total, count, nope, ErrorNotAllReposOnDevel
|
||||
}
|
||||
return total, count, nope, nil
|
||||
}
|
||||
|
||||
func (f *Forge) IsEverythingOnUser() (int, int, int, error) {
|
||||
var total int
|
||||
var count int
|
||||
var nope int
|
||||
|
||||
// first make sure every repo is on the master branch
|
||||
for repo := range f.Repos.IterAll() {
|
||||
total += 1
|
||||
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
|
||||
count += 1
|
||||
} else {
|
||||
nope += 1
|
||||
}
|
||||
}
|
||||
if total != count {
|
||||
return total, count, nope, ErrorNotAllReposOnUser
|
||||
}
|
||||
return total, count, nope, nil
|
||||
}
|
||||
|
||||
// trys to figure out if there is still something to update
|
||||
func (f *Forge) DoAllCheckoutMaster() error {
|
||||
now := time.Now()
|
||||
f.RillFuncError(rillCheckoutMaster)
|
||||
count := f.RillReload()
|
||||
if count != 0 {
|
||||
config.SetChanged("repos", true)
|
||||
}
|
||||
|
||||
total, count, nope, err := f.IsEverythingOnMaster()
|
||||
log.Printf("Master branch check. %d total repos. (%d ok) (%d not on master branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
|
||||
if err != nil {
|
||||
// display all repos not on master
|
||||
found := new(gitpb.Repos)
|
||||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
|
||||
found.Append(repo)
|
||||
}
|
||||
}
|
||||
f.PrintHumanTable(found)
|
||||
log.Printf("There are %d repos that are NOT on the master branch\n", found.Len())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func rillCheckoutMaster(repo *gitpb.Repo) error {
|
||||
if repo.IsDirty() {
|
||||
// never do dirty repos
|
||||
return nil
|
||||
}
|
||||
// 'giterr' means something is very wrong with this repo
|
||||
if repo.GetMasterVersion() == "giterr" {
|
||||
repo.CheckoutMaster()
|
||||
log.Info(repo.GetFullPath(), "master == giterr. BAD REPO")
|
||||
log.Info(repo.GetFullPath(), "master == giterr. BAD REPO git describe --tags master --always")
|
||||
log.Info(repo.GetFullPath(), "master == giterr. BAD REPO. todo: figure this out in rillCheckoutMaster()")
|
||||
// cmd := []string{"git", "checkout", "main"} // todo: figure out main
|
||||
// repo.RunVerbose(cmd)
|
||||
return log.Errorf("master version can not be determined")
|
||||
}
|
||||
if repo.GetCurrentBranchName() == repo.GetMasterBranchName() {
|
||||
// repo is already on master
|
||||
return nil
|
||||
}
|
||||
repo.CheckoutMaster()
|
||||
return nil
|
||||
}
|
||||
|
||||
func rillCheckoutUser(repo *gitpb.Repo) error {
|
||||
if repo.IsDirty() {
|
||||
// never do dirty repos
|
||||
return nil
|
||||
}
|
||||
if repo.GetCurrentBranchName() == repo.GetMasterBranchName() {
|
||||
// repo is already on devel branch. have to move them there first for now
|
||||
// return repo.CheckoutDevel()
|
||||
}
|
||||
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
|
||||
// repo is already on user branch
|
||||
return nil
|
||||
}
|
||||
if err := repo.CheckoutUser(); err != nil {
|
||||
log.Info(repo.GetFullPath(), err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// trys to figure out if there is still something to update
|
||||
func (f *Forge) DoAllCheckoutUser(force bool) error {
|
||||
now := time.Now()
|
||||
|
||||
if force {
|
||||
log.Info("going to force create user branches")
|
||||
if err := f.makeUserBranches(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
f.RillFuncError(rillCheckoutUser)
|
||||
count := f.RillReload()
|
||||
if count != 0 {
|
||||
config.SetChanged("repos", true)
|
||||
}
|
||||
|
||||
total, count, nope, err := f.IsEverythingOnUser()
|
||||
log.Printf("User branch check. %d total repos. (%d ok) (%d not on user branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
|
||||
if err != nil {
|
||||
// display all repos not on user
|
||||
found := new(gitpb.Repos)
|
||||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
||||
found.Append(repo)
|
||||
}
|
||||
}
|
||||
f.PrintHumanTable(found)
|
||||
log.Printf("There are %d repos that are NOT on the user branch\n", found.Len())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Forge) makeUserBranches() error {
|
||||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
branch := repo.GetUserBranchName()
|
||||
if repo.Exists(filepath.Join(".git/refs/heads", branch)) {
|
||||
continue
|
||||
}
|
||||
if repo.Exists(filepath.Join(".git/refs/remotes/origin", branch)) {
|
||||
cmd := []string{"git", "checkout", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
continue
|
||||
}
|
||||
cmd := []string{"git", "branch", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
cmd = []string{"git", "checkout", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func testReload(repo *gitpb.Repo) error {
|
||||
if !repo.HasChanged() {
|
||||
return nil
|
||||
}
|
||||
repo.ReloadCheck()
|
||||
return log.Errorf("repo changed")
|
||||
}
|
||||
|
||||
func (f *Forge) DoAllCheckoutDevelNew(force bool) error {
|
||||
f.makeDevelBranches()
|
||||
|
||||
// first run git checkout
|
||||
stats := f.RillFuncError(rillCheckoutDevel)
|
||||
for path, stat := range stats {
|
||||
dur := stat.End.Sub(stat.Start)
|
||||
if dur > 1*time.Second {
|
||||
log.Infof("%s git checkout took a long time (%s)\n", path, shell.FormatDuration(dur))
|
||||
}
|
||||
if stat.Err == nil {
|
||||
// there was no error
|
||||
continue
|
||||
}
|
||||
// didn't change to devel
|
||||
}
|
||||
|
||||
var counter int
|
||||
// recreate the repo protobuf
|
||||
stats = f.RillFuncError(testReload)
|
||||
for path, stat := range stats {
|
||||
dur := stat.End.Sub(stat.Start)
|
||||
if dur > 1*time.Second {
|
||||
log.Infof("%s # Reload took a long time (%s)\n", path, shell.FormatDuration(dur))
|
||||
}
|
||||
if stat.Err == nil {
|
||||
// repo didn't reload
|
||||
continue
|
||||
}
|
||||
// repo reloaded
|
||||
counter += 1
|
||||
}
|
||||
log.Info("reloaded", counter, "repos")
|
||||
config.SetChanged("repos", true)
|
||||
return nil
|
||||
}
|
||||
|
||||
// is every repo on the devel branch?
|
||||
func (f *Forge) DoAllCheckoutDevel(force bool) error {
|
||||
now := time.Now()
|
||||
if force {
|
||||
log.Info("going to force create devel branches")
|
||||
f.makeDevelBranches()
|
||||
}
|
||||
log.Info("going to rill:")
|
||||
f.RillFuncError(rillCheckoutDevel)
|
||||
count := f.RillReload()
|
||||
if count != 0 {
|
||||
config.SetChanged("repos", true)
|
||||
}
|
||||
|
||||
total, count, nope, err := f.IsEverythingOnDevel()
|
||||
log.Printf("Devel branch check. %d total repos. (%d ok) (%d not on devel branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
|
||||
if err != nil {
|
||||
// display all repos not on user
|
||||
found := new(gitpb.Repos)
|
||||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetCurrentBranchName() != repo.GetDevelBranchName() {
|
||||
found.Append(repo)
|
||||
}
|
||||
}
|
||||
f.PrintHumanTable(found)
|
||||
log.Printf("There are %d repos that are NOT on the devel branch\n", found.Len())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Forge) makeDevelBranches() error {
|
||||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
branch := repo.GetDevelBranchName()
|
||||
if repo.Exists(filepath.Join(".git/refs/heads", branch)) {
|
||||
continue
|
||||
}
|
||||
if repo.Exists(filepath.Join(".git/refs/remotes/origin", branch)) {
|
||||
cmd := []string{"git", "checkout", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
continue
|
||||
}
|
||||
cmd := []string{"git", "branch", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
cmd = []string{"git", "checkout", branch}
|
||||
repo.RunVerbose(cmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func rillCheckoutDevel(repo *gitpb.Repo) error {
|
||||
if repo.IsDirty() {
|
||||
// never do dirty repos
|
||||
return nil
|
||||
}
|
||||
if repo.GetCurrentBranchName() == repo.GetDevelBranchName() {
|
||||
// repo is already on devel branch
|
||||
return nil
|
||||
}
|
||||
repo.CheckoutDevel()
|
||||
return nil
|
||||
}
|
32
build.go
32
build.go
|
@ -58,6 +58,9 @@ func (f *Forge) doBuild(repo *gitpb.Repo, userFlags []string, goWhat string) err
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -115,7 +118,8 @@ func (f *Forge) doBuild(repo *gitpb.Repo, userFlags []string, goWhat string) err
|
|||
|
||||
// set standard ldflag options
|
||||
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)
|
||||
// add some standard golang flags
|
||||
ldflags := "-X main.VERSION=" + version + " "
|
||||
|
@ -125,9 +129,9 @@ func (f *Forge) doBuild(repo *gitpb.Repo, userFlags []string, goWhat string) err
|
|||
|
||||
testenv := os.Getenv("GO111MODULE")
|
||||
if testenv == "off" {
|
||||
log.Info("GO111MODULE=off", "f.goWork =", f.IsGoWork(), "f.gosrc =", f.GetGoSrc())
|
||||
log.Info("GO111MODULE=off", "f.goWork =", f.IsGoWork())
|
||||
} else {
|
||||
log.Info("GO111MODULE=", testenv, "f.goWork =", f.IsGoWork(), "f.gosrc =", f.GetGoSrc())
|
||||
log.Info("GO111MODULE=", testenv, "f.goWork =", f.IsGoWork())
|
||||
}
|
||||
log.Info("running:", repo.FullPath)
|
||||
log.Info("running:", cmd)
|
||||
|
@ -198,10 +202,28 @@ func (f *Forge) runAutogenpb(repo *gitpb.Repo) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// sortcut to find
|
||||
// used by guireleaser for now
|
||||
func (f *Forge) FindWorkingDirRepo() *gitpb.Repo {
|
||||
pwd, _ := os.Getwd()
|
||||
basedir := strings.TrimPrefix(pwd, f.GetGoSrc())
|
||||
basedir := strings.TrimPrefix(pwd, f.Config.ReposDir)
|
||||
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,162 @@
|
|||
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("%s repo go dependancy count: %d\n", 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\n", 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\n", 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\n", 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
|
||||
}
|
51
clone.go
51
clone.go
|
@ -1,3 +1,5 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
|
@ -27,7 +29,7 @@ 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)
|
||||
fullpath := filepath.Join(f.Config.ReposDir, gopath)
|
||||
if pb := f.FindAnyPath(fullpath); pb != nil {
|
||||
// repo already exists
|
||||
return pb, nil
|
||||
|
@ -99,6 +101,12 @@ func overridePath(gopath string) string {
|
|||
if strings.HasPrefix(gopath, "github.com/go-gl/glfw") {
|
||||
return "https://github.com/go-gl/glfw"
|
||||
}
|
||||
if strings.HasPrefix(gopath, "cloud.google.com/go") {
|
||||
return "https://github.com/googleapis/google-cloud-go"
|
||||
}
|
||||
if strings.HasPrefix(gopath, "go.opentelemetry.io/contrib") {
|
||||
return "https://github.com/open-telemetry/opentelemetry-go-contrib"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -148,7 +156,7 @@ func (f *Forge) goClonePop(gopath string) (*gitpb.Repo, error) {
|
|||
func (f *Forge) urlClone(gopath, giturl string) (*gitpb.Repo, error) {
|
||||
var err error
|
||||
|
||||
fullpath := filepath.Join(f.goSrc, gopath)
|
||||
fullpath := filepath.Join(f.Config.ReposDir, gopath)
|
||||
basedir, newdir := filepath.Split(fullpath)
|
||||
|
||||
// clone the URL directly
|
||||
|
@ -171,6 +179,10 @@ func (f *Forge) urlClone(gopath, giturl string) (*gitpb.Repo, error) {
|
|||
// 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) {
|
||||
return findGoImport(url)
|
||||
}
|
||||
|
||||
func findGoImport(url string) (string, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
|
@ -212,3 +224,38 @@ func findGoImport(url string) (string, error) {
|
|||
|
||||
return newurl, nil
|
||||
}
|
||||
|
||||
// GetNamesapce removes http://, https://, and .git suffix from the given URL if present.
|
||||
func GetNamespace(url string) string {
|
||||
// Trim protocol prefix
|
||||
if strings.HasPrefix(url, "http://") {
|
||||
url = strings.TrimPrefix(url, "http://")
|
||||
} else if strings.HasPrefix(url, "https://") {
|
||||
url = strings.TrimPrefix(url, "https://")
|
||||
}
|
||||
|
||||
// Trim trailing .git
|
||||
url = strings.TrimSuffix(url, ".git")
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func (f *Forge) Clone(url string) (*gitpb.Repo, error) {
|
||||
ns := GetNamespace(url)
|
||||
if ns == url {
|
||||
return nil, errors.New("todo: forgepb.Clone() fix url parsing")
|
||||
}
|
||||
|
||||
//
|
||||
// returns repo if namespace already exists
|
||||
if repo := f.Repos.FindByNamespace(ns); repo != nil {
|
||||
log.Info("FindByNamespace() worked = ", ns)
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
if repo, _ := f.urlClone(ns, url); repo != nil {
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("todo: forgepb.Clone() url failed " + url)
|
||||
}
|
||||
|
|
175
config.go
175
config.go
|
@ -1,172 +1,35 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
// functions to import and export the protobuf
|
||||
// data to and from config files
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/lib/gui/prep"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// returns err1 || err2
|
||||
func (f *Forge) ConfigSave() error {
|
||||
var err error
|
||||
// backup the current config files
|
||||
if e := backupConfig(); e != nil {
|
||||
log.Info("forge.BackupConfig() error", e)
|
||||
err = e
|
||||
// continue here? notsure. could be bad either way
|
||||
// out of disk space?
|
||||
|
||||
if f.Config == nil {
|
||||
return log.Errorf("forge.Config == nil")
|
||||
}
|
||||
if f.Config != nil {
|
||||
|
||||
if config.HasChanged("forge") {
|
||||
|
||||
// only let forge save the config files (?)
|
||||
if prep.AppName() == "forge" || prep.AppName() == "guireleaser" {
|
||||
log.Info("Okay, this is", prep.AppName())
|
||||
} else {
|
||||
log.Info("This is not forge")
|
||||
return log.Errorf("Only forge can save the config files")
|
||||
}
|
||||
|
||||
if e := f.Config.ConfigSave(); e != nil {
|
||||
log.Info("forge.Config.ConfigSave() error", e)
|
||||
err = e
|
||||
}
|
||||
}
|
||||
if f.Repos != nil {
|
||||
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 {
|
||||
log.Info("proto.Marshal() failed len", len(data), err)
|
||||
return err
|
||||
}
|
||||
log.Info("forgepb.ConfigSave() proto.Marshal() worked len", len(data))
|
||||
|
||||
s := f.FormatTEXT()
|
||||
configWrite("forge.text", []byte(s))
|
||||
|
||||
s = f.FormatJSON()
|
||||
configWrite("forge.json", []byte(s))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// load the ~/.config/forge/ files
|
||||
func (c *ForgeConfigs) ConfigLoad() error {
|
||||
if os.Getenv("FORGE_CONFIG") == "" {
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
fullpath := filepath.Join(homeDir, ".config/forge")
|
||||
os.Setenv("FORGE_CONFIG", fullpath)
|
||||
}
|
||||
// var data []byte
|
||||
// var err error
|
||||
if c == nil {
|
||||
// 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")
|
||||
}
|
||||
|
||||
if err := c.loadText(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// forge.text doesn't exist. try forge.json
|
||||
// this lets the user hand edit the JSON config
|
||||
// probably just deprecate this
|
||||
if data, err := loadFile("forge.json"); err != nil {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cpath := filepath.Join(os.Getenv("FORGE_CONFIG"), ".")
|
||||
if _, err := os.Stat(cpath); err == nil {
|
||||
log.Info("Something has gone wrong. Your", os.Getenv("FORGE_CONFIG"), "directory exists")
|
||||
log.Info("However, the config files could not be loaded")
|
||||
}
|
||||
|
||||
// first time user. make a template config file
|
||||
c.sampleConfig()
|
||||
|
||||
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) {
|
||||
fullname := filepath.Join(os.Getenv("FORGE_CONFIG"), filename)
|
||||
data, err := os.ReadFile(fullname)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// if file does not exist, just return nil. this
|
||||
// will cause ConfigLoad() to try the next config file like "forge.text"
|
||||
// because the user might want to edit the .config by hand
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
// log.Info("open config file :", err)
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func configWrite(filename string, data []byte) error {
|
||||
fullname := filepath.Join(os.Getenv("FORGE_CONFIG"), filename)
|
||||
|
||||
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
defer cfgfile.Close()
|
||||
if err != nil {
|
||||
log.Warn("open config file :", err)
|
||||
return err
|
||||
}
|
||||
if filename == "forge.text" {
|
||||
// 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"))
|
||||
}
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package forgepb
|
||||
|
||||
// thank chatgpt for this because why. why write this if you can have it
|
||||
// kick this out in 30 seconds
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
func backupConfig() error {
|
||||
// make a new dir to backup the files
|
||||
now := time.Now()
|
||||
// timestamp := now.Format("2022.07.18.190545") // 50yr shout out to K&R
|
||||
timestamp := now.Format("2006.01.02.150405") // bummer. other date doesn't work?
|
||||
srcDir := filepath.Join(os.Getenv("FORGE_CONFIG"))
|
||||
destDir := filepath.Join(os.Getenv("FORGE_CONFIG"), timestamp)
|
||||
|
||||
return backupFiles(srcDir, destDir)
|
||||
}
|
||||
|
||||
func backupFiles(srcDir string, destDir string) error {
|
||||
// Create the destination directory
|
||||
err := os.MkdirAll(destDir, os.ModePerm)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Failed to create directory: %v", err))
|
||||
}
|
||||
|
||||
// Read the contents of the source directory
|
||||
entries, err := os.ReadDir(srcDir)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Failed to read directory: %v", err))
|
||||
}
|
||||
|
||||
// Iterate over the entries in the source directory
|
||||
for _, entry := range entries {
|
||||
// Skip directories and files that do not have the .test extension
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Println("backing up file", entry.Name())
|
||||
srcPath := filepath.Join(srcDir, entry.Name())
|
||||
destPath := filepath.Join(destDir, entry.Name())
|
||||
|
||||
// Copy the file
|
||||
if err := copyFile(srcPath, destPath); err != nil {
|
||||
return errors.New(fmt.Sprintf("Failed to copy file %s: %v", entry.Name(), err))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyFile copies a file from src to dest
|
||||
func copyFile(src, dest string) error {
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
destFile, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
// Copy the content
|
||||
_, err = io.Copy(destFile, srcFile)
|
||||
return err
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package forgepb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (all *ForgeConfigs) sampleConfig() {
|
||||
new1 := new(ForgeConfig)
|
||||
new1.GoPath = "go.wit.com"
|
||||
new1.Writable = true
|
||||
new1.Directory = true
|
||||
all.Append(new1)
|
||||
|
||||
fmt.Println("first time user. adding an example config file with", len(all.ForgeConfigs), "repos")
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||
// Use of this source code is governed by the GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (f *Forge) CheckDirtyQuiet() {
|
||||
start := f.straightCheckDirty()
|
||||
now := time.Now()
|
||||
stats := f.RillRepos(doCheckDirty)
|
||||
end := f.straightCheckDirty()
|
||||
diff := end - start
|
||||
|
||||
var changed bool
|
||||
for _, s := range stats {
|
||||
if s.Err == nil {
|
||||
} else {
|
||||
config.SetChanged("repos", true)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
log.Printf("dirty check (%d dirty repos) (%d total repos) (%d changed) took:%s\n", end, f.Repos.Len(), diff, shell.FormatDuration(time.Since(now)))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Forge) CheckDirty() *gitpb.Repos {
|
||||
start := f.straightCheckDirty()
|
||||
|
||||
now := time.Now()
|
||||
stats := f.RillRepos(doCheckDirty)
|
||||
end := f.straightCheckDirty()
|
||||
diff := end - start
|
||||
log.Printf("dirty check (%d dirty repos) (%d total repos) (%d changed) took:%s\n", end, f.Repos.Len(), diff, shell.FormatDuration(time.Since(now)))
|
||||
|
||||
for i, s := range stats {
|
||||
if s.Err == nil {
|
||||
} else {
|
||||
log.Info(i, s.Err)
|
||||
config.SetChanged("repos", true)
|
||||
}
|
||||
}
|
||||
|
||||
return f.FindDirty()
|
||||
}
|
||||
|
||||
func (f *Forge) straightCheckDirty() int {
|
||||
var count int
|
||||
for repo := range f.Repos.IterAll() {
|
||||
if repo.IsDirty() {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func doCheckDirty(repo *gitpb.Repo) error {
|
||||
// reset these in here for now
|
||||
if repo.GetTargetVersion() != "" {
|
||||
repo.TargetVersion = ""
|
||||
}
|
||||
|
||||
if repo.IsDirty() {
|
||||
if repo.CheckDirty() {
|
||||
// nothing changed
|
||||
} else {
|
||||
log.Info("Repo changed to clean", repo.FullPath)
|
||||
return log.Errorf("%s repo changed to clean", repo.FullPath)
|
||||
}
|
||||
} else {
|
||||
if repo.CheckDirty() {
|
||||
log.Info("Repo changed to dirty", repo.FullPath)
|
||||
return log.Errorf("%s repo changed to dirty", repo.FullPath)
|
||||
} else {
|
||||
// nothing changed
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Forge) FindDirty() *gitpb.Repos {
|
||||
found := gitpb.NewRepos()
|
||||
|
||||
for repo := range f.Repos.IterByFullPath() {
|
||||
if repo.IsDirty() {
|
||||
found.AppendByFullPath(repo)
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package forgepb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
|
@ -16,21 +18,18 @@ import (
|
|||
// 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) bool {
|
||||
var good bool = true
|
||||
func (f *Forge) FinalGoDepsCheckOk(check *gitpb.Repo, verbose bool) error {
|
||||
if check == nil {
|
||||
log.Info("boo, check == nil")
|
||||
return false
|
||||
return errors.New("FinalGoDepsCheckOk() boo, check == nil")
|
||||
}
|
||||
|
||||
// parse the go.mod and go.sum files
|
||||
if !check.ParseGoSum() {
|
||||
log.Info("forge.FinalGoDepsCheckOk() failed")
|
||||
return false
|
||||
return fmt.Errorf("forge.ParseGoSum() failed. go.mod & go.sum are broken")
|
||||
}
|
||||
|
||||
if check.GetGoPrimitive() {
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
deps := check.GoDeps.SortByGoPath()
|
||||
|
@ -42,8 +41,10 @@ func (f *Forge) FinalGoDepsCheckOk(check *gitpb.Repo, verbose bool) bool {
|
|||
// skip this gopath because it's probably broken forever
|
||||
continue
|
||||
}
|
||||
log.Info("not found:", depRepo.GetGoPath())
|
||||
good = false
|
||||
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())
|
||||
|
@ -61,51 +62,94 @@ func (f *Forge) FinalGoDepsCheckOk(check *gitpb.Repo, verbose bool) bool {
|
|||
// skip this gopath because it's probably broken forever
|
||||
continue
|
||||
} else {
|
||||
log.Printf("%-48s error %10s vs %10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
|
||||
good = false
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if good {
|
||||
log.Printf("current repo %s go dependancy count: %d\n", check.GetGoPath(), check.GoDepsLen())
|
||||
}
|
||||
return good
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: this is a dumb hack & needs to be updated to talk to forged
|
||||
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)
|
||||
log.Info("CheckOverride() is bypassing", gopath)
|
||||
return true
|
||||
}
|
||||
if gopath == "github.com/posener/complete/v2" {
|
||||
log.Info("CheckOverride() is ignoring", gopath)
|
||||
log.Info("CheckOverride() is bypassing", gopath)
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(gopath, "github.com/go-gl") {
|
||||
log.Info("CheckOverride() is ignoring", gopath)
|
||||
log.Info("CheckOverride() is bypassing", gopath)
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(gopath, "google.golang.org") {
|
||||
log.Info("CheckOverride() is ignoring", gopath)
|
||||
log.Info("CheckOverride() is bypassing", gopath)
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(gopath, "cloud.google.com/go") {
|
||||
log.Info("CheckOverride() is bypassing", gopath)
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(gopath, "go.opencensus.io") {
|
||||
log.Info("CheckOverride() is ignoring", gopath)
|
||||
log.Info("CheckOverride() is bypassing", gopath)
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(gopath, "github.com/nicksnyder/go-i18n") {
|
||||
log.Info("CheckOverride() is ignoring", gopath)
|
||||
log.Info("CheckOverride() is bypassing", gopath)
|
||||
return true
|
||||
}
|
||||
// fuckit for now. just blacklist github.com
|
||||
if strings.HasPrefix(gopath, "github.com/") {
|
||||
log.Info("CheckOverride() is ignoring", gopath)
|
||||
log.Info("CheckOverride() is ignoring everything at github.com", 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
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package forgepb
|
|||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -116,6 +118,17 @@ func (fc *ForgeConfigs) DebName(gopath string) string {
|
|||
return normalBase
|
||||
}
|
||||
|
||||
// a work in progress
|
||||
func (f *Forge) IsPrivate(repo *gitpb.Repo) bool {
|
||||
namespace := repo.GetNamespace()
|
||||
if namespace == "" {
|
||||
// assume true
|
||||
return true
|
||||
}
|
||||
|
||||
return f.Config.IsPrivate(namespace)
|
||||
}
|
||||
|
||||
// is this a non-publishable repo?
|
||||
// matches package names from apt
|
||||
//
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// functions to import and export the protobuf
|
||||
// data to and from config files
|
||||
|
||||
// write to ~/.config/forge/
|
||||
func (cfg *ForgeConfigs) ConfigSave() error {
|
||||
var header string
|
||||
header += "\n"
|
||||
header += "# the forge config file\n"
|
||||
header += "# You can customize things like:\n"
|
||||
header += "#\n"
|
||||
header += "# * which repos you have write access to\n"
|
||||
header += "# * custom branch names for 'master', 'devel' and 'user'\n"
|
||||
header += "# * 'favorites' so you can remember which things you like\n"
|
||||
header += "#\n"
|
||||
header += "\n"
|
||||
return config.ConfigSaveWithHeader(cfg, header)
|
||||
}
|
||||
|
||||
func (cfg *ForgeConfigs) DumpENV() {
|
||||
if cfg.ReposPB != os.Getenv("FORGE_REPOSPB") {
|
||||
log.Infof("RepoPB file problem: cfg.ReposPB=%s != FORGE_REPOSPB=%s\n", cfg.ReposPB, os.Getenv("FORGE_REPOSPB"))
|
||||
}
|
||||
}
|
||||
|
||||
// load the ~/.config/forge/ files
|
||||
func (c *ForgeConfigs) ConfigLoad(fullpath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
}
|
||||
*/
|
|
@ -1,3 +1,5 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package forgepb;
|
||||
|
@ -12,33 +14,61 @@ import "google/protobuf/timestamp.proto"; // Import the well-known type for Time
|
|||
// package names sometimes must be different than the binary name
|
||||
// for example 'zookeeper' is packaged as 'zookeeper-go'
|
||||
// due to the prior apache foundation project. This happens and is ok!
|
||||
message ForgeConfig { // `autogenpb:nomutex`
|
||||
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 readOnly = 3; // the opposite, but needed for now because I don't know what I'm doing
|
||||
bool private = 4; // if the repo can be published
|
||||
bool directory = 5; // everything in this directory should use these writable & private values
|
||||
bool favorite = 6; // you like this. always git clone/go clone this repo
|
||||
bool interesting = 7; // this is something interesting you found and want to remember it
|
||||
message ForgeConfig { // `autogenpb:nomutex`
|
||||
string goPath = 1; // `autogenpb:unique` `autogenpb:sort` // Examples: 'go.wit.com/apps/go-clone' or "~/mythings" or "/home/src/foo"
|
||||
|
||||
string masterBranchName = 8; // git 'main' or 'master' branch name
|
||||
string develBranchName = 9; // whatever the git 'devel' branch name is
|
||||
string userBranchName = 10; // whatever your username branch is
|
||||
bool writable = 2; // if you have write access to the repo
|
||||
bool readOnly = 3; // the opposite, but needed for now because I don't know what I'm doing
|
||||
bool private = 4; // if the repo can be published
|
||||
bool directory = 5; // everything in this directory should use these writable & private values
|
||||
bool favorite = 6; // you like this. always git clone/go clone this repo
|
||||
bool interesting = 7; // this is something interesting you found and want to remember it
|
||||
|
||||
string debName = 11; // the actual name used with 'apt install' (or distro apt equivalent.
|
||||
// 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
|
||||
string masterBranchName = 8; // git 'main' or 'master' branch name
|
||||
string develBranchName = 9; // whatever the git 'devel' branch name is
|
||||
string userBranchName = 10; // whatever your username branch is
|
||||
|
||||
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
|
||||
string debName = 11; // the actual name used with 'apt install' (or distro apt equivalent.
|
||||
// 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
|
||||
|
||||
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
|
||||
string namespace = 14; // `autogenpb:unique` `autogenpb:sort`
|
||||
}
|
||||
|
||||
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 = 14; // what xterm the user wants as the default
|
||||
repeated string xtermArgv = 15; // the argv line for xterm
|
||||
enum ForgeMode {
|
||||
MASTER = 0; // "release mode"
|
||||
DEVEL = 1; // "patch mode"
|
||||
USER = 2; // "work mode"
|
||||
NORMAL = 3; // "normal mode" // use this when you are developing code
|
||||
CLEAN = 4; // indicates "clean" was run
|
||||
}
|
||||
|
||||
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
|
||||
string goSrc = 9; // is ~/go/src unless a go.work file is found
|
||||
bool pathLock = 10; // the path is locked
|
||||
string ReposPB = 11; // where the repos.pb is
|
||||
string ReposDir = 12; // where the repos are
|
||||
string PatchDir = 13; // patch dir
|
||||
string ForgeURL = 14; // forge URL
|
||||
string Filename = 15; // filename of the config file
|
||||
int32 rillX = 16; // used by rill
|
||||
int32 rillY = 17; // used by rill
|
||||
}
|
||||
|
||||
// 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; //
|
||||
}
|
||||
|
|
173
gitCheckout.go
173
gitCheckout.go
|
@ -1,173 +0,0 @@
|
|||
package forgepb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (f *Forge) GitPull() bool {
|
||||
f.Repos.RillGitPull(5, 5)
|
||||
/*
|
||||
|
||||
var localonly int
|
||||
var badmap int
|
||||
|
||||
log.Log(WARN, "running git pull everywhere")
|
||||
var failed int = 0
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if out, err := repo.GitPull(); err == nil {
|
||||
log.Log(WARN, "Ran git pull ok", repo.GetGoPath(), out)
|
||||
} else {
|
||||
failed += 1
|
||||
// repo.DumpTags()
|
||||
if errors.Is(repostatus.ErrorGitPullOnLocal, err) {
|
||||
localonly += 1
|
||||
continue
|
||||
}
|
||||
badmap += 1
|
||||
log.Log(WARN, "bad unknown git error", repo.GetGoPath(), out, err)
|
||||
}
|
||||
}
|
||||
log.Log(WARN, "Ran git pull in all repos. failure count =", failed)
|
||||
log.Log(WARN, "Ran git pull in all repos. bad errors =", badmap)
|
||||
if localonly != 0 {
|
||||
log.Log(WARN, "Ran git pull in all repos. ignored local only branches =", localonly)
|
||||
}
|
||||
*/
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *Forge) CheckoutDevel() bool {
|
||||
log.Log(WARN, "running git checkout devel everwhere")
|
||||
var failed int = 0
|
||||
var count int = 0
|
||||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
count += 1
|
||||
if repo.CheckoutDevel() {
|
||||
// checkout ok
|
||||
} else {
|
||||
dname := repo.GetDevelBranchName()
|
||||
if err := f.makeBranch(repo, dname); err != nil {
|
||||
log.Info(repo.GetGoPath(), "can not make devel branch", dname)
|
||||
failed += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Log(WARN, "Ran git checkout in", count, "repos. failure count =", failed)
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *Forge) MakeDevelBranch(repo *gitpb.Repo) error {
|
||||
dname := repo.GetDevelBranchName()
|
||||
if dname == "" {
|
||||
dname = f.configDevelBranchName(repo)
|
||||
}
|
||||
if err := f.makeBranch(repo, dname); err != nil {
|
||||
return err
|
||||
}
|
||||
repo.DevelBranchName = dname
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Forge) MakeUserBranch(repo *gitpb.Repo) error {
|
||||
uname := repo.GetUserBranchName()
|
||||
if uname == "" {
|
||||
uname = f.configUserBranchName(repo)
|
||||
}
|
||||
if err := f.makeBranch(repo, uname); err != nil {
|
||||
return err
|
||||
}
|
||||
repo.UserBranchName = uname
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Forge) makeBranch(repo *gitpb.Repo, bname string) error {
|
||||
if repo.IsLocalBranch(bname) {
|
||||
// branch already exists in refs/heads/
|
||||
return nil
|
||||
}
|
||||
if repo.IsBranch(bname) {
|
||||
// branch already exists refs/remotes/
|
||||
return nil
|
||||
} else {
|
||||
log.Info("makeBranch() says", bname, "does not exist")
|
||||
loop := repo.Tags.All()
|
||||
for loop.Scan() {
|
||||
t := loop.Next()
|
||||
log.Info("LocalTagExists() tag:", t.Refname)
|
||||
}
|
||||
}
|
||||
mname := repo.GetMasterBranchName()
|
||||
cname := repo.GetCurrentBranchName()
|
||||
if mname != cname {
|
||||
return errors.New("can only make branches from master branch")
|
||||
}
|
||||
cmd := []string{"git", "branch", bname}
|
||||
if err := repo.StrictRun(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Forge) CheckoutMaster() bool {
|
||||
log.Log(WARN, "running git checkout master everwhere")
|
||||
var failed int = 0
|
||||
var count int = 0
|
||||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
count += 1
|
||||
if repo.CheckoutMaster() {
|
||||
// checkout ok
|
||||
} else {
|
||||
failed += 1
|
||||
}
|
||||
}
|
||||
log.Log(WARN, "Ran git checkout in", count, "repos. failure count =", failed)
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *Forge) CheckoutUser() bool {
|
||||
log.Log(WARN, "running git checkout user everwhere")
|
||||
var failed int = 0
|
||||
var count int = 0
|
||||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
|
||||
// already on the user branch
|
||||
continue
|
||||
}
|
||||
count += 1
|
||||
if repo.CheckoutUser() {
|
||||
// checkout ok
|
||||
} else {
|
||||
failed += 1
|
||||
}
|
||||
}
|
||||
log.Log(WARN, "Ran git checkout in", count, "repos. failure count =", failed)
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *Forge) CheckoutUserForce() bool {
|
||||
log.Log(WARN, "running git checkout user everwhere")
|
||||
var failed int = 0
|
||||
var count int = 0
|
||||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
count += 1
|
||||
if repo.CheckoutUser() {
|
||||
// checkout ok
|
||||
} else {
|
||||
failed += 1
|
||||
}
|
||||
}
|
||||
log.Log(WARN, "Ran git checkout in", count, "repos. failure count =", failed)
|
||||
return true
|
||||
}
|
|
@ -84,6 +84,10 @@ type Origin struct {
|
|||
|
||||
// A Tags describes the available tags in a code repository.
|
||||
|
||||
func RunGoList(url string) (string, error) {
|
||||
return runGoList(url)
|
||||
}
|
||||
|
||||
func runGoList(url string) (string, error) {
|
||||
ver, err := getLatestVersion(url)
|
||||
if err != nil {
|
||||
|
|
86
goSrcFind.go
86
goSrcFind.go
|
@ -1,86 +0,0 @@
|
|||
package forgepb
|
||||
|
||||
// returns whatever your golang source dir is
|
||||
// If there is a go.work file in your parent, that directory will be returned
|
||||
// otherwise, return ~/go/src
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// look for a go.work file
|
||||
// otherwise use ~/go/src
|
||||
func (f *Forge) findGoSrc() (string, error) {
|
||||
pwd, err := os.Getwd()
|
||||
// startpwd, _ := os.Getwd()
|
||||
if err == nil {
|
||||
// Check for go.work in the current directory and then move up until root
|
||||
if pwd, err := digup(pwd); err == nil {
|
||||
f.goWork = true
|
||||
// found an existing go.work file
|
||||
// os.Chdir(pwd)
|
||||
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
|
||||
pwd, err = useGoSrc()
|
||||
return pwd, err
|
||||
}
|
||||
|
||||
// this is the 'old way" and works fine for me. I use it because I like the ~/go/src directory
|
||||
// because I know exactly what is in it: GO stuff & nothing else
|
||||
func useGoSrc() (string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pwd := filepath.Join(homeDir, "go/src")
|
||||
err = os.MkdirAll(pwd, os.ModePerm)
|
||||
return pwd, err
|
||||
}
|
||||
|
||||
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) {
|
||||
for {
|
||||
workFilePath := filepath.Join(path, "go.work")
|
||||
if _, err := os.Stat(workFilePath); err == nil {
|
||||
return path, nil // Found the go.work file
|
||||
} else if !os.IsNotExist(err) {
|
||||
return "", err // An error other than not existing
|
||||
}
|
||||
|
||||
parentPath := filepath.Dir(path)
|
||||
if parentPath == path {
|
||||
break // Reached the filesystem root
|
||||
}
|
||||
path = parentPath
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no go.work file found")
|
||||
}
|
203
goSrcScan.go
203
goSrcScan.go
|
@ -1,203 +0,0 @@
|
|||
package forgepb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/destel/rill"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (f *Forge) ScanGoSrc() (bool, error) {
|
||||
dirs, err := gitDirectoriesNew(f.goSrc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var gopaths []string
|
||||
for _, dir := range dirs {
|
||||
// log.Info("forge.ScanGoSrc()", dir)
|
||||
if strings.HasPrefix(dir, f.goSrc) {
|
||||
gopath := strings.TrimPrefix(dir, f.goSrc)
|
||||
gopath = strings.Trim(gopath, "/")
|
||||
if r := f.FindByGoPath(gopath); r != nil {
|
||||
// log.Info("already have", gopath)
|
||||
continue
|
||||
}
|
||||
gopaths = append(gopaths, gopath)
|
||||
} else {
|
||||
log.Log(WARN, "ScanGoSrc() bad:", dir)
|
||||
return false, errors.New("forgepb.ScanGoSrc() bad dir: " + dir)
|
||||
}
|
||||
}
|
||||
newcount, err := 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
|
||||
}
|
||||
|
||||
// doesn't enter the directory any further when it finds a .git/
|
||||
// not stupid like my old version
|
||||
func gitDirectoriesNew(srcDir string) ([]string, error) {
|
||||
var all []string
|
||||
var trip bool
|
||||
err := filepath.WalkDir(srcDir, 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)
|
||||
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")
|
||||
_, err2 := os.Stat(gitdir)
|
||||
if !os.IsNotExist(err2) {
|
||||
all = append(all, path)
|
||||
return filepath.SkipDir
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func gitDirectoriesOld(srcDir string) ([]string, error) {
|
||||
var all []string
|
||||
err := filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
log.Log(WARN, "Error accessing path:", path, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if the path is a directory and has a .git subdirectory
|
||||
if info.IsDir() && IsGitDir(path) {
|
||||
all = append(all, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Log(WARN, "Error walking the path:", srcDir, err)
|
||||
}
|
||||
|
||||
return all, err
|
||||
}
|
||||
|
||||
// IsGitDir checks if a .git directory exists inside the given directory
|
||||
func IsGitDir(dir string) bool {
|
||||
gitDir := filepath.Join(dir, ".git")
|
||||
info, err := os.Stat(gitDir)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return info.IsDir()
|
||||
}
|
||||
|
||||
// rill is awesome. long live rill
|
||||
// attempt scan with rill
|
||||
func (f *Forge) rillScanDirs(gopaths []string) (int, error) {
|
||||
// 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) (*gitpb.Repo, error) {
|
||||
return f.checkpath(id, "")
|
||||
})
|
||||
|
||||
var counter int
|
||||
// Activate users.
|
||||
// Concurrency = 10
|
||||
err := rill.ForEach(dirs, 10, func(repo *gitpb.Repo) error {
|
||||
counter += 1
|
||||
return nil
|
||||
})
|
||||
|
||||
return counter, err
|
||||
}
|
||||
|
||||
func (f *Forge) checkpath(gopath string, url string) (*gitpb.Repo, error) {
|
||||
fullpath := filepath.Join(f.GetGoSrc(), gopath)
|
||||
log.Info("checkpath()", gopath, fullpath)
|
||||
repo, err := f.NewGoRepo(gopath, "")
|
||||
if err != nil {
|
||||
log.Info("checkpath()", gopath, err)
|
||||
}
|
||||
return repo, err
|
||||
}
|
||||
|
||||
func (f *Forge) RillRedoGoMod() int {
|
||||
var all []*gitpb.Repo
|
||||
tmp := f.Repos.SortByFullPath()
|
||||
for tmp.Scan() {
|
||||
repo := tmp.Next()
|
||||
if !repo.IsValidDir() {
|
||||
log.Printf("%10s %-50s", "why am I in RillRedoGoMod? old?", 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, 50, func(id *gitpb.Repo) (*gitpb.Repo, error) {
|
||||
return id, nil
|
||||
})
|
||||
|
||||
err := rill.ForEach(dirs, 20, func(repo *gitpb.Repo) error {
|
||||
counter += 1
|
||||
// repo.RedoGoMod()
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Info("rill.ForEach() error:", err)
|
||||
}
|
||||
|
||||
return counter
|
||||
}
|
20
goWork.go
20
goWork.go
|
@ -1,3 +1,5 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
|
@ -16,7 +18,7 @@ func (f *Forge) MakeGoWork() error {
|
|||
// 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")
|
||||
filename := filepath.Join(f.Config.ReposDir, "go.work")
|
||||
workf, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -30,26 +32,10 @@ func (f *Forge) MakeGoWork() error {
|
|||
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,161 @@
|
|||
// Copyright 1994-2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os/user"
|
||||
"strings"
|
||||
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (f *Forge) HttpPost(base string, route string, data []byte) ([]byte, error) {
|
||||
// Fix using url.JoinPath (Best Practice)
|
||||
baseURL, _ := url.Parse(f.Config.ForgeURL) // "http://forge.grid.wit.com:2520")
|
||||
finalURL := baseURL.JoinPath(route) // Correctly produces ...:2520/patches
|
||||
|
||||
var err error
|
||||
var req *http.Request
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, finalURL.String(), bytes.NewBuffer(data))
|
||||
if req == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usr, _ := user.Current()
|
||||
req.Header.Set("author", usr.Username)
|
||||
req.Header.Set("hostname", f.hostname)
|
||||
|
||||
return rawHttpPost(req)
|
||||
}
|
||||
|
||||
func rawHttpPost(req *http.Request) ([]byte, error) {
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return []byte("client.Do(req) error"), err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return body, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func (f *Forge) LookupPBorig(check *gitpb.Repos) (*gitpb.Repos, error) {
|
||||
url := f.Config.ForgeURL + "lookup"
|
||||
|
||||
for repo := range check.IterByFullPath() {
|
||||
if repo.Namespace == "" {
|
||||
repo.Namespace = repo.GoInfo.GoPath
|
||||
}
|
||||
}
|
||||
|
||||
return check.SubmitReposPB(url)
|
||||
}
|
||||
|
||||
/*
|
||||
func (f *Forge) LookupPB(check *gitpb.Repos) (*gitpb.Repos, error) {
|
||||
url := f.forgeURL + "lookup"
|
||||
|
||||
queryPB := gitpb.NewRepos()
|
||||
|
||||
for repo := range check.IterByFullPath() {
|
||||
ns := repo.Namespace
|
||||
if ns == "" {
|
||||
ns = repo.GoInfo.GoPath
|
||||
}
|
||||
|
||||
newr := new(gitpb.Repo)
|
||||
newr.Namespace = ns
|
||||
|
||||
queryPB.AppendByNamespace(newr)
|
||||
}
|
||||
|
||||
return queryPB.SubmitReposPB(url)
|
||||
}
|
||||
|
||||
func (f *Forge) UpdatePB(check *gitpb.Repos) (*gitpb.Repos, error) {
|
||||
url := f.forgeURL + "update"
|
||||
|
||||
queryPB := gitpb.NewRepos()
|
||||
|
||||
for repo := range check.IterByFullPath() {
|
||||
ns := repo.Namespace
|
||||
if ns == "" {
|
||||
ns = repo.GoInfo.GoPath
|
||||
}
|
||||
|
||||
newr := new(gitpb.Repo)
|
||||
newr.Namespace = ns
|
||||
|
||||
queryPB.AppendByNamespace(newr)
|
||||
}
|
||||
|
||||
return queryPB.SubmitReposPB(url)
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// HTTPRequestToProto converts an *http.Request to our custom HttpRequest protobuf message.
|
||||
func (pb *Patches) AddHttpToPB(r *http.Request) error {
|
||||
if pb == nil {
|
||||
return log.Errorf("AddHttpToPB() pb was nil")
|
||||
}
|
||||
// Convert the header map. http.Header is a map[string][]string.
|
||||
// We'll just take the first value for each header for simplicity.
|
||||
headers := make(map[string]string)
|
||||
for name, values := range r.Header {
|
||||
if len(values) > 0 {
|
||||
headers[name] = strings.Join(values, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
pb.HttpRequest = &httppb.HttpRequest{
|
||||
Method: r.Method,
|
||||
URL: r.URL.String(),
|
||||
Proto: r.Proto,
|
||||
Headers: headers,
|
||||
IP: getClientIP(r),
|
||||
Host: r.Host,
|
||||
Hostname: r.Header.Get("hostname"),
|
||||
}
|
||||
// pb.HttpRequest.Route = cleanURL(r.URL.Path)
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
func getIpSimple(r *http.Request) string {
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
log.Printf("could not split host port: %v", err)
|
||||
return r.RemoteAddr // Fallback
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
// getClientIP inspects the request for common headers to find the true client IP.
|
||||
func getClientIP(r *http.Request) string {
|
||||
// Caddy sets the X-Forwarded-For header.
|
||||
if forwardedFor := r.Header.Get("X-Forwarded-For"); forwardedFor != "" {
|
||||
// The header can be a comma-separated list of IPs. The first one is the original client.
|
||||
ips := strings.Split(forwardedFor, ",")
|
||||
return strings.TrimSpace(ips[0])
|
||||
}
|
||||
|
||||
// Fallback to RemoteAddr if the header is not present.
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
return r.RemoteAddr
|
||||
}
|
||||
return host
|
||||
}
|
2
human.go
2
human.go
|
@ -1,3 +1,5 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
|
@ -59,14 +61,7 @@ func (f *Forge) HumanPrintRepo(check *gitpb.Repo) {
|
|||
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)
|
||||
// f.PrintHumanTable(check)
|
||||
|
||||
printTime("Last Pull", check.Times.LastPull.AsTime())
|
||||
printTime("Last Dirty", check.Times.LastDirty.AsTime())
|
||||
|
@ -86,7 +81,7 @@ func (f *Forge) testGoRepo(check *gitpb.Repo) {
|
|||
data, _ := os.ReadFile(filepath.Join(check.FullPath, "go.mod"))
|
||||
log.Info(string(data))
|
||||
|
||||
if f.FinalGoDepsCheckOk(check, true) {
|
||||
if err := f.FinalGoDepsCheckOk(check, true); err == nil {
|
||||
log.Info("forge.FinalGoDepsCheck(check) worked!")
|
||||
} else {
|
||||
log.Info("forge.FinalGoDepsCheck(check) failed. boo.")
|
||||
|
|
374
humanTable.go
374
humanTable.go
|
@ -1,186 +1,208 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"go.wit.com/lib/cobol"
|
||||
"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!
|
||||
// a console with a fixed with font. AKA: a typerwriter
|
||||
|
||||
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(standardTable8("repopath", "cur br", "age", "target", "master", "devel", "user", "curver", "repo type"))
|
||||
t := new(tally)
|
||||
|
||||
// print the header
|
||||
args := []string{"Namespace", "branch", "age", "user", "devel", "master", "", "lasttag", "next", "repo type"}
|
||||
sizes := []int{35, 9, 4, 13, 13, 13, 1, 12, 12, 8}
|
||||
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
||||
|
||||
all := allr.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
f.printRepoToTable(repo)
|
||||
count += 1
|
||||
f.printRepoToTable(repo, sizes, false)
|
||||
tallyBranchTotals(t, repo)
|
||||
}
|
||||
log.Info("Total git repositories:", count)
|
||||
log.Infof("Total repositories: %d (%d master) (%d devel) (%d user) (%d unknown)\n", t.total, t.master, t.devel, t.user, t.unknown)
|
||||
}
|
||||
|
||||
func (f *Forge) PrintForgedTable(allr *gitpb.Repos) {
|
||||
log.DaemonMode(true)
|
||||
|
||||
t := new(tally)
|
||||
|
||||
// print the header
|
||||
args := []string{"Namespace", "branch", "age", "master", "devel", "last tag", "", "", "", ""}
|
||||
sizes := []int{35, 9, 4, 13, 13, 13, 13, 12, 12, 8}
|
||||
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
||||
|
||||
all := allr.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
f.printForgedToTable(repo, sizes)
|
||||
tallyBranchTotals(t, repo)
|
||||
}
|
||||
log.Infof("Total repositories: %d (%d user) (%d devel) (%d master) (%d unknown)\n", t.total, t.user, t.devel, t.master, t.unknown)
|
||||
}
|
||||
|
||||
func (f *Forge) PrintHumanTableFull(allr *gitpb.Repos) {
|
||||
log.DaemonMode(true)
|
||||
|
||||
t := new(tally)
|
||||
|
||||
// print the header
|
||||
args := []string{"branch", "age", "user", "devel", "master", "", "lasttag", "next", "repo type", "Namespace"}
|
||||
sizes := []int{9, 4, 13, 13, 13, 1, 12, 12, 8, 0}
|
||||
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
||||
|
||||
all := allr.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
f.printRepoToTable(repo, sizes, true)
|
||||
tallyBranchTotals(t, repo)
|
||||
}
|
||||
log.Infof("Total repositories: %d (%d user) (%d devel) (%d master) (%d unknown)\n", t.total, t.user, t.devel, t.master, t.unknown)
|
||||
}
|
||||
|
||||
// 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(standardTable8("repopath", "cur br", "age", "target", "master", "devel", "user", "curver", "repo type"))
|
||||
all := allr.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
f.printRepoToTable(repo)
|
||||
t := new(tally)
|
||||
|
||||
// print the header
|
||||
args := []string{"Namespace", "branch", "age", "user", "devel", "master", "", "lasttag", "next", "repo type"}
|
||||
sizes := []int{35, 9, 4, 13, 13, 13, 1, 12, 12, 8}
|
||||
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
||||
|
||||
for repo := range allr.IterAll() {
|
||||
f.printRepoToTable(repo, sizes, false)
|
||||
if len(repo.DirtyList) != 0 {
|
||||
for _, line := range repo.DirtyList {
|
||||
log.Info("\t", line)
|
||||
log.Info(cobol.TerminalChomp("\t" + line))
|
||||
}
|
||||
}
|
||||
var mver string = repo.GetMasterVersion()
|
||||
repo.Tags.GetAge(mver)
|
||||
|
||||
count += 1
|
||||
tallyBranchTotals(t, repo)
|
||||
}
|
||||
log.Info("Total git repositories:", count)
|
||||
log.Infof("Total repositories: %d (%d user) (%d devel) (%d master) (%d unknown)\n", t.total, t.user, t.devel, t.master, t.unknown)
|
||||
}
|
||||
|
||||
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)
|
||||
// used to count which repos are on which branches (master/main, devel, user)
|
||||
type tally struct {
|
||||
total int
|
||||
master int
|
||||
devel int
|
||||
user int
|
||||
unknown int
|
||||
}
|
||||
|
||||
func standardTable8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 string) string {
|
||||
len1 := 40
|
||||
len2 := 12
|
||||
len3 := 6
|
||||
len4 := 12
|
||||
len5 := 16
|
||||
len6 := 16
|
||||
len7 := 16
|
||||
len8 := 16
|
||||
len9 := 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 "
|
||||
func tallyBranchTotals(t *tally, repo *gitpb.Repo) {
|
||||
t.total += 1
|
||||
|
||||
if len(arg5) > len5 {
|
||||
arg5 = arg5[:len5]
|
||||
if repo.GetCurrentBranchName() == repo.GetMasterBranchName() {
|
||||
t.master += 1
|
||||
return
|
||||
}
|
||||
s += "%-" + fmt.Sprintf("%d", len5) + "s "
|
||||
|
||||
if len(arg6) > len6 {
|
||||
arg6 = arg6[:len6]
|
||||
if repo.GetCurrentBranchName() == repo.GetDevelBranchName() {
|
||||
t.devel += 1
|
||||
return
|
||||
}
|
||||
s += "%-" + fmt.Sprintf("%d", len6) + "s "
|
||||
|
||||
if len(arg7) > len7 {
|
||||
arg7 = arg7[:len7]
|
||||
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
|
||||
t.user += 1
|
||||
return
|
||||
}
|
||||
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 "
|
||||
|
||||
return fmt.Sprintf(s, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
|
||||
log.Printf("unknown curr=%s user=%s devel=%s master=%s\n", repo.GetCurrentBranchName(), repo.GetUserBranchName(), repo.GetDevelBranchName(), repo.GetMasterBranchName())
|
||||
t.unknown += 1
|
||||
}
|
||||
|
||||
func (f *Forge) printRepoToTable(repo *gitpb.Repo) {
|
||||
func (f *Forge) printRepoToTable(repo *gitpb.Repo, sizes []int, full bool) {
|
||||
var end string
|
||||
if repo.IsDirty() {
|
||||
end += "(dirty) "
|
||||
}
|
||||
|
||||
// shortened version numbers
|
||||
var mhort string = repo.GetMasterVersion()
|
||||
var dhort string = repo.GetDevelVersion()
|
||||
var uhort string = repo.GetUserVersion()
|
||||
var uver string = repo.GetUserVersion()
|
||||
if uver == "uerr" {
|
||||
// blank these out
|
||||
uver = ""
|
||||
}
|
||||
var lasttag string = repo.GetLastTag()
|
||||
var thort string = repo.GetTargetVersion()
|
||||
var chort string = repo.GetCurrentBranchVersion()
|
||||
var chort string = "" // repo.GetCurrentBranchVersion()
|
||||
var cname string = repo.GetCurrentBranchName()
|
||||
|
||||
var gopath string = repo.GetGoPath()
|
||||
var gopath string = repo.GetNamespace()
|
||||
if gopath == "" {
|
||||
gopath = repo.GetFullPath()
|
||||
}
|
||||
var rtype string = repo.GetRepoType()
|
||||
switch rtype {
|
||||
case "binary":
|
||||
rtype = "GO bin"
|
||||
case "library":
|
||||
rtype = "GO lib"
|
||||
case "protobuf":
|
||||
rtype = "GO pb"
|
||||
}
|
||||
if f.IsPrivate(repo) {
|
||||
rtype = "priv"
|
||||
}
|
||||
|
||||
// ctime := repo.Tags.GetAge(mhort)
|
||||
// age := shell.FormatDuration(time.Since(ctime))
|
||||
age := shell.FormatDuration(repo.NewestAge())
|
||||
|
||||
start := standardTable8(gopath, cname, age, thort, mhort, dhort, uhort, chort, rtype)
|
||||
age := shell.FormatDuration(repo.BranchAge(cname))
|
||||
|
||||
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
end += "(readonly) "
|
||||
// end += "(readonly) "
|
||||
} else {
|
||||
end += "(rw) "
|
||||
}
|
||||
|
||||
if repo.IsDirty() {
|
||||
age = ""
|
||||
uver = "* " + uver
|
||||
end += "(dirty) "
|
||||
}
|
||||
|
||||
var args []string
|
||||
if full {
|
||||
args = []string{cname, age, uver, dhort, mhort, chort, lasttag, thort, rtype, gopath}
|
||||
} else {
|
||||
args = []string{gopath, cname, age, uver, dhort, mhort, chort, lasttag, thort, rtype}
|
||||
}
|
||||
start := cobol.StandardTableSize10(sizes, args)
|
||||
|
||||
if rtype == "protobuf" {
|
||||
if repo.GoInfo.GoBinary {
|
||||
end += "(binary) "
|
||||
}
|
||||
}
|
||||
|
||||
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() + ") "
|
||||
}
|
||||
|
||||
if repo.GetMasterBranchName() != "master" && repo.GetMasterBranchName() != "main" {
|
||||
end += "(m:" + repo.GetMasterBranchName() + ") "
|
||||
}
|
||||
|
@ -193,13 +215,95 @@ func (f *Forge) printRepoToTable(repo *gitpb.Repo) {
|
|||
end += "(u:" + repo.GetUserBranchName() + ") "
|
||||
}
|
||||
|
||||
switch repo.GetState() {
|
||||
case "PERFECT":
|
||||
case "unchanged":
|
||||
// end += "(invalid tag) "
|
||||
default:
|
||||
end += "(" + repo.GetState() + ") "
|
||||
debname := f.Config.DebName(repo.GetNamespace())
|
||||
if debname != filepath.Base(gopath) {
|
||||
end += "(deb:" + debname + ") "
|
||||
}
|
||||
|
||||
log.Info(start, end)
|
||||
log.Info(cobol.TerminalChomp(start + " " + end))
|
||||
}
|
||||
|
||||
func (f *Forge) printForgedToTable(repo *gitpb.Repo, sizes []int) {
|
||||
var end string
|
||||
|
||||
// shortened version numbers
|
||||
var mhort string = repo.GetMasterVersion()
|
||||
var dhort string = repo.GetDevelVersion()
|
||||
|
||||
var lasttag string = repo.GetLastTag()
|
||||
var cname string = repo.GetCurrentBranchName()
|
||||
|
||||
var ns string = repo.GetNamespace()
|
||||
|
||||
age := shell.FormatDuration(repo.BranchAge(cname))
|
||||
|
||||
var args []string
|
||||
args = []string{ns, cname, age, mhort, dhort, lasttag, "", "", "", ""}
|
||||
start := cobol.StandardTableSize10(sizes, args)
|
||||
|
||||
end += "todo"
|
||||
|
||||
log.Info(cobol.TerminalChomp(start + " " + end))
|
||||
}
|
||||
|
||||
func (psets *Patchsets) PrintTable() {
|
||||
if psets == nil {
|
||||
return
|
||||
}
|
||||
log.DaemonMode(true)
|
||||
|
||||
// print the header
|
||||
args := []string{"commit hash", "new hash", "", "", "name", "Repo Namespace", "", "", "", "", ""}
|
||||
sizes := []int{12, 12, 3, 3, 40, 80, 2, 2, 2, 2}
|
||||
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
||||
|
||||
var countCONTENTS int
|
||||
var countPARTS int
|
||||
for x, pset := range psets.GetPatchsets() {
|
||||
log.Info(pset.Uuid, pset.Name, pset.State)
|
||||
if pset.State == "DONE" {
|
||||
// old patchset
|
||||
continue
|
||||
}
|
||||
cId := log.Sprintf("%d", x)
|
||||
countCONTENTS += 1
|
||||
for i, p := range pset.Patches.GetPatches() {
|
||||
var args []string
|
||||
partId := log.Sprintf("%d", i)
|
||||
|
||||
_, fname := filepath.Split(p.GetFilename())
|
||||
args = []string{p.CommitHash, p.NewHash, cId, partId, fname, p.GetNamespace(), "", "", "", "", ""}
|
||||
|
||||
start := cobol.StandardTableSize10(sizes, args)
|
||||
log.Info(cobol.TerminalChomp(start))
|
||||
countPARTS += 1
|
||||
}
|
||||
}
|
||||
log.Infof("Total Contents (%d) Parts (%d)\n", countCONTENTS, countPARTS)
|
||||
}
|
||||
|
||||
func (patches *Patches) PrintTable() {
|
||||
if patches == nil {
|
||||
return
|
||||
}
|
||||
log.DaemonMode(true)
|
||||
|
||||
// print the header
|
||||
args := []string{"commit hash", "new hash", "", "", "name", "Repo Namespace", "", "", "", "", ""}
|
||||
sizes := []int{12, 12, 3, 3, 40, 80, 2, 2, 2, 2}
|
||||
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
||||
|
||||
var countPARTS int
|
||||
for x, p := range patches.GetPatches() {
|
||||
var args []string
|
||||
partId := log.Sprintf("%d", x)
|
||||
|
||||
_, fname := filepath.Split(p.GetFilename())
|
||||
args = []string{p.CommitHash, p.NewHash, partId, fname, p.GetNamespace(), "", "", "", "", ""}
|
||||
|
||||
start := cobol.StandardTableSize10(sizes, args)
|
||||
log.Info(cobol.TerminalChomp(start))
|
||||
countPARTS += 1
|
||||
}
|
||||
log.Infof("Total Patches (%d)\n", countPARTS)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
254
init.go
254
init.go
|
@ -1,132 +1,192 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/gui/shell"
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/lib/fhelp"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/lib/protobuf/zoopb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// todo: use initOnce
|
||||
// cache.go has Do()
|
||||
// f.initOnce.Do(f.initWork)
|
||||
/* better syntax from gin
|
||||
|
||||
Default returns an Engine instance with the Logger and Recovery middleware already attached.
|
||||
func Default(opts ...OptionFunc) *Engine {
|
||||
engine := New()
|
||||
engine.Use(Logger(), Recovery())
|
||||
return engine.With(opts...)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func Init() *Forge {
|
||||
f := InitPB()
|
||||
|
||||
f.Machine = new(zoopb.Machine)
|
||||
|
||||
if err := f.Machine.ConfigLoad(); err != nil {
|
||||
log.Log(WARN, "zoopb.ConfigLoad() failed", err)
|
||||
cfg := new(ForgeConfigs)
|
||||
err := config.ConfigLoad(cfg, "forge", "forge")
|
||||
if err != nil {
|
||||
log.Info("forge has not been configured yet filename =", cfg.Filename)
|
||||
log.Info("go install go.wit.com/apps/forge@latest")
|
||||
os.Exit(-1)
|
||||
}
|
||||
if f.Config.Username == "" {
|
||||
usr, _ := user.Current()
|
||||
f.Config.Username = usr.Username
|
||||
f.SetConfigSave(true)
|
||||
f := initFromConfig(cfg)
|
||||
if f.Config.Mode == ForgeMode_MASTER {
|
||||
log.Printf("forge.Init() %s len()=%d\n", f.Config.Filename, f.Repos.Len())
|
||||
fhelp.DumpENV("finit:")
|
||||
f.Config.DumpENV()
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
now := time.Now()
|
||||
start := f.Repos.Len()
|
||||
f.ScanGoSrc()
|
||||
end := f.Repos.Len()
|
||||
if (end - start) == 0 {
|
||||
log.Log(INFO, "forgepb.Scan() Scan did not find new git repositories. Total =", end)
|
||||
} 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 {
|
||||
func InitByAppname(argname string) *Forge {
|
||||
cfg := new(ForgeConfigs)
|
||||
err := config.ConfigLoad(cfg, argname, "forge")
|
||||
if err != nil {
|
||||
log.Info("forge has not been configured yet", cfg.Filename)
|
||||
log.Info("go install go.wit.com/apps/forge@latest")
|
||||
os.Exit(-1)
|
||||
}
|
||||
f := initFromConfig(cfg)
|
||||
log.Printf("forge.Init() %s len()=%d\n", f.Config.Filename, f.Repos.Len())
|
||||
return f
|
||||
}
|
||||
|
||||
func initFromConfig(cfg *ForgeConfigs) *Forge {
|
||||
f := new(Forge)
|
||||
|
||||
// TODO: rethink this but it works for now
|
||||
gosrc := os.Getenv("FORGE_GOSRC")
|
||||
if gosrc == "" {
|
||||
goSrcDir, err := f.findGoSrc()
|
||||
if err != nil {
|
||||
log.Log(WARN, "forge init() findGoSrc()", err)
|
||||
}
|
||||
os.Setenv("FORGE_GOSRC", goSrcDir)
|
||||
f.Config = cfg
|
||||
if f.configENV() {
|
||||
log.Info("ENV changed config. todo: save config here")
|
||||
f.Config.ConfigSave()
|
||||
}
|
||||
f.goSrc = os.Getenv("FORGE_GOSRC")
|
||||
|
||||
// also rethink this, but maybe this is the right thing to do
|
||||
if os.Getenv("FORGE_CONFIG") == "" {
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
fullpath := filepath.Join(homeDir, ".config/forge")
|
||||
os.Setenv("FORGE_CONFIG", fullpath)
|
||||
if f.Config.ReposPB != os.Getenv("FORGE_REPOSPB") {
|
||||
// if different, use the ENV var
|
||||
// this probably means that it gets saved as the default in the config
|
||||
// we probably want that (?)
|
||||
f.Config.ReposPB = os.Getenv("FORGE_REPOSPB")
|
||||
}
|
||||
if _, s := filepath.Split(f.Config.ReposPB); s != "repos.pb" {
|
||||
fhelp.DumpENV("forge:")
|
||||
f.Config.DumpENV()
|
||||
log.Infof("ReposPB invalid filename '%s'\n", f.Config.ReposPB)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// check again for go.work // user could have a go.work file in ~/go/src
|
||||
if f.goWorkExists() {
|
||||
f.goWork = true
|
||||
}
|
||||
f.Repos = gitpb.NewRepos()
|
||||
f.Repos.ConfigLoad(f.Config.ReposPB)
|
||||
|
||||
// print out the settings that will be used
|
||||
log.Log(INFO, "forgepb.Init() FORGE_CONFIG", os.Getenv("FORGE_CONFIG"))
|
||||
// init the Patchsets
|
||||
f.Patchsets = NewPatchsets()
|
||||
|
||||
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 = new(gitpb.Repos)
|
||||
f.Repos.ConfigLoad()
|
||||
}
|
||||
|
||||
// only init's the protobuf. intended to not scan or change anything
|
||||
func InitPB() *Forge {
|
||||
f := DetermineGoPath()
|
||||
f.InitPB()
|
||||
// todo: play with these / determine good values based on user's machine
|
||||
f.Config.RillX = 10
|
||||
f.Config.RillY = 20
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Forge) SetConfigSave(b bool) {
|
||||
f.configSave = b
|
||||
config.SetChanged("forge", b)
|
||||
}
|
||||
|
||||
// saves the config if there have been changes
|
||||
func (f *Forge) Exit() {
|
||||
// log.Info("forge.configSave =", f.configSave)
|
||||
if f.configSave {
|
||||
f.ConfigSave()
|
||||
if f.Config.Mode == ForgeMode_MASTER {
|
||||
fhelp.DumpENV("forge:")
|
||||
f.Config.DumpENV()
|
||||
}
|
||||
|
||||
f.ConfigSave()
|
||||
if f.Repos != nil {
|
||||
if config.HasChanged("repos") {
|
||||
if err := f.Repos.ConfigSave(f.Config.ReposPB); err != nil {
|
||||
log.Info("forge.Repos.ConfigSave() error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// log.Info("forge.Exit() ok")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// the first thing done is process any ENV settings
|
||||
// try to NOT use the ENV settings anywhere but here
|
||||
// all initial ENV settings should be stored in the forge struct
|
||||
func (f *Forge) setenv() {
|
||||
f.once.Do(func() {
|
||||
if err := fhelp.ConfigureENV(); err != nil {
|
||||
log.Info("forge ConfigureENV() failed", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
if f.Config == nil {
|
||||
log.Info("forge.Config() was nil")
|
||||
os.Exit(-1)
|
||||
}
|
||||
// f.forgeURL = os.Getenv("FORGE_URL")
|
||||
f.hostname = os.Getenv("HOSTNAME")
|
||||
if os.Getenv("FORGE_GOWORK") == "true" {
|
||||
f.goWork = true
|
||||
}
|
||||
|
||||
f.Config.ReposPB = os.Getenv("FORGE_REPOPB")
|
||||
f.Config.PatchDir = os.Getenv("FORGE_PATCHDIR")
|
||||
f.Config.ForgeURL = os.Getenv("FORGE_URL")
|
||||
})
|
||||
}
|
||||
|
||||
func (f *Forge) GetForgeURL() string {
|
||||
return f.Config.ForgeURL
|
||||
}
|
||||
|
||||
// set the URL for forge otherwise fallback to ENV or to forge.wit.com
|
||||
func (f *Forge) SetForgeURL(url string) {
|
||||
if url == "" {
|
||||
url = os.Getenv("FORGE_URL")
|
||||
}
|
||||
if url == "" {
|
||||
url = "https://forge.wit.com/"
|
||||
}
|
||||
f.Config.ForgeURL = url
|
||||
os.Setenv("FORGE_URL", f.Config.ForgeURL)
|
||||
log.Info("Forge URL has been set to", f.Config.ForgeURL)
|
||||
}
|
||||
|
||||
// the first thing done is process any ENV settings
|
||||
// try to NOT use the ENV settings anywhere but here
|
||||
// all initial ENV settings should be stored in the forge struct
|
||||
func (f *Forge) configENV() bool {
|
||||
var changed bool
|
||||
f.once.Do(func() {
|
||||
if err := fhelp.ConfigureENV(); err != nil {
|
||||
log.Info("forge ConfigureENV() failed", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
if os.Getenv("FORGE_REPOPB") != "" && f.Config.ReposPB != os.Getenv("FORGE_REPOPB") {
|
||||
log.Info("ENV: updating FORGE_REPOSPB from", f.Config.ReposPB, "to", os.Getenv("FORGE_REPOPB"))
|
||||
f.Config.ReposPB = os.Getenv("FORGE_REPOPB")
|
||||
changed = true
|
||||
}
|
||||
|
||||
if os.Getenv("FORGE_GOSRC") != "" && f.Config.ReposDir != os.Getenv("FORGE_GOSRC") {
|
||||
log.Info("ENV: updating FORGE_GOSRC from", f.Config.ReposDir, "to", os.Getenv("FORGE_GOSRC"))
|
||||
f.Config.ReposDir = os.Getenv("FORGE_GOSRC")
|
||||
changed = true
|
||||
}
|
||||
|
||||
if os.Getenv("FORGE_PATCHDIR") != "" && f.Config.PatchDir != os.Getenv("FORGE_PATCHDIR") {
|
||||
log.Info("ENV: updating FORGE_PATCHDIR from", f.Config.PatchDir, "to", os.Getenv("FORGE_PATCHDIRC"))
|
||||
f.Config.PatchDir = os.Getenv("FORGE_PATCHDIR")
|
||||
changed = true
|
||||
}
|
||||
|
||||
f.Config.ForgeURL = os.Getenv("FORGE_URL")
|
||||
f.hostname = os.Getenv("HOSTNAME")
|
||||
if os.Getenv("FORGE_GOWORK") == "true" {
|
||||
f.goWork = true
|
||||
}
|
||||
})
|
||||
if changed {
|
||||
// save config here
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -6,13 +6,50 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/lib/protobuf/httppb"
|
||||
"go.wit.com/log"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func (f *Forge) MakeDevelPatchSet() (*Patchset, error) {
|
||||
func (p *Patches) HttpPostVerbose(baseURL string, route string) (*Patches, *httppb.HttpRequest, error) {
|
||||
p.PrintTable()
|
||||
return p.HttpPost(baseURL, route)
|
||||
}
|
||||
|
||||
func (p *Patchsets) HttpPostVerbose(baseURL string, route string) (*Patchsets, *httppb.HttpRequest, error) {
|
||||
p.PrintTable()
|
||||
return p.HttpPost(baseURL, route)
|
||||
}
|
||||
|
||||
func newPatchset(name string) *Patchset {
|
||||
pset := new(Patchset)
|
||||
pset.Name = name
|
||||
pset.Ctime = timestamppb.New(time.Now())
|
||||
pset.Uuid = uuid.New().String()
|
||||
pset.Hostname, _ = os.Hostname()
|
||||
|
||||
return pset
|
||||
}
|
||||
|
||||
// creates a patchset
|
||||
// works from the user branches against the devel branches
|
||||
func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
|
||||
pset := newPatchset(name)
|
||||
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
|
||||
|
@ -23,17 +60,21 @@ func (f *Forge) MakeDevelPatchSet() (*Patchset, error) {
|
|||
all := f.Repos.SortByFullPath()
|
||||
for all.Scan() {
|
||||
repo := all.Next()
|
||||
userb := repo.GetUserBranchName()
|
||||
develb := repo.GetDevelBranchName()
|
||||
|
||||
if develb == "" {
|
||||
if !repo.IsLocalBranch(repo.GetUserBranchName()) {
|
||||
// log.Info("repo doesn't have user branch", repo.GetGoPath())
|
||||
continue
|
||||
}
|
||||
if userb == "" {
|
||||
if !repo.IsLocalBranch(repo.GetDevelBranchName()) {
|
||||
// log.Info("repo doesn't have devel branch", repo.GetGoPath())
|
||||
continue
|
||||
}
|
||||
pset.StartBranchName = develb
|
||||
pset.EndBranchName = userb
|
||||
|
||||
// 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
|
||||
|
@ -42,8 +83,9 @@ func (f *Forge) MakeDevelPatchSet() (*Patchset, error) {
|
|||
return pset, nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (f *Forge) MakeMasterPatchSet() (*Patchset, error) {
|
||||
pset := new(Patchset)
|
||||
pset := newPatchset("masterBranchPS")
|
||||
dir, err := os.MkdirTemp("", "forge")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -73,6 +115,7 @@ func (f *Forge) MakeMasterPatchSet() (*Patchset, error) {
|
|||
}
|
||||
return pset, nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
|
||||
startBranch := pset.StartBranchName
|
||||
|
@ -83,7 +126,19 @@ func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
|
|||
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 {
|
||||
|
@ -103,14 +158,16 @@ func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
return pset.addPatchFiles(repo)
|
||||
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)
|
||||
// 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 {
|
||||
|
@ -124,10 +181,10 @@ func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
|||
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)
|
||||
// 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)
|
||||
|
@ -138,9 +195,15 @@ func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
|||
patch.Filename, _ = filepath.Rel(p.TmpDir, path)
|
||||
patch.Data = data
|
||||
patch.parseData()
|
||||
patch.StartHash = repo.DevelHash()
|
||||
patch.StartHash = repo.ActualDevelHash()
|
||||
patch.NewHash = "na"
|
||||
patch.Namespace = repo.GetGoPath()
|
||||
if p.Patches == nil {
|
||||
p.Patches = new(Patches)
|
||||
}
|
||||
p.Patches.Append(patch)
|
||||
log.Info("ADDED PATCH FILE", path)
|
||||
p.Patches.Uuid = uuid.New().String()
|
||||
// log.Info("ADDED PATCH FILE", path)
|
||||
return nil
|
||||
})
|
||||
return baderr
|
||||
|
@ -159,6 +222,8 @@ func (p *Patch) parseData() string {
|
|||
switch fields[0] {
|
||||
case "From":
|
||||
p.CommitHash = fields[1]
|
||||
case "Subject:":
|
||||
p.Comment = line
|
||||
case "diff":
|
||||
p.Files = append(p.Files, line)
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
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,224 @@
|
|||
package forgepb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func (f *Forge) LoadPatchsets() error {
|
||||
f.Patchsets = NewPatchsets()
|
||||
|
||||
filename := filepath.Join(f.Config.PatchDir, "all-patches.pb")
|
||||
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = f.Patchsets.Unmarshal(data)
|
||||
if err != nil {
|
||||
log.Infof("LoadPatchsets() proto.Marshal() error %v\n", err)
|
||||
return err
|
||||
}
|
||||
// log.Infof("LoadPatchsets() found %d patches.\n", f.Patchsets.Len())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Forge) InitPatchsets() error {
|
||||
if err := f.LoadPatchsets(); err == nil {
|
||||
return nil
|
||||
} else {
|
||||
log.Info("LoadPatchsets() failed", err)
|
||||
}
|
||||
// TODO: check if Unmarshal failed here
|
||||
f.Patchsets = NewPatchsets()
|
||||
f.findAutoPatchset() // adds the default values
|
||||
return f.SavePatchsets()
|
||||
}
|
||||
|
||||
func (f *Forge) SavePatchsets() error {
|
||||
filename := filepath.Join(f.Config.PatchDir, "all-patches.pb")
|
||||
regfile, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
log.Info("SavePatchsets() filename open error:", filename, err)
|
||||
// fmt.Fprintln(w, "filename open error:", filename, err)
|
||||
return err
|
||||
}
|
||||
defer regfile.Close()
|
||||
|
||||
newpb := proto.Clone(f.Patchsets).(*Patchsets)
|
||||
if newpb == nil {
|
||||
for pset := range f.Patchsets.IterAll() {
|
||||
pset.ShowPatchsets()
|
||||
}
|
||||
return log.Errorf("SavePatchsets() Clone failed!")
|
||||
}
|
||||
|
||||
data, err := newpb.Marshal()
|
||||
if err != nil {
|
||||
log.Infof("SavePatchset() proto.Marshal() error %v\n", err)
|
||||
return err
|
||||
}
|
||||
log.Infof("SavePatchset() worked (%d) bytes on %d patches\n", len(data), f.Patchsets.Len())
|
||||
regfile.Write(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanSubject(line string) string {
|
||||
// Regular expression to remove "Subject:" and "[PATCH...]" patterns
|
||||
re := regexp.MustCompile(`(?i)^Subject:\s*(\[\s*PATCH[^\]]*\]\s*)?`)
|
||||
cleaned := re.ReplaceAllString(line, "")
|
||||
return strings.TrimSpace(cleaned)
|
||||
}
|
||||
|
||||
func (pb *Patchset) ShowPatchsets() error {
|
||||
author := "Author: " + pb.GitAuthorName
|
||||
author += " <" + pb.GitAuthorEmail + ">"
|
||||
log.Printf("%-16s %s %s %s\n", string(pb.Uuid)[0:8], pb.Name, pb.Comment, author)
|
||||
for _, patch := range pb.Patches.Patches {
|
||||
comment := cleanSubject(patch.Comment)
|
||||
log.Printf("\t%-8s %-50s %-50s\n", string(patch.CommitHash)[0:8], patch.Namespace, comment)
|
||||
}
|
||||
// for patch := range pb.IterAll() {
|
||||
// comment := cleanSubject(patch.Comment)
|
||||
// log.Info("\tnew patch:", patch.NewHash, "commithash:", patch.CommitHash, patch.Namespace, comment)
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
|
||||
// adds a patch. returns true if patch is new
|
||||
func (f *Forge) AddPatch(patch *Patch) bool {
|
||||
if f.findPatch(patch) {
|
||||
// log.Info("\talready found!!!!!!!", pset.Uuid, patch.Namespace)
|
||||
return false
|
||||
}
|
||||
if f.AddNewPatch(patch) {
|
||||
log.Info("\tnew patch added:", patch.CommitHash, patch.Gs, patch.Gae, patch.Gan)
|
||||
return true
|
||||
}
|
||||
log.Info("\tnew patch failed:", patch.CommitHash, patch.Gs)
|
||||
return false
|
||||
}
|
||||
|
||||
// adds a patchset or just the patches
|
||||
func (f *Forge) AddPatchset(pb *Patchset) bool {
|
||||
var changed bool
|
||||
// if the name of the patchset is "forge auto commit"
|
||||
// then just add all the patches
|
||||
if pb.Name == "forge auto commit" {
|
||||
author := "Author: " + pb.GitAuthorName
|
||||
author += " <" + pb.GitAuthorEmail + ">"
|
||||
|
||||
fmt.Println(pb.Name, pb.Comment, author)
|
||||
for _, patch := range pb.Patches.Patches {
|
||||
// log.Info("\tnew patch:", i, patch.CommitHash, patch.Namespace)
|
||||
if f.findPatch(patch) {
|
||||
// log.Info("\talready found!!!!!!!", pset.Uuid, patch.Namespace)
|
||||
} else {
|
||||
if f.AddNewPatch(patch) {
|
||||
log.Info("\tnew patch added:", patch.CommitHash, pb.Name, pb.Comment, author)
|
||||
changed = true
|
||||
} else {
|
||||
log.Info("\tnew patch failed:", patch.CommitHash, pb.Name, pb.Comment, author)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
// if you got here, this patchset was submitted with a name
|
||||
|
||||
// Has this patchset already been submitted?
|
||||
for pset := range f.Patchsets.IterAll() {
|
||||
if pset.Uuid == pb.Uuid {
|
||||
log.Info("ALREADY ADDED", pset.Uuid, pset.Name)
|
||||
return false
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
// Clone() this protobuf into me.forge.Patchsets
|
||||
var newpb *Patchset
|
||||
newpb = proto.Clone(pb).(*Patchset)
|
||||
if newpb != nil {
|
||||
f.Patchsets.Patchsets = append(f.Patchsets.Patchsets, newpb)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *Forge) findAutoPatchset() *Patchset {
|
||||
for pset := range f.Patchsets.IterAll() {
|
||||
if pset.Name == "forge auto commit" {
|
||||
return pset
|
||||
}
|
||||
}
|
||||
|
||||
var fauto *Patchset
|
||||
log.Warn("findAutoPatchset() had to create 'forge auto commit'")
|
||||
if fauto == nil {
|
||||
fauto = makeDefaultPatchset()
|
||||
f.Patchsets.Append(fauto)
|
||||
}
|
||||
return fauto
|
||||
}
|
||||
|
||||
func makeDefaultPatchset() *Patchset {
|
||||
fauto := new(Patchset)
|
||||
fauto.Name = "forge auto commit"
|
||||
fauto.Patches = NewPatches()
|
||||
fauto.Uuid = uuid.New().String()
|
||||
return fauto
|
||||
}
|
||||
|
||||
// adds submitted patches not
|
||||
// If the patch was actually new, return true
|
||||
func (f *Forge) AddNewPatch(patch *Patch) bool {
|
||||
// ignore patch if it's already here
|
||||
if f.findPatch(patch) {
|
||||
log.Info("already found patch", patch.CommitHash, patch.Namespace)
|
||||
return false
|
||||
}
|
||||
fauto := f.findAutoPatchset()
|
||||
if fauto == nil {
|
||||
// should have made the default patchset
|
||||
return false
|
||||
}
|
||||
fauto.Patches.Append(patch)
|
||||
return true
|
||||
}
|
||||
|
||||
// returns true if the patch already exists in the protobuf
|
||||
func (f *Forge) findPatch(newpatch *Patch) bool {
|
||||
// log.Info("\tlook for patch:", newpatch.CommitHash, newpatch.Namespace)
|
||||
|
||||
for pset := range f.Patchsets.IterAll() {
|
||||
for _, patch := range pset.Patches.Patches {
|
||||
if patch.CommitHash == newpatch.CommitHash {
|
||||
// log.Info("\tfound pset!!!!!!", pset.Uuid, patch.Namespace)
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *Forge) IsPatchApplied(newpatch *Patch) (*gitpb.Repo, bool) {
|
||||
log.Info("todo: find namespace and repo for patch", newpatch.Filename)
|
||||
if f.findPatch(newpatch) {
|
||||
log.Info("\tfindPatch() patch was found")
|
||||
} else {
|
||||
log.Info("\tfindPatch() patch was not found")
|
||||
}
|
||||
return nil, false
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package forgepb
|
||||
|
||||
// makes a new patches protobuf. These are all the patches on your machine.
|
||||
func NewPatches() *Patches {
|
||||
x := new(Patches)
|
||||
x.Uuid = "2679065e-c81d-4a00-aca4-03c158a834fb"
|
||||
x.Version = "v2.0.0 go.wit.com/lib/protobuf/forgepb"
|
||||
return x
|
||||
}
|
108
patchset.proto
108
patchset.proto
|
@ -1,43 +1,89 @@
|
|||
// 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 filename = 1; // `autogenpb:unique` `autogenpb:sort`
|
||||
bytes data = 2; //
|
||||
string repoPath = 3; // path to the git repo
|
||||
string branchName = 4; //
|
||||
string branchHash = 5; //
|
||||
google.protobuf.Timestamp ctime = 7; // the git commit timestamp of this patch
|
||||
string commitHash = 8; // the git commit hash of this patch
|
||||
string startHash = 9; // the start commit hash
|
||||
repeated string Files = 10; // the filenames this patch changes
|
||||
string namespace = 1; // the base repo git namespace
|
||||
bytes data = 2; // the raw data of the whole patch
|
||||
string gH = 3; // Commit Hash (%H)
|
||||
string gT = 4; // Tree Hash (%T)
|
||||
string gP = 5; // Parent Hashes (%P)
|
||||
string gs = 6; // Subject (%s)
|
||||
string gaI = 7; // Author Date, ISO 8601 format (%aI)
|
||||
string gan = 8; // Author Name (%an)
|
||||
string gae = 9; // Author Email (%ae)
|
||||
string gcI = 10; // Committer Date, ISO 8601 format (%cI)
|
||||
string gcn = 11; // Committer Name (%cn)
|
||||
string gce = 12; // Committer Email (%ce)
|
||||
string gN = 13; // Commit Notes (%N)
|
||||
string gGG = 14; // GPG Signature, raw (%GG)
|
||||
string gGS = 15; // GPG Signer Name (%GS)
|
||||
string gGK = 16; // GPG Key ID (%GK)
|
||||
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 `autogenpb:sort` `autogenpb:unique`
|
||||
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 { // `autogenpb:marshal`
|
||||
string uuid = 1; // `autogenpb:uuid:be926ad9-1111-484c-adf2-d96eeabf3079` // todo: add autogenpb support for this
|
||||
string version = 2; // `autogenpb:version:v0.0.45` // todo: add autogenpb support for this
|
||||
repeated Patch Patches = 3;
|
||||
// this is a "PATCH: [1/x]" series
|
||||
message Patches { // `autogenpb:marshal` `autogenpb:gui:Patch` `autogenpb:http`
|
||||
string uuid = 1; // `autogenpb:uuid:2679065e-c81d-4a00-aca4-03c158a834fb`
|
||||
string version = 2; // `autogenpb:version:v2.0.0`
|
||||
repeated Patch Patches = 3;
|
||||
string Error = 5; // when passing these around, if there is an error, store it here
|
||||
}
|
||||
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`
|
||||
string hostname = 14; //
|
||||
}
|
||||
|
||||
message Patchset { // `autogenpb:marshal`
|
||||
Patches patches = 1; // `autogenpb:unique` `autogenpb:sort`
|
||||
string name = 2; // `autogenpb:sort`
|
||||
string comment = 3; //
|
||||
string gitAuthorName = 4; // `autogenpb:sort`
|
||||
string gitAuthorEmail = 5; //
|
||||
google.protobuf.Timestamp ctime = 6; // create time of this patchset
|
||||
string tmpDir = 7; // temp dir
|
||||
string startBranchName = 8; //
|
||||
string endBranchName = 9; //
|
||||
string startBranchHash = 10; //
|
||||
string endBranchHash = 11; //
|
||||
}
|
||||
|
||||
message Patchsets { // `autogenpb:marshal`
|
||||
string uuid = 1; // `autogenpb:uuid:be926ad9-f07f-484c-adf2-d96eeabf3079` // todo: add autogenpb support for this
|
||||
string version = 2; // `autogenpb:version:v0.0.45` // todo: add autogenpb support for this
|
||||
repeated Patchset Patchsets = 3;
|
||||
message Patchsets { // `autogenpb:marshal` `autogenpb:gui` `autogenpb:nomutex` `autogenpb:http`
|
||||
string uuid = 1; // `autogenpb:uuid:be926ad9-f07f-484c-adf2-d96eeabf3079`
|
||||
string version = 2; // `autogenpb:version:v0.0.45`
|
||||
repeated Patchset Patchsets = 3;
|
||||
}
|
||||
|
|
70
repoNew.go
70
repoNew.go
|
@ -1,3 +1,5 @@
|
|||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
|
@ -7,23 +9,57 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (f *Forge) NewGoRepo(gopath string, url string) (*gitpb.Repo, error) {
|
||||
fullpath := filepath.Join(f.GetGoSrc(), gopath)
|
||||
fullpath := filepath.Join(f.Config.ReposDir, gopath)
|
||||
test := f.Repos.FindByFullPath(fullpath)
|
||||
if test != nil {
|
||||
return test, nil
|
||||
}
|
||||
if gopath == "" {
|
||||
log.Info("TODO: gopath was blank. I'm not sure what is happening here, but this needs to determine the repo Namespace. all calls to NewGoRepo() need to be deprecated. Someone will have to do this when I have funding for WIT. If you are the one looking at this, track me down and I will help do this correctly.")
|
||||
return nil, log.Errorf("gopath was blank")
|
||||
}
|
||||
repo, err := f.Repos.NewGoRepo(fullpath, gopath)
|
||||
if err != nil {
|
||||
log.Info("WARNING. NEW FAILED", fullpath)
|
||||
log.Info("WARNING. NEW FAILED", fullpath, err)
|
||||
return nil, err
|
||||
}
|
||||
// slices.Reverse(f.Repos.Repos)
|
||||
|
||||
repo.URL = url
|
||||
f.VerifyBranchNames(repo)
|
||||
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
repo.ReadOnly = true
|
||||
}
|
||||
repo.Reload()
|
||||
repo.ReloadCheck()
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
// used by the forge daemon
|
||||
func (f *Forge) AddNamespaceDir(ns string, fullpath string) (*gitpb.Repo, error) {
|
||||
test := f.Repos.FindByNamespace(ns)
|
||||
if test != nil {
|
||||
return test, fmt.Errorf("already have namespace")
|
||||
}
|
||||
repo, err := f.Repos.NewGoRepo(fullpath, ns)
|
||||
if err != nil {
|
||||
log.Info("WARNING. NEW FAILED", fullpath)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f.VerifyBranchNames(repo)
|
||||
repo.ReloadCheck()
|
||||
|
||||
// set the readonly flag based on the users' forge config
|
||||
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
repo.ReadOnly = true
|
||||
}
|
||||
config.SetChanged("repos", true)
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
|
@ -76,7 +112,7 @@ func (f *Forge) findMasterBranch(repo *gitpb.Repo) {
|
|||
if strings.HasPrefix(s, "ref: ") {
|
||||
fields := strings.Fields(s)
|
||||
_, bname := filepath.Split(fields[1])
|
||||
log.Info("Using master branch name from .git/ HEAD:", bname)
|
||||
// log.Info("Using master branch name from .git/ HEAD:", bname)
|
||||
repo.SetMasterBranchName(bname)
|
||||
return
|
||||
}
|
||||
|
@ -95,11 +131,6 @@ func (f *Forge) findMasterBranch(repo *gitpb.Repo) {
|
|||
|
||||
// TODO: figure out the name from git
|
||||
repo.SetMasterBranchName("master")
|
||||
if repo.CheckoutMaster() {
|
||||
} else {
|
||||
cmd := []string{"git", "branch", "master"}
|
||||
repo.Run(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// figure out what the name of the git devel branch is
|
||||
|
@ -108,11 +139,6 @@ func (f *Forge) findDevelBranch(repo *gitpb.Repo) {
|
|||
// check the forge config first
|
||||
if bname := f.Config.FindDevelBranch(repo.GetGoPath()); bname != "" {
|
||||
repo.SetDevelBranchName(bname)
|
||||
if repo.CheckoutDevel() {
|
||||
} else {
|
||||
cmd := []string{"git", "branch", bname}
|
||||
repo.Run(cmd)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -121,13 +147,7 @@ func (f *Forge) findDevelBranch(repo *gitpb.Repo) {
|
|||
return
|
||||
}
|
||||
|
||||
// TODO: figure out the name from git
|
||||
repo.SetDevelBranchName("devel")
|
||||
if repo.CheckoutDevel() {
|
||||
} else {
|
||||
cmd := []string{"git", "branch", "devel"}
|
||||
repo.Run(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// this is still in flux
|
||||
|
@ -149,13 +169,9 @@ func (f *Forge) VerifyBranchNames(repo *gitpb.Repo) {
|
|||
} else {
|
||||
// forcing for now. todo: warn users
|
||||
repo.SetUserBranchName(uname)
|
||||
if repo.CheckoutUser() {
|
||||
} else {
|
||||
cmd := []string{"git", "branch", uname}
|
||||
repo.Run(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
// log.Info("VerifyBranchNames", repo.GetMasterBranchName(), repo.GetDevelBranchName(), repo.GetUserBranchName())
|
||||
}
|
||||
|
||||
// what name should be used for the user branch?
|
||||
|
@ -201,7 +217,7 @@ func (f *Forge) AddFullPath(fulldir string) *gitpb.Repo {
|
|||
}
|
||||
|
||||
func (f *Forge) FindByGoPath(gopath string) *gitpb.Repo {
|
||||
fullpath := filepath.Join(f.GetGoSrc(), gopath)
|
||||
fullpath := filepath.Join(f.Config.ReposDir, gopath)
|
||||
return f.Repos.FindByFullPath(fullpath)
|
||||
}
|
||||
|
||||
|
@ -228,6 +244,6 @@ func (f *Forge) FindAnyPath(dir string) *gitpb.Repo {
|
|||
}
|
||||
|
||||
func (f *Forge) DeleteByGoPath(gopath string) bool {
|
||||
fullpath := filepath.Join(f.GetGoSrc(), gopath)
|
||||
fullpath := filepath.Join(f.Config.ReposDir, gopath)
|
||||
return f.Repos.DeleteByFullPath(fullpath)
|
||||
}
|
||||
|
|
166
rill.go
166
rill.go
|
@ -1,7 +1,11 @@
|
|||
package forgepb
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/destel/rill"
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
@ -30,7 +34,6 @@ func (f *Forge) rillUpdate(pool1 int, pool2 int) (int, error) {
|
|||
// 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)
|
||||
})
|
||||
|
||||
|
@ -39,16 +42,14 @@ func (f *Forge) rillUpdate(pool1 int, pool2 int) (int, error) {
|
|||
|
||||
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())
|
||||
log.Printf("%10s %-50s gopath=%s\n", "git dir is missing\n", repo.FullPath, repo.GetNamespace())
|
||||
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 {
|
||||
if repo.HasChanged() {
|
||||
// log.Info("repo changed ", repo.FullPath, repo.StateChange)
|
||||
if err := repo.ReloadCheck(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
@ -59,8 +60,157 @@ func (f *Forge) updateRepo(repo *gitpb.Repo) error {
|
|||
} 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 = 20
|
||||
|
||||
// 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
|
||||
for repo := range f.Repos.IterAll() {
|
||||
if !repo.IsValidDir() {
|
||||
log.Printf("%s %-50s\n", "got an invalid repo in forgepb.RillReload()", repo.GetFullPath())
|
||||
f.Repos.Delete(repo)
|
||||
log.Info("reposSave = true")
|
||||
config.SetChanged("repos", true)
|
||||
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.HasChanged() {
|
||||
return nil
|
||||
}
|
||||
repo.ReloadCheck()
|
||||
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) map[string]*RillStats {
|
||||
return f.RillRepos(rillf)
|
||||
}
|
||||
|
||||
func (f *Forge) ConfigRill(rillX int, rillY int) {
|
||||
f.Config.RillX = int32(rillX)
|
||||
f.Config.RillY = int32(rillY)
|
||||
log.Infof("Setting rill values to %d,%d\n", f.Config.RillX, f.Config.RillY)
|
||||
}
|
||||
|
||||
type RillStats struct {
|
||||
Err error
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
var rillMu sync.Mutex
|
||||
|
||||
// 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) RillRepos(rillf func(*gitpb.Repo) error) map[string]*RillStats {
|
||||
var all []*gitpb.Repo
|
||||
|
||||
var stats map[string]*RillStats
|
||||
stats = make(map[string]*RillStats)
|
||||
|
||||
for repo := range f.Repos.IterAll() {
|
||||
if !repo.IsValidDir() {
|
||||
log.Printf("got an invalid repo in forgepb.RillRepos() %-50s\n", repo.GetFullPath())
|
||||
f.Repos.Delete(repo)
|
||||
log.Info("reposSave = true")
|
||||
config.SetChanged("repos", true)
|
||||
continue
|
||||
}
|
||||
all = append(all, repo)
|
||||
}
|
||||
// log.Info("Rill Repos len =", len(all))
|
||||
// Convert a slice of user IDs into a channel
|
||||
ids := rill.FromSlice(all, nil)
|
||||
|
||||
var counter int
|
||||
var watch int = 10
|
||||
|
||||
// Read users from the API.
|
||||
// Concurrency = 20
|
||||
dirs := rill.Map(ids, int(f.Config.RillX), func(id *gitpb.Repo) (*gitpb.Repo, error) {
|
||||
return id, nil
|
||||
})
|
||||
|
||||
rill.ForEach(dirs, int(f.Config.RillY), func(repo *gitpb.Repo) error {
|
||||
// todo: make this a goroutine to show stats to the user
|
||||
rillMu.Lock()
|
||||
counter += 1
|
||||
if counter > watch {
|
||||
// log.Info("Processed", watch, "repos") // this doesn't work
|
||||
watch += 50
|
||||
}
|
||||
rillMu.Unlock()
|
||||
|
||||
rillSetStartTime(stats, repo.GetFullPath())
|
||||
if err := rillf(repo); err != nil {
|
||||
rillSetError(stats, repo.GetFullPath(), err)
|
||||
}
|
||||
rillSetEndTime(stats, repo.GetFullPath())
|
||||
return nil
|
||||
})
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
func rillSetError(stats map[string]*RillStats, fullpath string, err error) {
|
||||
rillMu.Lock()
|
||||
defer rillMu.Unlock()
|
||||
if s, ok := stats[fullpath]; ok {
|
||||
s.Err = err
|
||||
return
|
||||
}
|
||||
log.Info("WHAT THE FUCK STATS ERROR", fullpath)
|
||||
}
|
||||
|
||||
func rillSetStartTime(stats map[string]*RillStats, fullpath string) {
|
||||
rillMu.Lock()
|
||||
defer rillMu.Unlock()
|
||||
if s, ok := stats[fullpath]; ok {
|
||||
s.Start = time.Now()
|
||||
return
|
||||
}
|
||||
var s *RillStats
|
||||
s = new(RillStats)
|
||||
s.Start = time.Now()
|
||||
stats[fullpath] = s
|
||||
}
|
||||
|
||||
func rillSetEndTime(stats map[string]*RillStats, fullpath string) {
|
||||
rillMu.Lock()
|
||||
defer rillMu.Unlock()
|
||||
if s, ok := stats[fullpath]; ok {
|
||||
s.End = time.Now()
|
||||
return
|
||||
}
|
||||
log.Info("WHAT THE FUCK STATS END TIME", fullpath)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
package forgepb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/destel/rill"
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func reloadCheck(repo *gitpb.Repo) error {
|
||||
if err := repo.ReloadCheck(); err != nil {
|
||||
log.Infof("%s reload() says %v\n", repo.FullPath, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Forge) TestScan() error {
|
||||
f.Repos = gitpb.NewRepos()
|
||||
dirs, err := gitDirectoriesNew(f.Config.ReposDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, fullpath := range dirs {
|
||||
repo, err := gitpb.NewRepo(fullpath)
|
||||
if err != nil {
|
||||
log.Info("ReAdd() error", fullpath, err)
|
||||
}
|
||||
log.Info(i, "worked", repo.FullPath)
|
||||
repo = f.Repos.Append(repo)
|
||||
f.VerifyBranchNames(repo)
|
||||
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
repo.ReadOnly = true
|
||||
}
|
||||
repo.ReloadCheck()
|
||||
if i > 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Forge) checkNamespace(fullpath string) (*gitpb.Repo, error) {
|
||||
if repo := f.Repos.FindByFullPath(fullpath); repo != nil {
|
||||
return nil, nil
|
||||
}
|
||||
repo, err := gitpb.NewRepo(fullpath)
|
||||
if err != nil {
|
||||
log.Info(fullpath, err)
|
||||
return nil, err
|
||||
}
|
||||
return repo, err
|
||||
}
|
||||
|
||||
func (f *Forge) ScanRepoDir() error {
|
||||
dirs, err := gitDirectoriesNew(f.Config.ReposDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("doing reload()")
|
||||
stats := f.RillRepos(reloadCheck)
|
||||
for _, stat := range stats {
|
||||
if stat.Err == nil {
|
||||
continue
|
||||
}
|
||||
config.SetChanged("repos", true)
|
||||
}
|
||||
|
||||
newcount, err := f.rillScanDirsNew(dirs)
|
||||
if err != nil {
|
||||
log.Info("go src dir problem. exit for now?", err)
|
||||
return err
|
||||
}
|
||||
if newcount != 0 {
|
||||
log.Info("forge go src scan found", newcount, "repos")
|
||||
config.SetChanged("repos", true)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// rill is awesome. long live rill
|
||||
// attempt scan with rill
|
||||
func (f *Forge) rillScanDirsNew(fullpaths []string) (int, error) {
|
||||
// Convert a slice of user IDs into a channel
|
||||
ids := rill.FromSlice(fullpaths, nil)
|
||||
|
||||
// Read users from the API. // Concurrency = 20
|
||||
dirs := rill.Map(ids, int(f.Config.RillX), func(id string) (*gitpb.Repo, error) {
|
||||
return f.checkNamespace(id)
|
||||
})
|
||||
|
||||
var counter int
|
||||
// Activate users. // Concurrency = 10
|
||||
err := rill.ForEach(dirs, int(f.Config.RillY), func(repo *gitpb.Repo) error {
|
||||
if repo == nil {
|
||||
return nil
|
||||
}
|
||||
repo = f.Repos.Append(repo)
|
||||
f.VerifyBranchNames(repo)
|
||||
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
repo.ReadOnly = true
|
||||
}
|
||||
repo.ReloadCheck()
|
||||
counter += 1
|
||||
return nil
|
||||
})
|
||||
|
||||
return counter, err
|
||||
}
|
||||
|
||||
// doesn't enter the directory any further when it finds a .git/
|
||||
// not stupid like my old version
|
||||
func gitDirectoriesNew(srcDir string) ([]string, error) {
|
||||
var all []string
|
||||
var trip bool
|
||||
err := filepath.WalkDir(srcDir, 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)
|
||||
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":
|
||||
case "go.work.sum":
|
||||
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")
|
||||
_, err2 := os.Stat(gitdir)
|
||||
if !os.IsNotExist(err2) {
|
||||
all = append(all, path)
|
||||
return filepath.SkipDir
|
||||
}
|
||||
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
|
||||
}
|
20
structs.go
20
structs.go
|
@ -4,25 +4,17 @@ import (
|
|||
sync "sync"
|
||||
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/lib/protobuf/zoopb"
|
||||
)
|
||||
|
||||
// maybe an interface someday?
|
||||
type Forge struct {
|
||||
// one-time initialized data
|
||||
initOnce sync.Once
|
||||
initErr error // init error, if any
|
||||
|
||||
goSrc string // the path to go/src
|
||||
goWork bool // means the user is currently using a go.work file
|
||||
Config *ForgeConfigs // config repos for readonly, private, etc
|
||||
Repos *gitpb.Repos
|
||||
Machine *zoopb.Machine
|
||||
configSave bool
|
||||
}
|
||||
|
||||
func (f *Forge) GetGoSrc() string {
|
||||
return f.goSrc
|
||||
once sync.Once
|
||||
Config *ForgeConfigs // config repos for readonly, private, etc
|
||||
Repos *gitpb.Repos // the repo protobufs
|
||||
Patchsets *Patchsets // patches that are in progress
|
||||
hostname string // your hostname
|
||||
goWork bool // means the user is currently using a go.work file
|
||||
}
|
||||
|
||||
func (f *Forge) IsGoWork() bool {
|
||||
|
|
Loading…
Reference in New Issue