Compare commits
89 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 | |
|
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 |
20
Makefile
20
Makefile
|
@ -5,7 +5,7 @@
|
|||
# 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
|
||||
autogenpb --proto patchset.proto
|
||||
|
@ -27,5 +27,19 @@ clean:
|
|||
forgeConfig.pb.go: forgeConfig.proto
|
||||
autogenpb --proto forgeConfig.proto
|
||||
|
||||
patchset.pb.go: patchset.proto
|
||||
autogenpb --proto patchset.proto
|
||||
# patchset.pb.go: 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:
|
||||
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
|
||||
|
|
290
branches.go
290
branches.go
|
@ -1,6 +1,14 @@
|
|||
package forgepb
|
||||
|
||||
import "go.wit.com/log"
|
||||
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")
|
||||
|
@ -26,3 +34,283 @@ func (f *Forge) IsEverythingOnMaster() (int, int, int, error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
8
build.go
8
build.go
|
@ -129,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)
|
||||
|
@ -202,10 +202,10 @@ 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)
|
||||
}
|
||||
|
|
4
clone.go
4
clone.go
|
@ -29,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
|
||||
|
@ -156,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
|
||||
|
|
|
@ -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
|
||||
//
|
205
config.go
205
config.go
|
@ -2,180 +2,85 @@
|
|||
|
||||
package forgepb
|
||||
|
||||
// functions to import and export the protobuf
|
||||
// data to and from config files
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/lib/gui/prep"
|
||||
"go.wit.com/log"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// 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 f.HasFullScan() {
|
||||
f.Repos.HasFullScan = true
|
||||
t := time.Now()
|
||||
f.Repos.FullScan = timestamppb.New(t)
|
||||
}
|
||||
if e := f.Repos.ConfigSave(); e != nil {
|
||||
log.Info("forge.Repos.ConfigSave() error", e)
|
||||
err = e
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// write to ~/.config/forge/ unless ENV{FORGE_CONFIG} is set
|
||||
func (f *ForgeConfigs) ConfigSave() error {
|
||||
data, err := f.Marshal()
|
||||
if err != nil {
|
||||
log.Info("proto.Marshal() failed len", len(data), err)
|
||||
return err
|
||||
}
|
||||
// log.Info("forgepb.ConfigSave() proto.Marshal() worked len", len(data))
|
||||
// functions to import and export the protobuf
|
||||
// data to and from config files
|
||||
|
||||
s := f.FormatTEXT()
|
||||
configWrite("forge.text", []byte(s))
|
||||
|
||||
s = f.FormatJSON()
|
||||
configWrite("forge.json", []byte(s))
|
||||
|
||||
return nil
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
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)
|
||||
}
|
||||
// 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")
|
||||
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 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")
|
||||
/*
|
||||
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)
|
||||
}
|
||||
*/
|
||||
|
||||
// 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
|
||||
func (cfg *ForgeConfigs) InitDefaults() {
|
||||
}
|
||||
|
|
|
@ -1,74 +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"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
func backupConfig() error {
|
||||
// make a new dir to backup the files
|
||||
srcDir := filepath.Join(os.Getenv("FORGE_CONFIG"))
|
||||
destDir := filepath.Join(os.Getenv("FORGE_CONFIG"), "backup")
|
||||
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()
|
||||
|
||||
now := time.Now()
|
||||
timestamp := now.Format("2006.01.02.150405") // bummer. other date doesn't work?
|
||||
dest = dest + timestamp
|
||||
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
|
||||
}
|
|
@ -35,11 +35,15 @@ message ForgeConfig { // `autogenpb:nom
|
|||
|
||||
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`
|
||||
}
|
||||
|
||||
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`
|
||||
|
@ -51,7 +55,17 @@ message ForgeConfigs { // `autogenpb:mar
|
|||
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
|
||||
bool goWork = 9; // true if there is a go.work file
|
||||
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`
|
||||
|
|
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"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (f *Forge) GetHome() string {
|
||||
return f.goSrc
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
201
goSrcScan.go
201
goSrcScan.go
|
@ -1,201 +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
|
||||
}
|
||||
|
||||
// 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.GetGoSrc(), gopath)
|
||||
log.Info("forge creating protobuf for", fullpath)
|
||||
repo, err := f.NewGoRepo(gopath, "")
|
||||
if err != nil {
|
||||
log.Info("\tprotobuf error", gopath, err)
|
||||
}
|
||||
return repo, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
return repo, err
|
||||
}
|
18
goWork.go
18
goWork.go
|
@ -18,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
|
||||
|
@ -32,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
|
||||
|
|
83
http.go
83
http.go
|
@ -5,36 +5,46 @@ 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(url string, data []byte) ([]byte, error) {
|
||||
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, url, bytes.NewBuffer(data))
|
||||
// log.Info("httpPost() with len", len(data), "url", url)
|
||||
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", "fixme:hostname")
|
||||
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 {
|
||||
log.Error(err)
|
||||
return []byte("client.Do(req) error"), err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// log.Info("httpPost() with len", len(data))
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return body, err
|
||||
}
|
||||
|
||||
|
@ -42,7 +52,7 @@ func (f *Forge) HttpPost(url string, data []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
func (f *Forge) LookupPBorig(check *gitpb.Repos) (*gitpb.Repos, error) {
|
||||
url := f.forgeURL + "lookup"
|
||||
url := f.Config.ForgeURL + "lookup"
|
||||
|
||||
for repo := range check.IterByFullPath() {
|
||||
if repo.Namespace == "" {
|
||||
|
@ -53,6 +63,7 @@ func (f *Forge) LookupPBorig(check *gitpb.Repos) (*gitpb.Repos, error) {
|
|||
return check.SubmitReposPB(url)
|
||||
}
|
||||
|
||||
/*
|
||||
func (f *Forge) LookupPB(check *gitpb.Repos) (*gitpb.Repos, error) {
|
||||
url := f.forgeURL + "lookup"
|
||||
|
||||
|
@ -92,3 +103,59 @@ func (f *Forge) UpdatePB(check *gitpb.Repos) (*gitpb.Repos, error) {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
264
humanTable.go
264
humanTable.go
|
@ -3,37 +3,16 @@
|
|||
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)
|
||||
|
@ -41,9 +20,9 @@ func (f *Forge) PrintHumanTable(allr *gitpb.Repos) {
|
|||
t := new(tally)
|
||||
|
||||
// print the header
|
||||
args := []string{"namespace", "cur br", "age", "master", "devel", "user", "curver", "lasttag", "next", "repo type"}
|
||||
sizes := []int{40, 12, 6, 12, 16, 16, 16, 12, 12, 8}
|
||||
log.Info(standardTableSize10(sizes, args))
|
||||
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() {
|
||||
|
@ -60,9 +39,9 @@ func (f *Forge) PrintForgedTable(allr *gitpb.Repos) {
|
|||
t := new(tally)
|
||||
|
||||
// print the header
|
||||
args := []string{"namespace", "cur br", "age", "master", "devel", "last tag", "", "", "", ""}
|
||||
sizes := []int{40, 12, 6, 12, 16, 16, 16, 12, 12, 8}
|
||||
log.Info(standardTableSize10(sizes, args))
|
||||
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() {
|
||||
|
@ -70,7 +49,7 @@ func (f *Forge) PrintForgedTable(allr *gitpb.Repos) {
|
|||
f.printForgedToTable(repo, sizes)
|
||||
tallyBranchTotals(t, repo)
|
||||
}
|
||||
log.Infof("Total repositories: %d (%d master) (%d devel) (%d user) (%d unknown)\n", t.total, t.master, t.devel, t.user, t.unknown)
|
||||
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) {
|
||||
|
@ -79,9 +58,9 @@ func (f *Forge) PrintHumanTableFull(allr *gitpb.Repos) {
|
|||
t := new(tally)
|
||||
|
||||
// print the header
|
||||
args := []string{"cur br", "age", "master", "devel", "user", "curver", "lasttag", "next", "repo type", "namespace"}
|
||||
sizes := []int{12, 6, 12, 16, 16, 16, 12, 12, 8, 0}
|
||||
log.Info(standardTableSize10(sizes, args))
|
||||
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() {
|
||||
|
@ -89,7 +68,7 @@ func (f *Forge) PrintHumanTableFull(allr *gitpb.Repos) {
|
|||
f.printRepoToTable(repo, sizes, true)
|
||||
tallyBranchTotals(t, repo)
|
||||
}
|
||||
log.Infof("Total repositories: %d (%d master) (%d devel) (%d user) (%d unknown)\n", t.total, t.master, t.devel, t.user, t.unknown)
|
||||
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
|
||||
|
@ -99,15 +78,15 @@ func (f *Forge) PrintHumanTableDirty(allr *gitpb.Repos) {
|
|||
t := new(tally)
|
||||
|
||||
// print the header
|
||||
args := []string{"namespace", "cur br", "age", "master", "devel", "user", "curver", "lasttag", "next", "repo type"}
|
||||
sizes := []int{40, 12, 6, 12, 16, 16, 16, 12, 12, 8}
|
||||
log.Info(standardTableSize10(sizes, args))
|
||||
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()
|
||||
|
@ -115,7 +94,7 @@ func (f *Forge) PrintHumanTableDirty(allr *gitpb.Repos) {
|
|||
|
||||
tallyBranchTotals(t, repo)
|
||||
}
|
||||
log.Infof("Total repositories: %d (%d master) (%d devel) (%d user) (%d unknown)\n", t.total, t.master, t.devel, t.user, t.unknown)
|
||||
log.Infof("Total repositories: %d (%d user) (%d devel) (%d master) (%d unknown)\n", t.total, t.user, t.devel, t.master, t.unknown)
|
||||
}
|
||||
|
||||
// used to count which repos are on which branches (master/main, devel, user)
|
||||
|
@ -142,95 +121,42 @@ func tallyBranchTotals(t *tally, repo *gitpb.Repo) {
|
|||
t.user += 1
|
||||
return
|
||||
}
|
||||
log.Printf("unknown curr=%s master=%s devel=%s user=%s\n", repo.GetCurrentBranchName(), repo.GetMasterBranchName(), repo.GetDevelBranchName(), repo.GetUserBranchName())
|
||||
log.Printf("unknown curr=%s user=%s devel=%s master=%s\n", repo.GetCurrentBranchName(), repo.GetUserBranchName(), repo.GetDevelBranchName(), repo.GetMasterBranchName())
|
||||
t.unknown += 1
|
||||
}
|
||||
|
||||
func standardTable5(arg1, arg2, arg3, arg4, arg5 string) string {
|
||||
len1 := 40
|
||||
len2 := 12
|
||||
len3 := 12
|
||||
len4 := 16
|
||||
len5 := 8
|
||||
var s string
|
||||
if len(arg1) > len1 {
|
||||
arg1 = arg1[:len1]
|
||||
}
|
||||
s = "%-" + fmt.Sprintf("%d", len1) + "s "
|
||||
if len(arg2) > len2 {
|
||||
arg2 = arg2[:len2]
|
||||
}
|
||||
s += "%-" + fmt.Sprintf("%d", len2) + "s "
|
||||
if len(arg3) > len3 {
|
||||
arg3 = arg3[:len3]
|
||||
}
|
||||
s += "%-" + fmt.Sprintf("%d", len3) + "s "
|
||||
if len(arg4) > len4 {
|
||||
arg4 = arg4[:len4]
|
||||
}
|
||||
s += "%-" + fmt.Sprintf("%d", len4) + "s "
|
||||
if len(arg5) > len5 {
|
||||
arg5 = arg5[:len5]
|
||||
}
|
||||
s += "%-" + fmt.Sprintf("%d", len5) + "s"
|
||||
return fmt.Sprintf(s, arg1, arg2, arg3, arg4, arg5)
|
||||
}
|
||||
|
||||
/*
|
||||
func standardTable10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 string) string {
|
||||
args := []string{arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10}
|
||||
sizes := []int{40, 12, 6, 12, 16, 16, 16, 12, 12, 8}
|
||||
|
||||
return standardTableSize10(sizes, args)
|
||||
}
|
||||
*/
|
||||
|
||||
func standardTableSize10(sizes []int, args []string) string {
|
||||
var s string
|
||||
for i, si := range sizes {
|
||||
if si == 0 {
|
||||
s += "%-s "
|
||||
} else {
|
||||
s += "%-" + fmt.Sprintf("%d", si) + "s "
|
||||
if len(args[i]) > sizes[i] {
|
||||
args[i] = args[i][:sizes[i]]
|
||||
}
|
||||
}
|
||||
}
|
||||
// there must be a better syntax for this
|
||||
arg1 := args[0]
|
||||
arg2 := args[1]
|
||||
arg3 := args[2]
|
||||
arg4 := args[3]
|
||||
arg5 := args[4]
|
||||
arg6 := args[5]
|
||||
arg7 := args[6]
|
||||
arg8 := args[7]
|
||||
arg9 := args[8]
|
||||
arg10 := args[9]
|
||||
|
||||
return fmt.Sprintf(s, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
|
||||
// return fmt.Sprintf(s, args)
|
||||
}
|
||||
|
||||
func (f *Forge) printRepoToTable(repo *gitpb.Repo, sizes []int, full bool) {
|
||||
var end string
|
||||
|
||||
// shortened version numbers
|
||||
var mhort string = repo.GetMasterVersion()
|
||||
var dhort string = repo.GetDevelVersion()
|
||||
var uhort string = repo.GetUserVersion()
|
||||
if uhort == "uerr" {
|
||||
var uver string = repo.GetUserVersion()
|
||||
if uver == "uerr" {
|
||||
// blank these out
|
||||
uhort = ""
|
||||
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.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))
|
||||
|
@ -244,16 +170,17 @@ func (f *Forge) printRepoToTable(repo *gitpb.Repo, sizes []int, full bool) {
|
|||
|
||||
if repo.IsDirty() {
|
||||
age = ""
|
||||
uver = "* " + uver
|
||||
end += "(dirty) "
|
||||
}
|
||||
|
||||
var args []string
|
||||
if full {
|
||||
args = []string{cname, age, mhort, dhort, uhort, chort, lasttag, thort, rtype, gopath}
|
||||
args = []string{cname, age, uver, dhort, mhort, chort, lasttag, thort, rtype, gopath}
|
||||
} else {
|
||||
args = []string{gopath, cname, age, mhort, dhort, uhort, chort, lasttag, thort, rtype}
|
||||
args = []string{gopath, cname, age, uver, dhort, mhort, chort, lasttag, thort, rtype}
|
||||
}
|
||||
start := standardTableSize10(sizes, args)
|
||||
start := cobol.StandardTableSize10(sizes, args)
|
||||
|
||||
if rtype == "protobuf" {
|
||||
if repo.GoInfo.GoBinary {
|
||||
|
@ -261,23 +188,6 @@ func (f *Forge) printRepoToTable(repo *gitpb.Repo, sizes []int, full bool) {
|
|||
}
|
||||
}
|
||||
|
||||
if repo.GetMasterBranchName() != "master" && repo.GetMasterBranchName() != "main" {
|
||||
end += "(m:" + repo.GetMasterBranchName() + ") "
|
||||
}
|
||||
|
||||
if repo.GetDevelBranchName() != "devel" {
|
||||
end += "(d:" + repo.GetDevelBranchName() + ") "
|
||||
}
|
||||
|
||||
if repo.GetUserBranchName() != f.Config.Username {
|
||||
end += "(u:" + repo.GetUserBranchName() + ") "
|
||||
}
|
||||
|
||||
debname := f.Config.DebName(repo.GetGoPath())
|
||||
if debname != filepath.Base(gopath) {
|
||||
end += "(deb:" + debname + ") "
|
||||
}
|
||||
|
||||
switch repo.GetState() {
|
||||
case "PERFECT":
|
||||
case "unchanged":
|
||||
|
@ -293,7 +203,24 @@ func (f *Forge) printRepoToTable(repo *gitpb.Repo, sizes []int, full bool) {
|
|||
end += "(" + repo.GetState() + ") "
|
||||
}
|
||||
|
||||
log.Info(start, end)
|
||||
if repo.GetMasterBranchName() != "master" && repo.GetMasterBranchName() != "main" {
|
||||
end += "(m:" + repo.GetMasterBranchName() + ") "
|
||||
}
|
||||
|
||||
if repo.GetDevelBranchName() != "devel" {
|
||||
end += "(d:" + repo.GetDevelBranchName() + ") "
|
||||
}
|
||||
|
||||
if repo.GetUserBranchName() != f.Config.Username {
|
||||
end += "(u:" + repo.GetUserBranchName() + ") "
|
||||
}
|
||||
|
||||
debname := f.Config.DebName(repo.GetNamespace())
|
||||
if debname != filepath.Base(gopath) {
|
||||
end += "(deb:" + debname + ") "
|
||||
}
|
||||
|
||||
log.Info(cobol.TerminalChomp(start + " " + end))
|
||||
}
|
||||
|
||||
func (f *Forge) printForgedToTable(repo *gitpb.Repo, sizes []int) {
|
||||
|
@ -308,16 +235,79 @@ func (f *Forge) printForgedToTable(repo *gitpb.Repo, sizes []int) {
|
|||
|
||||
var ns string = repo.GetNamespace()
|
||||
|
||||
// ctime := repo.Tags.GetAge(mhort)
|
||||
// age := shell.FormatDuration(time.Since(ctime))
|
||||
age := shell.FormatDuration(repo.BranchAge(cname))
|
||||
|
||||
var args []string
|
||||
// args = []string{cname, age, mhort, dhort, uhort, chort, lasttag, thort, rtype, gopath}
|
||||
args = []string{ns, cname, age, mhort, dhort, lasttag, "", "", "", ""}
|
||||
start := standardTableSize10(sizes, args)
|
||||
start := cobol.StandardTableSize10(sizes, args)
|
||||
|
||||
end += "todo"
|
||||
|
||||
log.Info(start, end)
|
||||
log.Info(cobol.TerminalChomp(start + " " + end))
|
||||
}
|
||||
|
||||
func (psets *Sets) 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.GetSets() {
|
||||
log.Info(pset.Patches.Uuid, pset.Patches.Name, pset.Patches.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)
|
||||
*/
|
||||
}
|
||||
|
|
349
init.go
349
init.go
|
@ -6,147 +6,158 @@ 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/log"
|
||||
)
|
||||
|
||||
/* better syntax from gin
|
||||
|
||||
Default returns an Engine instance with the Logger and Recovery middleware already attached.
|
||||
func Default(opts ...OptionFunc) *Engine {
|
||||
debugPrintWARNINGDefault()
|
||||
engine := New()
|
||||
engine.Use(Logger(), Recovery())
|
||||
return engine.With(opts...)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// todo: use initOnce
|
||||
// cache.go has Do()
|
||||
// f.initOnce.Do(f.initWork)
|
||||
func Init() *Forge {
|
||||
f := InitPB()
|
||||
|
||||
/*
|
||||
f.Machine = new(zoopb.Machine)
|
||||
if err := f.Machine.ConfigLoad(); err != nil {
|
||||
log.Log(WARN, "zoopb.ConfigLoad() failed", err)
|
||||
f := new(Forge)
|
||||
cfg := new(ForgeConfigs)
|
||||
err := config.ConfigLoad(cfg, "forge", "forge")
|
||||
f.Config = cfg
|
||||
if err != nil {
|
||||
// fhelp.DumpENV("finit:")
|
||||
f.setenv()
|
||||
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
|
||||
}
|
||||
if f.Config.Username == "" {
|
||||
usr, _ := user.Current()
|
||||
f.Config.Username = usr.Username
|
||||
f.SetConfigSave(true)
|
||||
f.Config.ConfigSave()
|
||||
}
|
||||
|
||||
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.initFromConfig()
|
||||
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()
|
||||
}
|
||||
|
||||
// f.Machine.InitWit()
|
||||
|
||||
if f.hasFullScan {
|
||||
// duplicate time checking below. which one to keep?
|
||||
if f.FullScanAge() > time.Minute {
|
||||
log.Log(INFO, "forgepb.Scan() skipping scan. been run a minute ago", f.FullScanAge())
|
||||
return f
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
start := f.Repos.Len()
|
||||
f.ScanGoSrc()
|
||||
f.FullScanRan()
|
||||
end := f.Repos.Len()
|
||||
if (end - start) == 0 {
|
||||
log.Log(INFO, "forgepb.Scan() Scan did not find new git repositories. Total =", end)
|
||||
if f.FullScanAge() > time.Minute {
|
||||
f.rillUpdate(20, 10)
|
||||
}
|
||||
} else {
|
||||
log.Log(INFO, "forgepb.Scan() Scan found", end-start, "new git repositories. Total =", end)
|
||||
f.rillUpdate(20, 10)
|
||||
}
|
||||
|
||||
if f.configSave {
|
||||
// taking this out to debug Marshal() panic
|
||||
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 := 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.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)
|
||||
}
|
||||
|
||||
f.configDir = os.Getenv("FORGE_CONFIG")
|
||||
|
||||
// check again for go.work // user could have a go.work file in ~/go/src
|
||||
if f.goWorkExists() {
|
||||
f.goWork = true
|
||||
}
|
||||
|
||||
// print out the settings that will be used
|
||||
log.Log(INFO, "forgepb.Init() FORGE_CONFIG", os.Getenv("FORGE_CONFIG"))
|
||||
|
||||
f.Config = cfg
|
||||
f.initFromConfig()
|
||||
log.Printf("forge.Init() %s len()=%d\n", f.Config.Filename, f.Repos.Len())
|
||||
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)
|
||||
func (f *Forge) initFromConfig() {
|
||||
if f.configENV() {
|
||||
log.Info("ENV changed config. todo: save config here")
|
||||
f.Config.ConfigSave()
|
||||
}
|
||||
|
||||
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)")
|
||||
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.ConfigLoad()
|
||||
if f.Repos.HasFullScan {
|
||||
f.hasFullScan = true
|
||||
}
|
||||
f.Repos.ConfigLoad(f.Config.ReposPB)
|
||||
|
||||
f.forgeURL = "https://forge.wit.com/"
|
||||
|
||||
if os.Getenv("FORGE_URL") != "" {
|
||||
f.forgeURL = os.Getenv("FORGE_URL")
|
||||
log.Info("got forge url", f.forgeURL)
|
||||
}
|
||||
// init the Patchsets
|
||||
f.Patchsets = NewSets()
|
||||
|
||||
// todo: play with these / determine good values based on user's machine
|
||||
f.rillX = 10
|
||||
f.rillY = 20
|
||||
if f.Config.RillX == 0 {
|
||||
f.Config.RillX = 10
|
||||
}
|
||||
if f.Config.RillY == 0 {
|
||||
f.Config.RillY = 20
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Forge) SetConfigSave(b bool) {
|
||||
config.SetChanged("forge", b)
|
||||
}
|
||||
|
||||
// saves the config if there have been changes
|
||||
func (f *Forge) Exit() {
|
||||
// log.Info("forge.configSave =", f.configSave)
|
||||
if f.Config.Mode == ForgeMode_MASTER {
|
||||
// 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")
|
||||
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() {
|
||||
log.Info("doing setenv()")
|
||||
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_REPOSPB")
|
||||
f.Config.ReposDir = os.Getenv("FORGE_REPOSDIR")
|
||||
f.Config.PatchDir = os.Getenv("FORGE_PATCHDIR")
|
||||
f.Config.ForgeURL = os.Getenv("FORGE_URL")
|
||||
fhelp.DumpENV("setenv end()")
|
||||
})
|
||||
}
|
||||
|
||||
func (f *Forge) GetForgeURL() string {
|
||||
return f.Config.ForgeURL
|
||||
}
|
||||
|
||||
// set the URL for forge otherwise fallback to ENV or to forge.wit.com
|
||||
|
@ -157,95 +168,47 @@ func (f *Forge) SetForgeURL(url string) {
|
|||
if url == "" {
|
||||
url = "https://forge.wit.com/"
|
||||
}
|
||||
f.forgeURL = url
|
||||
log.Info("Forge URL has been set to", f.forgeURL)
|
||||
f.Config.ForgeURL = url
|
||||
os.Setenv("FORGE_URL", f.Config.ForgeURL)
|
||||
log.Info("Forge URL has been set to", f.Config.ForgeURL)
|
||||
}
|
||||
|
||||
func (f *Forge) GetForgeURL() string {
|
||||
return f.forgeURL
|
||||
}
|
||||
|
||||
func (f *Forge) InitMachine() {
|
||||
if f.Config.Username == "" {
|
||||
usr, _ := user.Current()
|
||||
f.Config.Username = usr.Username
|
||||
}
|
||||
f.hostname, _ = os.Hostname()
|
||||
// log.Info(hostname, err)
|
||||
}
|
||||
|
||||
// only init's the protobuf. intended to not scan or change anything
|
||||
func InitPB() *Forge {
|
||||
f := DetermineGoPath()
|
||||
f.InitPB()
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Forge) SetConfigSave(b bool) {
|
||||
f.configSave = b
|
||||
}
|
||||
|
||||
// saves the config if there have been changes
|
||||
func (f *Forge) Exit() {
|
||||
// log.Info("forge.configSave =", f.configSave)
|
||||
if f.configSave {
|
||||
f.ConfigSave()
|
||||
}
|
||||
// log.Info("forge.Exit() ok")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func RawInitPB() *Forge {
|
||||
f := new(Forge)
|
||||
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
// todo: check or exit if err?
|
||||
|
||||
// TODO: rethink this but it works for now
|
||||
if os.Getenv("FORGE_GOSRC") == "" {
|
||||
fullpath := filepath.Join(homeDir, ".cache/forge")
|
||||
err := os.MkdirAll(fullpath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Log(WARN, "mkdir failed", fullpath, err)
|
||||
// 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)
|
||||
}
|
||||
os.Setenv("FORGE_GOSRC", fullpath)
|
||||
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
|
||||
}
|
||||
f.goSrc = os.Getenv("FORGE_GOSRC")
|
||||
|
||||
// also rethink this, but maybe this is the right thing to do
|
||||
if os.Getenv("FORGE_CONFIG") == "" {
|
||||
fullpath := filepath.Join(homeDir, ".config/forge")
|
||||
os.MkdirAll(fullpath, os.ModePerm)
|
||||
os.Setenv("FORGE_CONFIG", fullpath)
|
||||
}
|
||||
|
||||
f.configDir = os.Getenv("FORGE_CONFIG")
|
||||
|
||||
// load the ~/.config/forge/ config
|
||||
f.Config = new(ForgeConfigs)
|
||||
if err := f.Config.ConfigLoad(); err != nil {
|
||||
log.Log(WARN, "forgepb.ConfigLoad() failed", err)
|
||||
}
|
||||
|
||||
f.Repos = gitpb.NewRepos()
|
||||
f.Repos.ConfigLoad()
|
||||
|
||||
f.forgeURL = "https://forge.wit.com/"
|
||||
|
||||
if os.Getenv("FORGE_URL") != "" {
|
||||
f.forgeURL = os.Getenv("FORGE_URL")
|
||||
log.Info("got forge url", f.forgeURL)
|
||||
}
|
||||
|
||||
// where patches are stored
|
||||
f.patchDir = f.goSrc
|
||||
if os.Getenv("FORGE_PATCHDIR") != "" {
|
||||
f.patchDir = os.Getenv("FORGE_PATCHDIR")
|
||||
}
|
||||
|
||||
// todo: play with these / determine good values based on user's machine
|
||||
f.rillX = 10
|
||||
f.rillY = 20
|
||||
|
||||
return f
|
||||
return changed
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright 1994-2025 WIT.COM Inc Licensed GPL 3.0
|
||||
|
||||
package forgepb
|
||||
|
||||
import (
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (f *Forge) GetPatchesets() (*Patchsets, error) {
|
||||
url := f.forgeURL + "GetPatchsets"
|
||||
log.Info("GetPatchsets() url", url)
|
||||
body, err := f.HttpPost(url, nil)
|
||||
if err != nil {
|
||||
log.Info("httpPost() failed:", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Info("GetPatchets() len(body)", len(body))
|
||||
var psets *Patchsets
|
||||
psets = new(Patchsets)
|
||||
err = psets.Unmarshal(body)
|
||||
if err != nil {
|
||||
log.Info("Unmarshal failed", err)
|
||||
return nil, err
|
||||
}
|
||||
/*
|
||||
filename := filepath.Join("/tmp", pbfile)
|
||||
f, _ := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
f.Write(body)
|
||||
f.Close()
|
||||
*/
|
||||
return psets, nil
|
||||
}
|
162
patchset.Make.go
162
patchset.Make.go
|
@ -1,32 +1,52 @@
|
|||
package forgepb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go.wit.com/lib/hostname"
|
||||
"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 newPatchset(name string) *Patchset {
|
||||
pset := new(Patchset)
|
||||
func (p *Patches) HttpPostVerbose(baseURL string, route string) (*Patches, *httppb.HttpRequest, error) {
|
||||
p.PrintTable()
|
||||
return p.HttpPost(baseURL, route)
|
||||
}
|
||||
|
||||
func (p *Set) HttpPostVerbose(baseURL string, route string) (*Set, *httppb.HttpRequest, error) {
|
||||
p.PrintTable()
|
||||
return p.HttpPost(baseURL, route)
|
||||
}
|
||||
|
||||
func (p *Sets) HttpPostVerbose(baseURL string, route string) (*Sets, *httppb.HttpRequest, error) {
|
||||
p.PrintTable()
|
||||
return p.HttpPost(baseURL, route)
|
||||
}
|
||||
|
||||
func newPatchset(name string) *Set {
|
||||
pset := new(Set)
|
||||
pset.Name = name
|
||||
pset.Ctime = timestamppb.New(time.Now())
|
||||
pset.Uuid = uuid.New().String()
|
||||
pset.Hostname, _ = os.Hostname()
|
||||
pset.Hostname, _ = hostname.Get()
|
||||
pset.Patches = NewPatches()
|
||||
|
||||
return pset
|
||||
}
|
||||
|
||||
// creates a patchset
|
||||
// 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)
|
||||
if os.Getenv("GIT_AUTHOR_NAME") == "" {
|
||||
return nil, fmt.Errorf("GIT_AUTHOR_NAME not set")
|
||||
|
@ -43,7 +63,7 @@ func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.RemoveAll(dir) // clean up
|
||||
// defer os.RemoveAll(dir) // clean up
|
||||
pset.TmpDir = dir
|
||||
|
||||
all := f.Repos.SortByFullPath()
|
||||
|
@ -59,6 +79,10 @@ func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
if repo.ActualGetDevelHash() == repo.ActualGetUserHash() {
|
||||
continue
|
||||
}
|
||||
|
||||
// make a patchset from user to devel
|
||||
// TODO: verify branches are otherwise exact
|
||||
pset.StartBranchName = repo.GetDevelBranchName()
|
||||
|
@ -72,41 +96,7 @@ func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
|
|||
return pset, nil
|
||||
}
|
||||
|
||||
/*
|
||||
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 {
|
||||
func (pset *Set) makePatchSetNew(repo *gitpb.Repo) error {
|
||||
startBranch := pset.StartBranchName
|
||||
endBranch := pset.EndBranchName
|
||||
repoDir := filepath.Join(pset.TmpDir, repo.GetGoPath())
|
||||
|
@ -143,22 +133,23 @@ func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
|
|||
return errors.New(fmt.Sprintf("git returned %d", r.Exit))
|
||||
}
|
||||
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
|
||||
return nil
|
||||
}
|
||||
|
||||
err = pset.addPatchFiles(repo)
|
||||
pset.Ctime = timestamppb.New(time.Now())
|
||||
err = pset.addPatchFiles(repo, repoDir)
|
||||
log.Infof("Added %d patches for %s len=%d\n", len(r.Stdout), repo.FullPath, pset.Patches.Len())
|
||||
// pset.PrintTable()
|
||||
return err
|
||||
}
|
||||
|
||||
// git show <original_commit_hash> | git patch-id
|
||||
// git cat-file -p <commit_hash> | grep tree
|
||||
// process each file in pDir/
|
||||
func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
||||
psetDir := repo.GetGoPath()
|
||||
tmpDir := p.TmpDir
|
||||
// log.Info("ADD PATCH FILES ADDED DIR", tmpDir)
|
||||
fullDir := filepath.Join(tmpDir, psetDir)
|
||||
func (p *Set) addPatchFiles(repo *gitpb.Repo, fullDir string) error {
|
||||
var baderr error
|
||||
// log.Info("ADD PATCH FILES ADDED DIR", fullDir)
|
||||
filepath.Walk(fullDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
// Handle possible errors, like permission issues
|
||||
|
@ -170,10 +161,6 @@ 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)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Info("addPatchFile() failed", path)
|
||||
|
@ -183,15 +170,22 @@ func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
|||
patch := new(Patch)
|
||||
patch.Filename, _ = filepath.Rel(p.TmpDir, path)
|
||||
patch.Data = data
|
||||
patch.parseData()
|
||||
patch.StartHash = repo.DevelHash()
|
||||
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.NewHash = "na"
|
||||
patch.Namespace = repo.GetGoPath()
|
||||
if p.Patches == nil {
|
||||
log.Info("SHOULD NOT HAVE HAPPENED. p.Patches == nil")
|
||||
p.Patches = new(Patches)
|
||||
}
|
||||
p.Patches.Append(patch)
|
||||
p.Patches.Uuid = uuid.New().String()
|
||||
// log.Info("ADDED PATCH FILE", path)
|
||||
return nil
|
||||
})
|
||||
|
@ -201,7 +195,7 @@ func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
|
|||
// looks at the git format-patch output
|
||||
// saves the commit Hash
|
||||
// saves the diff lines
|
||||
func (p *Patch) parseData() string {
|
||||
func (p *Patch) parseData() error {
|
||||
lines := strings.Split(string(p.Data), "\n")
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
|
@ -217,7 +211,7 @@ func (p *Patch) parseData() string {
|
|||
p.Files = append(p.Files, line)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
// just an example of how to walk only directories
|
||||
|
@ -242,3 +236,59 @@ func onlyWalkDirs(pDir string) error {
|
|||
})
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
package forgepb
|
||||
|
||||
// functions to import and export the protobuf
|
||||
// data to and from config files
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
func (f *Forge) SubmitDevelPatchSet(name string) (*Patchset, error) {
|
||||
pset, err := f.MakeDevelPatchSet(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := f.submitPatchset(pset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pset, nil
|
||||
}
|
||||
|
||||
func (f *Forge) submitPatchset(pset *Patchset) error {
|
||||
var url string
|
||||
url = f.forgeURL + "patchset"
|
||||
msg, err := pset.Marshal()
|
||||
if err != nil {
|
||||
log.Info("proto.Marshal() failed:", err)
|
||||
return err
|
||||
}
|
||||
log.Info("proto.Marshal() msg len", len(msg))
|
||||
body, err := f.HttpPost(url, msg)
|
||||
if err != nil {
|
||||
log.Info("httpPost() failed:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
newpb := NewPatches()
|
||||
if err := newpb.Unmarshal(body); err != nil {
|
||||
cfcheck := string(body[0:100])
|
||||
if strings.Contains(cfcheck, "<title>Just a moment...</title>") {
|
||||
return log.Errorf("Cloudflare throttled this attempt to submit. TODO: fix this")
|
||||
} else {
|
||||
log.Infof("forged DID NOT SEND BACK PROTOBUF len(body)=%d %s (TODO: look for failure on cloudflare 'is human' check here)\n", len(body), body[0:100])
|
||||
// log.Infof("TODO: try to identify data here len(body)=%d body[0:40]=%s\n", len(body), body[0:40])
|
||||
// log.Info("BODY START:", body[0:10], string(body[0:10]))
|
||||
// log.Info(string(body))
|
||||
// if err := newpb.UnmarshalTEXT(body[2:]); err == nil {
|
||||
// log.Info("wow, that did work. newpb.Len() =", newpb.Len())
|
||||
// }
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Total patches sent ok:", newpb.Len())
|
||||
return nil
|
||||
}
|
|
@ -8,14 +8,15 @@ import (
|
|||
"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()
|
||||
f.Patchsets = NewSets()
|
||||
|
||||
filename := filepath.Join(f.patchDir, "all-patches.pb")
|
||||
filename := filepath.Join(f.Config.PatchDir, "all-patches.pb")
|
||||
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
|
@ -27,12 +28,24 @@ func (f *Forge) LoadPatchsets() error {
|
|||
log.Infof("LoadPatchsets() proto.Marshal() error %v\n", err)
|
||||
return err
|
||||
}
|
||||
log.Infof("LoadPatchsets() worked ok %d\n", f.Patchsets.Len())
|
||||
// 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 = NewSets()
|
||||
f.findAutoPatchset() // adds the default values
|
||||
return f.SavePatchsets()
|
||||
}
|
||||
|
||||
func (f *Forge) SavePatchsets() error {
|
||||
filename := filepath.Join(f.patchDir, "all-patches.pb")
|
||||
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)
|
||||
|
@ -41,7 +54,7 @@ func (f *Forge) SavePatchsets() error {
|
|||
}
|
||||
defer regfile.Close()
|
||||
|
||||
newpb := proto.Clone(f.Patchsets).(*Patchsets)
|
||||
newpb := proto.Clone(f.Patchsets).(*Sets)
|
||||
if newpb == nil {
|
||||
for pset := range f.Patchsets.IterAll() {
|
||||
pset.ShowPatchsets()
|
||||
|
@ -54,7 +67,7 @@ func (f *Forge) SavePatchsets() error {
|
|||
log.Infof("SavePatchset() proto.Marshal() error %v\n", err)
|
||||
return err
|
||||
}
|
||||
log.Infof("SavePatchset() worked (%d) bytes\n", len(data))
|
||||
log.Infof("SavePatchset() worked (%d) bytes on %d patches\n", len(data), f.Patchsets.Len())
|
||||
regfile.Write(data)
|
||||
return nil
|
||||
}
|
||||
|
@ -66,7 +79,7 @@ func cleanSubject(line string) string {
|
|||
return strings.TrimSpace(cleaned)
|
||||
}
|
||||
|
||||
func (pb *Patchset) ShowPatchsets() error {
|
||||
func (pb *Set) 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)
|
||||
|
@ -74,36 +87,52 @@ func (pb *Patchset) ShowPatchsets() error {
|
|||
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)
|
||||
}
|
||||
*/
|
||||
// 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) {
|
||||
func (f *Forge) AddPatchset(pb *Set) 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 + ">"
|
||||
|
||||
// author := "Author: " + os.Getenv("GIT_AUTHOR_NAME")
|
||||
// author += " <" + os.Getenv("GIT_AUTHOR_EMAIL") + ">"
|
||||
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 {
|
||||
log.Info("\tnew patch:", pb.Name, pb.Comment, author)
|
||||
f.addRandomPatch(patch)
|
||||
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
|
||||
return changed
|
||||
}
|
||||
|
||||
// if you got here, this patchset was submitted with a name
|
||||
|
@ -112,55 +141,54 @@ func (f *Forge) AddPatchset(pb *Patchset) {
|
|||
for pset := range f.Patchsets.IterAll() {
|
||||
if pset.Uuid == pb.Uuid {
|
||||
log.Info("ALREADY ADDED", pset.Uuid, pset.Name)
|
||||
return
|
||||
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)
|
||||
}
|
||||
f.Patchsets.Append(pb)
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *Forge) findAutoPatchset() *Patchset {
|
||||
func (f *Forge) findAutoPatchset() *Set {
|
||||
for pset := range f.Patchsets.IterAll() {
|
||||
if pset.Name == "forge auto commit" {
|
||||
return pset
|
||||
}
|
||||
}
|
||||
|
||||
var fauto *Patchset
|
||||
var fauto *Set
|
||||
log.Warn("findAutoPatchset() had to create 'forge auto commit'")
|
||||
if fauto == nil {
|
||||
fauto = new(Patchset)
|
||||
fauto.Name = "forge auto commit"
|
||||
fauto.Patches = NewPatches()
|
||||
fauto.Uuid = uuid.New().String()
|
||||
f.Patchsets.Patchsets = append(f.Patchsets.Patchsets, fauto)
|
||||
fauto = makeDefaultPatchset()
|
||||
f.Patchsets.Append(fauto)
|
||||
}
|
||||
return fauto
|
||||
}
|
||||
|
||||
// adds submitted patches not specifically assigned to a patchset
|
||||
// to the generic patchset called "forge auto commit"
|
||||
func (f *Forge) addRandomPatch(patch *Patch) error {
|
||||
func makeDefaultPatchset() *Set {
|
||||
fauto := new(Set)
|
||||
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 nil
|
||||
return false
|
||||
}
|
||||
fauto := f.findAutoPatchset()
|
||||
if fauto == nil {
|
||||
return log.Errorf("no default place yet")
|
||||
// should have made the default patchset
|
||||
return false
|
||||
}
|
||||
newpb := proto.Clone(patch).(*Patch)
|
||||
if newpb == nil {
|
||||
return log.Errorf("proto.Clone returned nil")
|
||||
}
|
||||
fauto.Patches.Patches = append(fauto.Patches.Patches, newpb)
|
||||
return nil
|
||||
fauto.Patches.Append(patch)
|
||||
return true
|
||||
}
|
||||
|
||||
// returns true if the patch already exists in the protobuf
|
||||
|
@ -179,3 +207,13 @@ func (f *Forge) findPatch(newpatch *Patch) bool {
|
|||
|
||||
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,11 @@
|
|||
package forgepb
|
||||
|
||||
/*
|
||||
// makes a new patches protobuf. These are all the patches on your machine.
|
||||
func NewPatches() *Patchs {
|
||||
x := new(Patchs)
|
||||
x.Uuid = "2679065e-c81d-4a00-aca4-03c158a834fb"
|
||||
x.Version = "v2.0.0 go.wit.com/lib/protobuf/forgepb"
|
||||
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; // after some deliberation, I think I'll just try variable names
|
||||
string gT = 4;
|
||||
string gP = 5;
|
||||
string gs = 6;
|
||||
string gaI = 7; // that exactly match what git uses.
|
||||
string gan = 8;
|
||||
string gae = 9;
|
||||
string gcI = 10;
|
||||
string gcn = 11;
|
||||
string gce = 12;
|
||||
string gN = 13;
|
||||
string gGG = 14;
|
||||
string gGS = 15;
|
||||
string gGK = 16;
|
||||
string newHash = 17; // new hash
|
||||
string state = 18; // the 'state' of the patch
|
||||
string filename = 19; // `autogenpb:unique` `autogenpb:sort`
|
||||
string startHash = 20; // the start commit hash
|
||||
string commitHash = 21; // the git commit hash of this patch `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`
|
||||
string uuid = 1; // `autogenpb:uuid:2679065e-c81d-4a00-aca4-03c158a834fb`
|
||||
string version = 2; // `autogenpb:version:v2.0.0`
|
||||
repeated Patch Patches = 3;
|
||||
}
|
||||
|
||||
message Patchset { // `autogenpb:marshal`
|
||||
Patches patches = 1; //
|
||||
string name = 2; // `autogenpb:sort`
|
||||
string comment = 3; //
|
||||
string gitAuthorName = 4; // `autogenpb:sort`
|
||||
string gitAuthorEmail = 5; //
|
||||
google.protobuf.Timestamp ctime = 6; // create time of the patchset
|
||||
string tmpDir = 7; // temp dir
|
||||
string startBranchName = 8; //
|
||||
string endBranchName = 9; //
|
||||
string startBranchHash = 10; //
|
||||
string endBranchHash = 11; //
|
||||
string state = 12; // the state of the patch
|
||||
string uuid = 13; // `autogenpb:sort` `autogenpb:unique`
|
||||
string hostname = 14; //
|
||||
}
|
||||
|
||||
message Patchsets { // `autogenpb:marshal` `autogenpb:gui`
|
||||
string uuid = 1; // `autogenpb:uuid:be926ad9-f07f-484c-adf2-d96eeabf3079`
|
||||
string version = 2; // `autogenpb:version:v0.0.45`
|
||||
repeated Patchset Patchsets = 3;
|
||||
}
|
21
repoNew.go
21
repoNew.go
|
@ -9,12 +9,13 @@ 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
|
||||
|
@ -35,7 +36,7 @@ func (f *Forge) NewGoRepo(gopath string, url string) (*gitpb.Repo, error) {
|
|||
if f.Config.IsReadOnly(repo.GetGoPath()) {
|
||||
repo.ReadOnly = true
|
||||
}
|
||||
repo.Reload()
|
||||
repo.ReloadCheck()
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
|
@ -52,12 +53,13 @@ func (f *Forge) AddNamespaceDir(ns string, fullpath string) (*gitpb.Repo, error)
|
|||
}
|
||||
|
||||
f.VerifyBranchNames(repo)
|
||||
repo.Reload()
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -162,6 +164,9 @@ func (f *Forge) VerifyBranchNames(repo *gitpb.Repo) {
|
|||
|
||||
if repo.GetUserBranchName() == "" {
|
||||
uname := f.configUserBranchName(repo)
|
||||
if uname == "" {
|
||||
log.Info("configUserBranchName() ERROR: failed with blank")
|
||||
}
|
||||
if repo.IsBranch(uname) {
|
||||
repo.SetUserBranchName(uname)
|
||||
} else {
|
||||
|
@ -169,6 +174,9 @@ func (f *Forge) VerifyBranchNames(repo *gitpb.Repo) {
|
|||
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?
|
||||
|
@ -184,6 +192,9 @@ func (f *Forge) configUserBranchName(repo *gitpb.Repo) string {
|
|||
if uname != "" {
|
||||
return uname
|
||||
}
|
||||
if f.Config.Username == "" {
|
||||
// something is wrong!
|
||||
}
|
||||
|
||||
// use the os.Username
|
||||
uname = f.Config.Username
|
||||
|
@ -214,7 +225,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)
|
||||
}
|
||||
|
||||
|
@ -241,6 +252,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)
|
||||
}
|
||||
|
|
37
rill.go
37
rill.go
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/destel/rill"
|
||||
"go.wit.com/lib/config"
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
@ -33,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)
|
||||
})
|
||||
|
||||
|
@ -42,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\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.configSave = true
|
||||
return nil
|
||||
}
|
||||
|
||||
if repo.DidRepoChange() {
|
||||
f.configSave = true
|
||||
if repo.HasChanged() {
|
||||
// log.Info("repo changed ", repo.FullPath, repo.StateChange)
|
||||
if err := repo.Reload(); err != nil {
|
||||
if err := repo.ReloadCheck(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
@ -62,7 +60,6 @@ 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
|
||||
|
@ -79,7 +76,10 @@ 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.GetGoPath())
|
||||
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)
|
||||
|
@ -95,11 +95,10 @@ func (f *Forge) RillReload() int {
|
|||
})
|
||||
|
||||
rill.ForEach(dirs, RillY, func(repo *gitpb.Repo) error {
|
||||
if !repo.DidRepoChange() {
|
||||
if !repo.HasChanged() {
|
||||
return nil
|
||||
}
|
||||
f.configSave = true
|
||||
repo.Reload()
|
||||
repo.ReloadCheck()
|
||||
counter += 1
|
||||
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) {
|
||||
f.rillX = rillX
|
||||
f.rillY = rillY
|
||||
log.Infof("Setting rill values to %d,%d\n", f.rillX, f.rillY)
|
||||
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 {
|
||||
|
@ -141,11 +140,15 @@ func (f *Forge) RillRepos(rillf func(*gitpb.Repo) error) map[string]*RillStats {
|
|||
|
||||
for repo := range f.Repos.IterAll() {
|
||||
if !repo.IsValidDir() {
|
||||
log.Printf("got an invalid repo in forgepb.RillRepos() %-50s\n", repo.GetGoPath())
|
||||
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)
|
||||
|
||||
|
@ -154,11 +157,11 @@ func (f *Forge) RillRepos(rillf func(*gitpb.Repo) error) map[string]*RillStats {
|
|||
|
||||
// Read users from the API.
|
||||
// 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
|
||||
})
|
||||
|
||||
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
|
||||
rillMu.Lock()
|
||||
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
|
||||
}
|
||||
*/
|
44
structs.go
44
structs.go
|
@ -2,7 +2,6 @@ package forgepb
|
|||
|
||||
import (
|
||||
sync "sync"
|
||||
"time"
|
||||
|
||||
"go.wit.com/lib/protobuf/gitpb"
|
||||
)
|
||||
|
@ -10,45 +9,14 @@ import (
|
|||
// 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
|
||||
configDir string // normally ~/.config/forge
|
||||
goWork bool // means the user is currently using a go.work file
|
||||
Config *ForgeConfigs // config repos for readonly, private, etc
|
||||
Repos *gitpb.Repos // the repo protobufs
|
||||
configSave bool // if you need to save the config because things changed
|
||||
hasFullScan bool // track last scan so it can be throttled
|
||||
fullscan time.Time // time of the last scan so it can be throttled
|
||||
hostname string // your hostname
|
||||
forgeURL string // URL to use to forge.wit.com
|
||||
rillX int // used for Rill()
|
||||
rillY int // used for Rill()
|
||||
Patchsets *Patchsets // patches that are in progress
|
||||
patchDir string // where patches are stored
|
||||
}
|
||||
|
||||
func (f *Forge) GetGoSrc() string {
|
||||
return f.goSrc
|
||||
}
|
||||
|
||||
func (f *Forge) GetConfigDir() string {
|
||||
return f.configDir
|
||||
once sync.Once
|
||||
Config *ForgeConfigs // config repos for readonly, private, etc
|
||||
Repos *gitpb.Repos // the repo protobufs
|
||||
Patchsets *Sets // 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 {
|
||||
return f.goWork
|
||||
}
|
||||
|
||||
func (f *Forge) HasFullScan() bool {
|
||||
return f.Repos.HasFullScan
|
||||
}
|
||||
|
||||
func (f *Forge) FullScanRan() {
|
||||
f.fullscan = time.Now()
|
||||
}
|
||||
|
||||
func (f *Forge) FullScanAge() time.Duration {
|
||||
fs := f.Repos.FullScan.AsTime()
|
||||
return time.Since(fs)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue