Compare commits

...

66 Commits

Author SHA1 Message Date
Jeff Carr 7e1804f6e3 needed FindByUuid() 2025-03-12 09:21:29 -05:00
Jeff Carr 8ceec9210d track applied or upstream for patches 2025-03-11 12:00:38 -05:00
Jeff Carr cf6db578a4 store ctime 2025-03-10 23:26:57 -05:00
Jeff Carr da485dcc3f generate a patchset table gui 2025-03-10 13:52:46 -05:00
Jeff Carr 5377a89d2c autogenpb gui code for patchsets 2025-03-10 09:12:08 -05:00
Jeff Carr f7d6dfa6a7 clearer text 2025-03-03 23:49:51 -06:00
Jeff Carr 281ffbc75b minor cleanups 2025-03-02 07:49:45 -06:00
Jeff Carr 4193f41847 move to better patchset protobuf 2025-03-02 04:13:27 -06:00
Jeff Carr 9d5bae8a14 add a 'forge mode' concept 2025-03-02 03:03:45 -06:00
Jeff Carr f540aab434 remove zoopb from forge 2025-02-22 06:53:42 -06:00
Jeff Carr a170250cb4 wrong logic 2025-02-21 17:17:05 -06:00
Jeff Carr 2fc67512c8 changes from gitpb 2025-02-21 09:34:24 -06:00
Jeff Carr eaadfa21d3 add a specific http POST option 2025-02-16 12:05:35 -06:00
Jeff Carr 22ebf174c8 show the lasttag 2025-02-15 18:18:30 -06:00
Jeff Carr 9a87c93bad quiet output 2025-02-15 12:31:00 -06:00
Jeff Carr b7b18626d8 more on Machine.Init() 2025-02-15 12:21:36 -06:00
Jeff Carr 7900b1416e add IdentifyProtobuf() 2025-02-15 05:35:01 -06:00
Jeff Carr 5c84b9ab66 needed to bypass go-deb problems 2025-02-14 20:43:47 -06:00
Jeff Carr c09e292a66 something to do abitrary dirs 2025-02-14 18:39:36 -06:00
Jeff Carr 3278f6400e add GetHome() 2025-02-10 23:30:15 -06:00
Jeff Carr 83ad663fc0 must send an error on nil 2025-02-09 15:05:23 -06:00
Jeff Carr f70f54615f fix nil 2025-02-09 14:48:47 -06:00
Jeff Carr 018772dbfb more things to check if release is needed 2025-02-08 06:32:59 -06:00
Jeff Carr 9baa477990 add a date and a uuid to the patchset 2025-02-02 15:07:23 -06:00
Your Name ab01c2cd60 auto formatting using autogenpb 2024-01-01 12:00:00 -06:00
Jeff Carr c89f101fb2 namechange 2025-02-01 06:58:27 -06:00
Jeff Carr 23d7ad1581 various updates 2025-02-01 06:57:57 -06:00
Jeff Carr ec4acd425c save the path to the config dir 2025-01-31 13:47:45 -06:00
Jeff Carr 0614066fdb add 'state' 2025-01-30 18:00:30 -06:00
Your Name b60279b19a a better protobuf file to switch to later 2024-01-01 12:00:00 -06:00
Jeff Carr 95fcacfde0 add a MakePatchset() 2025-01-30 07:42:25 -06:00
Jeff Carr b6a71a515f url via ENV 2025-01-30 06:21:15 -06:00
Jeff Carr 47ee3f1493 rm old code 2025-01-30 04:44:15 -06:00
Jeff Carr 329710f9e7 rm old code 2025-01-30 02:24:34 -06:00
Jeff Carr f7b5e1a83e try a way to track the times so they can be throttled 2025-01-30 01:15:15 -06:00
Jeff Carr 7c37e3841a move this here from go-mod-sum 2025-01-29 21:24:26 -06:00
Jeff Carr f146bf4ef0 quiet lots of debugging output 2025-01-29 20:00:49 -06:00
Jeff Carr d9d90e9e12 ignore stuff 2025-01-29 12:25:06 -06:00
Jeff Carr 393b91c415 store the new hash 2025-01-29 12:24:42 -06:00
Jeff Carr b412e50df0 save git commit msg and repo namespace 2025-01-29 12:24:42 -06:00
Jeff Carr aa06450042 minor 2025-01-29 12:24:42 -06:00
Jeff Carr 121e9f08da print how many patches there were 2025-01-29 12:24:42 -06:00
Jeff Carr df19b5b8f8 cleanup debugging output 2025-01-29 12:24:42 -06:00
Jeff Carr 0efc3c67ca turn of the verbose logging 2025-01-29 12:24:42 -06:00
Jeff Carr 667257595d add submit patchset 2025-01-29 12:24:42 -06:00
Jeff Carr 76a0347fdf mv 2025-01-29 12:24:42 -06:00
Jeff Carr 66738e4300 debugging releaser 2025-01-20 07:59:14 -06:00
Jeff Carr 3e4b1ddc83 cleanup protofile in total violaton of protobuf rules 2025-01-20 05:09:46 -06:00
Jeff Carr 9ec7b4394f make HttpPost() global 2025-01-20 03:31:19 -06:00
Jeff Carr 1191b9b65d moved GetPatchsets() here from forge 2025-01-20 03:23:33 -06:00
Jeff Carr 52b8a4e312 need better handling here 2025-01-20 01:40:32 -06:00
Jeff Carr b770759167 rm old code 2025-01-19 16:07:50 -06:00
Jeff Carr 0898c24f45 trying to debug the logic since it's failing 2025-01-19 11:52:20 -06:00
Jeff Carr 58c64cd53b might show branch age in table finally 2025-01-19 10:48:50 -06:00
Jeff Carr f29f25b9b7 hide uerr in table 2025-01-19 09:25:39 -06:00
Jeff Carr 4328692039 redo with rill 2025-01-19 04:32:20 -06:00
Jeff Carr 244bf612f9 load repos output is cleaner 2025-01-19 03:18:47 -06:00
Jeff Carr f4ac491490 debugging, but runs ok 2025-01-19 02:37:31 -06:00
Jeff Carr bdf9d97cf9 reorder target to the end 2025-01-19 00:36:32 -06:00
Jeff Carr a822e1e4f0 switch some more things to use rill 2025-01-18 23:26:19 -06:00
Jeff Carr cee7e25f3d rill is awesome. thank Viktor 2025-01-18 11:11:16 -06:00
Jeff Carr 9b8cb52b7b show deb package name if different than the standard 2025-01-18 10:34:38 -06:00
Jeff Carr 538531f503 this is a dumb idea. never do this. fix the repo. 2025-01-18 04:48:34 -06:00
Jeff Carr e8f29e593d more human readable but standard version timestamp 2025-01-18 03:14:41 -06:00
Jeff Carr b8d0864c37 show abnormal branch 2025-01-17 13:20:40 -06:00
Jeff Carr 156af56859 init patches 2025-01-17 10:59:28 -06:00
26 changed files with 894 additions and 355 deletions

6
.gitignore vendored
View File

@ -1,5 +1,5 @@
go.*
*.swp
*.patch
*.mbox
*.pb.go
forgeConfig/forgeConfig

View File

@ -20,7 +20,7 @@ goimports:
goimports -w *.go
clean:
rm -f *.pb.go
rm -f *.pb.go *.patch
-rm -f go.*
go-mod-clean --purge

View File

@ -58,6 +58,9 @@ func (f *Forge) doBuild(repo *gitpb.Repo, userFlags []string, goWhat string) err
data, _ := repo.ReadFile(".forge")
log.Info(".forge =", string(data))
log.Info("todo: do the custom build instructions")
basedir, filename := filepath.Split(repo.GetGoPath())
log.Info("touching filename", basedir, filename)
repo.RunVerbose([]string{"touch", filename})
return nil
}
@ -115,7 +118,8 @@ func (f *Forge) doBuild(repo *gitpb.Repo, userFlags []string, goWhat string) err
// set standard ldflag options
now := time.Now()
datestamp := now.UTC().Format("2006/01/02_1504_UTC")
// datestamp := now.UTC().Format("2006/01/02_1504_UTC")
datestamp := now.UTC().Format("2006-01-02_15:04:05_UTC") // 2006-01-02 15:04:05 UTC
// log.Info("datestamp =", datestamp)
// add some standard golang flags
ldflags := "-X main.VERSION=" + version + " "
@ -205,3 +209,21 @@ func (f *Forge) FindWorkingDirRepo() *gitpb.Repo {
basedir = strings.Trim(basedir, "/")
return f.FindByGoPath(basedir)
}
// Never do this. this is stupid. fix your repo
// Never try to version & release broken repos
// leave this code here as a reminder to never attempt this
func (f *Forge) forgeIgnoreGoMod(repo *gitpb.Repo) bool {
if !repo.Exists(".forge") {
return false
}
log.Info("custom build instructions")
data, _ := repo.ReadFile(".forge")
log.Info(".forge =", string(data))
for _, line := range strings.Split(string(data), "\n") {
if strings.Contains(line, "forge:ignore:gomod") { // this is stupid
return true
}
}
return false
}

161
cleanGoSum.go Normal file
View File

@ -0,0 +1,161 @@
package forgepb
import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
// This will recreate your go.sum and go.mod files
var cleanVerbose bool = false
// checks to see if every 'master' git branch version
// matches the go.sum file
func (f *Forge) CleanGoDepsCheckOk(check *gitpb.Repo) error {
var err error = nil
var fixes [][]string
log.Printf("current repo %s go dependancy count: %d", check.GetGoPath(), check.GoDepsLen())
if check.GoDeps == nil {
return errors.New("check.GoDeps == nil")
}
all := check.GoDeps.SortByGoPath()
for all.Scan() {
depRepo := all.Next()
found := f.FindByGoPath(depRepo.GetGoPath())
if found == nil {
if f.CheckOverride(depRepo.GetGoPath()) {
// skip this gopath because it's probably broken forever
continue
}
log.Info("not found:", depRepo.GetGoPath())
err = errors.New("not found: " + depRepo.GetGoPath())
continue
}
// log.Info("found dep", depRepo.GetGoPath())
if depRepo.GetVersion() != found.GetMasterVersion() {
check := f.FindByGoPath(depRepo.GetGoPath())
var ends string
if check.CheckDirty() {
ends = "(dirty) "
}
if f.Config.IsReadOnly(check.GetGoPath()) {
ends += "(ignoring read-only) "
if cleanVerbose {
log.Printf("%-48s ok error .%s. vs .%s. %s", depRepo.GetGoPath(),
depRepo.GetVersion(), found.GetMasterVersion(), ends)
}
} else {
if f.CheckOverride(depRepo.GetGoPath()) {
ends += "(override) "
if cleanVerbose {
log.Printf("%-48s ok error .%s. vs .%s. %s", depRepo.GetGoPath(),
depRepo.GetVersion(), found.GetMasterVersion(), ends)
// skip this gopath because it's probably broken forever
}
continue
} else {
log.Printf("%-48s error %10s vs %10s %s", depRepo.GetGoPath(),
depRepo.GetVersion(), found.GetMasterVersion(), ends)
errs := fmt.Sprintf("%s error %s vs %s %s", depRepo.GetGoPath(),
depRepo.GetVersion(), found.GetMasterVersion(), ends)
if ok, _ := ValidGoVersion(found.GetMasterVersion()); ok {
// can't go get invalid version numbers
cmd := []string{"go", "get", depRepo.GetGoPath() + "@" + found.GetMasterVersion()}
fixes = append(fixes, cmd)
}
err = errors.New(errs)
}
}
}
}
for i, cmd := range fixes {
log.Info("try cmd", i, cmd)
check.RunRealtime(cmd)
}
return err
}
func (f *Forge) TrimGoSum(check *gitpb.Repo) error {
var stuff map[string]string
stuff = make(map[string]string)
var modver map[string]string
modver = make(map[string]string)
var good map[string]bool
good = make(map[string]bool)
if check == nil {
log.Info("boo, check == nil")
return errors.New("*repo == nil")
}
filename := filepath.Join(filepath.Join(check.FullPath, "go.sum"))
data, err := os.ReadFile(filename)
if err != nil {
return err
}
for _, line := range strings.Split(string(data), "\n") {
parts := strings.Fields(line)
if len(parts) < 3 {
log.Info("WIERD OR BAD:", line)
continue
}
gopath := parts[0]
version := parts[1]
hash := parts[2]
if strings.HasSuffix(version, "/go.mod") {
if _, ok := stuff[gopath]; ok {
if cleanVerbose {
log.Info("MATCHED: gopath:", gopath, "version:", version)
}
modver[gopath] = version + " " + hash
good[gopath] = true
} else {
if cleanVerbose {
log.Info("GARBAGE: gopath:", gopath, "version:", version)
}
}
} else {
if cleanVerbose {
log.Info("GOOD : gopath:", gopath, "version:", version)
}
stuff[gopath] = version + " " + hash
}
}
keys := make([]string, 0, len(stuff))
for k := range stuff {
keys = append(keys, k)
}
// rewrite the go.sum file
newfilename := filepath.Join(filepath.Join(check.FullPath, "go.sum"))
newf, err := os.OpenFile(newfilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer newf.Close()
sort.Strings(keys)
for _, gopath := range keys {
if good[gopath] {
fmt.Fprintf(newf, "%s %s\n", gopath, stuff[gopath])
fmt.Fprintf(newf, "%s %s\n", gopath, modver[gopath])
check := f.FindByGoPath(gopath)
if check == nil {
log.Info("gopath does not really exist:", gopath)
}
}
}
// fmt.Fprintln(newf, "test")
return nil
}

View File

@ -1,3 +1,5 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
import (

View File

@ -1,3 +1,5 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
// functions to import and export the protobuf
@ -8,8 +10,10 @@ import (
"fmt"
"os"
"path/filepath"
"time"
"go.wit.com/log"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
)
func (f *Forge) ConfigSave() error {
@ -28,6 +32,11 @@ func (f *Forge) ConfigSave() error {
}
}
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
@ -43,7 +52,7 @@ func (f *ForgeConfigs) ConfigSave() error {
log.Info("proto.Marshal() failed len", len(data), err)
return err
}
log.Info("forgepb.ConfigSave() proto.Marshal() worked len", len(data))
// log.Info("forgepb.ConfigSave() proto.Marshal() worked len", len(data))
s := f.FormatTEXT()
configWrite("forge.text", []byte(s))

View File

@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
"time"
@ -15,12 +14,8 @@ import (
func backupConfig() error {
// make a new dir to backup the files
now := time.Now()
// timestamp := now.Format("2022.07.18.190545") // 50yr shout out to K&R
timestamp := now.Format("2006.01.02.150405") // bummer. other date doesn't work?
srcDir := filepath.Join(os.Getenv("FORGE_CONFIG"))
destDir := filepath.Join(os.Getenv("FORGE_CONFIG"), timestamp)
destDir := filepath.Join(os.Getenv("FORGE_CONFIG"), "backup")
return backupFiles(srcDir, destDir)
}
@ -44,7 +39,7 @@ func backupFiles(srcDir string, destDir string) error {
continue
}
log.Println("backing up file", entry.Name())
// log.Println("backing up file", entry.Name())
srcPath := filepath.Join(srcDir, entry.Name())
destPath := filepath.Join(destDir, entry.Name())
@ -64,6 +59,9 @@ func copyFile(src, dest string) error {
}
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

View File

@ -1,6 +1,8 @@
package forgepb
import (
"errors"
"fmt"
"strings"
"go.wit.com/lib/protobuf/gitpb"
@ -16,21 +18,18 @@ import (
// it re-scans the go.sum file. DOES NOT MODIFY ANYTHING
// this is the last thing to run to double check everything
// before 'git tag' or git push --tags
func (f *Forge) FinalGoDepsCheckOk(check *gitpb.Repo, verbose bool) bool {
var good bool = true
func (f *Forge) FinalGoDepsCheckOk(check *gitpb.Repo, verbose bool) error {
if check == nil {
log.Info("boo, check == nil")
return false
return errors.New("FinalGoDepsCheckOk() boo, check == nil")
}
// parse the go.mod and go.sum files
if !check.ParseGoSum() {
log.Info("forge.FinalGoDepsCheckOk() failed")
return false
return fmt.Errorf("forge.ParseGoSum() failed. go.mod & go.sum are broken")
}
if check.GetGoPrimitive() {
return true
return nil
}
deps := check.GoDeps.SortByGoPath()
@ -42,8 +41,10 @@ func (f *Forge) FinalGoDepsCheckOk(check *gitpb.Repo, verbose bool) bool {
// skip this gopath because it's probably broken forever
continue
}
log.Info("not found:", depRepo.GetGoPath())
good = false
return fmt.Errorf("dep not found: %s", depRepo.GetGoPath())
}
if depRepo.GetVersion() == found.GetMasterVersion() {
// log.Printf("%-48s error ?? %-10s vs %-10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
continue
}
// log.Info("found dep", depRepo.GetGoPath())
@ -61,51 +62,93 @@ func (f *Forge) FinalGoDepsCheckOk(check *gitpb.Repo, verbose bool) bool {
// skip this gopath because it's probably broken forever
continue
} else {
log.Printf("%-48s error %10s vs %10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
good = false
// log.Printf("%-48s error ?? %-10s vs %-10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
// log.Printf("%-48s error %10s vs %10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
return fmt.Errorf("%-48s error %10s vs %10s", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
}
}
}
}
if good {
log.Printf("current repo %s go dependancy count: %d\n", check.GetGoPath(), check.GoDepsLen())
}
return good
return nil
}
func (f *Forge) CheckOverride(gopath string) bool {
if gopath == "cloud.google.com/go" {
log.Info("CheckOverride() is ignoring", gopath)
// log.Info("CheckOverride() is ignoring", gopath)
return true
}
if gopath == "bou.ke/monkey" {
log.Info("CheckOverride() is ignoring", gopath)
// log.Info("CheckOverride() is ignoring", gopath)
return true
}
if gopath == "github.com/posener/complete/v2" {
log.Info("CheckOverride() is ignoring", gopath)
// log.Info("CheckOverride() is ignoring", gopath)
return true
}
if strings.HasPrefix(gopath, "github.com/go-gl") {
log.Info("CheckOverride() is ignoring", gopath)
// log.Info("CheckOverride() is ignoring", gopath)
return true
}
if strings.HasPrefix(gopath, "google.golang.org") {
log.Info("CheckOverride() is ignoring", gopath)
// log.Info("CheckOverride() is ignoring", gopath)
return true
}
if strings.HasPrefix(gopath, "go.opencensus.io") {
log.Info("CheckOverride() is ignoring", gopath)
// log.Info("CheckOverride() is ignoring", gopath)
return true
}
if strings.HasPrefix(gopath, "github.com/nicksnyder/go-i18n") {
log.Info("CheckOverride() is ignoring", gopath)
// log.Info("CheckOverride() is ignoring", gopath)
return true
}
// fuckit for now. just blacklist github.com
if strings.HasPrefix(gopath, "github.com/") {
log.Info("CheckOverride() is ignoring", gopath)
// log.Info("CheckOverride() is ignoring", gopath)
return true
}
return false
}
func (f *Forge) TestGoDepsCheckOk(godeps *gitpb.GoDeps, verbose bool) error {
if godeps == nil {
return errors.New("forge.TestGoDepsCheckOk() godeps == nil")
}
all := godeps.SortByGoPath()
for all.Scan() {
depRepo := all.Next()
found := f.FindByGoPath(depRepo.GetGoPath())
if found == nil {
if f.CheckOverride(depRepo.GetGoPath()) {
// skip this gopath because it's probably broken forever
continue
}
return fmt.Errorf("dep not found: %s", depRepo.GetGoPath())
}
if depRepo.GetVersion() == found.GetMasterVersion() {
// log.Printf("%-48s error ?? %-10s vs %-10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
continue
}
// log.Info("found dep", depRepo.GetGoPath())
if depRepo.GetVersion() != found.GetTargetVersion() {
check := f.FindByGoPath(depRepo.GetGoPath())
if f.Config.IsReadOnly(check.GetGoPath()) {
if verbose {
log.Printf("%-48s ok error .%s. vs .%s. (ignoring read-only repo)\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
}
} else {
if f.CheckOverride(depRepo.GetGoPath()) {
if verbose {
log.Printf("%-48s ok error .%s. vs .%s. (forge.CheckOverride())\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
}
// skip this gopath because it's probably broken forever
continue
} else {
// log.Printf("%-48s error ?? %-10s vs %-10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
// log.Printf("%-48s error %10s vs %10s\n", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetTargetVersion())
return fmt.Errorf("%-48s error %10s vs %10s", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetMasterVersion())
}
}
}
}
return nil
}

View File

@ -1,3 +1,5 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
syntax = "proto3";
package forgepb;
@ -12,33 +14,49 @@ import "google/protobuf/timestamp.proto"; // Import the well-known type for Time
// package names sometimes must be different than the binary name
// for example 'zookeeper' is packaged as 'zookeeper-go'
// due to the prior apache foundation project. This happens and is ok!
message ForgeConfig { // `autogenpb:nomutex`
string goPath = 1; // `autogenpb:unique` `autogenpb:sort` // Examples: 'go.wit.com/apps/go-clone' or "~/mythings" or "/home/src/foo"
message ForgeConfig { // `autogenpb:nomutex`
string goPath = 1; // `autogenpb:unique` `autogenpb:sort` // Examples: 'go.wit.com/apps/go-clone' or "~/mythings" or "/home/src/foo"
bool writable = 2; // if you have write access to the repo
bool readOnly = 3; // the opposite, but needed for now because I don't know what I'm doing
bool private = 4; // if the repo can be published
bool directory = 5; // everything in this directory should use these writable & private values
bool favorite = 6; // you like this. always git clone/go clone this repo
bool interesting = 7; // this is something interesting you found and want to remember it
bool writable = 2; // if you have write access to the repo
bool readOnly = 3; // the opposite, but needed for now because I don't know what I'm doing
bool private = 4; // if the repo can be published
bool directory = 5; // everything in this directory should use these writable & private values
bool favorite = 6; // you like this. always git clone/go clone this repo
bool interesting = 7; // this is something interesting you found and want to remember it
string masterBranchName = 8; // git 'main' or 'master' branch name
string develBranchName = 9; // whatever the git 'devel' branch name is
string userBranchName = 10; // whatever your username branch is
string masterBranchName = 8; // git 'main' or 'master' branch name
string develBranchName = 9; // whatever the git 'devel' branch name is
string userBranchName = 10; // whatever your username branch is
string debName = 11; // the actual name used with 'apt install' (or distro apt equivalent.
// todo: appeal to everyone to alias 'apt' on fedora, gentoo, arch, etc to alias 'apt install'
// so we can make easier instructions for new linux users. KISS
string debName = 11; // the actual name used with 'apt install' (or distro apt equivalent.
// todo: appeal to everyone to alias 'apt' on fedora, gentoo, arch, etc to alias 'apt install'
// so we can make easier instructions for new linux users. KISS
google.protobuf.Timestamp verstamp = 12; // the git commit timestamp of the version
string goSrc = 13; // is ~/go/src unless a go.work file is found
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
}
message ForgeConfigs { // `autogenpb:marshal` `autogenpb:nomutex`
string uuid = 1; // `autogenpb:uuid:1941cd4f-1cfd-4bf6-aa75-c2c391907e81`
string version = 2; // `autogenpb:version:v0.0.47`
repeated ForgeConfig ForgeConfigs = 3;
string username = 4; // what to use for the user branch (default ENV{USER})
string xterm = 14; // what xterm the user wants as the default
repeated string xtermArgv = 15; // the argv line for xterm
// todo: fix autogenpb to look for enum
enum ForgeMode {
MASTER = 0; // "release mode"
DEVEL = 1; // "patch mode"
USER = 2; // "work mode"
}
message ForgeConfigs { // `autogenpb:marshal` `autogenpb:nomutex`
string uuid = 1; // `autogenpb:uuid:1941cd4f-1cfd-4bf6-aa75-c2c391907e81`
string version = 2; // `autogenpb:version:v0.0.47`
repeated ForgeConfig ForgeConfigs = 3;
string username = 4; // what to use for the user branch (default ENV{USER})
string xterm = 5; // what xterm the user wants as the default
repeated string xtermArgv = 6; // the argv line for xterm
string defaultGui = 7; // default GUI plugin to use
ForgeMode mode = 8; // what "mode" forge is in
}
// this generic message is used by autogen to identify and
// then dump the uuid and version from any arbitrary .pb file
message Identify { // `autogenpb:marshal`
string uuid = 1; //
string version = 2; //
}

View File

@ -1,173 +0,0 @@
package forgepb
import (
"errors"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
func (f *Forge) GitPull() bool {
f.Repos.RillGitPull(5, 5)
/*
var localonly int
var badmap int
log.Log(WARN, "running git pull everywhere")
var failed int = 0
for all.Scan() {
repo := all.Next()
if out, err := repo.GitPull(); err == nil {
log.Log(WARN, "Ran git pull ok", repo.GetGoPath(), out)
} else {
failed += 1
// repo.DumpTags()
if errors.Is(repostatus.ErrorGitPullOnLocal, err) {
localonly += 1
continue
}
badmap += 1
log.Log(WARN, "bad unknown git error", repo.GetGoPath(), out, err)
}
}
log.Log(WARN, "Ran git pull in all repos. failure count =", failed)
log.Log(WARN, "Ran git pull in all repos. bad errors =", badmap)
if localonly != 0 {
log.Log(WARN, "Ran git pull in all repos. ignored local only branches =", localonly)
}
*/
return true
}
func (f *Forge) CheckoutDevel() bool {
log.Log(WARN, "running git checkout devel everwhere")
var failed int = 0
var count int = 0
all := f.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
count += 1
if repo.CheckoutDevel() {
// checkout ok
} else {
dname := repo.GetDevelBranchName()
if err := f.makeBranch(repo, dname); err != nil {
log.Info(repo.GetGoPath(), "can not make devel branch", dname)
failed += 1
}
}
}
log.Log(WARN, "Ran git checkout in", count, "repos. failure count =", failed)
return true
}
func (f *Forge) MakeDevelBranch(repo *gitpb.Repo) error {
dname := repo.GetDevelBranchName()
if dname == "" {
dname = f.configDevelBranchName(repo)
}
if err := f.makeBranch(repo, dname); err != nil {
return err
}
repo.DevelBranchName = dname
return nil
}
func (f *Forge) MakeUserBranch(repo *gitpb.Repo) error {
uname := repo.GetUserBranchName()
if uname == "" {
uname = f.configUserBranchName(repo)
}
if err := f.makeBranch(repo, uname); err != nil {
return err
}
repo.UserBranchName = uname
return nil
}
func (f *Forge) makeBranch(repo *gitpb.Repo, bname string) error {
if repo.IsLocalBranch(bname) {
// branch already exists in refs/heads/
return nil
}
if repo.IsBranch(bname) {
// branch already exists refs/remotes/
return nil
} else {
log.Info("makeBranch() says", bname, "does not exist")
loop := repo.Tags.All()
for loop.Scan() {
t := loop.Next()
log.Info("LocalTagExists() tag:", t.Refname)
}
}
mname := repo.GetMasterBranchName()
cname := repo.GetCurrentBranchName()
if mname != cname {
return errors.New("can only make branches from master branch")
}
cmd := []string{"git", "branch", bname}
if err := repo.StrictRun(cmd); err != nil {
return err
}
return nil
}
func (f *Forge) CheckoutMaster() bool {
log.Log(WARN, "running git checkout master everwhere")
var failed int = 0
var count int = 0
all := f.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
count += 1
if repo.CheckoutMaster() {
// checkout ok
} else {
failed += 1
}
}
log.Log(WARN, "Ran git checkout in", count, "repos. failure count =", failed)
return true
}
func (f *Forge) CheckoutUser() bool {
log.Log(WARN, "running git checkout user everwhere")
var failed int = 0
var count int = 0
all := f.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
// already on the user branch
continue
}
count += 1
if repo.CheckoutUser() {
// checkout ok
} else {
failed += 1
}
}
log.Log(WARN, "Ran git checkout in", count, "repos. failure count =", failed)
return true
}
func (f *Forge) CheckoutUserForce() bool {
log.Log(WARN, "running git checkout user everwhere")
var failed int = 0
var count int = 0
all := f.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
count += 1
if repo.CheckoutUser() {
// checkout ok
} else {
failed += 1
}
}
log.Log(WARN, "Ran git checkout in", count, "repos. failure count =", failed)
return true
}

View File

@ -12,6 +12,10 @@ import (
"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) {

View File

@ -46,6 +46,13 @@ func (f *Forge) ScanGoSrc() (bool, error) {
return true, err
}
func (f *Forge) ScanDir(dir string) *gitpb.Repo {
// repo, err := f.NewGoRepo(gopath, "")
repo, err := f.Repos.NewGoRepo(dir, "")
log.Info("need to implement ScanDir()", dir, err)
return repo
}
// doesn't enter the directory any further when it finds a .git/
// not stupid like my old version
func gitDirectoriesNew(srcDir string) ([]string, error) {
@ -160,44 +167,10 @@ func (f *Forge) rillScanDirs(gopaths []string) (int, error) {
func (f *Forge) checkpath(gopath string, url string) (*gitpb.Repo, error) {
fullpath := filepath.Join(f.GetGoSrc(), gopath)
log.Info("checkpath()", gopath, fullpath)
log.Info("forge creating protobuf for", fullpath)
repo, err := f.NewGoRepo(gopath, "")
if err != nil {
log.Info("checkpath()", gopath, err)
log.Info("\tprotobuf error", gopath, err)
}
return repo, err
}
func (f *Forge) RillRedoGoMod() int {
var all []*gitpb.Repo
tmp := f.Repos.SortByFullPath()
for tmp.Scan() {
repo := tmp.Next()
if !repo.IsValidDir() {
log.Printf("%10s %-50s", "why am I in RillRedoGoMod? old?", repo.GetGoPath())
continue
}
all = append(all, repo)
}
// Convert a slice of user IDs into a channel
ids := rill.FromSlice(all, nil)
var counter int
// Read users from the API.
// Concurrency = 20
dirs := rill.Map(ids, 50, func(id *gitpb.Repo) (*gitpb.Repo, error) {
return id, nil
})
err := rill.ForEach(dirs, 20, func(repo *gitpb.Repo) error {
counter += 1
// repo.RedoGoMod()
return nil
})
if err != nil {
log.Info("rill.ForEach() error:", err)
}
return counter
}

View File

@ -1,3 +1,5 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
import (

78
http.go Normal file
View File

@ -0,0 +1,78 @@
// Copyright 1994-2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
import (
"bytes"
"io/ioutil"
"net/http"
"os/user"
"go.wit.com/log"
)
/*
func (f *Forge) HttpPostMachine(url string) ([]byte, error) {
if f.Machine == nil {
// run f.InitMachine() here?
log.Info("you must run f.InitMachine()")
return nil, fmt.Errorf("you must run f.InitMachine()")
}
if f.Machine.Hostname == "" {
log.Info("WTF. hostname is blank")
} else {
log.Info("GOOD. hostname is set to", f.Machine.Hostname)
}
log.Info("GOOD2. hostname is set to", f.Machine.Hostname)
msg, err := f.Machine.Marshal()
if err != nil {
log.Info("proto.Marshal() failed:", err)
return nil, err
}
log.Info("GOOD3. hostname is set to", f.Machine.Hostname)
check := new(zoopb.Machine)
check.Unmarshal(msg)
if check == nil {
log.Info("WTF. check == nil")
}
log.Info("good? check.hostname =", check.Hostname)
return f.HttpPost(url, msg)
}
*/
func (f *Forge) HttpPost(url string, data []byte) ([]byte, error) {
var err error
var req *http.Request
req, err = http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data))
// log.Info("httpPost() with len", len(data), "url", url)
usr, _ := user.Current()
req.Header.Set("author", usr.Username)
/*
if f.Machine == nil {
// run f.InitMachine() here?
log.Info("you must run f.InitMachine()")
return nil, fmt.Errorf("you must run f.InitMachine()")
}
*/
req.Header.Set("hostname", "fixme:hostname")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Error(err)
return []byte("client.Do(req) error"), err
}
defer resp.Body.Close()
// log.Info("httpPost() with len", len(data))
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error(err)
return body, err
}
return body, nil
}

View File

@ -1,3 +1,5 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
import (

View File

@ -1,3 +1,5 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
import (
@ -86,7 +88,7 @@ func (f *Forge) testGoRepo(check *gitpb.Repo) {
data, _ := os.ReadFile(filepath.Join(check.FullPath, "go.mod"))
log.Info(string(data))
if f.FinalGoDepsCheckOk(check, true) {
if err := f.FinalGoDepsCheckOk(check, true); err == nil {
log.Info("forge.FinalGoDepsCheck(check) worked!")
} else {
log.Info("forge.FinalGoDepsCheck(check) failed. boo.")

View File

@ -1,7 +1,10 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
import (
"fmt"
"path/filepath"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/gitpb"
@ -37,7 +40,7 @@ func (f *Forge) PrintHumanTable(allr *gitpb.Repos) {
var count int
// log.Info(standardStart5("gopath", "cur name", "master", "user", "repo type"))
log.Info(standardTable8("repopath", "cur br", "age", "target", "master", "devel", "user", "curver", "repo type"))
log.Info(standardTable10("repopath", "cur br", "age", "master", "devel", "user", "curver", "lasttag", "next", "repo type"))
all := allr.SortByFullPath()
for all.Scan() {
repo := all.Next()
@ -53,8 +56,9 @@ func (f *Forge) PrintHumanTableDirty(allr *gitpb.Repos) {
var count int
// log.Info(standardStart5("gopath", "cur name", "master", "user", "repo type"))
log.Info(standardTable8("repopath", "cur br", "age", "target", "master", "devel", "user", "curver", "repo type"))
all := allr.SortByFullPath()
log.Info(standardTable10("repopath", "cur br", "age", "master", "devel", "user", "curver", "lasttag", "next", "repo type"))
// all := allr.SortByFullPath()
all := allr.All()
for all.Scan() {
repo := all.Next()
f.printRepoToTable(repo)
@ -101,7 +105,7 @@ func standardTable5(arg1, arg2, arg3, arg4, arg5 string) string {
return fmt.Sprintf(s, arg1, arg2, arg3, arg4, arg5)
}
func standardTable8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 string) string {
func standardTable10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 string) string {
len1 := 40
len2 := 12
len3 := 6
@ -109,8 +113,9 @@ func standardTable8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 string)
len5 := 16
len6 := 16
len7 := 16
len8 := 16
len9 := 8
len8 := 12
len9 := 12
len10 := 8
var s string
if len(arg1) > len1 {
arg1 = arg1[:len1]
@ -137,12 +142,12 @@ func standardTable8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 string)
if len(arg6) > len6 {
arg6 = arg6[:len6]
}
s += "%-" + fmt.Sprintf("%d", len6) + "s "
s += "%-" + fmt.Sprintf("%d", len6) + "s "
if len(arg7) > len7 {
arg7 = arg7[:len7]
}
s += "%-" + fmt.Sprintf("%d", len7) + "s "
s += "%-" + fmt.Sprintf("%d", len7) + "s "
if len(arg8) > len8 {
arg8 = arg8[:len8]
@ -154,17 +159,26 @@ func standardTable8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 string)
}
s += "%-" + fmt.Sprintf("%d", len9) + "s "
return fmt.Sprintf(s, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
if len(arg10) > len10 {
arg10 = arg10[:len10]
}
s += "%-" + fmt.Sprintf("%d", len10) + "s "
return fmt.Sprintf(s, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
}
func (f *Forge) printRepoToTable(repo *gitpb.Repo) {
var end string
if repo.IsDirty() {
end += "(dirty) "
}
// shortened version numbers
var mhort string = repo.GetMasterVersion()
var dhort string = repo.GetDevelVersion()
var uhort string = repo.GetUserVersion()
if uhort == "uerr" {
// blank these out
uhort = ""
}
var lasttag string = repo.GetLastTag()
var thort string = repo.GetTargetVersion()
var chort string = repo.GetCurrentBranchVersion()
var cname string = repo.GetCurrentBranchName()
@ -174,13 +188,27 @@ func (f *Forge) printRepoToTable(repo *gitpb.Repo) {
// ctime := repo.Tags.GetAge(mhort)
// age := shell.FormatDuration(time.Since(ctime))
age := shell.FormatDuration(repo.NewestAge())
start := standardTable8(gopath, cname, age, thort, mhort, dhort, uhort, chort, rtype)
age := shell.FormatDuration(repo.BranchAge(cname))
if f.Config.IsReadOnly(repo.GetGoPath()) {
end += "(readonly) "
// end += "(readonly) "
} else {
end += "(rw) "
}
if repo.IsDirty() {
age = ""
end += "(dirty) "
}
start := standardTable10(gopath, cname, age, mhort, dhort, uhort, chort, lasttag, thort, rtype)
if rtype == "protobuf" {
if repo.GoInfo.GoBinary {
end += "(binary) "
}
}
if repo.GetMasterBranchName() != "master" && repo.GetMasterBranchName() != "main" {
end += "(m:" + repo.GetMasterBranchName() + ") "
}
@ -193,9 +221,21 @@ func (f *Forge) printRepoToTable(repo *gitpb.Repo) {
end += "(u:" + repo.GetUserBranchName() + ") "
}
debname := f.Config.DebName(repo.GetGoPath())
if debname != filepath.Base(gopath) {
end += "(deb:" + debname + ") "
}
switch repo.GetState() {
case "PERFECT":
case "unchanged":
case "dirty":
case "unknown branches":
if repo.CurrentTag == nil {
end += "(" + repo.GetState() + ") "
} else {
end += "(unknown branch " + repo.CurrentTag.Refname + ") "
}
// end += "(invalid tag) "
default:
end += "(" + repo.GetState() + ") "

17
identify.go Normal file
View File

@ -0,0 +1,17 @@
package forgepb
import (
"go.wit.com/log"
)
// print the protobuf in human form
func IdentifyProtobuf(data []byte) error {
var pb *Identify
pb = new(Identify)
if err := pb.Unmarshal(data); err != nil {
log.Info("data can't be identified as a standard protobuf. len =", len(data))
return err
}
log.Info("Identify protobuf file uuid =", pb.Uuid, "version =", pb.Version)
return nil
}

69
init.go
View File

@ -1,3 +1,5 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
import (
@ -8,7 +10,6 @@ import (
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/lib/protobuf/zoopb"
"go.wit.com/log"
)
@ -18,11 +19,12 @@ import (
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.Machine = new(zoopb.Machine)
if err := f.Machine.ConfigLoad(); err != nil {
log.Log(WARN, "zoopb.ConfigLoad() failed", err)
}
*/
if f.Config.Username == "" {
usr, _ := user.Current()
f.Config.Username = usr.Username
@ -38,20 +40,31 @@ func Init() *Forge {
f.SetConfigSave(true)
}
f.Machine.InitWit()
// 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)
}
f.rillUpdate(20, 10)
if f.configSave {
// taking this out to debug Marshal() panic
// os.Exit(-1)
@ -83,6 +96,8 @@ func DetermineGoPath() *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
@ -106,8 +121,42 @@ func (f *Forge) InitPB() {
} else {
log.Log(INFO, "forgepb.Init() FORGE_GOSRC ", os.Getenv("FORGE_GOSRC"), "(go.work = false)")
}
f.Repos = new(gitpb.Repos)
f.Repos = gitpb.NewRepos()
f.Repos.ConfigLoad()
if f.Repos.HasFullScan {
f.hasFullScan = true
}
if os.Getenv("FORGE_URL") != "" {
forgeURL = os.Getenv("FORGE_URL")
log.Info("got forge url", forgeURL)
}
}
func (f *Forge) InitMachine() {
/*
f.Machine = new(zoopb.Machine)
if err := f.Machine.ConfigLoad(); err != nil {
log.Log(WARN, "zoopb.ConfigLoad() failed", err)
f.Machine.InitWit()
}
*/
if f.Config.Username == "" {
usr, _ := user.Current()
f.Config.Username = usr.Username
}
/*
if f.Machine.Hostname == "" {
r, err := shell.RunVerbose([]string{"hostname", "-f"})
if err == nil {
tmp := strings.Join(r.Stdout, "\n")
f.Machine.Hostname = strings.TrimSpace(tmp)
}
}
*/
}
// only init's the protobuf. intended to not scan or change anything

17
mode.go Normal file
View File

@ -0,0 +1,17 @@
// Copyright 1994-2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
// TODO: implement i18n with the protobuf's
func (f *Forge) GetMode() string {
switch f.Config.Mode {
case ForgeMode_MASTER:
return "Release Mode (master branch)"
case ForgeMode_DEVEL:
return "Patch Mode (devel branch)"
case ForgeMode_USER:
return "Hack Mode (user branch)"
default:
return f.Config.Mode.String()
}
}

34
patchset.Get.go Normal file
View File

@ -0,0 +1,34 @@
// Copyright 1994-2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
import (
"go.wit.com/log"
)
var forgeURL string = "https://go.wit.com/"
func (f *Forge) GetPatchesets() (*Patchsets, error) {
url := forgeURL + "GetPatchsets"
log.Info("GetPatchsets() url", url)
body, err := f.HttpPost(url, nil)
if err != nil {
log.Info("httpPost() failed:", err)
return nil, err
}
log.Info("GetPatchets() len(body)", len(body))
var psets *Patchsets
psets = new(Patchsets)
err = psets.Unmarshal(body)
if err != nil {
log.Info("Unmarshal failed", err)
return nil, err
}
/*
filename := filepath.Join("/tmp", pbfile)
f, _ := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
f.Write(body)
f.Close()
*/
return psets, nil
}

View File

@ -6,13 +6,32 @@ import (
"os"
"path/filepath"
"strings"
"time"
"github.com/google/uuid"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
)
func (f *Forge) MakeDevelPatchSet() (*Patchset, error) {
// creates a patchset
// works from the user branches against the devel branches
func (f *Forge) MakeDevelPatchSet(name string) (*Patchset, error) {
pset := new(Patchset)
pset.Name = name
pset.Ctime = timestamppb.New(time.Now())
pset.Uuid = uuid.New().String()
if os.Getenv("GIT_AUTHOR_NAME") == "" {
return nil, fmt.Errorf("GIT_AUTHOR_NAME not set")
} else {
pset.GitAuthorName = os.Getenv("GIT_AUTHOR_NAME")
}
if os.Getenv("GIT_AUTHOR_EMAIL") == "" {
return nil, fmt.Errorf("GIT_AUTHOR_EMAIL not set")
} else {
pset.GitAuthorEmail = os.Getenv("GIT_AUTHOR_EMAIL")
}
dir, err := os.MkdirTemp("", "forge")
if err != nil {
return nil, err
@ -23,17 +42,21 @@ func (f *Forge) MakeDevelPatchSet() (*Patchset, error) {
all := f.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
userb := repo.GetUserBranchName()
develb := repo.GetDevelBranchName()
if develb == "" {
if !repo.IsLocalBranch(repo.GetUserBranchName()) {
// log.Info("repo doesn't have user branch", repo.GetGoPath())
continue
}
if userb == "" {
if !repo.IsLocalBranch(repo.GetDevelBranchName()) {
// log.Info("repo doesn't have devel branch", repo.GetGoPath())
continue
}
pset.StartBranchName = develb
pset.EndBranchName = userb
// make a patchset from user to devel
// TODO: verify branches are otherwise exact
pset.StartBranchName = repo.GetDevelBranchName()
pset.EndBranchName = repo.GetUserBranchName()
err := pset.makePatchSetNew(repo)
if err != nil {
return nil, err
@ -42,6 +65,17 @@ func (f *Forge) MakeDevelPatchSet() (*Patchset, error) {
return pset, nil
}
func (f *Forge) SubmitDevelPatchSet(name string) (*Patchset, error) {
pset, err := f.MakeDevelPatchSet(name)
if err != nil {
return nil, err
}
if err := f.submitPatchset(pset); err != nil {
return nil, err
}
return pset, nil
}
func (f *Forge) MakeMasterPatchSet() (*Patchset, error) {
pset := new(Patchset)
dir, err := os.MkdirTemp("", "forge")
@ -83,7 +117,19 @@ func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
return err
}
// maybe better? maybe worse?
// git format-patch -o patches --stdout <commit-range> > my-patch.mbox
// git format-patch --stdout -5 > my-patch.mbox # last 5 patches
// git am < my-patch.mbox
// git format-patch branch1..branch2
// export GIT_COMMITTER_DATE="2024-01-01T12:00:00"
// export GIT_AUTHOR_DATE="2024-01-01T12:00:00"
// export GIT_COMMITTER_NAME="Your Name"
// export GIT_COMMITTER_EMAIL="your.email@example.com"
// export GIT_AUTHOR_NAME="Your Name"
// export GIT_AUTHOR_EMAIL="your.email@example.com"
// git am < patch.mbox
cmd := []string{"git", "format-patch", "-o", repoDir, startBranch + ".." + endBranch}
r := repo.Run(cmd)
if r.Error != nil {
@ -103,14 +149,16 @@ func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
return nil
}
return pset.addPatchFiles(repo)
err = pset.addPatchFiles(repo)
pset.Ctime = timestamppb.New(time.Now())
return err
}
// process each file in pDir/
func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
psetDir := repo.GetGoPath()
tmpDir := p.TmpDir
log.Info("ADD PATCH FILES ADDED DIR", tmpDir)
// log.Info("ADD PATCH FILES ADDED DIR", tmpDir)
fullDir := filepath.Join(tmpDir, psetDir)
var baderr error
filepath.Walk(fullDir, func(path string, info os.FileInfo, err error) error {
@ -124,10 +172,10 @@ func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
if info.IsDir() {
return nil
}
log.Info("IS THIS A FULL PATH ?", path)
log.Info("trim this from path ?", fullDir)
log.Info("trim this from path ?", psetDir)
log.Info("trim this from path ?", tmpDir)
// log.Info("IS THIS A FULL PATH ?", path)
// log.Info("trim this from path ?", fullDir)
// log.Info("trim this from path ?", psetDir)
// log.Info("trim this from path ?", tmpDir)
data, err := os.ReadFile(path)
if err != nil {
log.Info("addPatchFile() failed", path)
@ -139,8 +187,14 @@ func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
patch.Data = data
patch.parseData()
patch.StartHash = repo.DevelHash()
patch.NewHash = "na"
patch.RepoNamespace = repo.GetGoPath()
if p.Patches == nil {
p.Patches = new(Patches)
}
p.Patches.Append(patch)
log.Info("ADDED PATCH FILE", path)
p.Patches.Uuid = uuid.New().String()
// log.Info("ADDED PATCH FILE", path)
return nil
})
return baderr
@ -159,6 +213,8 @@ func (p *Patch) parseData() string {
switch fields[0] {
case "From":
p.CommitHash = fields[1]
case "Subject:":
p.Comment = line
case "diff":
p.Files = append(p.Files, line)
}
@ -188,3 +244,29 @@ func onlyWalkDirs(pDir string) error {
})
return baderr
}
func (f *Forge) submitPatchset(pset *Patchset) error {
var url string
url = forgeURL + "patchset"
msg, err := pset.Marshal()
if err != nil {
log.Info("proto.Marshal() failed:", err)
return err
}
log.Info("proto.Marshal() msg len", len(msg))
body, err := f.HttpPost(url, msg)
if err != nil {
log.Info("httpPost() failed:", err)
return err
}
test := strings.TrimSpace(string(body))
lines := strings.Split(test, "\n")
count := 0
for _, line := range lines {
log.Info("got back:", line)
count += 1
}
log.Info("Total patches:", count)
return nil
}

View File

@ -1,43 +1,86 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
syntax = "proto3";
package forgepb;
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
// Forge doesn't need this kind of specificity
// but this is what the patch files contain and how git sees them
// message Blob {
// string hunkLine = 1;
// bytes data = 2;
// }
//
// message File {
// string filename = 1;
// string hashLine = 2;
// repeated Blob Blobs = 3;
// }
//
// message Patch {
// repeated File Files = 1;
// string repoNamespace = 2;
// string gH = 3;
// string gaI = 4;
// string gcI = 5;
// }
// git log -1 --format="%H %aI %cI %an %ae %cn %ce"
message Patch {
string filename = 1; // `autogenpb:unique` `autogenpb:sort`
bytes data = 2; //
string repoPath = 3; // path to the git repo
string branchName = 4; //
string branchHash = 5; //
google.protobuf.Timestamp ctime = 7; // the git commit timestamp of this patch
string commitHash = 8; // the git commit hash of this patch
string startHash = 9; // the start commit hash
repeated string Files = 10; // the filenames this patch changes
string repoNamespace = 1; // the base repo git URL
bytes data = 2; // the raw data of the whole patch
string gH = 3; // after some deliberation, I think I'll just try variable names
string gT = 4;
string gP = 5;
string gs = 6;
string gaI = 7; // that exactly match what git uses.
string gan = 8;
string gae = 9;
string gcI = 10;
string gcn = 11;
string gce = 12;
string gN = 13;
string gGG = 14;
string gGS = 15;
string gGK = 16;
string newHash = 17; // new hash
string state = 18; // the 'state' of the patch
string filename = 19; // `autogenpb:unique` `autogenpb:sort`
string startHash = 20; // the start commit hash
string commitHash = 21; // the git commit hash of this patch
string comment = 22; // the git commit message (in patch form)
repeated string Files = 23; // the filenames this patch changes
google.protobuf.Timestamp ctime = 24; // create time of the patch
bool applied = 25; // have you applied this patch?
bool upstream = 26; // has this patch been applied upstream?
}
message Patches { // `autogenpb:marshal`
string uuid = 1; // `autogenpb:uuid:be926ad9-1111-484c-adf2-d96eeabf3079` // todo: add autogenpb support for this
string version = 2; // `autogenpb:version:v0.0.45` // todo: add autogenpb support for this
repeated Patch Patches = 3;
message Patches { // this is a "PATCH: [1/x]" series `autogenpb:gui:Patch`
string uuid = 1; // `autogenpb:uuid:be926ad9-1111-484c-adf2-d96eeabf3079`
string version = 2; // `autogenpb:version:v0.0.45`
repeated Patch Patches = 3;
}
message Patchset { // `autogenpb:marshal`
Patches patches = 1; // `autogenpb:unique` `autogenpb:sort`
string name = 2; // `autogenpb:sort`
string comment = 3; //
string gitAuthorName = 4; // `autogenpb:sort`
string gitAuthorEmail = 5; //
google.protobuf.Timestamp ctime = 6; // create time of this patchset
string tmpDir = 7; // temp dir
string startBranchName = 8; //
string endBranchName = 9; //
string startBranchHash = 10; //
string endBranchHash = 11; //
message Patchset { // `autogenpb:marshal`
Patches patches = 1; //
string name = 2; // `autogenpb:sort`
string comment = 3; //
string gitAuthorName = 4; // `autogenpb:sort`
string gitAuthorEmail = 5; //
google.protobuf.Timestamp ctime = 6; // create time of the patchset
string tmpDir = 7; // temp dir
string startBranchName = 8; //
string endBranchName = 9; //
string startBranchHash = 10; //
string endBranchHash = 11; //
string state = 12; // the state of the patch
string uuid = 13; // `autogenpb:sort` `autogenpb:unique`
}
message Patchsets { // `autogenpb:marshal`
string uuid = 1; // `autogenpb:uuid:be926ad9-f07f-484c-adf2-d96eeabf3079` // todo: add autogenpb support for this
string version = 2; // `autogenpb:version:v0.0.45` // todo: add autogenpb support for this
repeated Patchset Patchsets = 3;
message Patchsets { // `autogenpb:marshal` `autogenpb:gui`
string uuid = 1; // `autogenpb:uuid:be926ad9-f07f-484c-adf2-d96eeabf3079`
string version = 2; // `autogenpb:version:v0.0.45`
repeated Patchset Patchsets = 3;
}

View File

@ -1,3 +1,5 @@
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
package forgepb
import (
@ -13,11 +15,17 @@ import (
func (f *Forge) NewGoRepo(gopath string, url string) (*gitpb.Repo, error) {
fullpath := filepath.Join(f.GetGoSrc(), gopath)
test := f.Repos.FindByFullPath(fullpath)
if test != nil {
return test, nil
}
repo, err := f.Repos.NewGoRepo(fullpath, gopath)
if err != nil {
log.Info("WARNING. NEW FAILED", fullpath)
return nil, err
}
// slices.Reverse(f.Repos.Repos)
repo.URL = url
f.VerifyBranchNames(repo)
if f.Config.IsReadOnly(repo.GetGoPath()) {
@ -76,7 +84,7 @@ func (f *Forge) findMasterBranch(repo *gitpb.Repo) {
if strings.HasPrefix(s, "ref: ") {
fields := strings.Fields(s)
_, bname := filepath.Split(fields[1])
log.Info("Using master branch name from .git/ HEAD:", bname)
// log.Info("Using master branch name from .git/ HEAD:", bname)
repo.SetMasterBranchName(bname)
return
}
@ -95,11 +103,13 @@ func (f *Forge) findMasterBranch(repo *gitpb.Repo) {
// TODO: figure out the name from git
repo.SetMasterBranchName("master")
/* no longer checkout on Init()
if repo.CheckoutMaster() {
} else {
cmd := []string{"git", "branch", "master"}
repo.Run(cmd)
}
*/
}
// figure out what the name of the git devel branch is
@ -108,11 +118,13 @@ func (f *Forge) findDevelBranch(repo *gitpb.Repo) {
// check the forge config first
if bname := f.Config.FindDevelBranch(repo.GetGoPath()); bname != "" {
repo.SetDevelBranchName(bname)
/* no longer checkout on Init()
if repo.CheckoutDevel() {
} else {
cmd := []string{"git", "branch", bname}
repo.Run(cmd)
}
*/
return
}
@ -121,13 +133,14 @@ func (f *Forge) findDevelBranch(repo *gitpb.Repo) {
return
}
// TODO: figure out the name from git
repo.SetDevelBranchName("devel")
/* no longer checkout on Init()
if repo.CheckoutDevel() {
} else {
cmd := []string{"git", "branch", "devel"}
repo.Run(cmd)
}
*/
}
// this is still in flux
@ -149,11 +162,6 @@ func (f *Forge) VerifyBranchNames(repo *gitpb.Repo) {
} else {
// forcing for now. todo: warn users
repo.SetUserBranchName(uname)
if repo.CheckoutUser() {
} else {
cmd := []string{"git", "branch", uname}
repo.Run(cmd)
}
}
}
}

89
rill.go
View File

@ -1,6 +1,8 @@
package forgepb
import (
"sync"
"github.com/destel/rill"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
@ -64,3 +66,90 @@ func (f *Forge) updateRepo(repo *gitpb.Repo) error {
}
return nil
}
var RillX int = 10
var RillY int = 10
// x is the size of the queued up pool (shouldn't matter here for this I think)
// y is how many simultanous functions will run
// todo: tune and compute x,y by # of CPUs and disk io
// todo: store x,y in forge config ? (or compute them. notsure)
func (f *Forge) RillReload() int {
var all []*gitpb.Repo
tmp := f.Repos.All()
for tmp.Scan() {
repo := tmp.Next()
if !repo.IsValidDir() {
log.Printf("%s %-50s", "got an invalid repo in forgepb.RillFuncError()", repo.GetGoPath())
continue
}
all = append(all, repo)
}
// Convert a slice of user IDs into a channel
ids := rill.FromSlice(all, nil)
var counter int
// Read users from the API.
// Concurrency = 20
dirs := rill.Map(ids, RillX, func(repo *gitpb.Repo) (*gitpb.Repo, error) {
return repo, nil
})
rill.ForEach(dirs, RillY, func(repo *gitpb.Repo) error {
if !repo.DidRepoChange() {
return nil
}
f.configSave = true
repo.Reload()
counter += 1
return nil
})
return counter
}
// x is the size of the queued up pool (shouldn't matter here for this I think)
// y is how many simultanous functions will run
// todo: tune and compute x,y by # of CPUs and disk io
// todo: store x,y in forge config ? (or compute them. notsure)
func (f *Forge) RillFuncError(rillf func(*gitpb.Repo) error) int {
var all []*gitpb.Repo
tmp := f.Repos.All()
for tmp.Scan() {
repo := tmp.Next()
if !repo.IsValidDir() {
log.Printf("%s %-50s", "got an invalid repo in forgepb.RillFuncError()", repo.GetGoPath())
continue
}
all = append(all, repo)
}
// Convert a slice of user IDs into a channel
ids := rill.FromSlice(all, nil)
var counter int
var watch int = 10
var meMu sync.Mutex
// Read users from the API.
// Concurrency = 20
dirs := rill.Map(ids, RillX, func(id *gitpb.Repo) (*gitpb.Repo, error) {
return id, nil
})
err := rill.ForEach(dirs, RillY, func(repo *gitpb.Repo) error {
meMu.Lock()
counter += 1
if counter > watch {
// log.Info("Processed", watch, "repos") // this doesn't work
watch += 50
}
meMu.Unlock()
return rillf(repo)
})
if err != nil {
log.Info("rill.ForEach() error:", err)
}
return counter
}

View File

@ -2,29 +2,48 @@ package forgepb
import (
sync "sync"
"time"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/lib/protobuf/zoopb"
)
// maybe an interface someday?
type Forge struct {
// one-time initialized data
initOnce sync.Once
initErr error // init error, if any
goSrc string // the path to go/src
goWork bool // means the user is currently using a go.work file
Config *ForgeConfigs // config repos for readonly, private, etc
Repos *gitpb.Repos
Machine *zoopb.Machine
configSave bool
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
// Machine *zoopb.Machine // things for virtigo to track vm's
configSave bool // if you need to save the config because things changed
hasFullScan bool // track last scan so it can be throttled
fullscan time.Time // time of the last scan so it can be throttled
}
func (f *Forge) GetGoSrc() string {
return f.goSrc
}
func (f *Forge) GetConfigDir() string {
return f.configDir
}
func (f *Forge) IsGoWork() bool {
return f.goWork
}
func (f *Forge) HasFullScan() bool {
return f.Repos.HasFullScan
}
func (f *Forge) FullScanRan() {
f.fullscan = time.Now()
}
func (f *Forge) FullScanAge() time.Duration {
fs := f.Repos.FullScan.AsTime()
return time.Since(fs)
}