Compare commits
23 Commits
Author | SHA1 | Date |
---|---|---|
|
2232aa5823 | |
|
8e3008cfe8 | |
|
884d8db585 | |
|
4eada310b8 | |
|
643be0abea | |
|
81fc15e743 | |
|
6bfd8c78ab | |
|
060c304a43 | |
|
6890e68af3 | |
|
5325d874a8 | |
|
a1a524e933 | |
|
c752d40839 | |
|
e953b998ae | |
|
1762042e8b | |
|
98ecc249c0 | |
|
cb6394f34a | |
|
2fccf60335 | |
|
2b3bd66ef2 | |
|
27a539b879 | |
|
0f895e83e6 | |
|
ce4af38e8b | |
|
d406ee5f21 | |
|
bc9509e43b |
12
Makefile
12
Makefile
|
@ -5,7 +5,7 @@
|
||||||
# go install
|
# go install
|
||||||
|
|
||||||
|
|
||||||
all: forgeConfig.pb.go patchset.pb.go goimports vet
|
all: forgeConfig.pb.go patch.pb.go set.pb.go goimports vet
|
||||||
|
|
||||||
generate: clean
|
generate: clean
|
||||||
autogenpb --proto patchset.proto
|
autogenpb --proto patchset.proto
|
||||||
|
@ -27,8 +27,14 @@ clean:
|
||||||
forgeConfig.pb.go: forgeConfig.proto
|
forgeConfig.pb.go: forgeConfig.proto
|
||||||
autogenpb --proto forgeConfig.proto
|
autogenpb --proto forgeConfig.proto
|
||||||
|
|
||||||
patchset.pb.go: patchset.proto
|
# patchset.pb.go: patchset.proto
|
||||||
autogenpb --proto patchset.proto
|
# autogenpb --proto patchset.proto
|
||||||
|
|
||||||
|
patch.pb.go: patch.proto
|
||||||
|
autogenpb --proto patch.proto
|
||||||
|
|
||||||
|
set.pb.go: set.proto
|
||||||
|
autogenpb --proto set.proto
|
||||||
|
|
||||||
protoc-test:
|
protoc-test:
|
||||||
cd ~/go/src && protoc \
|
cd ~/go/src && protoc \
|
||||||
|
|
27
branches.go
27
branches.go
|
@ -1,10 +1,10 @@
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/lib/config"
|
||||||
"go.wit.com/lib/gui/shell"
|
"go.wit.com/lib/gui/shell"
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
|
@ -81,7 +81,7 @@ func (f *Forge) DoAllCheckoutMaster() error {
|
||||||
f.RillFuncError(rillCheckoutMaster)
|
f.RillFuncError(rillCheckoutMaster)
|
||||||
count := f.RillReload()
|
count := f.RillReload()
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
f.ConfigSave()
|
config.SetChanged("repos", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
total, count, nope, err := f.IsEverythingOnMaster()
|
total, count, nope, err := f.IsEverythingOnMaster()
|
||||||
|
@ -111,13 +111,12 @@ func rillCheckoutMaster(repo *gitpb.Repo) error {
|
||||||
// 'giterr' means something is very wrong with this repo
|
// 'giterr' means something is very wrong with this repo
|
||||||
if repo.GetMasterVersion() == "giterr" {
|
if repo.GetMasterVersion() == "giterr" {
|
||||||
repo.CheckoutMaster()
|
repo.CheckoutMaster()
|
||||||
log.Info("master == giterr. BAD REPO", repo.GetFullPath())
|
log.Info(repo.GetFullPath(), "master == giterr. BAD REPO")
|
||||||
log.Info("master == giterr. BAD REPO", repo.GetFullPath())
|
log.Info(repo.GetFullPath(), "master == giterr. BAD REPO git describe --tags master --always")
|
||||||
log.Info("master == giterr. BAD REPO", repo.GetFullPath())
|
log.Info(repo.GetFullPath(), "master == giterr. BAD REPO. todo: figure this out in rillCheckoutMaster()")
|
||||||
cmd := []string{"git", "checkout", "main"} // todo: figure out main
|
// cmd := []string{"git", "checkout", "main"} // todo: figure out main
|
||||||
repo.RunVerbose(cmd)
|
// repo.RunVerbose(cmd)
|
||||||
os.Exit(-1)
|
return log.Errorf("master version can not be determined")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
if repo.GetCurrentBranchName() == repo.GetMasterBranchName() {
|
if repo.GetCurrentBranchName() == repo.GetMasterBranchName() {
|
||||||
// repo is already on master
|
// repo is already on master
|
||||||
|
@ -161,7 +160,7 @@ func (f *Forge) DoAllCheckoutUser(force bool) error {
|
||||||
f.RillFuncError(rillCheckoutUser)
|
f.RillFuncError(rillCheckoutUser)
|
||||||
count := f.RillReload()
|
count := f.RillReload()
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
f.ConfigSave()
|
config.SetChanged("repos", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
total, count, nope, err := f.IsEverythingOnUser()
|
total, count, nope, err := f.IsEverythingOnUser()
|
||||||
|
@ -205,10 +204,10 @@ func (f *Forge) makeUserBranches() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testReload(repo *gitpb.Repo) error {
|
func testReload(repo *gitpb.Repo) error {
|
||||||
if !repo.DidRepoChange() {
|
if !repo.HasChanged() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
repo.Reload()
|
repo.ReloadCheck()
|
||||||
return log.Errorf("repo changed")
|
return log.Errorf("repo changed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +244,7 @@ func (f *Forge) DoAllCheckoutDevelNew(force bool) error {
|
||||||
counter += 1
|
counter += 1
|
||||||
}
|
}
|
||||||
log.Info("reloaded", counter, "repos")
|
log.Info("reloaded", counter, "repos")
|
||||||
f.configSave = true
|
config.SetChanged("repos", true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +259,7 @@ func (f *Forge) DoAllCheckoutDevel(force bool) error {
|
||||||
f.RillFuncError(rillCheckoutDevel)
|
f.RillFuncError(rillCheckoutDevel)
|
||||||
count := f.RillReload()
|
count := f.RillReload()
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
f.ConfigSave()
|
config.SetChanged("repos", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
total, count, nope, err := f.IsEverythingOnDevel()
|
total, count, nope, err := f.IsEverythingOnDevel()
|
||||||
|
|
84
config.go
84
config.go
|
@ -3,7 +3,9 @@
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.wit.com/lib/fhelp"
|
"os"
|
||||||
|
|
||||||
|
"go.wit.com/lib/config"
|
||||||
"go.wit.com/lib/gui/prep"
|
"go.wit.com/lib/gui/prep"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
@ -16,29 +18,69 @@ func (f *Forge) ConfigSave() error {
|
||||||
return log.Errorf("forge.Config == nil")
|
return log.Errorf("forge.Config == nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Config.Mode != ForgeMode_NORMAL {
|
if config.HasChanged("forge") {
|
||||||
fhelp.DumpENV("forge:")
|
// only let forge save the config files (?)
|
||||||
f.Config.DumpENV()
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
// only let forge save the config files (?)
|
if e := f.Config.ConfigSave(); e != nil {
|
||||||
if prep.AppName() == "forge" || prep.AppName() == "guireleaser" {
|
log.Info("forge.Config.ConfigSave() error", e)
|
||||||
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(f.Config.ReposPB); e != nil {
|
|
||||||
log.Info("forge.Repos.ConfigSave() error", e)
|
|
||||||
err = e
|
err = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 += "# * sometimes protobuf TEXT can fail so as a backup this also creates a .json file\n"
|
||||||
|
header += "#\n"
|
||||||
|
header += "\n"
|
||||||
|
return config.ConfigSaveWithHeader(cfg, header)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ForgeConfigs) DumpENV() {
|
||||||
|
log.Infof("CfgPB.Filename = %s\n", cfg.Filename)
|
||||||
|
log.Infof("CfgPB.ReposPB = %s\n", cfg.ReposPB)
|
||||||
|
log.Infof("CfgPB.ReposDir = %s\n", cfg.ReposDir)
|
||||||
|
log.Infof("CfgPB.PatchDir = %s\n", cfg.PatchDir)
|
||||||
|
log.Infof("CfgPB.ForgeURL = %s\n", cfg.ForgeURL)
|
||||||
|
if cfg.GoWork {
|
||||||
|
log.Infof("CfgPB.GoWork = %v\n", cfg.GoWork)
|
||||||
|
}
|
||||||
|
log.Infof("CfgPB.Mode = %s\n", cfg.Mode)
|
||||||
|
// log.Infof("CfgPB.Hostname=%s\n", cfg.Hostname)
|
||||||
|
|
||||||
|
if cfg.ReposPB != os.Getenv("FORGE_REPOSPB") {
|
||||||
|
log.Infof("CfgPB file problem: cfg.ReposPB=%s != FORGE_REPOSPB=%s\n", cfg.ReposPB, os.Getenv("FORGE_REPOSPB"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (cfg *ForgeConfigs) InitDefaults() {
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ package forgepb
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/lib/config"
|
||||||
"go.wit.com/lib/gui/shell"
|
"go.wit.com/lib/gui/shell"
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
|
@ -22,8 +23,7 @@ func (f *Forge) CheckDirtyQuiet() {
|
||||||
for _, s := range stats {
|
for _, s := range stats {
|
||||||
if s.Err == nil {
|
if s.Err == nil {
|
||||||
} else {
|
} else {
|
||||||
// log.Info("forge SetConfigSave(true)")
|
config.SetChanged("repos", true)
|
||||||
f.SetConfigSave(true)
|
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ func (f *Forge) CheckDirty() *gitpb.Repos {
|
||||||
if s.Err == nil {
|
if s.Err == nil {
|
||||||
} else {
|
} else {
|
||||||
log.Info(i, s.Err)
|
log.Info(i, s.Err)
|
||||||
f.SetConfigSave(true)
|
config.SetChanged("repos", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,13 +74,11 @@ func doCheckDirty(repo *gitpb.Repo) error {
|
||||||
} else {
|
} else {
|
||||||
log.Info("Repo changed to clean", repo.FullPath)
|
log.Info("Repo changed to clean", repo.FullPath)
|
||||||
return log.Errorf("%s repo changed to clean", repo.FullPath)
|
return log.Errorf("%s repo changed to clean", repo.FullPath)
|
||||||
// f.SetConfigSave(true)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if repo.CheckDirty() {
|
if repo.CheckDirty() {
|
||||||
log.Info("Repo changed to dirty", repo.FullPath)
|
log.Info("Repo changed to dirty", repo.FullPath)
|
||||||
return log.Errorf("%s repo changed to dirty", repo.FullPath)
|
return log.Errorf("%s repo changed to dirty", repo.FullPath)
|
||||||
// f.SetConfigSave(true)
|
|
||||||
} else {
|
} else {
|
||||||
// nothing changed
|
// nothing changed
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
|
||||||
|
|
||||||
package forgepb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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() {
|
|
||||||
log.Info("todo: DumpENV()")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -55,13 +55,15 @@ message ForgeConfigs { // `autogenpb:mar
|
||||||
repeated string xtermArgv = 6; // the argv line for xterm
|
repeated string xtermArgv = 6; // the argv line for xterm
|
||||||
string defaultGui = 7; // default GUI plugin to use
|
string defaultGui = 7; // default GUI plugin to use
|
||||||
ForgeMode mode = 8; // what "mode" forge is in
|
ForgeMode mode = 8; // what "mode" forge is in
|
||||||
string goSrc = 9; // is ~/go/src unless a go.work file is found
|
bool goWork = 9; // true if there is a go.work file
|
||||||
bool pathLock = 10; // the path is locked
|
bool pathLock = 10; // the path is locked
|
||||||
string ReposPB = 11; // where the repos.pb is
|
string ReposPB = 11; // where the repos.pb is
|
||||||
string ReposDir = 12; // where the repos are
|
string ReposDir = 12; // where the repos are
|
||||||
string PatchDir = 13; // patch dir
|
string PatchDir = 13; // patch dir
|
||||||
string ForgeURL = 14; // forge URL
|
string ForgeURL = 14; // forge URL
|
||||||
string Filename = 15; // filename of the config file
|
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
|
// this generic message is used by autogen to identify and
|
||||||
|
|
90
goSrcFind.go
90
goSrcFind.go
|
@ -1,90 +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"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *Forge) GetHome() string {
|
|
||||||
return f.Config.ReposDir
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// 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.Config.ReposDir, "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")
|
|
||||||
}
|
|
225
goSrcScan.go
225
goSrcScan.go
|
@ -1,225 +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 reloadCheck(repo *gitpb.Repo) error {
|
|
||||||
if err := repo.ReloadCheck(); err != nil {
|
|
||||||
log.Infof("%s reload() says %v\n", repo.FullPath, err)
|
|
||||||
// configSave = true
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Forge) ScanGoSrc() (bool, error) {
|
|
||||||
dirs, err := gitDirectoriesNew(f.Config.ReposDir)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stats := f.RillRepos(reloadCheck)
|
|
||||||
for _, stat := range stats {
|
|
||||||
if stat.Err == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
f.SetConfigSave(true)
|
|
||||||
/*
|
|
||||||
dur := stat.End.Sub(stat.Start)
|
|
||||||
if dur > time.Second {
|
|
||||||
log.Infof("%s checkRemoteBranches() took a long time (%s) (err=%v)\n", path, shell.FormatDuration(dur), stat.Err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
var gopaths []string
|
|
||||||
for _, dir := range dirs {
|
|
||||||
// log.Info("forge.ScanGoSrc()", dir)
|
|
||||||
if strings.HasPrefix(dir, f.Config.ReposDir) {
|
|
||||||
gopath := strings.TrimPrefix(dir, f.Config.ReposDir)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns a repo protobuf for a directory if the directory is a git repo
|
|
||||||
func (f *Forge) ScanDir(dir string) *gitpb.Repo {
|
|
||||||
repo, err := f.Repos.NewRepo(dir, "")
|
|
||||||
if err != nil {
|
|
||||||
log.Info("ScanDir() error", dir, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return repo
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
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.Config.ReposDir, gopath)
|
|
||||||
log.Info("forge creating protobuf for", fullpath)
|
|
||||||
repo, err := f.NewGoRepo(gopath, "")
|
|
||||||
if err != nil {
|
|
||||||
log.Info("\tprotobuf error", gopath, err)
|
|
||||||
}
|
|
||||||
return repo, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// deletes the repo from the protobuf (pray your mutex locks are working)
|
|
||||||
// re-scans the repo
|
|
||||||
// returns the new repo
|
|
||||||
func (f *Forge) ReAdd(repo *gitpb.Repo) (*gitpb.Repo, error) {
|
|
||||||
if repo == nil {
|
|
||||||
return nil, log.Errorf("can't delete repo == nil")
|
|
||||||
}
|
|
||||||
fullpath := repo.GetFullPath()
|
|
||||||
ns := repo.GetNamespace()
|
|
||||||
if !f.Repos.Delete(repo) {
|
|
||||||
return nil, log.Errorf("delete of repo failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
repo, err := f.AddNamespaceDir(ns, fullpath)
|
|
||||||
if err != nil {
|
|
||||||
log.Info("ReAdd() error", fullpath, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
f.configSave = true
|
|
||||||
return repo, err
|
|
||||||
}
|
|
|
@ -215,7 +215,7 @@ func (f *Forge) printRepoToTable(repo *gitpb.Repo, sizes []int, full bool) {
|
||||||
end += "(u:" + repo.GetUserBranchName() + ") "
|
end += "(u:" + repo.GetUserBranchName() + ") "
|
||||||
}
|
}
|
||||||
|
|
||||||
debname := f.Config.DebName(repo.GetGoPath())
|
debname := f.Config.DebName(repo.GetNamespace())
|
||||||
if debname != filepath.Base(gopath) {
|
if debname != filepath.Base(gopath) {
|
||||||
end += "(deb:" + debname + ") "
|
end += "(deb:" + debname + ") "
|
||||||
}
|
}
|
||||||
|
@ -246,7 +246,7 @@ func (f *Forge) printForgedToTable(repo *gitpb.Repo, sizes []int) {
|
||||||
log.Info(cobol.TerminalChomp(start + " " + end))
|
log.Info(cobol.TerminalChomp(start + " " + end))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (psets *Patchsets) PrintTable() {
|
func (psets *Sets) PrintTable() {
|
||||||
if psets == nil {
|
if psets == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -257,29 +257,31 @@ func (psets *Patchsets) PrintTable() {
|
||||||
sizes := []int{12, 12, 3, 3, 40, 80, 2, 2, 2, 2}
|
sizes := []int{12, 12, 3, 3, 40, 80, 2, 2, 2, 2}
|
||||||
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
||||||
|
|
||||||
var countCONTENTS int
|
/*
|
||||||
var countPARTS int
|
var countCONTENTS int
|
||||||
for x, pset := range psets.GetPatchsets() {
|
var countPARTS int
|
||||||
log.Info(pset.Uuid, pset.Name, pset.State)
|
for x, pset := range psets.GetSets() {
|
||||||
if pset.State == "DONE" {
|
log.Info(pset.Patches.Uuid, pset.Patches.Name, pset.Patches.State)
|
||||||
// old patchset
|
if pset.State == "DONE" {
|
||||||
continue
|
// old patchset
|
||||||
}
|
continue
|
||||||
cId := log.Sprintf("%d", x)
|
}
|
||||||
countCONTENTS += 1
|
cId := log.Sprintf("%d", x)
|
||||||
for i, p := range pset.Patches.GetPatches() {
|
countCONTENTS += 1
|
||||||
var args []string
|
for i, p := range pset.Patches.GetPatches() {
|
||||||
partId := log.Sprintf("%d", i)
|
var args []string
|
||||||
|
partId := log.Sprintf("%d", i)
|
||||||
|
|
||||||
_, fname := filepath.Split(p.GetFilename())
|
_, fname := filepath.Split(p.GetFilename())
|
||||||
args = []string{p.CommitHash, p.NewHash, cId, partId, fname, p.GetNamespace(), "", "", "", "", ""}
|
args = []string{p.CommitHash, p.NewHash, cId, partId, fname, p.GetNamespace(), "", "", "", "", ""}
|
||||||
|
|
||||||
start := cobol.StandardTableSize10(sizes, args)
|
start := cobol.StandardTableSize10(sizes, args)
|
||||||
log.Info(cobol.TerminalChomp(start))
|
log.Info(cobol.TerminalChomp(start))
|
||||||
countPARTS += 1
|
countPARTS += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Infof("Total Contents (%d) Parts (%d)\n", countCONTENTS, countPARTS)
|
log.Infof("Total Contents (%d) Parts (%d)\n", countCONTENTS, countPARTS)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
func (patches *Patches) PrintTable() {
|
func (patches *Patches) PrintTable() {
|
||||||
|
@ -293,17 +295,19 @@ func (patches *Patches) PrintTable() {
|
||||||
sizes := []int{12, 12, 3, 3, 40, 80, 2, 2, 2, 2}
|
sizes := []int{12, 12, 3, 3, 40, 80, 2, 2, 2, 2}
|
||||||
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
log.Info(cobol.TerminalChomp(cobol.StandardTableSize10(sizes, args)))
|
||||||
|
|
||||||
var countPARTS int
|
/*
|
||||||
for x, p := range patches.GetPatches() {
|
var countPARTS int
|
||||||
var args []string
|
for x, p := range patches.GetPatches() {
|
||||||
partId := log.Sprintf("%d", x)
|
var args []string
|
||||||
|
partId := log.Sprintf("%d", x)
|
||||||
|
|
||||||
_, fname := filepath.Split(p.GetFilename())
|
_, fname := filepath.Split(p.GetFilename())
|
||||||
args = []string{p.CommitHash, p.NewHash, partId, fname, p.GetNamespace(), "", "", "", "", ""}
|
args = []string{p.CommitHash, p.NewHash, partId, fname, p.GetNamespace(), "", "", "", "", ""}
|
||||||
|
|
||||||
start := cobol.StandardTableSize10(sizes, args)
|
start := cobol.StandardTableSize10(sizes, args)
|
||||||
log.Info(cobol.TerminalChomp(start))
|
log.Info(cobol.TerminalChomp(start))
|
||||||
countPARTS += 1
|
countPARTS += 1
|
||||||
}
|
}
|
||||||
log.Infof("Total Patches (%d)\n", countPARTS)
|
log.Infof("Total Patches (%d)\n", countPARTS)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
94
init.go
94
init.go
|
@ -4,6 +4,8 @@ package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"go.wit.com/lib/config"
|
"go.wit.com/lib/config"
|
||||||
"go.wit.com/lib/fhelp"
|
"go.wit.com/lib/fhelp"
|
||||||
|
@ -12,27 +14,42 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
/* better syntax from gin
|
/* better syntax from gin
|
||||||
|
|
||||||
Default returns an Engine instance with the Logger and Recovery middleware already attached.
|
Default returns an Engine instance with the Logger and Recovery middleware already attached.
|
||||||
func Default(opts ...OptionFunc) *Engine {
|
func Default(opts ...OptionFunc) *Engine {
|
||||||
debugPrintWARNINGDefault()
|
|
||||||
engine := New()
|
engine := New()
|
||||||
engine.Use(Logger(), Recovery())
|
engine.Use(Logger(), Recovery())
|
||||||
return engine.With(opts...)
|
return engine.With(opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func Init() *Forge {
|
func Init() *Forge {
|
||||||
|
f := new(Forge)
|
||||||
cfg := new(ForgeConfigs)
|
cfg := new(ForgeConfigs)
|
||||||
err := config.ConfigLoad(cfg, "forge", "forge")
|
err := config.ConfigLoad(cfg, "forge", "forge")
|
||||||
|
f.Config = cfg
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("forge has not been configured yet filename =", cfg.Filename)
|
// fhelp.DumpENV("finit:")
|
||||||
log.Info("go install go.wit.com/apps/forge@latest")
|
f.setenv()
|
||||||
os.Exit(-1)
|
if !fhelp.QuestionUser("This is your first time using forge, use these default values?") {
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
f.Config.InitDefaults()
|
||||||
|
f.Config.ConfigSave()
|
||||||
|
f.initFromConfig()
|
||||||
|
f.Config.DumpENV()
|
||||||
|
return f
|
||||||
}
|
}
|
||||||
f := initFromConfig(cfg)
|
if f.Config.Username == "" {
|
||||||
if f.Config.Mode != ForgeMode_NORMAL {
|
usr, _ := user.Current()
|
||||||
|
f.Config.Username = usr.Username
|
||||||
|
f.Config.ConfigSave()
|
||||||
|
}
|
||||||
|
f.initFromConfig()
|
||||||
|
if f.Config.Mode == ForgeMode_MASTER {
|
||||||
log.Printf("forge.Init() %s len()=%d\n", f.Config.Filename, f.Repos.Len())
|
log.Printf("forge.Init() %s len()=%d\n", f.Config.Filename, f.Repos.Len())
|
||||||
fhelp.DumpENV("finit:")
|
// fhelp.DumpENV("finit:")
|
||||||
f.Config.DumpENV()
|
f.Config.DumpENV()
|
||||||
}
|
}
|
||||||
return f
|
return f
|
||||||
|
@ -46,51 +63,66 @@ func InitByAppname(argname string) *Forge {
|
||||||
log.Info("go install go.wit.com/apps/forge@latest")
|
log.Info("go install go.wit.com/apps/forge@latest")
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
f := initFromConfig(cfg)
|
f := new(Forge)
|
||||||
|
f.Config = cfg
|
||||||
|
f.initFromConfig()
|
||||||
log.Printf("forge.Init() %s len()=%d\n", f.Config.Filename, f.Repos.Len())
|
log.Printf("forge.Init() %s len()=%d\n", f.Config.Filename, f.Repos.Len())
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func initFromConfig(cfg *ForgeConfigs) *Forge {
|
func (f *Forge) initFromConfig() {
|
||||||
f := new(Forge)
|
|
||||||
f.Config = cfg
|
|
||||||
if f.configENV() {
|
if f.configENV() {
|
||||||
log.Info("ENV changed config. todo: save config here")
|
log.Info("ENV changed config. todo: save config here")
|
||||||
f.Config.ConfigSave()
|
f.Config.ConfigSave()
|
||||||
}
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
f.Repos = gitpb.NewRepos()
|
f.Repos = gitpb.NewRepos()
|
||||||
f.Repos.ConfigLoad(f.Config.ReposPB)
|
f.Repos.ConfigLoad(f.Config.ReposPB)
|
||||||
|
|
||||||
// init the Patchsets
|
// init the Patchsets
|
||||||
f.Patchsets = NewPatchsets()
|
f.Patchsets = NewSets()
|
||||||
|
|
||||||
// todo: play with these / determine good values based on user's machine
|
// todo: play with these / determine good values based on user's machine
|
||||||
f.rillX = 10
|
if f.Config.RillX == 0 {
|
||||||
f.rillY = 20
|
f.Config.RillX = 10
|
||||||
return f
|
}
|
||||||
}
|
if f.Config.RillY == 0 {
|
||||||
|
f.Config.RillY = 20
|
||||||
/*
|
|
||||||
func (f *Forge) InitMachine() {
|
|
||||||
if f.Config.Username == "" {
|
|
||||||
usr, _ := user.Current()
|
|
||||||
f.Config.Username = usr.Username
|
|
||||||
}
|
}
|
||||||
f.hostname, _ = os.Hostname()
|
|
||||||
// log.Info(hostname, err)
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
func (f *Forge) SetConfigSave(b bool) {
|
func (f *Forge) SetConfigSave(b bool) {
|
||||||
f.configSave = b
|
config.SetChanged("forge", b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// saves the config if there have been changes
|
// saves the config if there have been changes
|
||||||
func (f *Forge) Exit() {
|
func (f *Forge) Exit() {
|
||||||
// log.Info("forge.configSave =", f.configSave)
|
// log.Info("forge.configSave =", f.configSave)
|
||||||
if f.configSave {
|
if f.Config.Mode == ForgeMode_MASTER {
|
||||||
f.ConfigSave()
|
// fhelp.DumpENV("forge:")
|
||||||
|
// f.Config.DumpENV()
|
||||||
|
// todo: tell the user to switch to NORMAL mode
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
// log.Info("forge.Exit() ok")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
@ -101,6 +133,7 @@ func (f *Forge) Exit() {
|
||||||
// all initial ENV settings should be stored in the forge struct
|
// all initial ENV settings should be stored in the forge struct
|
||||||
func (f *Forge) setenv() {
|
func (f *Forge) setenv() {
|
||||||
f.once.Do(func() {
|
f.once.Do(func() {
|
||||||
|
log.Info("doing setenv()")
|
||||||
if err := fhelp.ConfigureENV(); err != nil {
|
if err := fhelp.ConfigureENV(); err != nil {
|
||||||
log.Info("forge ConfigureENV() failed", err)
|
log.Info("forge ConfigureENV() failed", err)
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
|
@ -109,16 +142,17 @@ func (f *Forge) setenv() {
|
||||||
log.Info("forge.Config() was nil")
|
log.Info("forge.Config() was nil")
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
// f.configDir = os.Getenv("FORGE_CONFIG")
|
|
||||||
// f.forgeURL = os.Getenv("FORGE_URL")
|
// f.forgeURL = os.Getenv("FORGE_URL")
|
||||||
f.hostname = os.Getenv("HOSTNAME")
|
f.hostname = os.Getenv("HOSTNAME")
|
||||||
if os.Getenv("FORGE_GOWORK") == "true" {
|
if os.Getenv("FORGE_GOWORK") == "true" {
|
||||||
f.goWork = true
|
f.goWork = true
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Config.ReposPB = os.Getenv("FORGE_REPOPB")
|
f.Config.ReposPB = os.Getenv("FORGE_REPOSPB")
|
||||||
|
f.Config.ReposDir = os.Getenv("FORGE_REPOSDIR")
|
||||||
f.Config.PatchDir = os.Getenv("FORGE_PATCHDIR")
|
f.Config.PatchDir = os.Getenv("FORGE_PATCHDIR")
|
||||||
f.Config.ForgeURL = os.Getenv("FORGE_URL")
|
f.Config.ForgeURL = os.Getenv("FORGE_URL")
|
||||||
|
fhelp.DumpENV("setenv end()")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
// 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 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?
|
||||||
|
string patchId = 27; // patchId `autogenpb:unique`
|
||||||
|
string treeHash = 28; // final tree Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = 4; // when passing these around, if there is an error, store it here
|
||||||
|
}
|
151
patchset.Make.go
151
patchset.Make.go
|
@ -1,14 +1,17 @@
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"go.wit.com/lib/hostname"
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
"go.wit.com/lib/protobuf/httppb"
|
"go.wit.com/lib/protobuf/httppb"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
|
@ -20,24 +23,30 @@ func (p *Patches) HttpPostVerbose(baseURL string, route string) (*Patches, *http
|
||||||
return p.HttpPost(baseURL, route)
|
return p.HttpPost(baseURL, route)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Patchsets) HttpPostVerbose(baseURL string, route string) (*Patchsets, *httppb.HttpRequest, error) {
|
func (p *Set) HttpPostVerbose(baseURL string, route string) (*Set, *httppb.HttpRequest, error) {
|
||||||
p.PrintTable()
|
p.PrintTable()
|
||||||
return p.HttpPost(baseURL, route)
|
return p.HttpPost(baseURL, route)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPatchset(name string) *Patchset {
|
func (p *Sets) HttpPostVerbose(baseURL string, route string) (*Sets, *httppb.HttpRequest, error) {
|
||||||
pset := new(Patchset)
|
p.PrintTable()
|
||||||
|
return p.HttpPost(baseURL, route)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPatchset(name string) *Set {
|
||||||
|
pset := new(Set)
|
||||||
pset.Name = name
|
pset.Name = name
|
||||||
pset.Ctime = timestamppb.New(time.Now())
|
pset.Ctime = timestamppb.New(time.Now())
|
||||||
pset.Uuid = uuid.New().String()
|
pset.Uuid = uuid.New().String()
|
||||||
pset.Hostname, _ = os.Hostname()
|
pset.Hostname, _ = hostname.Get()
|
||||||
|
pset.Patches = NewPatches()
|
||||||
|
|
||||||
return pset
|
return pset
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a patchset
|
// creates a patchset
|
||||||
// works from the user branches against the devel branches
|
// works from the user branches against the devel branches
|
||||||
func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
|
func (f *Forge) MakeDevelPatchSet(name string) (*Set, error) {
|
||||||
pset := newPatchset(name)
|
pset := newPatchset(name)
|
||||||
if os.Getenv("GIT_AUTHOR_NAME") == "" {
|
if os.Getenv("GIT_AUTHOR_NAME") == "" {
|
||||||
return nil, fmt.Errorf("GIT_AUTHOR_NAME not set")
|
return nil, fmt.Errorf("GIT_AUTHOR_NAME not set")
|
||||||
|
@ -54,7 +63,7 @@ func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(dir) // clean up
|
// defer os.RemoveAll(dir) // clean up
|
||||||
pset.TmpDir = dir
|
pset.TmpDir = dir
|
||||||
|
|
||||||
all := f.Repos.SortByFullPath()
|
all := f.Repos.SortByFullPath()
|
||||||
|
@ -70,6 +79,10 @@ func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if repo.ActualGetDevelHash() == repo.ActualGetUserHash() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// make a patchset from user to devel
|
// make a patchset from user to devel
|
||||||
// TODO: verify branches are otherwise exact
|
// TODO: verify branches are otherwise exact
|
||||||
pset.StartBranchName = repo.GetDevelBranchName()
|
pset.StartBranchName = repo.GetDevelBranchName()
|
||||||
|
@ -83,41 +96,7 @@ func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
|
||||||
return pset, nil
|
return pset, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
func (pset *Set) makePatchSetNew(repo *gitpb.Repo) error {
|
||||||
func (f *Forge) MakeMasterPatchSet() (*Patchset, error) {
|
|
||||||
pset := newPatchset("masterBranchPS")
|
|
||||||
dir, err := os.MkdirTemp("", "forge")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dir) // clean up
|
|
||||||
pset.TmpDir = dir
|
|
||||||
|
|
||||||
all := f.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
startb := repo.GetMasterBranchName()
|
|
||||||
endb := repo.GetUserBranchName()
|
|
||||||
|
|
||||||
if startb == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if endb == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// log.Info("repo", repo.GetGoPath(), startb, "..", endb)
|
|
||||||
pset.StartBranchName = startb
|
|
||||||
pset.EndBranchName = endb
|
|
||||||
err := pset.makePatchSetNew(repo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pset, nil
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
|
|
||||||
startBranch := pset.StartBranchName
|
startBranch := pset.StartBranchName
|
||||||
endBranch := pset.EndBranchName
|
endBranch := pset.EndBranchName
|
||||||
repoDir := filepath.Join(pset.TmpDir, repo.GetGoPath())
|
repoDir := filepath.Join(pset.TmpDir, repo.GetGoPath())
|
||||||
|
@ -154,22 +133,23 @@ func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
|
||||||
return errors.New(fmt.Sprintf("git returned %d", r.Exit))
|
return errors.New(fmt.Sprintf("git returned %d", r.Exit))
|
||||||
}
|
}
|
||||||
if len(r.Stdout) == 0 {
|
if len(r.Stdout) == 0 {
|
||||||
|
log.Infof("No patches in %s (%s,%s)\n", repo.FullPath, repo.ActualGetDevelHash(), repo.ActualGetUserHash())
|
||||||
// git created no files to add
|
// git created no files to add
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pset.addPatchFiles(repo)
|
err = pset.addPatchFiles(repo, repoDir)
|
||||||
pset.Ctime = timestamppb.New(time.Now())
|
log.Infof("Added %d patches for %s len=%d\n", len(r.Stdout), repo.FullPath, pset.Patches.Len())
|
||||||
|
// pset.PrintTable()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// git show <original_commit_hash> | git patch-id
|
||||||
|
// git cat-file -p <commit_hash> | grep tree
|
||||||
// process each file in pDir/
|
// process each file in pDir/
|
||||||
func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
func (p *Set) addPatchFiles(repo *gitpb.Repo, fullDir string) error {
|
||||||
psetDir := repo.GetGoPath()
|
|
||||||
tmpDir := p.TmpDir
|
|
||||||
// log.Info("ADD PATCH FILES ADDED DIR", tmpDir)
|
|
||||||
fullDir := filepath.Join(tmpDir, psetDir)
|
|
||||||
var baderr error
|
var baderr error
|
||||||
|
// log.Info("ADD PATCH FILES ADDED DIR", fullDir)
|
||||||
filepath.Walk(fullDir, func(path string, info os.FileInfo, err error) error {
|
filepath.Walk(fullDir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Handle possible errors, like permission issues
|
// Handle possible errors, like permission issues
|
||||||
|
@ -181,10 +161,6 @@ func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// log.Info("IS THIS A FULL PATH ?", path)
|
|
||||||
// log.Info("trim this from path ?", fullDir)
|
|
||||||
// log.Info("trim this from path ?", psetDir)
|
|
||||||
// log.Info("trim this from path ?", tmpDir)
|
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("addPatchFile() failed", path)
|
log.Info("addPatchFile() failed", path)
|
||||||
|
@ -194,15 +170,22 @@ func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
||||||
patch := new(Patch)
|
patch := new(Patch)
|
||||||
patch.Filename, _ = filepath.Rel(p.TmpDir, path)
|
patch.Filename, _ = filepath.Rel(p.TmpDir, path)
|
||||||
patch.Data = data
|
patch.Data = data
|
||||||
patch.parseData()
|
if err := patch.parseData(); err != nil {
|
||||||
|
log.Info("parseData() failed", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := findPatchId(repo, patch); err != nil {
|
||||||
|
log.Info("findPatchId() failed", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
patch.StartHash = repo.ActualDevelHash()
|
patch.StartHash = repo.ActualDevelHash()
|
||||||
patch.NewHash = "na"
|
patch.NewHash = "na"
|
||||||
patch.Namespace = repo.GetGoPath()
|
patch.Namespace = repo.GetGoPath()
|
||||||
if p.Patches == nil {
|
if p.Patches == nil {
|
||||||
|
log.Info("SHOULD NOT HAVE HAPPENED. p.Patches == nil")
|
||||||
p.Patches = new(Patches)
|
p.Patches = new(Patches)
|
||||||
}
|
}
|
||||||
p.Patches.Append(patch)
|
p.Patches.Append(patch)
|
||||||
p.Patches.Uuid = uuid.New().String()
|
|
||||||
// log.Info("ADDED PATCH FILE", path)
|
// log.Info("ADDED PATCH FILE", path)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -212,7 +195,7 @@ func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
||||||
// looks at the git format-patch output
|
// looks at the git format-patch output
|
||||||
// saves the commit Hash
|
// saves the commit Hash
|
||||||
// saves the diff lines
|
// saves the diff lines
|
||||||
func (p *Patch) parseData() string {
|
func (p *Patch) parseData() error {
|
||||||
lines := strings.Split(string(p.Data), "\n")
|
lines := strings.Split(string(p.Data), "\n")
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
fields := strings.Fields(line)
|
fields := strings.Fields(line)
|
||||||
|
@ -228,7 +211,7 @@ func (p *Patch) parseData() string {
|
||||||
p.Files = append(p.Files, line)
|
p.Files = append(p.Files, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// just an example of how to walk only directories
|
// just an example of how to walk only directories
|
||||||
|
@ -253,3 +236,59 @@ func onlyWalkDirs(pDir string) error {
|
||||||
})
|
})
|
||||||
return baderr
|
return baderr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func runPipe() error {
|
||||||
|
func findPatchId(repo *gitpb.Repo, p *Patch) error {
|
||||||
|
if p.CommitHash == "" {
|
||||||
|
return log.Errorf("%s commit hash not found", p.Filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Create the command to get the diff for the commit.
|
||||||
|
// "git show" is the perfect tool for this.
|
||||||
|
cmdShow := exec.Command("git", "show", p.CommitHash)
|
||||||
|
cmdShow.Dir = repo.GetFullPath()
|
||||||
|
|
||||||
|
// 2. Create the command to calculate the patch-id from stdin.
|
||||||
|
cmdPipeID := exec.Command("git", "patch-id", "--stable")
|
||||||
|
cmdPipeID.Dir = repo.GetFullPath()
|
||||||
|
|
||||||
|
// 3. Connect the output of "git show" to the input of "git patch-id".
|
||||||
|
// This is the Go equivalent of the shell pipe `|`.
|
||||||
|
pipe, err := cmdShow.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create pipe: %w", err)
|
||||||
|
}
|
||||||
|
cmdPipeID.Stdin = pipe
|
||||||
|
|
||||||
|
// 4. We need a buffer to capture the final output from git patch-id.
|
||||||
|
var output bytes.Buffer
|
||||||
|
cmdPipeID.Stdout = &output
|
||||||
|
|
||||||
|
// 5. Start the reading command (patch-id) first.
|
||||||
|
if err := cmdPipeID.Start(); err != nil {
|
||||||
|
return fmt.Errorf("failed to start git-patch-id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Run the writing command (show). This will block until it's done.
|
||||||
|
if err := cmdShow.Run(); err != nil {
|
||||||
|
return fmt.Errorf("failed to run git-show: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Wait for the reading command to finish.
|
||||||
|
if err := cmdPipeID.Wait(); err != nil {
|
||||||
|
return fmt.Errorf("failed to wait for git-patch-id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(output.String())
|
||||||
|
if len(fields) != 2 {
|
||||||
|
return fmt.Errorf("git-patch-id produced empty output")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fields[1] != p.CommitHash {
|
||||||
|
return fmt.Errorf("patchid did not match %s != %v", p.CommitHash, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// log.Infof("hash=%s patchid(%v) %s\n", p.CommitHash, fields, p.Filename)
|
||||||
|
p.PatchId = fields[0]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (f *Forge) LoadPatchsets() error {
|
func (f *Forge) LoadPatchsets() error {
|
||||||
f.Patchsets = NewPatchsets()
|
f.Patchsets = NewSets()
|
||||||
|
|
||||||
filename := filepath.Join(f.Config.PatchDir, "all-patches.pb")
|
filename := filepath.Join(f.Config.PatchDir, "all-patches.pb")
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ func (f *Forge) InitPatchsets() error {
|
||||||
log.Info("LoadPatchsets() failed", err)
|
log.Info("LoadPatchsets() failed", err)
|
||||||
}
|
}
|
||||||
// TODO: check if Unmarshal failed here
|
// TODO: check if Unmarshal failed here
|
||||||
f.Patchsets = NewPatchsets()
|
f.Patchsets = NewSets()
|
||||||
f.findAutoPatchset() // adds the default values
|
f.findAutoPatchset() // adds the default values
|
||||||
return f.SavePatchsets()
|
return f.SavePatchsets()
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ func (f *Forge) SavePatchsets() error {
|
||||||
}
|
}
|
||||||
defer regfile.Close()
|
defer regfile.Close()
|
||||||
|
|
||||||
newpb := proto.Clone(f.Patchsets).(*Patchsets)
|
newpb := proto.Clone(f.Patchsets).(*Sets)
|
||||||
if newpb == nil {
|
if newpb == nil {
|
||||||
for pset := range f.Patchsets.IterAll() {
|
for pset := range f.Patchsets.IterAll() {
|
||||||
pset.ShowPatchsets()
|
pset.ShowPatchsets()
|
||||||
|
@ -79,7 +79,7 @@ func cleanSubject(line string) string {
|
||||||
return strings.TrimSpace(cleaned)
|
return strings.TrimSpace(cleaned)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pb *Patchset) ShowPatchsets() error {
|
func (pb *Set) ShowPatchsets() error {
|
||||||
author := "Author: " + pb.GitAuthorName
|
author := "Author: " + pb.GitAuthorName
|
||||||
author += " <" + pb.GitAuthorEmail + ">"
|
author += " <" + pb.GitAuthorEmail + ">"
|
||||||
log.Printf("%-16s %s %s %s\n", string(pb.Uuid)[0:8], pb.Name, pb.Comment, author)
|
log.Printf("%-16s %s %s %s\n", string(pb.Uuid)[0:8], pb.Name, pb.Comment, author)
|
||||||
|
@ -109,7 +109,7 @@ func (f *Forge) AddPatch(patch *Patch) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// adds a patchset or just the patches
|
// adds a patchset or just the patches
|
||||||
func (f *Forge) AddPatchset(pb *Patchset) bool {
|
func (f *Forge) AddPatchset(pb *Set) bool {
|
||||||
var changed bool
|
var changed bool
|
||||||
// if the name of the patchset is "forge auto commit"
|
// if the name of the patchset is "forge auto commit"
|
||||||
// then just add all the patches
|
// then just add all the patches
|
||||||
|
@ -146,23 +146,18 @@ func (f *Forge) AddPatchset(pb *Patchset) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone() this protobuf into me.forge.Patchsets
|
f.Patchsets.Append(pb)
|
||||||
var newpb *Patchset
|
|
||||||
newpb = proto.Clone(pb).(*Patchset)
|
|
||||||
if newpb != nil {
|
|
||||||
f.Patchsets.Patchsets = append(f.Patchsets.Patchsets, newpb)
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Forge) findAutoPatchset() *Patchset {
|
func (f *Forge) findAutoPatchset() *Set {
|
||||||
for pset := range f.Patchsets.IterAll() {
|
for pset := range f.Patchsets.IterAll() {
|
||||||
if pset.Name == "forge auto commit" {
|
if pset.Name == "forge auto commit" {
|
||||||
return pset
|
return pset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fauto *Patchset
|
var fauto *Set
|
||||||
log.Warn("findAutoPatchset() had to create 'forge auto commit'")
|
log.Warn("findAutoPatchset() had to create 'forge auto commit'")
|
||||||
if fauto == nil {
|
if fauto == nil {
|
||||||
fauto = makeDefaultPatchset()
|
fauto = makeDefaultPatchset()
|
||||||
|
@ -171,8 +166,8 @@ func (f *Forge) findAutoPatchset() *Patchset {
|
||||||
return fauto
|
return fauto
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeDefaultPatchset() *Patchset {
|
func makeDefaultPatchset() *Set {
|
||||||
fauto := new(Patchset)
|
fauto := new(Set)
|
||||||
fauto.Name = "forge auto commit"
|
fauto.Name = "forge auto commit"
|
||||||
fauto.Patches = NewPatches()
|
fauto.Patches = NewPatches()
|
||||||
fauto.Uuid = uuid.New().String()
|
fauto.Uuid = uuid.New().String()
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package forgepb
|
package forgepb
|
||||||
|
|
||||||
|
/*
|
||||||
// makes a new patches protobuf. These are all the patches on your machine.
|
// makes a new patches protobuf. These are all the patches on your machine.
|
||||||
func NewPatches() *Patches {
|
func NewPatches() *Patchs {
|
||||||
x := new(Patches)
|
x := new(Patchs)
|
||||||
x.Uuid = "2679065e-c81d-4a00-aca4-03c158a834fb"
|
x.Uuid = "2679065e-c81d-4a00-aca4-03c158a834fb"
|
||||||
x.Version = "v2.0.0 go.wit.com/lib/protobuf/forgepb"
|
x.Version = "v2.0.0 go.wit.com/lib/protobuf/forgepb"
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
// 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 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?
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 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;
|
|
||||||
}
|
|
16
repoNew.go
16
repoNew.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"go.wit.com/lib/config"
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
@ -35,7 +36,7 @@ func (f *Forge) NewGoRepo(gopath string, url string) (*gitpb.Repo, error) {
|
||||||
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||||
repo.ReadOnly = true
|
repo.ReadOnly = true
|
||||||
}
|
}
|
||||||
repo.Reload()
|
repo.ReloadCheck()
|
||||||
return repo, nil
|
return repo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,13 +53,13 @@ func (f *Forge) AddNamespaceDir(ns string, fullpath string) (*gitpb.Repo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.VerifyBranchNames(repo)
|
f.VerifyBranchNames(repo)
|
||||||
repo.Reload()
|
repo.ReloadCheck()
|
||||||
|
|
||||||
// set the readonly flag based on the users' forge config
|
// set the readonly flag based on the users' forge config
|
||||||
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||||
repo.ReadOnly = true
|
repo.ReadOnly = true
|
||||||
}
|
}
|
||||||
f.configSave = true
|
config.SetChanged("repos", true)
|
||||||
return repo, nil
|
return repo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +164,9 @@ func (f *Forge) VerifyBranchNames(repo *gitpb.Repo) {
|
||||||
|
|
||||||
if repo.GetUserBranchName() == "" {
|
if repo.GetUserBranchName() == "" {
|
||||||
uname := f.configUserBranchName(repo)
|
uname := f.configUserBranchName(repo)
|
||||||
|
if uname == "" {
|
||||||
|
log.Info("configUserBranchName() ERROR: failed with blank")
|
||||||
|
}
|
||||||
if repo.IsBranch(uname) {
|
if repo.IsBranch(uname) {
|
||||||
repo.SetUserBranchName(uname)
|
repo.SetUserBranchName(uname)
|
||||||
} else {
|
} else {
|
||||||
|
@ -170,6 +174,9 @@ func (f *Forge) VerifyBranchNames(repo *gitpb.Repo) {
|
||||||
repo.SetUserBranchName(uname)
|
repo.SetUserBranchName(uname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if repo.GetUserBranchName() == "" || repo.GetDevelBranchName() == "" {
|
||||||
|
log.Infof("VerifyBranchNames() failed m=%s d=%s u=%s\n", repo.GetMasterBranchName(), repo.GetDevelBranchName(), repo.GetUserBranchName())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// what name should be used for the user branch?
|
// what name should be used for the user branch?
|
||||||
|
@ -185,6 +192,9 @@ func (f *Forge) configUserBranchName(repo *gitpb.Repo) string {
|
||||||
if uname != "" {
|
if uname != "" {
|
||||||
return uname
|
return uname
|
||||||
}
|
}
|
||||||
|
if f.Config.Username == "" {
|
||||||
|
// something is wrong!
|
||||||
|
}
|
||||||
|
|
||||||
// use the os.Username
|
// use the os.Username
|
||||||
uname = f.Config.Username
|
uname = f.Config.Username
|
||||||
|
|
33
rill.go
33
rill.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/destel/rill"
|
"github.com/destel/rill"
|
||||||
|
"go.wit.com/lib/config"
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
@ -33,7 +34,6 @@ func (f *Forge) rillUpdate(pool1 int, pool2 int) (int, error) {
|
||||||
// Concurrency = 10
|
// Concurrency = 10
|
||||||
err := rill.ForEach(rills, pool2, func(repo *gitpb.Repo) error {
|
err := rill.ForEach(rills, pool2, func(repo *gitpb.Repo) error {
|
||||||
counter += 1
|
counter += 1
|
||||||
// log.Info("rill.ForEach() gopath=", repo.GetGoPath())
|
|
||||||
return f.updateRepo(repo)
|
return f.updateRepo(repo)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -42,16 +42,14 @@ func (f *Forge) rillUpdate(pool1 int, pool2 int) (int, error) {
|
||||||
|
|
||||||
func (f *Forge) updateRepo(repo *gitpb.Repo) error {
|
func (f *Forge) updateRepo(repo *gitpb.Repo) error {
|
||||||
if !repo.IsValidDir() {
|
if !repo.IsValidDir() {
|
||||||
log.Printf("%10s %-50s gopath=%s\n", "git dir is missing\n", 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.Repos.DeleteByFullPath(repo.FullPath)
|
||||||
f.configSave = true
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if repo.DidRepoChange() {
|
if repo.HasChanged() {
|
||||||
f.configSave = true
|
|
||||||
// log.Info("repo changed ", repo.FullPath, repo.StateChange)
|
// log.Info("repo changed ", repo.FullPath, repo.StateChange)
|
||||||
if err := repo.Reload(); err != nil {
|
if err := repo.ReloadCheck(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -62,7 +60,6 @@ func (f *Forge) updateRepo(repo *gitpb.Repo) error {
|
||||||
} else {
|
} else {
|
||||||
log.Info("readonly flag on repo is wrong", repo.GetGoPath())
|
log.Info("readonly flag on repo is wrong", repo.GetGoPath())
|
||||||
repo.ReadOnly = true
|
repo.ReadOnly = true
|
||||||
f.configSave = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -80,6 +77,9 @@ func (f *Forge) RillReload() int {
|
||||||
for repo := range f.Repos.IterAll() {
|
for repo := range f.Repos.IterAll() {
|
||||||
if !repo.IsValidDir() {
|
if !repo.IsValidDir() {
|
||||||
log.Printf("%s %-50s\n", "got an invalid repo in forgepb.RillReload()", repo.GetFullPath())
|
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
|
continue
|
||||||
}
|
}
|
||||||
all = append(all, repo)
|
all = append(all, repo)
|
||||||
|
@ -95,11 +95,10 @@ func (f *Forge) RillReload() int {
|
||||||
})
|
})
|
||||||
|
|
||||||
rill.ForEach(dirs, RillY, func(repo *gitpb.Repo) error {
|
rill.ForEach(dirs, RillY, func(repo *gitpb.Repo) error {
|
||||||
if !repo.DidRepoChange() {
|
if !repo.HasChanged() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
f.configSave = true
|
repo.ReloadCheck()
|
||||||
repo.Reload()
|
|
||||||
counter += 1
|
counter += 1
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -116,9 +115,9 @@ func (f *Forge) RillFuncError(rillf func(*gitpb.Repo) error) map[string]*RillSta
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Forge) ConfigRill(rillX int, rillY int) {
|
func (f *Forge) ConfigRill(rillX int, rillY int) {
|
||||||
f.rillX = rillX
|
f.Config.RillX = int32(rillX)
|
||||||
f.rillY = rillY
|
f.Config.RillY = int32(rillY)
|
||||||
log.Infof("Setting rill values to %d,%d\n", f.rillX, f.rillY)
|
log.Infof("Setting rill values to %d,%d\n", f.Config.RillX, f.Config.RillY)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RillStats struct {
|
type RillStats struct {
|
||||||
|
@ -142,10 +141,14 @@ func (f *Forge) RillRepos(rillf func(*gitpb.Repo) error) map[string]*RillStats {
|
||||||
for repo := range f.Repos.IterAll() {
|
for repo := range f.Repos.IterAll() {
|
||||||
if !repo.IsValidDir() {
|
if !repo.IsValidDir() {
|
||||||
log.Printf("got an invalid repo in forgepb.RillRepos() %-50s\n", repo.GetFullPath())
|
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
|
continue
|
||||||
}
|
}
|
||||||
all = append(all, repo)
|
all = append(all, repo)
|
||||||
}
|
}
|
||||||
|
// log.Info("Rill Repos len =", len(all))
|
||||||
// Convert a slice of user IDs into a channel
|
// Convert a slice of user IDs into a channel
|
||||||
ids := rill.FromSlice(all, nil)
|
ids := rill.FromSlice(all, nil)
|
||||||
|
|
||||||
|
@ -154,11 +157,11 @@ func (f *Forge) RillRepos(rillf func(*gitpb.Repo) error) map[string]*RillStats {
|
||||||
|
|
||||||
// Read users from the API.
|
// Read users from the API.
|
||||||
// Concurrency = 20
|
// Concurrency = 20
|
||||||
dirs := rill.Map(ids, f.rillX, func(id *gitpb.Repo) (*gitpb.Repo, error) {
|
dirs := rill.Map(ids, int(f.Config.RillX), func(id *gitpb.Repo) (*gitpb.Repo, error) {
|
||||||
return id, nil
|
return id, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
rill.ForEach(dirs, f.rillY, func(repo *gitpb.Repo) error {
|
rill.ForEach(dirs, int(f.Config.RillY), func(repo *gitpb.Repo) error {
|
||||||
// todo: make this a goroutine to show stats to the user
|
// todo: make this a goroutine to show stats to the user
|
||||||
rillMu.Lock()
|
rillMu.Lock()
|
||||||
counter += 1
|
counter += 1
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// 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
|
||||||
|
import "patch.proto"; // Import the well-known type for Timestamp
|
||||||
|
|
||||||
|
message Set { // `autogenpb:http`
|
||||||
|
Patches patches = 1;
|
||||||
|
string uuid = 2;
|
||||||
|
google.protobuf.Timestamp ctime = 3; // when the patches were submitted
|
||||||
|
string submitter = 4; // who submitted these // deprecate this
|
||||||
|
string name = 5; // "fixes for foo"
|
||||||
|
string gitAuthorName = 6;
|
||||||
|
string gitAuthorEmail = 7;
|
||||||
|
string hostname = 8;
|
||||||
|
string tmpDir = 9; // temp dir for 'git am' deprecate this
|
||||||
|
string startBranchName = 10; // deprecate this
|
||||||
|
string endBranchName = 11; // deprecate this
|
||||||
|
string startBranchHash = 12; // deprecate this
|
||||||
|
string endBranchHash = 13; // deprecate this
|
||||||
|
string comment = 14; // deprecate this
|
||||||
|
string state = 15; // deprecate this
|
||||||
|
}
|
||||||
|
|
||||||
|
message Sets { // `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 Set sets = 3;
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package forgepb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/lib/cobol"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (pset *Set) PrintTable() {
|
||||||
|
if pset == nil || pset.Patches == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.DaemonMode(true) // don't timestamp lines
|
||||||
|
|
||||||
|
tablePB := pset.Patches.makeStandardTable()
|
||||||
|
tablePB.MakeTable()
|
||||||
|
tablePB.PrintTable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mt *PatchesTable) PrintTable() {
|
||||||
|
// log.Info("ShowTable() SENDING TO GUI")
|
||||||
|
mt.MakeTable()
|
||||||
|
cobol.PrintTable(mt.pb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *Patches) makeStandardTable() *PatchesTable {
|
||||||
|
t := pb.NewTable("tagList")
|
||||||
|
t.NewUuid()
|
||||||
|
|
||||||
|
col := t.AddNamespace()
|
||||||
|
col.Width = 28
|
||||||
|
|
||||||
|
col = t.AddCommitHash()
|
||||||
|
col.Width = 8
|
||||||
|
|
||||||
|
col = t.AddPatchId()
|
||||||
|
col.Width = 8
|
||||||
|
|
||||||
|
col = t.AddNewHash()
|
||||||
|
col.Width = 8
|
||||||
|
|
||||||
|
col = t.AddTimeFunc("ctime", func(p *Patch) time.Time {
|
||||||
|
// todo
|
||||||
|
return p.Ctime.AsTime()
|
||||||
|
})
|
||||||
|
col.Width = 4
|
||||||
|
|
||||||
|
/*
|
||||||
|
col = t.AddTimeFunc("age", func(repo *gitpb.GitTag) time.Time {
|
||||||
|
// todo
|
||||||
|
return time.Now()
|
||||||
|
})
|
||||||
|
col.Width = 4
|
||||||
|
*/
|
||||||
|
|
||||||
|
// col = t.AddStringFunc("filename", func(p *Patch) string {
|
||||||
|
// _, base := filepath.Split(p.Filename)
|
||||||
|
// return base
|
||||||
|
// })
|
||||||
|
// col.Width = 24
|
||||||
|
|
||||||
|
col = t.AddComment()
|
||||||
|
col.Width = 80
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (pb *Sets) makeStandardTablePB() *SetsTable {
|
||||||
|
t := pb.NewTable("tagList")
|
||||||
|
t.NewUuid()
|
||||||
|
|
||||||
|
col := t.AddUuid()
|
||||||
|
col.Width = 12
|
||||||
|
|
||||||
|
col = t.AddTimeFunc("ctime", func(pset *Set) time.Time {
|
||||||
|
// todo
|
||||||
|
return pset.Ctime.AsTime()
|
||||||
|
})
|
||||||
|
col.Width = 4
|
||||||
|
|
||||||
|
col = t.AddComment()
|
||||||
|
col.Width = -1
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
*/
|
19
structs.go
19
structs.go
|
@ -9,19 +9,12 @@ import (
|
||||||
// maybe an interface someday?
|
// maybe an interface someday?
|
||||||
type Forge struct {
|
type Forge struct {
|
||||||
// one-time initialized data
|
// one-time initialized data
|
||||||
once sync.Once
|
once sync.Once
|
||||||
Config *ForgeConfigs // config repos for readonly, private, etc
|
Config *ForgeConfigs // config repos for readonly, private, etc
|
||||||
Repos *gitpb.Repos // the repo protobufs
|
Repos *gitpb.Repos // the repo protobufs
|
||||||
Patchsets *Patchsets // patches that are in progress
|
Patchsets *Sets // patches that are in progress
|
||||||
configSave bool // if you need to save the config because things changed
|
hostname string // your hostname
|
||||||
hostname string // your hostname
|
goWork bool // means the user is currently using a go.work file
|
||||||
rillX int // used for Rill()
|
|
||||||
rillY int // used for Rill()
|
|
||||||
goWork bool // means the user is currently using a go.work file
|
|
||||||
// goSrc string // the path to go/src
|
|
||||||
// forgeURL string // URL to use to forge.wit.com
|
|
||||||
// configDir string // normally ~/.config/forge
|
|
||||||
// patchDir string // where patches are stored
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Forge) IsGoWork() bool {
|
func (f *Forge) IsGoWork() bool {
|
||||||
|
|
Loading…
Reference in New Issue