Compare commits
No commits in common. "master" and "v0.22.11" have entirely different histories.
|
@ -1,5 +1,4 @@
|
||||||
*.swp
|
*.swp
|
||||||
*.pb
|
|
||||||
go.mod
|
go.mod
|
||||||
go.sum
|
go.sum
|
||||||
/resources/*.so
|
/resources/*.so
|
||||||
|
|
97
Makefile
97
Makefile
|
@ -1,52 +1,20 @@
|
||||||
VERSION = $(shell git describe --tags)
|
VERSION = $(shell git describe --tags)
|
||||||
BUILDTIME = $(shell date +%Y.%m.%d_%H%M)
|
BUILDTIME = $(shell date +%Y.%m.%d)
|
||||||
|
|
||||||
# make build # go build using your git cloned repos (GO111MODULE=off)
|
all: goimports vet install
|
||||||
# make install # go install using your git cloned repos (GO111MODULE=off)
|
forge
|
||||||
# make gocui # try the ncurses gui plugin
|
|
||||||
# make andlabs # try the andlabs gui plugin (uses GTK)
|
|
||||||
|
|
||||||
default: install-verbose tag
|
|
||||||
|
|
||||||
tag:
|
|
||||||
forge tag list
|
|
||||||
|
|
||||||
vet:
|
vet:
|
||||||
@GO111MODULE=off go vet
|
@GO111MODULE=off go vet
|
||||||
@echo this go binary package builds okay
|
@echo this go binary package builds okay
|
||||||
|
|
||||||
build: goimports vet plugin
|
verbose:
|
||||||
GO111MODULE=off go build \
|
GO111MODULE=off go build -v -x \
|
||||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||||
|
|
||||||
install: goimports plugin
|
install: goimports
|
||||||
GO111MODULE=off go install \
|
GO111MODULE=off go install \
|
||||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||||
cp -f ~/go/bin/forge ~/go/bin/last.forge # this is a hack so that go-deb can build a .deb file for forge # TODO: remove this
|
|
||||||
|
|
||||||
install-verbose: goimports vet plugin
|
|
||||||
GO111MODULE=off go install -v -x \
|
|
||||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
|
||||||
|
|
||||||
install-raw: goimports vet plugin
|
|
||||||
go install \
|
|
||||||
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
|
||||||
|
|
||||||
plugin:
|
|
||||||
rm -f resources/*.so
|
|
||||||
# -cp ../../toolkits/gocui/gocui.so resources/
|
|
||||||
|
|
||||||
GTK: clean install
|
|
||||||
forge --gui andlabs
|
|
||||||
|
|
||||||
GTK-verbose: clean install
|
|
||||||
forge --gui andlabs --gui-verbose
|
|
||||||
|
|
||||||
CUI: install
|
|
||||||
forge --gui gocui
|
|
||||||
|
|
||||||
CUI-verbose: install
|
|
||||||
forge --gui gocui --gui-verbose >/tmp/forge.log 2>&1
|
|
||||||
|
|
||||||
goimports:
|
goimports:
|
||||||
reset
|
reset
|
||||||
|
@ -54,33 +22,36 @@ goimports:
|
||||||
@# // to globally reset paths:
|
@# // to globally reset paths:
|
||||||
@# // gofmt -w -r '"go.wit.com/gui/gadgets" -> "go.wit.com/lib/gadgets"' *.go
|
@# // gofmt -w -r '"go.wit.com/gui/gadgets" -> "go.wit.com/lib/gadgets"' *.go
|
||||||
|
|
||||||
clean:
|
gocui: install
|
||||||
-rm -f forge go.*
|
forge --gui gocui
|
||||||
# -rm -f ~/go/src/repos.pb
|
|
||||||
go-mod-clean purge
|
|
||||||
|
|
||||||
identify-protobuf:
|
redomod-all:
|
||||||
autogenpb --identify ~/go/src/repos.pb
|
forge --do-RedoGoMod
|
||||||
|
|
||||||
devel:
|
redomod-erase:
|
||||||
forge clean devel --force --verbose
|
forge --do-RedoGoMod --do-erase
|
||||||
|
|
||||||
|
private: install
|
||||||
|
reset
|
||||||
|
forge --find-private
|
||||||
|
|
||||||
|
fix: install
|
||||||
|
reset
|
||||||
|
forge --fix
|
||||||
|
|
||||||
|
readonly: install
|
||||||
|
reset
|
||||||
|
forge --do-list --find-readonly
|
||||||
|
|
||||||
|
config: install
|
||||||
|
forge --config
|
||||||
|
|
||||||
|
scan: install
|
||||||
|
reset
|
||||||
|
forge --do-scan
|
||||||
|
|
||||||
pull: install
|
pull: install
|
||||||
FORGE_URL="https://forge.grid.wit.com/" forge pull check
|
forge --do-git-pull
|
||||||
|
|
||||||
# cloudflare blocks POST due to captcha checks / human detection?
|
mine: install
|
||||||
# POST must be direct socket. probably for the best anyway
|
forge --find-mine
|
||||||
submit:
|
|
||||||
FORGE_URL="https://forge.grid.wit.com/" forge patch submit "forge auto commit"
|
|
||||||
|
|
||||||
commit:
|
|
||||||
FORGE_URL="https://forge.grid.wit.com/" forge commit --all
|
|
||||||
|
|
||||||
check: install
|
|
||||||
FORGE_URL="https://forge.grid.wit.com/" forge patch check
|
|
||||||
|
|
||||||
doc:
|
|
||||||
echo "/*" > doc.go
|
|
||||||
forge -h >> doc.go
|
|
||||||
echo "*/" >> doc.go
|
|
||||||
echo "package main" >> doc.go
|
|
||||||
|
|
22
PATENTS
22
PATENTS
|
@ -1,22 +0,0 @@
|
||||||
Additional IP Rights Grant (Patents)
|
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
|
||||||
WIT.COM Inc. as part of the go.wit.com project.
|
|
||||||
|
|
||||||
WIT.COM Inc. hereby grants to You a perpetual, worldwide, non-exclusive,
|
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
|
||||||
transfer and otherwise run, modify and propagate the contents of this
|
|
||||||
implementation on go.wit.com, where such license applies only to those patent
|
|
||||||
claims, both currently owned or controlled by WIT.COM Inc. and acquired in
|
|
||||||
the future, licensable by WIT.COM Inc. that are necessarily infringed by this
|
|
||||||
implementation on go.wit.com. This grant does not include claims that would be
|
|
||||||
infringed only as a consequence of further modification of this
|
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
|
||||||
order or agree to the institution of patent litigation against any
|
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
|
||||||
that this implementation on go.wit.com or any code incorporated within this
|
|
||||||
implementation on go.wit.com constitutes direct or contributory patent
|
|
||||||
infringement, or inducement of patent infringement, then any patent
|
|
||||||
rights granted to you under this License for this implementation on go.wit.com
|
|
||||||
shall terminate as of the date such litigation is filed.
|
|
79
README.md
79
README.md
|
@ -1,79 +0,0 @@
|
||||||
# forge
|
|
||||||
|
|
||||||
forge is a GUI front end for 'git' designed with the
|
|
||||||
intent of simplifying federated git development.
|
|
||||||
|
|
||||||
## Install:
|
|
||||||
|
|
||||||
* go install go.wit.com/apps/forge@latest
|
|
||||||
|
|
||||||
## Theory
|
|
||||||
|
|
||||||
* Software engineering is the art of making things work.
|
|
||||||
* Release engineering is the art of making things perfect.
|
|
||||||
|
|
||||||
## Rules
|
|
||||||
|
|
||||||
1) forge is only a GUI and wrapper around 'git'
|
|
||||||
2) forge _only_ os.Exec()'s git. Anything forge does
|
|
||||||
can be done on the command line using 'git' directly
|
|
||||||
3) forge's default behavior is to use 3 branches:
|
|
||||||
a) The git upstream master/main branch
|
|
||||||
b) A "devel" branch that is published
|
|
||||||
c) a "user" branch that can be local only to the developer
|
|
||||||
|
|
||||||
## Development Goals
|
|
||||||
|
|
||||||
* have a GUI that also works on the command line
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
This can be used to maintain git repositories
|
|
||||||
expiremental work on federated git
|
|
||||||
|
|
||||||
* Scans directories looking for git repositories
|
|
||||||
* The default behavior is to use 3 branches. 'master or main', 'devel', '<username>'
|
|
||||||
* shows tags and dirty state
|
|
||||||
* uses a GUI or the console(console display needs work)
|
|
||||||
* always wrap around 'git' -- it basically just types 'git' commands really fast
|
|
||||||
|
|
||||||
## building from sources
|
|
||||||
|
|
||||||
```
|
|
||||||
# note as of Feb 2025. Forge has an option in forge to build itself.
|
|
||||||
# The instructions below are out of date, but provide the general idea.
|
|
||||||
# accurate instructions are in the forge code itself
|
|
||||||
|
|
||||||
go install go.wit.com/apps/go-clone@latest # this tool makes it easier to 'git clone' repos and recursively 'git clone' the dependancies
|
|
||||||
go install go.wit.com/apps/autogenpb@latest # this tool will generate the protobuf *pb.go files (also Marshal(), Sort(), etc.)
|
|
||||||
|
|
||||||
go-clone go.wit.com/apps/forge # this will 'git clone' about 20 repos into ~/go/src (or where your go.work file is)
|
|
||||||
|
|
||||||
cd go.wit.com/lib/protobuf/forgepb
|
|
||||||
make # autogenpb will make .pb.go, marshal.pb.go and sort.pb.go files
|
|
||||||
cd go.wit.com/lib/protobuf/gitpb
|
|
||||||
make # autogenpb will make .pb.go, marshal.pb.go and sort.pb.go files
|
|
||||||
|
|
||||||
cd go.wit.com/apps/forge
|
|
||||||
make # this runs GO111MODULE=off go build insuring that your using only your git sources
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debian packages:
|
|
||||||
|
|
||||||
Instructions are on https://mirrors.wit.com/
|
|
||||||
|
|
||||||
## possible 'git bug' integration ideas:
|
|
||||||
|
|
||||||
```
|
|
||||||
git pull origin +refs/bugs/\*:refs/bugs/\*
|
|
||||||
git pull origin +refs/identities/\*:refs/identities/\*
|
|
||||||
|
|
||||||
|
|
||||||
# remove the caches
|
|
||||||
rm -rf .git/git-bug
|
|
||||||
|
|
||||||
|
|
||||||
# rebuild the cache with any command
|
|
||||||
git bug user
|
|
||||||
```
|
|
|
@ -1,48 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
// saves the patches in ~/.config/forge/currentpatches/
|
|
||||||
func savePatchset(pset *forgepb.Patchset) error {
|
|
||||||
log.Info("savePatches() NAME", pset.Name)
|
|
||||||
log.Info("savePatches() COMMENT", pset.Comment)
|
|
||||||
log.Info("savePatches() GIT_AUTHOR_NAME", pset.GetGitAuthorName())
|
|
||||||
log.Info("savePatches() GIT_AUTHOR_EMAIL", pset.GetGitAuthorEmail())
|
|
||||||
log.Info("savePatches() Branch Name", pset.GetStartBranchName())
|
|
||||||
log.Info("savePatches() Start Hash", pset.GetStartBranchHash())
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// From 18ee541f89be2e9f9a91c54873da87885e8ffdf5 Mon Sep 17 00:00:00 2001
|
|
||||||
// From: Jeff Carr <jcarr@wit.com>
|
|
||||||
// Date: Sun, 5 Jan 2025 01:18:47 -0600
|
|
||||||
// Subject: [PATCH] 'forge dirty' will find and list only dirty repos
|
|
||||||
|
|
||||||
// list patches in jcarr but not in devel
|
|
||||||
// git log --format="%H %Subject" jcarr --not devel
|
|
||||||
func countCurrentPatches(repo *gitpb.Repo) int {
|
|
||||||
cmd := []string{"git", "log", "--format=\"%H %s\"", "--no-merges", "jcarr", "--not", "devel"}
|
|
||||||
result := repo.Run(cmd)
|
|
||||||
return len(result.Stdout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doRegister(newurl string) error {
|
|
||||||
var url string
|
|
||||||
url = me.urlbase + "/register?url=" + newurl
|
|
||||||
body, err := me.forge.HttpPost(url, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Info("httpPost() failed:", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
test := strings.TrimSpace(string(body))
|
|
||||||
for _, line := range strings.Split(test, "\n") {
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
log.Info("server returned:", line)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
*/
|
|
216
argv.go
216
argv.go
|
@ -1,205 +1,47 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"go.wit.com/lib/gui/prep"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
this parses the command line arguements using alex flint's go-arg
|
this parses the command line arguements
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var argv args
|
var argv args
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
Help *EmptyCmd `arg:"subcommand:help" help:"New to forge? This is for you.'"`
|
Config bool `arg:"--config" help:"work from your .config/forge/ configuration"`
|
||||||
Checkout *CheckoutCmd `arg:"subcommand:checkout" help:"switch branches using 'git checkout'"`
|
FindAll bool `arg:"--find-all" help:"select every repo"`
|
||||||
Clean *CleanCmd `arg:"subcommand:clean" help:"start over at the beginning"`
|
FindReadOnly bool `arg:"--find-readonly" help:"include read-only repos"`
|
||||||
Commit *CommitCmd `arg:"subcommand:commit" help:"'git commit' but errors out if on wrong branch"`
|
FindMine bool `arg:"--find-mine" help:"download private and writeable repos"`
|
||||||
Config *ConfigCmd `arg:"subcommand:config" help:"show your .config/forge/ settings"`
|
FindFavorites bool `arg:"--find-favorites" help:"download repos marked as favorites"`
|
||||||
Dirty *DirtyCmd `arg:"subcommand:dirty" help:"show dirty git repos"`
|
FindPrivate bool `arg:"--find-private" help:"list private repos in .config/forge/"`
|
||||||
GitFetch *FindCmd `arg:"subcommand:fetch" help:"run 'git fetch master'"`
|
DoList bool `arg:"--do-list" help:"list found repos"`
|
||||||
Gui *EmptyCmd `arg:"subcommand:gui" help:"open the gui"`
|
DoScan bool `arg:"--do-scan" help:"rescan your repos"`
|
||||||
List *FindCmd `arg:"subcommand:list" help:"print a table of the current repos"`
|
DoClone bool `arg:"--do-clone" help:"go-clone things you are missing"`
|
||||||
Merge *MergeCmd `arg:"subcommand:merge" help:"merge branches"`
|
DoForce bool `arg:"--do-force" help:"force redo go-clone"`
|
||||||
Normal *NormalCmd `arg:"subcommand:normal" help:"set every repo to the default state for software development"`
|
DoGitPull bool `arg:"--do-git-pull" help:"run 'git pull' on all your repos"`
|
||||||
Patch *PatchCmd `arg:"subcommand:patch" help:"make patchsets"`
|
DoBuild bool `arg:"--do-build" default:"true" help:"also try to build it"`
|
||||||
Pull *PullCmd `arg:"subcommand:pull" help:"run 'git pull'"`
|
DoInstall bool `arg:"--do-install" help:"try to install every binary package"`
|
||||||
Tag *TagCmd `arg:"subcommand:tag" help:"manage git tags"`
|
DoRedoGoMod bool `arg:"--do-RedoGoMod" help:"remake all the go.sum and go.mod files"`
|
||||||
URL string `arg:"--connect" help:"forge url"`
|
DoErase bool `arg:"--do-EraseGoMod" help:"erase the go.mod and go.sum files"`
|
||||||
All bool `arg:"--all" help:"git commit --all"`
|
DoGui bool `arg:"--do-gui" help:"test the gui"`
|
||||||
Build string `arg:"--build" help:"build a repo"`
|
DryRun bool `arg:"--dry-run" help:"show what would be run"`
|
||||||
Install string `arg:"--install" help:"install a repo"`
|
Fix bool `arg:"--fix" help:"fix config, save config & exit"`
|
||||||
BuildForge bool `arg:"--forge-rebuild" help:"download and rebuild forge"`
|
|
||||||
Force bool `arg:"--force" help:"try to strong arm things"`
|
|
||||||
Verbose bool `arg:"--verbose" help:"show more output"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type EmptyCmd struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
type NormalCmd struct {
|
|
||||||
On *EmptyCmd `arg:"subcommand:on" help:"turn normal mode on"`
|
|
||||||
Off *EmptyCmd `arg:"subcommand:off" help:"turn normal mode off"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommitCmd struct {
|
|
||||||
Submit bool `arg:"--submit" default:"true" help:"submit the patches to forge"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type testCmd string
|
|
||||||
|
|
||||||
type CleanCmd struct {
|
|
||||||
Verify *EmptyCmd `arg:"subcommand:verify" help:"rescan repo"`
|
|
||||||
Repo string `arg:"--repo" help:"which repo to look at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CleanDevelCmd struct {
|
|
||||||
Force bool `arg:"--force" help:"try to strong arm things"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PatchCmd struct {
|
|
||||||
Check *EmptyCmd `arg:"subcommand:check" help:"check the state of the patches"`
|
|
||||||
List *EmptyCmd `arg:"subcommand:list" help:"your downloaded patchsets"`
|
|
||||||
Get *EmptyCmd `arg:"subcommand:get" help:"get the new patchsets"`
|
|
||||||
Show *EmptyCmd `arg:"subcommand:show" help:"your pending commits to your code"`
|
|
||||||
Submit *SubmitCmd `arg:"subcommand:submit" help:"submit your commits"`
|
|
||||||
Repos *SubmitCmd `arg:"subcommand:repos" help:"show repos with patches"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubmitCmd struct {
|
|
||||||
Match string `arg:"positional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PullCmd struct {
|
|
||||||
Check *EmptyCmd `arg:"subcommand:check" help:"check repo versions"`
|
|
||||||
Dirty *EmptyCmd `arg:"subcommand:dirty" help:"only check dirty repos"`
|
|
||||||
Patches *EmptyCmd `arg:"subcommand:patches" help:"only check repos with patches"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TagCmd struct {
|
|
||||||
List *EmptyCmd `arg:"subcommand:list" help:"list the tags"`
|
|
||||||
Clean *EmptyCmd `arg:"subcommand:clean" help:"clean out old and duplicate tags"`
|
|
||||||
Delete string `arg:"--delete" help:"delete a tag"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigAddCmd struct {
|
|
||||||
Path string `arg:"--path" help:"absolute path of the git repo"`
|
|
||||||
GoPath string `arg:"--gopath" help:"GO path of the git repo"`
|
|
||||||
Directory bool `arg:"--directory" default:"false" help:"repo is a directory to match against"`
|
|
||||||
ReadOnly bool `arg:"--readonly" default:"false" help:"repo is readonly"`
|
|
||||||
Writable bool `arg:"--writable" default:"false" help:"repo is writable"`
|
|
||||||
Favorite bool `arg:"--favorite" default:"false" help:"forge will always go-clone or git clone this"`
|
|
||||||
Private bool `arg:"--private" default:"false" help:"repo can not be published"`
|
|
||||||
Interesting bool `arg:"--interesting" default:"false" help:"something you decided was cool"`
|
|
||||||
DebName string `arg:"--debname" help:"the name of the debian package (or rpm, etc)"`
|
|
||||||
Master string `arg:"--master" help:"the git 'master' or 'main' branch name"`
|
|
||||||
Devel string `arg:"--devel" help:"the git devel branch name"`
|
|
||||||
User string `arg:"--user" help:"the git user branch name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigCmd struct {
|
|
||||||
Add *ConfigAddCmd `arg:"subcommand:add" help:"add a config setting"`
|
|
||||||
Fix *EmptyCmd `arg:"subcommand:fix" help:"fix .config/forge/ and/or repos.pb protobuf file"`
|
|
||||||
List *EmptyCmd `arg:"subcommand:list" help:"list your config settings"`
|
|
||||||
Delete string `arg:"--delete" help:"delete this repo"`
|
|
||||||
Register string `arg:"--register" help:"register your git URL (foo.com/mystuff) or (github.com/foo/bar)"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CheckoutCmd struct {
|
|
||||||
User *FindCmd `arg:"subcommand:user" help:"git checkout user"`
|
|
||||||
Devel *FindCmd `arg:"subcommand:devel" help:"git checkout devel"`
|
|
||||||
Master *FindCmd `arg:"subcommand:master" help:"git checkout master"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MergeCmd struct {
|
|
||||||
Devel *FindCmd `arg:"subcommand:devel" help:"merge user to devel"`
|
|
||||||
Master *FindCmd `arg:"subcommand:master" help:"merge devel to master"`
|
|
||||||
Publish *EmptyCmd `arg:"subcommand:publish" help:"increment versions and publish master branch"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DirtyCmd struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
type FindCmd struct {
|
|
||||||
All bool `arg:"--all" help:"select every repo (the default)"`
|
|
||||||
Mine bool `arg:"--mine" help:"your repos as defined in the forge config"`
|
|
||||||
Favorites bool `arg:"--favorites" help:"your repos configured as favorites"`
|
|
||||||
Private bool `arg:"--private" help:"your private repos from your .config/forge/"`
|
|
||||||
Dirty bool `arg:"--dirty" help:"only use dirty git repos"`
|
|
||||||
User bool `arg:"--user" help:"show repos on the user branch"`
|
|
||||||
Full bool `arg:"--full" help:"show full repo names"`
|
|
||||||
// ReadOnly bool `arg:"--readonly" help:"include read-only repos"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (args) Version() string {
|
func (args) Version() string {
|
||||||
return ARGNAME + " " + VERSION + " Built on " + BUILDTIME
|
return "forge " + VERSION + " Built on " + BUILDTIME
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a args) Description() string {
|
func (a args) Description() string {
|
||||||
return `
|
return `
|
||||||
forge -- a tool to manage lots of git repos. forge includes a GUI and TUI.
|
forge -- in the spirit of things like sourceforge
|
||||||
|
|
||||||
forge only executes the 'git' command. Everything it does, you can run by hand with 'git'.
|
Examples:
|
||||||
|
forge --config # shows your forge config (~/.config/forge/)
|
||||||
|
forge --mine # find your private and writable repos
|
||||||
|
forge --favorites # find configured as favorites
|
||||||
|
|
||||||
|
forge --git-pull # run 'git pull' in every repo
|
||||||
|
forge --build --dry-run # build every binary package (but just show what would run)
|
||||||
|
forge --mine --clone # clone every package you have in your config file
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles shell autocomplete
|
|
||||||
func DoAutoComplete(pb *prep.Auto) {
|
|
||||||
switch pb.Cmd {
|
|
||||||
case "checkout":
|
|
||||||
pb.Autocomplete2("devel master user")
|
|
||||||
case "clean":
|
|
||||||
pb.Autocomplete2("")
|
|
||||||
case "commit":
|
|
||||||
pb.Autocomplete2("--all")
|
|
||||||
case "config":
|
|
||||||
fmt.Println("add fix list")
|
|
||||||
case "dirty":
|
|
||||||
fmt.Println("")
|
|
||||||
case "gui":
|
|
||||||
fmt.Println("")
|
|
||||||
case "--gui":
|
|
||||||
pb.Autocomplete2("andlabs gocui")
|
|
||||||
case "list":
|
|
||||||
pb.Autocomplete2("--mine --favorites --dirty")
|
|
||||||
case "merge":
|
|
||||||
pb.Autocomplete2("devel master --all")
|
|
||||||
case "normal":
|
|
||||||
pb.Autocomplete2("on off")
|
|
||||||
case "pull":
|
|
||||||
pb.Autocomplete2("--force check")
|
|
||||||
case "patch":
|
|
||||||
fmt.Println("check get list repos submit show")
|
|
||||||
case "tag":
|
|
||||||
fmt.Println("list --delete clean")
|
|
||||||
default:
|
|
||||||
if pb.Cmd == "" {
|
|
||||||
pb.Autocomplete2("help list checkout clean commit dirty fetch gui normal merge patch pull tag --gui")
|
|
||||||
} else {
|
|
||||||
pb.Autocomplete2("list checkout clean commit dirty normal merge tag")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args) Appname() string {
|
|
||||||
return ARGNAME
|
|
||||||
}
|
|
||||||
|
|
||||||
func ifBlank(arg string) bool {
|
|
||||||
if arg == "''" {
|
|
||||||
// if empty, the user has not typed something
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a args) DoAutoComplete(autoArgv *prep.Auto) {
|
|
||||||
DoAutoComplete(autoArgv)
|
|
||||||
}
|
|
||||||
|
|
8
build
8
build
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/bash -x
|
|
||||||
|
|
||||||
# include forgeConfig
|
|
||||||
mkdir -p files/usr/bin/
|
|
||||||
cp ../utils/wit-utils/forgeConfig/forgeConfig files/usr/bin/
|
|
||||||
|
|
||||||
mkdir -p files/usr/share/bash-completion/completions/
|
|
||||||
forge --bash > files/usr/share/bash-completion/completions/forge
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ah yes, COBOL. what a throwback. for those that know
|
||||||
|
// then you know exactly what is in this file. For those that don't, here it is:
|
||||||
|
|
||||||
|
// All this does is output human readable text formatted to be viewable on
|
||||||
|
// a console with a fixed with font. AKA: a typerwriter. Which is exactly
|
||||||
|
// what COBOL did in the 1970's (60s? notsure) And the 80s.
|
||||||
|
|
||||||
|
// So, you want to dump out stuff on the console. Let's see. Something like
|
||||||
|
|
||||||
|
/*
|
||||||
|
forge --favorites
|
||||||
|
|
||||||
|
go.wit.com/apps/myapp v0.2.0 (installed)
|
||||||
|
go.wit.com/lib/somethingfun v0.0.7 (not downloaded)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// anyway, you get the idea. This is also called COBOL because it does on
|
||||||
|
// thing and truncates every line output to the columns you see with stty -a
|
||||||
|
// my monitor is huge, so it's not going to work at 80x24. 160x48 is better
|
||||||
|
// actually, I'd predict some of these will probably end up 240 wide
|
||||||
|
// long live good eyesight and 4K monitors!
|
||||||
|
|
||||||
|
func doCobol() {
|
||||||
|
log.DaemonMode(true)
|
||||||
|
|
||||||
|
log.Info(standardStart5("gopath", "cur name", "master", "user", "repo type"))
|
||||||
|
repos := me.found.SortByGoPath()
|
||||||
|
for repos.Scan() {
|
||||||
|
repo := repos.Next()
|
||||||
|
verifyPrint(repo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardStart5(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 verifyPrint(repo *gitpb.Repo) {
|
||||||
|
var end string
|
||||||
|
if repo.CheckDirty() {
|
||||||
|
end += "(dirty) "
|
||||||
|
}
|
||||||
|
s := make(map[string]string)
|
||||||
|
if !verify(repo, s) {
|
||||||
|
log.Info("going to delete", repo.GoPath)
|
||||||
|
if argv.Fix {
|
||||||
|
me.forge.Repos.DeleteByGoPath(repo.GetGoPath())
|
||||||
|
me.forge.Repos.ConfigSave()
|
||||||
|
} else {
|
||||||
|
log.Info("need argv --real to delete", repo.GoPath)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if me.forge.Config.IsReadOnly(repo.GoPath) {
|
||||||
|
if repo.ReadOnly {
|
||||||
|
} else {
|
||||||
|
log.Info("readonly flag on repo is wrong", repo.GoPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mhort string = s["mver"]
|
||||||
|
var uhort string = s["uver"]
|
||||||
|
var cname string = s["cname"]
|
||||||
|
|
||||||
|
// start := fmt.Sprintf("%-40s %-12s %-12s %-12s %-8s", s["gopath"], cname, mhort, uhort, s["rtype"])
|
||||||
|
start := standardStart5(s["gopath"], cname, mhort, uhort, s["rtype"])
|
||||||
|
|
||||||
|
if me.forge.Config.IsReadOnly(repo.GoPath) {
|
||||||
|
end += "(readonly) "
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
func verify(repo *gitpb.Repo, s map[string]string) bool {
|
||||||
|
if !repo.IsValid() {
|
||||||
|
// return false
|
||||||
|
}
|
||||||
|
s["gopath"] = repo.GetGoPath()
|
||||||
|
s["rtype"] = repo.RepoType()
|
||||||
|
|
||||||
|
s["mname"] = repo.GetMasterBranchName()
|
||||||
|
if s["mname"] == "" {
|
||||||
|
log.Info("verify() no master branch name", repo.GoPath)
|
||||||
|
s["mver"] = repo.GetMasterVersion()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// only verify the master branch name with read-only repos
|
||||||
|
if me.forge.Config.IsReadOnly(repo.GoPath) {
|
||||||
|
s["mver"] = repo.GetMasterVersion()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
s["dname"] = repo.GetDevelBranchName()
|
||||||
|
if s["dname"] == "" {
|
||||||
|
log.Info("verify() no devel branch name", repo.GoPath)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s["uname"] = repo.GetUserBranchName()
|
||||||
|
if s["uname"] == "" {
|
||||||
|
log.Info("verify() no user branch name", repo.GoPath)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s["cname"] = repo.GetCurrentBranchName()
|
||||||
|
|
||||||
|
s["mver"] = repo.GetMasterVersion()
|
||||||
|
if s["mver"] == "" {
|
||||||
|
log.Info("verify() no master branch name", repo.GoPath, repo.GetMasterBranchName())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s["dver"] = repo.GetDevelVersion()
|
||||||
|
if s["dver"] == "" {
|
||||||
|
log.Info("verify() no devel branch name", repo.GoPath, repo.GetDevelBranchName())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s["uver"] = repo.GetUserVersion()
|
||||||
|
if s["uver"] == "" {
|
||||||
|
log.Info("verify() no user branch name", repo.GoPath, repo.GetUserBranchName())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s["cver"] = repo.GetCurrentBranchVersion()
|
||||||
|
s["url"] = repo.URL
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
36
config.go
36
config.go
|
@ -1,36 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
// functions to import and export the protobuf
|
|
||||||
// data to and from config files
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"go.wit.com/lib/config"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func forgeConfigSave() error {
|
|
||||||
return me.forge.Config.ConfigSave()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setForgeMode(fmode forgepb.ForgeMode) {
|
|
||||||
if me.forge.Config.Mode == fmode {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("changing mode", me.forge.Config.Mode, fmode)
|
|
||||||
me.forge.Config.Mode = fmode
|
|
||||||
config.SetChanged("forge", true)
|
|
||||||
me.forge.Config.ConfigSave()
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleConfig(all *forgepb.ForgeConfigs) {
|
|
||||||
new1 := new(forgepb.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")
|
|
||||||
}
|
|
8
control
8
control
|
@ -1,8 +0,0 @@
|
||||||
Source: forge
|
|
||||||
Build-Depends: golang, protoc-gen-go, autogenpb, go-mod-clean
|
|
||||||
Package: forge
|
|
||||||
Maintainer: Jeff Carr <jcarr@wit.com>
|
|
||||||
Architecture: amd64
|
|
||||||
Depends: go-gui-toolkits, autogenpb, go-mod-clean, go-clone
|
|
||||||
Recommends: go-clone
|
|
||||||
Description: 'forge' an attempt at federated 'git'
|
|
88
debug.go
88
debug.go
|
@ -1,88 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// An app to submit patches for the 30 GO GUI repos
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/gui"
|
|
||||||
"go.wit.com/lib/debugger"
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if debugger.ArgDebug() {
|
|
||||||
log.Info("cmd line --debugger == true")
|
|
||||||
go func() {
|
|
||||||
log.Sleep(2)
|
|
||||||
debugger.DebugWindow()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func debug() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
gui.Crash(r, "forge debug()")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
for {
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
if me.repoAllB != nil {
|
|
||||||
tmp := fmt.Sprintf("All (%d)", me.forge.Repos.Len())
|
|
||||||
me.repoAllB.SetLabel(tmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if me.repoDevelMergeB != nil {
|
|
||||||
found := findMergeToDevel()
|
|
||||||
tmp := fmt.Sprintf("needs merge to devel (%d)", found.Len())
|
|
||||||
me.repoDevelMergeB.SetLabel(tmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if me.repoWritableB != nil {
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
|
|
||||||
}
|
|
||||||
tmp := fmt.Sprintf("writable (%d)", found.Len())
|
|
||||||
me.repoWritableB.SetLabel(tmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
dirty := me.forge.CheckDirty()
|
|
||||||
|
|
||||||
if me.repoDirtyB != nil {
|
|
||||||
tmp := fmt.Sprintf("dirty (%d)", dirty.Len())
|
|
||||||
me.repoDirtyB.SetLabel(tmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if me.reposWinB != nil {
|
|
||||||
tmp := fmt.Sprintf("Repos (%d)", me.forge.Repos.Len())
|
|
||||||
if dirty.Len() > 0 {
|
|
||||||
tmp = fmt.Sprintf("Repos (%d) (%d dirty)", me.forge.Repos.Len(), dirty.Len())
|
|
||||||
}
|
|
||||||
me.reposWinB.SetLabel(tmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for new patches
|
|
||||||
log.Info("should check for packages here")
|
|
||||||
// if err := me.forge.ListPatches(); err != nil {
|
|
||||||
// log.Info("List Patchsets Failed", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
log.Printf("finished a forge scan here in (%s)\n", shell.FormatDuration(time.Since(now)))
|
|
||||||
time.Sleep(90 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
20
debugger.go
20
debugger.go
|
@ -1,20 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
enables GUI options and the debugger in your application
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.wit.com/lib/debugger"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if debugger.ArgDebug() {
|
|
||||||
log.Info("cmd line --debugger == true")
|
|
||||||
go func() {
|
|
||||||
log.Sleep(2)
|
|
||||||
debugger.DebugWindow()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
47
doBuild.go
47
doBuild.go
|
@ -1,47 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func doBuild() error {
|
|
||||||
v := []string{}
|
|
||||||
if argv.Verbose {
|
|
||||||
v = []string{"-v", "-x"}
|
|
||||||
}
|
|
||||||
|
|
||||||
gopath := argv.Build
|
|
||||||
|
|
||||||
repo := me.forge.FindByGoPath(gopath)
|
|
||||||
if repo == nil {
|
|
||||||
return fmt.Errorf("rep not found: %s", gopath)
|
|
||||||
}
|
|
||||||
if err := me.forge.Build(repo, v); err != nil {
|
|
||||||
log.Warn("Build failed:", repo.GetGoPath(), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func doInstall() error {
|
|
||||||
v := []string{}
|
|
||||||
if argv.Verbose {
|
|
||||||
v = []string{"-v", "-x"}
|
|
||||||
}
|
|
||||||
|
|
||||||
gopath := argv.Install
|
|
||||||
repo := me.forge.FindByGoPath(gopath)
|
|
||||||
if repo == nil {
|
|
||||||
return fmt.Errorf("rep not found: %s", gopath)
|
|
||||||
}
|
|
||||||
if err := me.forge.Install(repo, v); err != nil {
|
|
||||||
log.Warn("Install failed", repo.GetGoPath(), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// trys to figure out if there is still something to update
|
|
||||||
|
|
||||||
func doCheckout() error {
|
|
||||||
if argv.Checkout.User != nil {
|
|
||||||
start := time.Now()
|
|
||||||
err := me.forge.DoAllCheckoutUser(argv.Force)
|
|
||||||
dur := time.Since(start)
|
|
||||||
log.Printf("Checked out %d user braches in %s\n", me.forge.Repos.Len(), shell.FormatDuration(dur))
|
|
||||||
if err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Checkout.Devel != nil {
|
|
||||||
// setForgeMode(forgepb.ForgeMode_DEVEL)
|
|
||||||
if err := me.forge.DoAllCheckoutDevelNew(argv.Force); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Checkout.Master != nil {
|
|
||||||
setForgeMode(forgepb.ForgeMode_MASTER) // disable "normal" mode if set
|
|
||||||
|
|
||||||
if err := me.forge.DoAllCheckoutMaster(); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
badExit(fmt.Errorf("did not specify what branch to checkout"))
|
|
||||||
return nil
|
|
||||||
}
|
|
272
doClean.go
272
doClean.go
|
@ -1,272 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkRemoteBranches(repo *gitpb.Repo) error {
|
|
||||||
if err := repo.ReloadCheck(); err != nil {
|
|
||||||
log.Info("need to reload", repo.FullPath)
|
|
||||||
}
|
|
||||||
if repo.VerifyRemoteAndLocalBranches(repo.GetDevelBranchName()) {
|
|
||||||
} else {
|
|
||||||
return log.Errorf("remote devel is out of sync with local: todo: git pull or git fetch")
|
|
||||||
}
|
|
||||||
if repo.VerifyRemoteAndLocalBranches(repo.GetMasterBranchName()) {
|
|
||||||
} else {
|
|
||||||
return log.Errorf("remote master is out of sync with local: todo: git pull or git fetch")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// reverts all repos back to the original master branches
|
|
||||||
// automatically deletes local devel and user branches
|
|
||||||
func doClean() error {
|
|
||||||
setForgeMode(forgepb.ForgeMode_CLEAN)
|
|
||||||
|
|
||||||
if argv.Clean.Verify != nil {
|
|
||||||
stats := me.forge.RillRepos(checkRemoteBranches)
|
|
||||||
for path, stat := range stats {
|
|
||||||
if stat.Err == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dur := stat.End.Sub(stat.Start)
|
|
||||||
if dur > time.Second {
|
|
||||||
log.Infof("%s checkRemoteBranches() took a long time (%s) (err=%v)\n", path, shell.FormatDuration(dur), stat.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// log.Infof("%-60s, %-60s %v %s\n", stat.Start, stat.End.String(), dur, path)
|
|
||||||
// log.Infof("%-30v %s %v\n", dur, path, stat.Err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix this to work, then delete all the other options for "forge clean'
|
|
||||||
if err := me.forge.DoAllCheckoutMaster(); err != nil {
|
|
||||||
// badExit(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if repo.IsDirty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// when publishing, clean out the details of that if it's still there
|
|
||||||
if repo.GetTargetVersion() != "" {
|
|
||||||
repo.SetTargetVersion("")
|
|
||||||
configSave = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to delete user
|
|
||||||
if err := doRepoCleanUser(repo); err != nil {
|
|
||||||
log.Info(repo.GetGoPath(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to delete devel
|
|
||||||
doRepoCleanDevel(repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
|
|
||||||
total := 0
|
|
||||||
// find all repos that aren't "clean"
|
|
||||||
all = me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
total += 1
|
|
||||||
|
|
||||||
// find repos not on master branch
|
|
||||||
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// find dirty repos
|
|
||||||
if repo.IsDirty() {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// find repos that still have a local user branch
|
|
||||||
if repo.IsLocalBranch(repo.GetUserBranchName()) {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// find repos that still have a local devel branch
|
|
||||||
if repo.IsLocalBranch(repo.GetDevelBranchName()) {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// display all the repos that aren't clean to the user
|
|
||||||
log.Printf("\n") // padding for now
|
|
||||||
if argv.Verbose {
|
|
||||||
me.forge.PrintHumanTableDirty(found)
|
|
||||||
} else {
|
|
||||||
me.forge.PrintHumanTable(found)
|
|
||||||
}
|
|
||||||
log.Printf("\n") // padding for now
|
|
||||||
|
|
||||||
var hmm int
|
|
||||||
hmm = found.Len()
|
|
||||||
if hmm == 0 {
|
|
||||||
log.Printf("%d repos are not clean\n", hmm)
|
|
||||||
} else {
|
|
||||||
log.Info("All repos are clean", total, hmm)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func doesLocalBranchExist(repo *gitpb.Repo, branch string) bool {
|
|
||||||
return repo.Exists(filepath.Join(".git/refs/heads", branch))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
func doRepoCleanDevel(repo *gitpb.Repo) error {
|
|
||||||
if !repo.IsLocalBranch(repo.GetDevelBranchName()) {
|
|
||||||
// there is no local branch named 'devel'
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
|
|
||||||
return log.Errorf("%s not on master branch:", repo.GetFullPath())
|
|
||||||
}
|
|
||||||
if repo.IsDirty() {
|
|
||||||
return log.Errorf("%s is dirty:", repo.GetFullPath())
|
|
||||||
}
|
|
||||||
if err := justDeleteTheDevelBranchAlready(repo); err != nil {
|
|
||||||
log.Info("justDeleteTheDevel() err", repo.GetGoPath(), err)
|
|
||||||
configSave = true
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// removes all local user branches
|
|
||||||
func doRepoCleanUser(repo *gitpb.Repo) error {
|
|
||||||
if repo.IsDirty() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
bruser := repo.GetUserBranchName()
|
|
||||||
brdevel := repo.GetDevelBranchName()
|
|
||||||
|
|
||||||
if repo.IsBranchRemote(bruser) {
|
|
||||||
log.Info("forge is designed to always have local only user branches", bruser)
|
|
||||||
return fmt.Errorf("forge is designed to always have local only user branches")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !repo.IsLocalBranch(bruser) {
|
|
||||||
// there is no local user branch
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// will you loose work if you delete your user branch?
|
|
||||||
// if DevelBranchExists()
|
|
||||||
// then if UserBranchCommits exist in DevelBranch
|
|
||||||
// DeleteUserBranch is safe
|
|
||||||
if repo.IsLocalBranch(brdevel) {
|
|
||||||
b1 := repo.CountDiffObjects(bruser, "refs/heads/"+brdevel) // should be zero
|
|
||||||
if b1 == 0 {
|
|
||||||
// every user branch exists in devel. delete user branch
|
|
||||||
cmd := []string{"git", "branch", "-D", bruser}
|
|
||||||
// log.Info("USER IS IN DEVEL", repo.GetGoPath(), cmd)
|
|
||||||
_, err := repo.RunVerboseOnError(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
brmaster := repo.GetMasterBranchName()
|
|
||||||
|
|
||||||
// will you loose work if you delete your user branch?
|
|
||||||
// if master branch exists()
|
|
||||||
// then if all user commits exist in master
|
|
||||||
// delete user branch is safe
|
|
||||||
if repo.IsLocalBranch(brmaster) {
|
|
||||||
b1 := repo.CountDiffObjects(bruser, "refs/heads/"+brmaster) // should be zero
|
|
||||||
if b1 == 0 {
|
|
||||||
cmd := []string{"git", "branch", "-D", bruser}
|
|
||||||
// log.Info("USER IS IN DEVEL", repo.GetGoPath(), cmd)
|
|
||||||
_, err := repo.RunVerboseOnError(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("%s branch has unique commits", bruser)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if you call this, there is no going back. no checks anymore. nothing
|
|
||||||
// it deletes the 'devel' branch. git branch -D "devel". END OF STORY
|
|
||||||
func justDeleteTheDevelBranchAlready(repo *gitpb.Repo) error {
|
|
||||||
branch := repo.GetDevelBranchName()
|
|
||||||
remote := filepath.Join("origin", branch)
|
|
||||||
|
|
||||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
|
||||||
if repo.IsDevelRemote() {
|
|
||||||
// just make sure the remote & local branches are the same
|
|
||||||
return repo.DeleteLocalDevelBranch()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check against remote if it exists
|
|
||||||
if repo.IsDevelRemote() {
|
|
||||||
b1 := repo.CountDiffObjects(branch, remote) // should be zero
|
|
||||||
if b1 == 0 {
|
|
||||||
cmd := []string{"git", "branch", "-D", repo.GetDevelBranchName()}
|
|
||||||
// log.Info("DEVEL IS IN REMOTE", repo.GetGoPath(), cmd)
|
|
||||||
_, err := repo.RunVerboseOnError(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cmd := []string{"git", "push"}
|
|
||||||
log.Info("DEVEL LOCAL NEEDS GIT PUSH TO REMOTE", repo.GetGoPath(), cmd)
|
|
||||||
err := repo.RunVerbose(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// remote doesn't exist, check against master
|
|
||||||
master := repo.GetMasterBranchName()
|
|
||||||
b1 := repo.CountDiffObjects(branch, "refs/heads/"+master) // should be zero
|
|
||||||
if b1 == 0 {
|
|
||||||
cmd := []string{"git", "branch", "-D", repo.GetDevelBranchName()}
|
|
||||||
// log.Info("DEVEL IS IN REMOTE", repo.GetGoPath(), cmd)
|
|
||||||
_, err := repo.RunVerboseOnError(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cmd := []string{"git", "merge something somehow"}
|
|
||||||
log.Info("devel local, remote and master branches are wrong", repo.GetGoPath(), cmd, b1)
|
|
||||||
// _, err := repo.RunVerbose(cmd)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func doGitReset() {
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
|
||||||
// log.Info("is readonly", repo.GetGoPath())
|
|
||||||
if repo.CheckDirty() {
|
|
||||||
log.Info("is readonly and dirty", repo.GetGoPath())
|
|
||||||
cmd := []string{"git", "reset", "--hard"}
|
|
||||||
repo.RunRealtime(cmd)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// log.Info("is not readonly", repo.GetGoPath())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
99
doCommit.go
99
doCommit.go
|
@ -1,99 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"go.wit.com/lib/config"
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func doCommit() error {
|
|
||||||
if argv.All {
|
|
||||||
found := me.forge.CheckDirty()
|
|
||||||
var newpatches bool
|
|
||||||
for repo := range found.IterAll() {
|
|
||||||
log.Info("do a commit on repo", repo.GetGoPath())
|
|
||||||
if err := doCommitRepo(repo); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
newpatches = true
|
|
||||||
repo.CheckDirty()
|
|
||||||
}
|
|
||||||
if newpatches {
|
|
||||||
config.SetChanged("repos", true)
|
|
||||||
return doPatchSubmit()
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
repo := findCurrentPwdRepoOrDie()
|
|
||||||
|
|
||||||
if !repo.CheckDirty() {
|
|
||||||
okExit(log.Sprintf("this repo %s is not dirty.\n\n--all # commit all changes in all repos", repo.GetFullPath()))
|
|
||||||
} else {
|
|
||||||
log.Info("repo is dirty", repo.GetFullPath())
|
|
||||||
}
|
|
||||||
|
|
||||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
found.Append(repo)
|
|
||||||
me.forge.PrintHumanTable(found)
|
|
||||||
log.Info("")
|
|
||||||
log.Info("wrong branch. Can not commit on", repo.GetCurrentBranchName())
|
|
||||||
log.Info("")
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Setenv("LESS", "-XR")
|
|
||||||
if err := shell.Exec([]string{"git", "diff"}); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.All {
|
|
||||||
if err := shell.ExecCheck([]string{"git", "add", "--all"}); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := shell.ExecCheck([]string{"git", "commit", "--all"}); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return doPatchSubmit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func doCommitRepo(repo *gitpb.Repo) error {
|
|
||||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
found.Append(repo)
|
|
||||||
me.forge.PrintHumanTable(found)
|
|
||||||
log.Info("")
|
|
||||||
log.Info("wrong branch. Can not commit on", repo.GetCurrentBranchName())
|
|
||||||
log.Info("")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
os.Chdir(repo.GetFullPath())
|
|
||||||
|
|
||||||
os.Setenv("LESS", "-XR")
|
|
||||||
if err := shell.Exec([]string{"git", "diff"}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.All {
|
|
||||||
if err := shell.ExecCheck([]string{"git", "add", "--all"}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := shell.ExecCheck([]string{"git", "commit", "--all"}); err != nil {
|
|
||||||
shell.ExecCheck([]string{"git", "reset"})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("git commit ok. forge done")
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doScan() {
|
||||||
|
me.forge.ScanGoSrc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func doGitPull() {
|
||||||
|
allerr := me.found.RillGitPull(40, 5)
|
||||||
|
|
||||||
|
all := me.found.SortByGoPath()
|
||||||
|
for all.Scan() {
|
||||||
|
repo := all.Next()
|
||||||
|
result := allerr[repo]
|
||||||
|
if result.Error == gitpb.ErrorGitPullOnDirty {
|
||||||
|
log.Info("skip git pull. repo is dirty", repo.GoPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if result.Error == gitpb.ErrorGitPullOnLocal {
|
||||||
|
log.Info("skip git pull. local branch ", repo.GoPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if result.Exit == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("git pull error:", repo.GoPath, result.Error)
|
||||||
|
log.Info("git pull error:", repo.GoPath, result.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func doFix() {
|
||||||
|
all := me.found.SortByGoPath()
|
||||||
|
for all.Scan() {
|
||||||
|
repo := all.Next()
|
||||||
|
if !repo.IsValid() {
|
||||||
|
log.Printf("%10s %-50s", "old?\n", repo.GetGoPath())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf("running on: %-50s\n", repo.GetGoPath())
|
||||||
|
cmd := []string{"ls"}
|
||||||
|
repo.Run(cmd)
|
||||||
|
}
|
||||||
|
}
|
75
doConfig.go
75
doConfig.go
|
@ -1,75 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func doConfig() {
|
|
||||||
if argv.Config.Delete != "" {
|
|
||||||
me.forge.DeleteByGoPath(argv.Config.Delete)
|
|
||||||
me.forge.SetConfigSave(true)
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Config.Fix != nil {
|
|
||||||
log.Info("todo")
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if argv.Config.Register != "" {
|
|
||||||
if err := doRegister(argv.Config.Register); err == nil {
|
|
||||||
okExit("attempting to register " + argv.Config.Register)
|
|
||||||
} else {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// try to add, then save config and exit
|
|
||||||
if argv.Config.Add != nil {
|
|
||||||
log.Info("going to add a new repo", argv.Config.Add.GoPath)
|
|
||||||
deleteGoPath(me.forge, argv.Config.Add.GoPath)
|
|
||||||
new1 := forgepb.ForgeConfig{
|
|
||||||
GoPath: argv.Config.Add.GoPath,
|
|
||||||
Writable: argv.Config.Add.Writable,
|
|
||||||
ReadOnly: argv.Config.Add.ReadOnly,
|
|
||||||
Private: argv.Config.Add.Private,
|
|
||||||
Directory: argv.Config.Add.Directory,
|
|
||||||
Favorite: argv.Config.Add.Favorite,
|
|
||||||
Interesting: argv.Config.Add.Interesting,
|
|
||||||
MasterBranchName: argv.Config.Add.Master,
|
|
||||||
DevelBranchName: argv.Config.Add.Devel,
|
|
||||||
UserBranchName: argv.Config.Add.User,
|
|
||||||
}
|
|
||||||
|
|
||||||
me.forge.Config.Append(&new1)
|
|
||||||
me.forge.ConfigSave()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("config.PathLock =", me.forge.Config.PathLock)
|
|
||||||
|
|
||||||
me.forge.ConfigPrintTable()
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteGoPath(f *forgepb.Forge, gopath string) bool {
|
|
||||||
var deleted bool = false
|
|
||||||
for {
|
|
||||||
if f.Config.DeleteByGoPath(gopath) {
|
|
||||||
log.Info("deleted ok", gopath)
|
|
||||||
deleted = true
|
|
||||||
} else {
|
|
||||||
log.Info("did not delete", gopath)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return deleted
|
|
||||||
}
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Delete(repo *gitpb.Repo, s map[string]string) bool {
|
||||||
|
if repo.Published == nil {
|
||||||
|
log.Info("published is nil", repo.Published)
|
||||||
|
} else {
|
||||||
|
log.Info("published len", repo.Published.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a new one here
|
||||||
|
newr := gitpb.Repo{
|
||||||
|
FullPath: repo.FullPath,
|
||||||
|
GoPath: repo.GoPath,
|
||||||
|
URL: repo.URL,
|
||||||
|
Tags: repo.Tags,
|
||||||
|
LastPull: repo.LastPull,
|
||||||
|
MasterBranchName: repo.MasterBranchName,
|
||||||
|
DevelBranchName: repo.DevelBranchName,
|
||||||
|
UserBranchName: repo.UserBranchName,
|
||||||
|
GoLibrary: repo.GoLibrary,
|
||||||
|
GoBinary: repo.GoBinary,
|
||||||
|
GoPrimitive: repo.GoPrimitive,
|
||||||
|
GoPlugin: repo.GoPlugin,
|
||||||
|
GoDeps: repo.GoDeps,
|
||||||
|
LastGoDep: repo.LastGoDep,
|
||||||
|
Dirty: repo.Dirty,
|
||||||
|
Published: repo.Published,
|
||||||
|
TargetVersion: repo.TargetVersion,
|
||||||
|
ReadOnly: repo.ReadOnly,
|
||||||
|
GoProtobuf: repo.GoProtobuf,
|
||||||
|
}
|
||||||
|
if argv.Fix {
|
||||||
|
me.forge.Repos.AppendUniqueGoPath(&newr)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
16
doDirty.go
16
doDirty.go
|
@ -1,16 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
func doDirty() {
|
|
||||||
found := me.forge.CheckDirty()
|
|
||||||
if found.Len() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if argv.Verbose {
|
|
||||||
me.forge.PrintHumanTableDirty(found)
|
|
||||||
} else {
|
|
||||||
me.forge.PrintHumanTable(found)
|
|
||||||
}
|
|
||||||
}
|
|
198
doFind.go
198
doFind.go
|
@ -1,198 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
)
|
|
||||||
|
|
||||||
// this populates a slice of protobuf records representing each git repo
|
|
||||||
// var found []*gitpb.Repo
|
|
||||||
//
|
|
||||||
// so, it makes a subset of repos that are then used performing actions on
|
|
||||||
//
|
|
||||||
// by default, it adds every repo
|
|
||||||
|
|
||||||
func doFind() *gitpb.Repos {
|
|
||||||
if argv.List == nil {
|
|
||||||
return findAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.List.Mine {
|
|
||||||
return findMine()
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.List.Dirty {
|
|
||||||
return me.forge.FindDirty()
|
|
||||||
}
|
|
||||||
|
|
||||||
return findAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FindCmd) findRepos() *gitpb.Repos {
|
|
||||||
if f == nil {
|
|
||||||
return findMine()
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.All {
|
|
||||||
return findAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Private {
|
|
||||||
return findPrivate()
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Mine {
|
|
||||||
return findMine()
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Favorites {
|
|
||||||
return findFavorites()
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Dirty {
|
|
||||||
return me.forge.FindDirty()
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.User {
|
|
||||||
return findUser()
|
|
||||||
}
|
|
||||||
|
|
||||||
return findAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
func findPrivate() *gitpb.Repos {
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
if me.forge.Config.IsPrivate(repo.GetGoPath()) {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
// finds repos that are writable
|
|
||||||
func findMine() *gitpb.Repos {
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
|
|
||||||
// log.Printf("get mine %s\n", me.forge.GetGoSrc())
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
|
|
||||||
if me.forge.Config.IsWritable(repo.GetGoPath()) {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
// finds repos the user has marked as favorites in the forge .config
|
|
||||||
func findFavorites() *gitpb.Repos {
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
|
|
||||||
// log.Printf("get favorites %s\n", me.forge.GetGoSrc())
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
|
|
||||||
if me.forge.Config.IsFavorite(repo.GetGoPath()) {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func findAll() *gitpb.Repos {
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func find50() *gitpb.Repos {
|
|
||||||
count := 0
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
if count > 50 {
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func findUser() *gitpb.Repos {
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func findPublishable() *gitpb.Repos {
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
if repo.GetTargetVersion() == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func findReposWithPatches() *gitpb.Repos {
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
if repo.GetTargetVersion() != "" {
|
|
||||||
// add everything that has a target version set
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if repo.IsDirty() {
|
|
||||||
// always add dirty branches
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if repo.GetUserVersion() == "" || repo.GetUserVersion() == "uerr" {
|
|
||||||
// skip anything without a user branch
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if repo.GetUserVersion() != repo.GetDevelVersion() {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// show anything that differs between 'devel' & 'master' branches
|
|
||||||
if repo.GetDevelVersion() != repo.GetMasterVersion() {
|
|
||||||
// this repo.State code isn't great, but it got me here quickly
|
|
||||||
// I'll defend my code by saying it's faster for me if I do dumb things
|
|
||||||
// sometimes and fix them later. Probably some employee will have to
|
|
||||||
// fix this. if that is the case I owe you lunch. or stock options
|
|
||||||
if repo.State == "DEVEL behind MASTER" {
|
|
||||||
// log.Info("repo state", repo.FullPath, repo.State)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore read-only repos for checks below here
|
|
||||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is an old test to see if the current 'last tag' is accurate and should be removed
|
|
||||||
if repo.GetLastTag() != repo.GetMasterVersion() {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
repo.FindLastTag()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
362
doGui.go
362
doGui.go
|
@ -1,362 +1,40 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
// An app to submit patches for the 30 GO GUI repos
|
// An app to submit patches for the 30 GO GUI repos
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/gui"
|
"go.wit.com/gui"
|
||||||
"go.wit.com/lib/gadgets"
|
"go.wit.com/lib/gadgets"
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func doGui() {
|
func doGui() {
|
||||||
win := gadgets.NewGenericWindow("Forge: A federated git development tool by WIT.COM", "Current Settings")
|
me.myGui = gui.New()
|
||||||
win.Custom = func() {
|
me.myGui.Default()
|
||||||
log.Warn("MAIN WINDOW CLOSE")
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
grid := win.Group.RawGrid()
|
me.mainWindow = gadgets.RawBasicWindow("submit & test patchsets")
|
||||||
if me.forge.Config.GetPathLock() {
|
me.mainWindow.Make()
|
||||||
me.goSrcPwd = gadgets.NewOneLiner(grid, "Working Directory")
|
me.mainWindow.Show()
|
||||||
me.goSrcPwd.SetText(me.forge.Config.ReposDir)
|
me.mainbox = me.mainWindow.Box()
|
||||||
} else {
|
|
||||||
me.goSrcEdit = gadgets.NewBasicEntry(grid, "Working Directory")
|
|
||||||
me.goSrcEdit.SetText(me.forge.Config.ReposDir)
|
|
||||||
me.goSrcEdit.Custom = func() {
|
|
||||||
log.Info("updating text to", me.goSrcEdit.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lockpath := grid.NewCheckbox("Lock").SetChecked(me.forge.Config.PathLock)
|
|
||||||
lockpath.Custom = func() {
|
|
||||||
if lockpath.IsChecked() {
|
|
||||||
log.Info("lock working directory")
|
|
||||||
me.forge.Config.PathLock = true
|
|
||||||
} else {
|
|
||||||
log.Info("unlock working directory")
|
|
||||||
me.forge.Config.PathLock = false
|
|
||||||
}
|
|
||||||
me.forge.Config.ConfigSave()
|
|
||||||
okExit("you must restart forge after changing the Path Lock")
|
|
||||||
}
|
|
||||||
|
|
||||||
grid.NextRow()
|
// disable the interface while everything is scanned
|
||||||
|
me.Disable()
|
||||||
|
|
||||||
// use ENV GIT_AUTHOR
|
vbox2 := me.mainbox.NewVerticalBox("BOX2")
|
||||||
me.gitAuthor = gadgets.NewOneLiner(grid, "Git Author")
|
globalBuildOptions(vbox2)
|
||||||
grid.NextRow()
|
me.summary = submitPatchesBox(vbox2)
|
||||||
|
|
||||||
if os.Getenv("GIT_AUTHOR_NAME") == "" {
|
me.repos = makeRepoView()
|
||||||
me.gitAuthor.SetText("ENV GIT_AUTHOR_NAME is unset")
|
|
||||||
} else {
|
|
||||||
author := os.Getenv("GIT_AUTHOR_NAME")
|
|
||||||
author += " <" + os.Getenv("GIT_AUTHOR_EMAIL") + ">"
|
|
||||||
me.gitAuthor.SetText(author)
|
|
||||||
}
|
|
||||||
|
|
||||||
grid.NextRow()
|
// processing is done. update the repo summary box
|
||||||
|
me.summary.Update()
|
||||||
|
|
||||||
gridM := win.Stack.RawGrid()
|
me.Enable()
|
||||||
|
|
||||||
var insertWin *gadgets.GenericWindow
|
// intermittently scans the status indefinitly
|
||||||
s := fmt.Sprintf("Repos (%d)", me.forge.Repos.Len())
|
me.repos.View.Watchdog(func() {
|
||||||
me.reposWinB = gridM.NewButton(s, func() {
|
log.Info("In main()")
|
||||||
// if the window exists, just toggle it open or closed
|
// processing is done. update the repo summary box
|
||||||
if insertWin != nil {
|
me.summary.Update()
|
||||||
insertWin.Toggle()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
insertWin = makeReposWinNew()
|
|
||||||
})
|
|
||||||
|
|
||||||
var patchesWin *stdPatchTableWin
|
|
||||||
var patchButton *gui.Node
|
|
||||||
patchButton = gridM.NewButton("Your patches", func() {
|
|
||||||
if patchesWin != nil {
|
|
||||||
patchesWin.Toggle()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !isPatchingSafe() {
|
|
||||||
patchButton.SetLabel("not safe yet")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// patchesWin = makePatchesWin(me.forge.Patchsets)
|
|
||||||
notdone := new(forgepb.Patches)
|
|
||||||
|
|
||||||
all := me.forge.Patchsets.All()
|
|
||||||
for all.Scan() {
|
|
||||||
pset := all.Next()
|
|
||||||
if pset.State == "DONE" {
|
|
||||||
// skip old patchsets
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
AddAllPatches(notdone, pset, false)
|
|
||||||
// AddNotDonePatches(notdone, pset, false)
|
|
||||||
}
|
|
||||||
notdone.PrintTable()
|
|
||||||
|
|
||||||
patchesWin = makePatchesWin(notdone)
|
|
||||||
})
|
|
||||||
|
|
||||||
var pubWin *gadgets.GenericWindow
|
|
||||||
gridM.NewButton("Publish", func() {
|
|
||||||
if pubWin != nil {
|
|
||||||
pubWin.Toggle()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pubWin = makePublishWindow()
|
|
||||||
})
|
|
||||||
|
|
||||||
var oldWin *gadgets.GenericWindow
|
|
||||||
gridM.NewButton("old", func() {
|
|
||||||
if oldWin != nil {
|
|
||||||
oldWin.Toggle()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
oldWin = makeOldStuff()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is the magic that generates a window directly from the protocol buffer
|
|
||||||
func makeStandardReposGrid(pb *gitpb.Repos) *gitpb.ReposTable {
|
|
||||||
t := pb.NewTable("testDirty")
|
|
||||||
t.NewUuid()
|
|
||||||
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
|
|
||||||
return r.GetNamespace()
|
|
||||||
})
|
|
||||||
// t.Custom = func() {
|
|
||||||
// log.Info("close grid?")
|
|
||||||
// }
|
|
||||||
sf.Custom = func(r *gitpb.Repo) {
|
|
||||||
log.Info("do button click on", r.GetNamespace())
|
|
||||||
}
|
|
||||||
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
|
|
||||||
return repo.NewestTime()
|
|
||||||
})
|
|
||||||
t.AddMasterVersion()
|
|
||||||
t.AddDevelVersion()
|
|
||||||
t.AddUserVersion()
|
|
||||||
t.AddCurrentBranchName()
|
|
||||||
t.AddState()
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is the magic that generates a window directly from the protocol buffer
|
|
||||||
func makeStandardReposWindow(title string, pb *gitpb.Repos) (*gitpb.ReposTable, *gui.Node) {
|
|
||||||
win := gadgets.RawBasicWindow(title)
|
|
||||||
win.Make()
|
|
||||||
win.Show()
|
|
||||||
win.Custom = func() {
|
|
||||||
// sets the hidden flag to false so Toggle() works
|
|
||||||
win.Hide()
|
|
||||||
}
|
|
||||||
box := win.Box().NewBox("bw vbox", false)
|
|
||||||
|
|
||||||
t := makeStandardReposGrid(pb)
|
|
||||||
t.SetParent(box)
|
|
||||||
t.ShowTable()
|
|
||||||
return t, box
|
|
||||||
}
|
|
||||||
|
|
||||||
func findMergeToDevel() *gitpb.Repos {
|
|
||||||
found := gitpb.NewRepos()
|
|
||||||
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
// this sees if user has patches for devel. If it does, add it to found
|
|
||||||
if repo.CountDiffObjects(repo.GetUserBranchName(), repo.GetDevelBranchName()) > 0 {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
now := time.Now()
|
|
||||||
if found.Len() == 0 {
|
|
||||||
log.Info("nothing to merge with devel")
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
// me.forge.PrintHumanTable(found)
|
|
||||||
|
|
||||||
// check for merges from devel
|
|
||||||
total, count, nope, _ := me.forge.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)))
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func findMergeToMaster() *gitpb.Repos {
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
|
|
||||||
if me.forge.Config.IsReadOnly(repo.GetNamespace()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if repo.IsDirty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if repo.GetMasterVersion() != repo.GetDevelVersion() {
|
|
||||||
me.found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// this sees if devel is behind master. IT SHOULD NOT BE
|
|
||||||
if repo.CountDiffObjects(repo.GetMasterBranchName(), repo.GetDevelBranchName()) == 0 {
|
|
||||||
// everything is normal
|
|
||||||
} else {
|
|
||||||
repo.State = "DEVEL < MASTER"
|
|
||||||
log.Info("SERIOUS ERROR. DEVEL BRANCH IS BEHIND MASTER", repo.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
// this sees if devel has patches for master. If it does, add it to me.found
|
|
||||||
if repo.CountDiffObjects(repo.GetDevelBranchName(), repo.GetMasterBranchName()) > 0 {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
now := time.Now()
|
|
||||||
if found.Len() == 0 {
|
|
||||||
log.Info("nothing to merge with master")
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
me.forge.PrintHumanTable(found)
|
|
||||||
|
|
||||||
// check for merges from devel
|
|
||||||
total, count, nope, _ := me.forge.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)))
|
|
||||||
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeDevelToMaster(doit bool) {
|
|
||||||
found := findMergeToMaster()
|
|
||||||
|
|
||||||
if !doit {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
all := found.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
log.Info("repo:", repo.GetNamespace())
|
|
||||||
if result, err := repo.MergeToMaster(); err == nil {
|
|
||||||
log.Warn("THINGS SEEM OK", repo.GetFullPath())
|
|
||||||
for _, line := range result.Stdout {
|
|
||||||
log.Warn("stdout:", line)
|
|
||||||
}
|
|
||||||
for _, line := range result.Stderr {
|
|
||||||
log.Warn("stderr:", line)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Warn("THINGS FAILED ", repo.GetFullPath())
|
|
||||||
log.Warn("err", err)
|
|
||||||
if result == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for _, line := range result.Stdout {
|
|
||||||
log.Warn("stdout:", line)
|
|
||||||
}
|
|
||||||
for _, line := range result.Stderr {
|
|
||||||
log.Warn("stderr:", line)
|
|
||||||
}
|
|
||||||
log.Warn("THINGS FAILED ", repo.GetFullPath())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
me.forge.SetConfigSave(true)
|
|
||||||
// view.Update()
|
|
||||||
}
|
|
||||||
me.forge.ConfigSave()
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeUserToDevel(doit bool) {
|
|
||||||
found := findMergeToDevel()
|
|
||||||
|
|
||||||
if !doit {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
all := found.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
bruser := repo.GetUserBranchName()
|
|
||||||
brdevel := repo.GetDevelBranchName()
|
|
||||||
|
|
||||||
if repo.GetUserVersion() == "uerr" {
|
|
||||||
// no user branch
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("trying", bruser, repo.GetUserVersion())
|
|
||||||
|
|
||||||
b1 := repo.CountDiffObjects(bruser, brdevel) // should be zero
|
|
||||||
if b1 == 0 {
|
|
||||||
// log.Info("User is already merged into Devel", repo.GetNamespace(), cmd)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("merging user into devel repo:", repo.GetNamespace())
|
|
||||||
if result, err := repo.MergeToDevel(); err == nil {
|
|
||||||
log.Warn("THINGS SEEM OK", repo.GetFullPath())
|
|
||||||
for _, line := range result.Stdout {
|
|
||||||
log.Warn("stdout:", line)
|
|
||||||
}
|
|
||||||
for _, line := range result.Stderr {
|
|
||||||
log.Warn("stderr:", line)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Warn("THINGS FAILED ", repo.GetFullPath())
|
|
||||||
log.Warn("err", err)
|
|
||||||
if result == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for _, line := range result.Stdout {
|
|
||||||
log.Warn("stdout:", line)
|
|
||||||
}
|
|
||||||
for _, line := range result.Stderr {
|
|
||||||
log.Warn("stderr:", line)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
me.forge.SetConfigSave(true)
|
|
||||||
// view.Update()
|
|
||||||
}
|
|
||||||
me.forge.ConfigSave()
|
|
||||||
}
|
|
||||||
|
|
||||||
// old things before they are removed, deprecated, fixed, etc
|
|
||||||
func makeOldStuff() *gadgets.GenericWindow {
|
|
||||||
oldWin := gadgets.NewGenericWindow("old code", "old code on it's way out")
|
|
||||||
|
|
||||||
grid := oldWin.Group.RawGrid()
|
|
||||||
|
|
||||||
// var reposWin *gadgets.GenericWindow
|
|
||||||
var reposWin *stdReposTableWin
|
|
||||||
grid.NewButton("Fix Repos", func() {
|
|
||||||
if reposWin != nil {
|
|
||||||
reposWin.Toggle()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reposWin = makeReposWin()
|
|
||||||
})
|
|
||||||
|
|
||||||
var howtoWin *gadgets.GenericWindow
|
|
||||||
grid.NewButton("Tutorial", func() {
|
|
||||||
if howtoWin != nil {
|
|
||||||
howtoWin.Toggle()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
howtoWin = makeHowtoWin()
|
|
||||||
})
|
|
||||||
|
|
||||||
grid.NextRow()
|
|
||||||
grid.NewLabel("")
|
|
||||||
|
|
||||||
return oldWin
|
|
||||||
}
|
|
||||||
|
|
168
doMerge.go
168
doMerge.go
|
@ -1,168 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/lib/config"
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func doMerge() error {
|
|
||||||
if argv.All == true {
|
|
||||||
start := time.Now()
|
|
||||||
repos, err := doMergeDevel()
|
|
||||||
dur := time.Since(start)
|
|
||||||
if err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
log.Printf("Merged %d devel branches in %s\n", repos.Len(), shell.FormatDuration(dur))
|
|
||||||
|
|
||||||
start = time.Now()
|
|
||||||
repos, err = doMergeMaster()
|
|
||||||
dur = time.Since(start)
|
|
||||||
if err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
log.Printf("Merged %d master branches in %s\n", repos.Len(), shell.FormatDuration(dur))
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
if argv.Merge.Devel != nil {
|
|
||||||
start := time.Now()
|
|
||||||
repos, err := doMergeDevel()
|
|
||||||
dur := time.Since(start)
|
|
||||||
if err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
log.Printf("Merged %d devel branches in %s\n", repos.Len(), shell.FormatDuration(dur))
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
if argv.Merge.Master != nil {
|
|
||||||
start := time.Now()
|
|
||||||
repos, err := doMergeMaster()
|
|
||||||
dur := time.Since(start)
|
|
||||||
if err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
log.Printf("Merged %d master branches in %s\n", repos.Len(), shell.FormatDuration(dur))
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
repo := findCurrentPwdRepoOrDie()
|
|
||||||
if err := repoMergeToDevel(repo); err != nil {
|
|
||||||
badRepoExit(repo, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func doMergeReport() *forgepb.Patches {
|
|
||||||
found := forgepb.NewPatches()
|
|
||||||
for repo := range me.forge.Repos.IterAll() {
|
|
||||||
if repo.GetDevelVersion() == repo.GetMasterVersion() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tmp := log.Sprintf("%s..%s", repo.GetMasterVersion(), repo.GetDevelVersion())
|
|
||||||
r, err := repo.RunStrict([]string{"git", "log", "--pretty=format:%H", tmp})
|
|
||||||
_ = err
|
|
||||||
for i, line := range r.Stdout {
|
|
||||||
log.Info(i, line, repo.FullPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func doMergeDevel() (*gitpb.Repos, error) {
|
|
||||||
var err error
|
|
||||||
done := gitpb.NewRepos()
|
|
||||||
found := findMergeToDevel()
|
|
||||||
for repo := range found.IterAll() {
|
|
||||||
if repo.CheckDirty() {
|
|
||||||
log.Info("repo is dirty", repo.GetFullPath())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Infof("%s starting git merge\n", repo.FullPath)
|
|
||||||
if repo.CheckoutDevel() {
|
|
||||||
log.Info("checkout devel failed", repo.GetGoPath())
|
|
||||||
err = log.Errorf("checkout devel failed")
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
// hash differences when merging user into devel branch
|
|
||||||
out := repo.GetBranchDifferences(repo.GetDevelBranchName(), repo.GetUserBranchName())
|
|
||||||
for i, hash := range out {
|
|
||||||
log.Info("MERGE HASH FROM USER TO DEVEL", i, hash)
|
|
||||||
}
|
|
||||||
if _, err := repo.MergeToDevel(); err != nil {
|
|
||||||
log.Info("merge from user failed", repo.GetGoPath(), err)
|
|
||||||
err = log.Errorf("merge from user failed")
|
|
||||||
// log.Info(strings.Join(r.Stdout, "\n"))
|
|
||||||
// log.Info(strings.Join(r.Stderr, "\n"))
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
done.Append(repo)
|
|
||||||
config.SetChanged("repos", true)
|
|
||||||
}
|
|
||||||
return done, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func repoMergeToDevel(repo *gitpb.Repo) error {
|
|
||||||
if repo.CheckDirty() {
|
|
||||||
return log.Errorf("can not merge. repo is dirty")
|
|
||||||
}
|
|
||||||
log.Infof("%s starting git merge\n", repo.FullPath)
|
|
||||||
if repo.CheckoutDevel() {
|
|
||||||
log.Info("checkout devel failed", repo.GetGoPath())
|
|
||||||
err := log.Errorf("checkout devel failed")
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
// hash differences when merging user into devel branch
|
|
||||||
out := repo.GetBranchDifferences(repo.GetDevelBranchName(), repo.GetUserBranchName())
|
|
||||||
for i, hash := range out {
|
|
||||||
log.Info("MERGE HASH FROM USER TO DEVEL", i, hash)
|
|
||||||
}
|
|
||||||
if _, err := repo.MergeToDevel(); err != nil {
|
|
||||||
log.Info("merge from user failed", repo.GetGoPath(), err)
|
|
||||||
// err := log.Errorf("merge from user failed")
|
|
||||||
// log.Info(strings.Join(r.Stdout, "\n"))
|
|
||||||
// log.Info(strings.Join(r.Stderr, "\n"))
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
config.SetChanged("repos", true)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func doMergeMaster() (*gitpb.Repos, error) {
|
|
||||||
var err error
|
|
||||||
setForgeMode(forgepb.ForgeMode_MASTER)
|
|
||||||
|
|
||||||
done := gitpb.NewRepos()
|
|
||||||
found := findMergeToMaster()
|
|
||||||
for repo := range found.IterAll() {
|
|
||||||
if repo.CheckDirty() {
|
|
||||||
log.Info("repo is dirty", repo.GetGoPath())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Starting merge on", repo.GetGoPath())
|
|
||||||
if repo.CheckoutMaster() {
|
|
||||||
log.Info("checkout devel failed", repo.GetGoPath())
|
|
||||||
err = log.Errorf("checkout devel failed")
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := repo.MergeToMaster(); err != nil {
|
|
||||||
log.Info("merge from user failed", repo.GetGoPath(), err)
|
|
||||||
err = log.Errorf("merge from user failed")
|
|
||||||
// log.Info(strings.Join(r.Stdout, "\n"))
|
|
||||||
// log.Info(strings.Join(r.Stderr, "\n"))
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
done.Append(repo)
|
|
||||||
config.SetChanged("repos", true)
|
|
||||||
}
|
|
||||||
return done, err
|
|
||||||
}
|
|
112
doNormal.go
112
doNormal.go
|
@ -1,112 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// checks that repos are in a "normal" state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/lib/config"
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func doNormal() bool {
|
|
||||||
me.forge.CheckDirtyQuiet()
|
|
||||||
|
|
||||||
var count int
|
|
||||||
stats := me.forge.RillRepos(checkNormalRepoState)
|
|
||||||
for path, stat := range stats {
|
|
||||||
dur := stat.End.Sub(stat.Start)
|
|
||||||
if dur > 10*time.Second {
|
|
||||||
log.Infof("%s checkNormalRepoState() took a long time (%s)\n", path, shell.FormatDuration(dur))
|
|
||||||
}
|
|
||||||
if stat.Err == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// log.Infof("%-60s, %-60s %v %s\n", stat.Start, stat.End.String(), dur, path)
|
|
||||||
// log.Infof("%-30v %s %v\n", dur, path, stat.Err)
|
|
||||||
// log.Info("got path", path, stat.Err)
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
if count > 0 {
|
|
||||||
log.Info("Some repos are not in a 'normal' state. error count =", count)
|
|
||||||
log.Info("TODO: list the repos here. forge patch repos?")
|
|
||||||
dumpWorkRepos()
|
|
||||||
config.SetChanged("repos", true)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 99% of the time, the repos during development should be on your user branch.
|
|
||||||
// error out if it's not
|
|
||||||
// this checks to see if a devel and user branch exist
|
|
||||||
// this needs to run each time in case repos were added manually by the user
|
|
||||||
// this also verifies that
|
|
||||||
func checkNormalRepoState(repo *gitpb.Repo) error {
|
|
||||||
var err error
|
|
||||||
tmp := filepath.Join(me.forge.Config.ReposDir, repo.GetNamespace())
|
|
||||||
if tmp != repo.FullPath {
|
|
||||||
log.Infof("%s != %s\n", repo.FullPath, tmp)
|
|
||||||
if strings.HasPrefix(repo.FullPath, me.forge.Config.ReposDir) {
|
|
||||||
tmp = strings.TrimPrefix(repo.FullPath, me.forge.Config.ReposDir)
|
|
||||||
tmp = strings.Trim(tmp, "/")
|
|
||||||
repo.Namespace = tmp
|
|
||||||
err = log.Errorf("namespace set to filepath")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// log.Infof("%s == %s\n", repo.FullPath, tmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = strings.Trim(repo.Namespace, "/")
|
|
||||||
if tmp != repo.Namespace {
|
|
||||||
err = log.Errorf("junk in ns %s", repo.Namespace)
|
|
||||||
repo.Namespace = tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
if repo.GetMasterBranchName() == "" {
|
|
||||||
me.forge.VerifyBranchNames(repo)
|
|
||||||
log.Info("ABNORMAL: master branch name was blank in", repo.GetFullPath())
|
|
||||||
}
|
|
||||||
if repo.GetMasterBranchName() == "" {
|
|
||||||
me.forge.VerifyBranchNames(repo)
|
|
||||||
err = log.Errorf("master branch name blank")
|
|
||||||
}
|
|
||||||
if repo.GetDevelBranchName() == "" {
|
|
||||||
me.forge.VerifyBranchNames(repo)
|
|
||||||
err = log.Errorf("devel branch name blank")
|
|
||||||
}
|
|
||||||
if repo.GetUserBranchName() == "" {
|
|
||||||
me.forge.VerifyBranchNames(repo)
|
|
||||||
err = log.Errorf("user branch name blank")
|
|
||||||
}
|
|
||||||
if repo.GetGoPath() == repo.GetNamespace() {
|
|
||||||
// log.Info(repo.FullPath, "gopath == namespace", repo.GetGoPath(), repo.GetNamespace())
|
|
||||||
} else {
|
|
||||||
log.Info(repo.FullPath, "gopath != namespace", repo.GetGoPath(), repo.GetNamespace())
|
|
||||||
}
|
|
||||||
repo.MakeLocalDevelBranch()
|
|
||||||
|
|
||||||
repo.VerifyRemoteAndLocalBranches(repo.GetDevelBranchName())
|
|
||||||
repo.VerifyRemoteAndLocalBranches(repo.GetMasterBranchName())
|
|
||||||
|
|
||||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
|
||||||
log.Infof("changing to user(%s) branch: %s\n", repo.GetUserBranchName(), repo.FullPath)
|
|
||||||
repo.CheckoutUser()
|
|
||||||
repo.ReloadCheck()
|
|
||||||
err = log.Errorf("now on user branch")
|
|
||||||
}
|
|
||||||
|
|
||||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) != repo.GetReadOnly() {
|
|
||||||
repo.ReadOnly = me.forge.Config.IsReadOnly(repo.GetGoPath())
|
|
||||||
log.Info("damnit", repo.FullPath)
|
|
||||||
err = log.Errorf("readonly bit wrong")
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
261
doPatch.go
261
doPatch.go
|
@ -1,261 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"go.wit.com/lib/fhelp"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func doPatchInit() {
|
|
||||||
if me.forge.Patchsets != nil {
|
|
||||||
if me.forge.Patchsets.Len() == 0 {
|
|
||||||
// log.Info("IGNORE: patches are empty")
|
|
||||||
} else {
|
|
||||||
log.Info("IGNORE: patches already initalized len =", me.forge.Patchsets.Len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := me.forge.LoadPatchsets(); err != nil {
|
|
||||||
log.Info("patches failed to open", err)
|
|
||||||
if err := me.forge.SavePatchsets(); err != nil {
|
|
||||||
log.Warn("savePatchsets() failed", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isPatchingSafe() bool {
|
|
||||||
if me.forge.Config.Mode == forgepb.ForgeMode_NORMAL {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
log.Info("This patch command is not safe to run now")
|
|
||||||
log.Info("you must reset the state of your git repositories. Run:")
|
|
||||||
log.Info("")
|
|
||||||
log.Info("forge normal")
|
|
||||||
log.Info("")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// submit's current working patches
|
|
||||||
func doPatchSubmit() error {
|
|
||||||
pset, err := me.forge.MakeDevelPatchSet("testing")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if pset.Patches == nil {
|
|
||||||
log.Info("pset.Patches == nil")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if pset.Patches.Len() == 0 {
|
|
||||||
log.Info("did not find any patches")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pset.PrintTable()
|
|
||||||
_, _, err = pset.HttpPost(myServer(), "new")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func doPatch() error {
|
|
||||||
if argv.Patch.Repos != nil {
|
|
||||||
dumpWorkRepos()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Patch.Submit != nil {
|
|
||||||
return doPatchSubmit()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isPatchingSafe() {
|
|
||||||
return log.Errorf("not safe to work on patches")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Patch.Get != nil {
|
|
||||||
psets := forgepb.NewSets()
|
|
||||||
newpb, _, _ := psets.HttpPostVerbose(myServer(), "get")
|
|
||||||
newpb.PrintTable()
|
|
||||||
me.forge.Patchsets = newpb
|
|
||||||
me.forge.SavePatchsets()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Patch.Check != nil {
|
|
||||||
/*
|
|
||||||
old := findExpired()
|
|
||||||
// old.PrintTable()
|
|
||||||
for p := range old.IterAll() {
|
|
||||||
log.Info("patch", p.Filename, p.Namespace)
|
|
||||||
}
|
|
||||||
newpb, err := old.HttpPostVerbose(myServer(), "check")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
newpb.PrintTable()
|
|
||||||
*/
|
|
||||||
log.Info("do something here to find patches merged to devel")
|
|
||||||
doMergeReport()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Patch.List != nil {
|
|
||||||
var changed bool
|
|
||||||
newpatches := new(forgepb.Set)
|
|
||||||
newpatches.Patches = forgepb.NewPatches()
|
|
||||||
for pset := range me.forge.Patchsets.IterAll() {
|
|
||||||
pset.PrintTable()
|
|
||||||
for patch := range pset.Patches.IterAll() {
|
|
||||||
changed = true
|
|
||||||
if patch.NewHash == "" || patch.NewHash == "na" {
|
|
||||||
if newpatches.Patches.AppendByPatchId(patch) {
|
|
||||||
log.Info("patchId added here", patch.PatchId)
|
|
||||||
} else {
|
|
||||||
log.Info("patchId already here", patch.PatchId)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := setNewCommitHash(patch); err != nil {
|
|
||||||
log.Infof("%s bad check on patch failure %v\n", patch.Filename, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("newhash set already here", patch.PatchId, patch.NewHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if changed {
|
|
||||||
if err := me.forge.SavePatchsets(); err != nil {
|
|
||||||
log.Warn("savePatchsets() failed", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Info("NEW PATCHES TABLE")
|
|
||||||
newpatches.PrintTable()
|
|
||||||
for patch := range newpatches.Patches.IterAll() {
|
|
||||||
if err := setNewCommitHash(patch); err == nil {
|
|
||||||
log.Info("newhash set already here", patch.PatchId, patch.NewHash)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Infof("%s is new\n", patch.Filename)
|
|
||||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("\tCould not find namespace:", patch.Namespace)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fhelp.QuestionUser("apply this patch?") {
|
|
||||||
newhash, err := applyAndTrackPatch(repo, patch)
|
|
||||||
log.Info("apply results:", newhash, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
/*
|
|
||||||
if newpatches.Len() != 0 {
|
|
||||||
for patch := range newpatches.IterAll() {
|
|
||||||
log.Info("new patch:", patch.CommitHash, patch.NewHash, patch.Filename)
|
|
||||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("\tCould not find namespace:", patch.Namespace)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return log.Errorf("patches need to be applied")
|
|
||||||
}
|
|
||||||
|
|
||||||
// return doPatchList()
|
|
||||||
applied := findApplied()
|
|
||||||
if applied == nil || applied.Len() == 0 {
|
|
||||||
log.Info("no patches have been appled to the devel branch yet")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// for patch := range applied.IterAll() {
|
|
||||||
// log.Info("SEND APPLIED: newhash:", patch.NewHash, "commithash:", patch.CommitHash, "patch", patch.Namespace)
|
|
||||||
// }
|
|
||||||
newpb, _, err := applied.HttpPostVerbose(myServer(), "applied")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
newpb.PrintTable()
|
|
||||||
return nil
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
// if nothing, show patches & dirty repos
|
|
||||||
me.forge.Patchsets.PrintTable()
|
|
||||||
dumpWorkRepos()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shows repos that are:
|
|
||||||
// - git dirty repos
|
|
||||||
// - repos with 'user' branch patches not in 'devel' branch
|
|
||||||
// - repos with awaiting master branch verions
|
|
||||||
//
|
|
||||||
// return true if any are found
|
|
||||||
func dumpWorkRepos() bool {
|
|
||||||
// always run dirty first
|
|
||||||
me.forge.CheckDirtyQuiet()
|
|
||||||
|
|
||||||
// if no option is given to patch, list out the
|
|
||||||
// repos that have patches ready in them
|
|
||||||
found := findReposWithPatches()
|
|
||||||
if found.Len() == 0 {
|
|
||||||
log.Info("you currently have no repos with patches")
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
me.forge.PrintHumanTable(found)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns bad if patches can not be applied
|
|
||||||
// logic is not great here but it was a first pass
|
|
||||||
func dumpPatchset(pset *forgepb.Set) bool {
|
|
||||||
// don't even bother to continue if we already know it's broken
|
|
||||||
if pset.State == "BROKEN" {
|
|
||||||
log.Printf("Patchset Name: %-24s Author: %s <%s> IS BAD\n", pset.Name, pset.GetGitAuthorName(), pset.GetGitAuthorEmail())
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
log.Printf("Patchset Name: %-24s Author: %s <%s> IS GOOD\n", pset.Name, pset.GetGitAuthorName(), pset.GetGitAuthorEmail())
|
|
||||||
}
|
|
||||||
|
|
||||||
var count int
|
|
||||||
var bad int
|
|
||||||
all := pset.Patches.SortByFilename()
|
|
||||||
for all.Scan() {
|
|
||||||
p := all.Next()
|
|
||||||
if IsValidPatch(p) {
|
|
||||||
// ok
|
|
||||||
} else {
|
|
||||||
pset.State = "BROKEN"
|
|
||||||
bad += 1
|
|
||||||
}
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
log.Info("pset has", count, "total patches, ", bad, "bad patches")
|
|
||||||
if bad == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsValidPatch(p *forgepb.Patch) bool {
|
|
||||||
basepath, filename := filepath.Split(p.Filename)
|
|
||||||
repo := me.forge.FindByGoPath(basepath)
|
|
||||||
if argv.Verbose {
|
|
||||||
log.Info("start:", p.StartHash, "end:", p.CommitHash, "file:", basepath, filename, "devel version", repo.GetDevelVersion())
|
|
||||||
}
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("can not apply patch! repo not found", basepath, filename)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if repo.ActualDevelHash() != p.StartHash {
|
|
||||||
log.Info("can not apply patch! devel hash mismatch", basepath, filename)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if repo.ActualDevelHash() == p.StartHash {
|
|
||||||
log.Info("local devel hash:", repo.ActualDevelHash(), "matches patch hash", p.StartHash, "and can be applied")
|
|
||||||
}
|
|
||||||
log.Info("start:", p.StartHash, "end:", p.CommitHash, "file:", basepath, filename, "devel version", repo.GetDevelVersion())
|
|
||||||
for _, line := range p.Files {
|
|
||||||
log.Info("\t", line)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
111
doPull.go
111
doPull.go
|
@ -1,111 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// is every repo on the devel branch?
|
|
||||||
func doPull() error {
|
|
||||||
if argv.Pull.Check != nil {
|
|
||||||
// stats := me.forge.RillFuncError(rillPull)
|
|
||||||
log.Info("TODO: actually git pull here? this is a bad idea. stopping.")
|
|
||||||
submit := gitpb.NewRepos()
|
|
||||||
for repo := range me.forge.Repos.IterByFullPath() {
|
|
||||||
newrepo := new(gitpb.Repo)
|
|
||||||
newrepo.MasterHash = repo.MasterHash
|
|
||||||
newrepo.DevelHash = repo.DevelHash
|
|
||||||
newrepo.Namespace = repo.Namespace
|
|
||||||
newrepo.URL = repo.URL
|
|
||||||
submit.Append(newrepo)
|
|
||||||
}
|
|
||||||
updatepb, regPB, err := submit.HttpPost(myServer(), "check")
|
|
||||||
if regPB == nil || err != nil {
|
|
||||||
log.Info("regPB==nil or err:", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Infof("pull check %s pb.Len()=%d client.Len()=%d server.Len()=%d err=%v\n", regPB.URL, updatepb.Len(), regPB.ClientDataLen, regPB.ServerDataLen, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// below this, you must not be in 'normal' mode
|
|
||||||
if me.forge.Config.Mode == forgepb.ForgeMode_NORMAL {
|
|
||||||
log.Info("you must check out the devel or master branches")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Force == true {
|
|
||||||
now := time.Now()
|
|
||||||
stats := me.forge.RillFuncError(rillPull)
|
|
||||||
count := me.forge.RillReload()
|
|
||||||
if count != 0 {
|
|
||||||
me.forge.ConfigSave()
|
|
||||||
}
|
|
||||||
|
|
||||||
total, count, nope, _ := me.forge.IsEverythingOnMaster()
|
|
||||||
log.Printf("Master branch check. %d total repos. (%d git pulled) (%d not on master branch) (%s) git pull total=FIXME%d\n", total, count, nope, shell.FormatDuration(time.Since(now)), len(stats))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("do a pull check here?")
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func rillPull(repo *gitpb.Repo) error {
|
|
||||||
if repo.IsDirty() {
|
|
||||||
// never do dirty repos
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
t, _ := repo.LastGitPull()
|
|
||||||
if time.Since(t) < time.Minute*10 && !argv.Force {
|
|
||||||
if argv.Verbose {
|
|
||||||
log.Info(repo.GetFullPath(), "git pulled too recently", shell.FormatDuration(time.Since(t)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cur := repo.GetCurrentBranchName()
|
|
||||||
if !repo.IsBranchRemote(cur) {
|
|
||||||
if argv.Verbose {
|
|
||||||
log.Info(repo.GetFullPath(), "branch is not remote", cur)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmd []string
|
|
||||||
cmd = append(cmd, "git", "pull")
|
|
||||||
err := repo.RunVerbose(cmd)
|
|
||||||
if err != nil {
|
|
||||||
log.Info(repo.GetFullPath(), "git pull err:", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// git fetch origin master:master
|
|
||||||
func rillFetchMaster(repo *gitpb.Repo) error {
|
|
||||||
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
|
|
||||||
// only fetch when branch is on the user branch
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
branch := repo.GetMasterBranchName()
|
|
||||||
cmd := []string{"git", "fetch", "origin", branch + ":" + branch}
|
|
||||||
err := repo.RunVerbose(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func doGitFetch() {
|
|
||||||
me.forge.RillFuncError(rillFetchMaster)
|
|
||||||
count := me.forge.RillReload()
|
|
||||||
if count != 0 {
|
|
||||||
me.forge.ConfigSave()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
// An app to submit patches for the 30 GO GUI repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doRedoGoMod() {
|
||||||
|
me.forge.RillRedoGoMod()
|
||||||
|
os.Exit(0)
|
||||||
|
repos := me.forge.Repos.SortByGoPath()
|
||||||
|
for repos.Scan() {
|
||||||
|
repo := repos.Next()
|
||||||
|
if !repo.IsValid() {
|
||||||
|
log.Printf("%10s %-50s", "old?", repo.GetGoPath())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf("running on: %-50s", repo.GetGoPath())
|
||||||
|
repo.RedoGoMod()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doEraseGoMod() {
|
||||||
|
/*
|
||||||
|
var cmds [][]string
|
||||||
|
cmds = append(cmds, []string{"rm", "-f", "go.mod", "go.sum"})
|
||||||
|
errs := me.forge.RillCmds(me.packs, cmds)
|
||||||
|
foreach x, y := range errs {
|
||||||
|
log.Info("EraseGoMod() error", x.GoPath, y)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
79
doSync.go
79
doSync.go
|
@ -1,79 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// trys to figure out if there is still something to update
|
|
||||||
|
|
||||||
/*
|
|
||||||
func doSync() error {
|
|
||||||
if argv.Pull.Sync.Clean != nil {
|
|
||||||
return doSyncClean()
|
|
||||||
}
|
|
||||||
if argv.Pull.Sync.User != nil {
|
|
||||||
return doSyncUser()
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("nothing to do")
|
|
||||||
}
|
|
||||||
|
|
||||||
func doSyncClean() error {
|
|
||||||
log.Printf("todo: fix this?")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func doSyncUser() error {
|
|
||||||
me.forge.ConfigRill(10, 20)
|
|
||||||
if allerr := me.forge.RillRepos(syncDevelBranch); len(allerr) != 0 {
|
|
||||||
log.Info("RillFunc() failed", allerr)
|
|
||||||
return fmt.Errorf("Rill doSyncUser() error count = %d", len(allerr))
|
|
||||||
} else {
|
|
||||||
log.Info("Rill syncDevelBranch() ok count =", len(allerr))
|
|
||||||
}
|
|
||||||
|
|
||||||
argv.Force = true
|
|
||||||
if err := doAllCheckoutUser(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func syncDevelBranch(repo *gitpb.Repo) error {
|
|
||||||
branch := repo.GetDevelBranchName()
|
|
||||||
if repo.Exists(filepath.Join(".git/refs/heads", branch)) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if repo.Exists(filepath.Join(".git/refs/remotes/origin", branch)) {
|
|
||||||
cmd := []string{"git", "checkout", branch}
|
|
||||||
err := repo.RunVerbose(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cmd := []string{"git", "branch", branch}
|
|
||||||
repo.RunVerbose(cmd)
|
|
||||||
cmd = []string{"git", "checkout", branch}
|
|
||||||
err := repo.RunVerbose(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func syncDevelBranches() error {
|
|
||||||
all := me.forge.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
|
|
||||||
}
|
|
||||||
*/
|
|
125
doTag.go
125
doTag.go
|
@ -1,125 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// checks that repos are in a "normal" state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/lib/fhelp"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func FindRepoByFullPath(wd string) *gitpb.Repo {
|
|
||||||
for repo := range me.forge.Repos.IterAll() {
|
|
||||||
if repo.FullPath == wd {
|
|
||||||
return repo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findCurrentPwdRepoOrDie() *gitpb.Repo {
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
repo := FindRepoByFullPath(wd)
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("Could not find repo:", wd)
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
return repo
|
|
||||||
}
|
|
||||||
|
|
||||||
func doTag() error {
|
|
||||||
wd, _ := os.Getwd()
|
|
||||||
if argv.Tag.List != nil {
|
|
||||||
repo := findCurrentPwdRepoOrDie()
|
|
||||||
|
|
||||||
tagTablePB := makeTagTablePB(repo, repo.Tags)
|
|
||||||
// tbox := win.Bottom.Box().SetProgName("TBOX")
|
|
||||||
// t.SetParent(tbox)
|
|
||||||
tagTablePB.MakeTable()
|
|
||||||
tagTablePB.PrintTable()
|
|
||||||
log.Info("list tags here", repo.Namespace)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Tag.Delete != "" {
|
|
||||||
repo := FindRepoByFullPath(wd)
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("Could not find repo:", wd)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the git tag already exists somehow
|
|
||||||
/*
|
|
||||||
if !repo.LocalTagExists(testtag) {
|
|
||||||
log.Info("Tag", testtag, "does not exist")
|
|
||||||
return log.Errorf("%s TAG DOES NOT EXIST %s", repo.FullPath, testtag)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
testtag := argv.Tag.Delete
|
|
||||||
if !argv.Force {
|
|
||||||
if !fhelp.QuestionUser(log.Sprintf("delete tag '%s'?", testtag)) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Info("Delete tag here", testtag)
|
|
||||||
|
|
||||||
// delete local and remote tag
|
|
||||||
repo.RunVerbose([]string{"git", "tag", "--delete", testtag})
|
|
||||||
repo.RunVerbose([]string{"git", "push", "--delete", "origin", testtag})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("do other tag stuff here")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeTagTablePB(repo *gitpb.Repo, pb *gitpb.GitTags) *gitpb.GitTagsTable {
|
|
||||||
t := pb.NewTable("tagList")
|
|
||||||
t.NewUuid()
|
|
||||||
|
|
||||||
col := t.AddHash()
|
|
||||||
col.Width = 12
|
|
||||||
|
|
||||||
col = t.AddStringFunc("bashash", func(tag *gitpb.GitTag) string {
|
|
||||||
_, base := filepath.Split(tag.Refname)
|
|
||||||
cmd, err := repo.RunStrict([]string{"git", "log", "-1", base, "--format=%H"})
|
|
||||||
if err != nil {
|
|
||||||
return "err"
|
|
||||||
}
|
|
||||||
if len(cmd.Stdout) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return cmd.Stdout[0]
|
|
||||||
})
|
|
||||||
col.Width = 12
|
|
||||||
|
|
||||||
col = t.AddTimeFunc("ctime", func(tag *gitpb.GitTag) time.Time {
|
|
||||||
// todo
|
|
||||||
return tag.Creatordate.AsTime()
|
|
||||||
})
|
|
||||||
col.Width = 4
|
|
||||||
|
|
||||||
col = t.AddTimeFunc("age", func(repo *gitpb.GitTag) time.Time {
|
|
||||||
// todo
|
|
||||||
return time.Now()
|
|
||||||
})
|
|
||||||
col.Width = 4
|
|
||||||
|
|
||||||
col = t.AddStringFunc("Ref Name", func(r *gitpb.GitTag) string {
|
|
||||||
_, ref := filepath.Split(r.GetRefname())
|
|
||||||
return ref
|
|
||||||
})
|
|
||||||
col.Width = 16
|
|
||||||
|
|
||||||
col = t.AddSubject()
|
|
||||||
col.Width = -1
|
|
||||||
|
|
||||||
return t
|
|
||||||
}
|
|
41
doc.go
41
doc.go
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
forge -- a tool to manage lots of git repos. forge includes a GUI and TUI.
|
|
||||||
|
|
||||||
forge only executes the 'git' command. Everything it does, you can run by hand with 'git'.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
--debugger open the debugger window
|
|
||||||
--logger open the log.* control window
|
|
||||||
--gui GUI select the plugin (andlabs,gocui,etc)
|
|
||||||
--gui-verbose enable all logging
|
|
||||||
--bash generate bash completion
|
|
||||||
--bash generate bash completion
|
|
||||||
--connect CONNECT forge url
|
|
||||||
--all git commit --all
|
|
||||||
--build BUILD build a repo
|
|
||||||
--install INSTALL install a repo
|
|
||||||
--forge-rebuild download and rebuild forge
|
|
||||||
--force try to strong arm things
|
|
||||||
--verbose show more output
|
|
||||||
--help, -h display this help and exit
|
|
||||||
--version display version and exit
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
|
|
||||||
help New to forge? This is for you.'
|
|
||||||
checkout switch branches using 'git checkout'
|
|
||||||
clean start over at the beginning
|
|
||||||
commit 'git commit' but errors out if on wrong branch
|
|
||||||
config show your .config/forge/ settings
|
|
||||||
dirty show dirty git repos
|
|
||||||
fetch run 'git fetch master'
|
|
||||||
gui open the gui
|
|
||||||
list print a table of the current repos
|
|
||||||
merge merge branches
|
|
||||||
normal set every repo to the default state for software development
|
|
||||||
patch make patchsets
|
|
||||||
pull run 'git pull'
|
|
||||||
tag manage git tags
|
|
||||||
*/
|
|
||||||
package main
|
|
33
exit.go
33
exit.go
|
@ -1,33 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"go.wit.com/gui"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func okExit(thing string) {
|
|
||||||
gui.UnloadToolkits()
|
|
||||||
if configSave {
|
|
||||||
me.forge.SetConfigSave(configSave)
|
|
||||||
}
|
|
||||||
if thing != "" {
|
|
||||||
log.Info("forge exit:", thing, "ok")
|
|
||||||
}
|
|
||||||
me.forge.Exit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func badExit(err error) {
|
|
||||||
log.Info("forge failed: ", err, me.forge.Config.ReposDir)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func badRepoExit(repo *gitpb.Repo, err error) {
|
|
||||||
log.Printf("%s FAILED: %v\n", repo.GetNamespace(), err)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "go.wit.com/log"
|
||||||
|
|
||||||
|
// retuns true if nothing was done
|
||||||
|
func findConfig() bool {
|
||||||
|
var done bool = false
|
||||||
|
if argv.FindMine {
|
||||||
|
findConfigMine()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
if argv.FindAll {
|
||||||
|
findConfigAll()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
if !done {
|
||||||
|
findConfigAll()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
// finds config repos that are writable
|
||||||
|
func findConfigMine() {
|
||||||
|
loop := me.forge.Config.SortByGoPath()
|
||||||
|
for loop.Scan() {
|
||||||
|
r := loop.Next()
|
||||||
|
gopath := r.GoPath
|
||||||
|
if r.GetDirectory() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if me.forge.Config.IsWritable(gopath) {
|
||||||
|
log.Info("mine:", gopath)
|
||||||
|
me.foundPaths = append(me.foundPaths, gopath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get everything in your config
|
||||||
|
func findConfigAll() {
|
||||||
|
loop := me.forge.Config.SortByGoPath()
|
||||||
|
for loop.Scan() {
|
||||||
|
r := loop.Next()
|
||||||
|
gopath := r.GoPath
|
||||||
|
if r.GetDirectory() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if me.forge.Config.IsWritable(gopath) {
|
||||||
|
log.Info("mine:", gopath)
|
||||||
|
me.foundPaths = append(me.foundPaths, gopath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func findRepos() bool {
|
||||||
|
var done bool = false
|
||||||
|
if argv.FindAll {
|
||||||
|
findAll()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if argv.FindPrivate {
|
||||||
|
findPrivate()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if argv.FindMine {
|
||||||
|
findMine()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
if argv.FindFavorites {
|
||||||
|
findFavorites()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the 'default' behavior when no command line arguments are given
|
||||||
|
// if no argv was set, select repos marked as 'mine'
|
||||||
|
if !done {
|
||||||
|
findMine()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPrivate() {
|
||||||
|
repos := me.forge.Repos.SortByGoPath()
|
||||||
|
for repos.Scan() {
|
||||||
|
repo := repos.Next()
|
||||||
|
if me.forge.Config.IsPrivate(repo.GoPath) {
|
||||||
|
me.found.AppendUniqueGoPath(repo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finds repos that are writable
|
||||||
|
func findMine() {
|
||||||
|
// log.Printf("get mine %s\n", me.forge.GetGoSrc())
|
||||||
|
repos := me.forge.Repos.SortByGoPath()
|
||||||
|
for repos.Scan() {
|
||||||
|
repo := repos.Next()
|
||||||
|
if me.forge.Config.IsWritable(repo.GoPath) {
|
||||||
|
me.found.AppendUniqueGoPath(repo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finds repos that are writable
|
||||||
|
func findFavorites() {
|
||||||
|
// log.Printf("get favorites %s\n", me.forge.GetGoSrc())
|
||||||
|
repos := me.forge.Repos.SortByGoPath()
|
||||||
|
for repos.Scan() {
|
||||||
|
repo := repos.Next()
|
||||||
|
if me.forge.Config.IsFavorite(repo.GoPath) {
|
||||||
|
me.found.AppendUniqueGoPath(repo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findAll() {
|
||||||
|
var configsave bool
|
||||||
|
repos := me.forge.Repos.SortByGoPath()
|
||||||
|
for repos.Scan() {
|
||||||
|
repo := repos.Next()
|
||||||
|
if me.forge.Config.IsReadOnly(repo.GoPath) && !argv.FindReadOnly {
|
||||||
|
if repo.ReadOnly {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Info("todo: ConfigSave() readonly flag on repo is wrong", repo.GoPath)
|
||||||
|
repo.ReadOnly = true
|
||||||
|
configsave = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
me.found.AppendUniqueGoPath(repo)
|
||||||
|
}
|
||||||
|
if configsave {
|
||||||
|
log.Info("should ConfigSave here")
|
||||||
|
me.forge.Repos.ConfigSave()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"go.wit.com/gui"
|
||||||
|
"go.wit.com/lib/gadgets"
|
||||||
|
"go.wit.com/lib/gui/shell"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doesExist(path string) bool {
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// only errors on bad errors
|
||||||
|
func quickCmd(fullpath string, cmd []string) bool {
|
||||||
|
if me.autoDryRun.Checked() {
|
||||||
|
log.Warn("RUN --dry-run", fullpath, cmd)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
log.Warn("RUN:", fullpath, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := shell.PathRun(fullpath, cmd)
|
||||||
|
if result.Error != nil {
|
||||||
|
log.Warn("quickCmd() cmd =", cmd)
|
||||||
|
log.Warn("quickCmd() err =", result.Error)
|
||||||
|
log.Warn("quickCmd() b =", result.Exit)
|
||||||
|
log.Warn("quickCmd() output =", result.Stdout)
|
||||||
|
return false
|
||||||
|
} else if result.Exit != 0 {
|
||||||
|
log.Warn("quickCmd() b =", result.Exit)
|
||||||
|
log.Warn("quickCmd() output =", result.Stdout)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
log.Warn("quickCmd() output = ", result.Stdout)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func globalBuildOptions(vbox *gui.Node) {
|
||||||
|
group1 := vbox.NewGroup("Global Build Options")
|
||||||
|
grid := group1.NewGrid("buildOptions", 0, 0)
|
||||||
|
|
||||||
|
// me.autoWorkingPwd = gadgets.NewOneLiner(grid, "working directory (pwd)")
|
||||||
|
me.userHomePwd = gadgets.NewOneLiner(grid, "user home")
|
||||||
|
grid.NextRow()
|
||||||
|
me.goSrcPwd = gadgets.NewOneLiner(grid, "go src home")
|
||||||
|
grid.NextRow()
|
||||||
|
|
||||||
|
usr, _ := user.Current()
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Error getting home directory:", err)
|
||||||
|
homeDir = "/home/autotypist"
|
||||||
|
}
|
||||||
|
me.userHomePwd.SetText(homeDir)
|
||||||
|
srcDir := filepath.Join(homeDir, "go/src")
|
||||||
|
me.goSrcPwd.SetText(srcDir)
|
||||||
|
|
||||||
|
// select the branch you want to test, build and develop against
|
||||||
|
// this lets you select your user branch, but, when you are happy
|
||||||
|
// you can merge everything into the devel branch and make sure it actually
|
||||||
|
// works. Then, when that is good, merge and version everything in master
|
||||||
|
me.setBranchB = grid.NewButton("set current branch to:", func() {
|
||||||
|
targetName := me.newBranch.String()
|
||||||
|
log.Warn("setting all branches to", targetName)
|
||||||
|
loop := me.repos.View.ReposSortByName()
|
||||||
|
for loop.Scan() {
|
||||||
|
repo := loop.Repo()
|
||||||
|
repo.Status.CheckoutBranch(targetName)
|
||||||
|
repo.Scan()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
me.newBranch = grid.NewCombobox()
|
||||||
|
me.newBranch.AddText("master")
|
||||||
|
me.newBranch.AddText("devel")
|
||||||
|
me.newBranch.AddText(usr.Username)
|
||||||
|
me.newBranch.SetText(usr.Username)
|
||||||
|
|
||||||
|
// checking this will automatically make the branches off of devel
|
||||||
|
me.autoCreateBranches = grid.NewCheckbox("create if missing").SetChecked(true)
|
||||||
|
grid.NextRow()
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.wit.com/gui"
|
||||||
|
"go.wit.com/lib/gui/repolist"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hideFunction(r *repolist.RepoRow) {
|
||||||
|
/*
|
||||||
|
if r.Status.IsDirty() {
|
||||||
|
r.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if me.autoHideReadOnly.Checked() {
|
||||||
|
if r.Status.ReadOnly() {
|
||||||
|
r.Hide()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if me.autoHidePerfect.Checked() {
|
||||||
|
if r.IsPerfect() {
|
||||||
|
r.Hide()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.Show()
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func globalDisplayOptions(vbox *gui.Node) {
|
||||||
|
group1 := vbox.NewGroup("Global Display Options")
|
||||||
|
|
||||||
|
group1.NewButton("Show Repository Window", func() {
|
||||||
|
// globalDisplaySetRepoState()
|
||||||
|
if me.repos.Hidden() {
|
||||||
|
me.repos.Show()
|
||||||
|
} else {
|
||||||
|
me.repos.Hide()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
me.autoHideReadOnly = group1.NewCheckbox("Hide read-only repos").SetChecked(true)
|
||||||
|
me.autoHideReadOnly.Custom = func() {
|
||||||
|
me.repos.View.RegisterHideFunction(hideFunction)
|
||||||
|
me.repos.View.ScanRepositories()
|
||||||
|
}
|
||||||
|
|
||||||
|
me.autoHidePerfect = group1.NewCheckbox("Hide Perfectly clean repos").SetChecked(true)
|
||||||
|
me.autoHidePerfect.Custom = func() {
|
||||||
|
me.repos.View.RegisterHideFunction(hideFunction)
|
||||||
|
me.repos.View.ScanRepositories()
|
||||||
|
}
|
||||||
|
scanbox := group1.Box().Horizontal()
|
||||||
|
me.autoScanReposCB = scanbox.NewCheckbox("auto scan").SetChecked(true)
|
||||||
|
|
||||||
|
scanbox.NewButton("scan now", func() {
|
||||||
|
log.Info("re-scanning repos now")
|
||||||
|
i, s := me.repos.View.ScanRepositories()
|
||||||
|
log.Info("re-scanning repos done", i, s)
|
||||||
|
})
|
||||||
|
}
|
340
helperPatches.go
340
helperPatches.go
|
@ -1,340 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func makeReposTablePB(pb *gitpb.Repos) *gitpb.ReposTable {
|
|
||||||
t := pb.NewTable("quickListRepos")
|
|
||||||
t.NewUuid()
|
|
||||||
|
|
||||||
sf := t.AddStringFunc("Namespace", func(r *gitpb.Repo) string {
|
|
||||||
return r.GetNamespace()
|
|
||||||
})
|
|
||||||
sf.Width = 16
|
|
||||||
|
|
||||||
userVer := t.AddStringFunc("user", func(repo *gitpb.Repo) string {
|
|
||||||
ver := repo.GetUserVersion()
|
|
||||||
return ver
|
|
||||||
})
|
|
||||||
userVer.Width = 4
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
type stdPatchsetTableWin struct {
|
|
||||||
sync.Mutex
|
|
||||||
win *gadgets.GenericWindow // the machines gui window
|
|
||||||
box *gui.Node // the machines gui parent box widget
|
|
||||||
TB *forgepb.SetsTable // the gui table buffer
|
|
||||||
update bool // if the window should be updated
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *stdPatchsetTableWin) Toggle() {
|
|
||||||
if w == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if w.win == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.win.Toggle()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
etimef := func(e *forgepb.Set) string {
|
|
||||||
etime := e.Etime.AsTime()
|
|
||||||
s := etime.Format("2006/01/02 15:04")
|
|
||||||
if strings.HasPrefix(s, "1970/") {
|
|
||||||
// just show a blank if it's not set
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
t.AddStringFunc("etime", etimef)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
ctimef := func(p *forgepb.Set) string {
|
|
||||||
ctime := p.Ctime.AsTime()
|
|
||||||
return ctime.Format("2006/01/02 15:04")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func setPatchsetState(p *forgepb.Set) {
|
|
||||||
var bad bool
|
|
||||||
var good bool
|
|
||||||
var done bool = true
|
|
||||||
|
|
||||||
all := p.Patches.All()
|
|
||||||
for all.Scan() {
|
|
||||||
patch := all.Next()
|
|
||||||
// log.Info("patch:", patch.StartHash, patch.CommitHash, patch.Namespace, patch.Filename)
|
|
||||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("could not find repo", patch.Namespace)
|
|
||||||
bad = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err := repo.GetHashName(patch.CommitHash); err == nil {
|
|
||||||
// this patch has been applied
|
|
||||||
patch.Applied = true
|
|
||||||
done = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if name, err := repo.GetHashName(patch.StartHash); err == nil {
|
|
||||||
// it might be possible to apply this patch
|
|
||||||
log.Info("patch may be good:", patch.Namespace, name, patch.CommitHash, patch.Filename)
|
|
||||||
good = true
|
|
||||||
} else {
|
|
||||||
// probably screwed up git trees
|
|
||||||
log.Info("patch with unknown origin:", patch.Namespace, name, err, patch.CommitHash, patch.Filename)
|
|
||||||
bad = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if bad {
|
|
||||||
p.State = "BAD"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if good {
|
|
||||||
p.State = "TRY"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if done {
|
|
||||||
p.State = "DONE"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanSubject(line string) string {
|
|
||||||
// Regular expression to remove "Subject:" and "[PATCH...]" patterns
|
|
||||||
re := regexp.MustCompile(`(?i)^Subject:\s*(\[\s*PATCH[^\]]*\]\s*)?`)
|
|
||||||
cleaned := re.ReplaceAllString(line, "")
|
|
||||||
return strings.TrimSpace(cleaned)
|
|
||||||
}
|
|
||||||
|
|
||||||
// jcarr@framebook:~/go/src/go.wit.com/lib/protobuf/forgepb$ git branch --contains 4a27e7702b9b975b066ec9d2ee7ac932d86552e3
|
|
||||||
// * jcarr
|
|
||||||
// jcarr@framebook:~/go/src/go.wit.com/lib/protobuf/forgepb$ git merge-base --is-ancestor "4a27e7702b9b975b066ec9d2ee7ac932d86552e3" "devel" ; echo $?
|
|
||||||
// 1
|
|
||||||
// jcarr@framebook:~/go/src/go.wit.com/lib/protobuf/forgepb$ git merge-base --is-ancestor "4a27e7702b9b975b066ec9d2ee7ac932d86552e3" "jcarr" ; echo $?
|
|
||||||
// 0
|
|
||||||
|
|
||||||
func findCommitByHash(hash string, subject string) (string, error) {
|
|
||||||
cmd := exec.Command("git", "log", "--pretty=format:%H %s")
|
|
||||||
var out bytes.Buffer
|
|
||||||
cmd.Stdout = &out
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := strings.Split(out.String(), "\n")
|
|
||||||
for _, line := range lines {
|
|
||||||
if strings.Contains(strings.ToLower(line), strings.ToLower(subject)) {
|
|
||||||
return strings.Fields(line)[0], nil // return the commit hash
|
|
||||||
}
|
|
||||||
if strings.Fields(line)[0] == hash {
|
|
||||||
return "", fmt.Errorf("start commit found: %s", hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("no commit found for subject: %s", subject)
|
|
||||||
}
|
|
||||||
|
|
||||||
func findCommitBySubject(subject string) (string, error) {
|
|
||||||
cmd := exec.Command("git", "log", "--pretty=format:%H %s", "--grep="+subject, "-i")
|
|
||||||
var out bytes.Buffer
|
|
||||||
cmd.Stdout = &out
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := strings.Split(out.String(), "\n")
|
|
||||||
for _, line := range lines {
|
|
||||||
if strings.Contains(strings.ToLower(line), strings.ToLower(subject)) {
|
|
||||||
return strings.Fields(line)[0], nil // return the commit hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("no commit found for subject: %s", subject)
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if PB changed
|
|
||||||
func setNewCommitHash(patch *forgepb.Patch) error {
|
|
||||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
|
||||||
if repo == nil {
|
|
||||||
return log.Errorf("could not find repo %s", patch.Namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
comment := cleanSubject(patch.Comment)
|
|
||||||
|
|
||||||
os.Chdir(repo.GetFullPath())
|
|
||||||
newhash, err := findCommitBySubject(comment)
|
|
||||||
if err != nil {
|
|
||||||
return log.Errorf("patch: not found hash: %s %s %s %s %v", patch.CommitHash, patch.Namespace, comment, newhash, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
patchId, err := repo.FindPatchId(newhash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
patch.PatchId = patchId
|
|
||||||
patch.NewHash = newhash
|
|
||||||
|
|
||||||
log.Info("patch: found hash:", patch.CommitHash, newhash, patch.Namespace, comment)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddAllPatches(notdone *forgepb.Patches, pset *forgepb.Set, full bool) {
|
|
||||||
for patch := range pset.Patches.IterAll() {
|
|
||||||
comment := cleanSubject(patch.Comment)
|
|
||||||
|
|
||||||
if found := notdone.FindByCommitHash(patch.CommitHash); found != nil {
|
|
||||||
log.Info("duplicate commit hash", patch.Namespace, "patch:", patch.NewHash, "commithash:", patch.CommitHash, comment)
|
|
||||||
// continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// log.Info("adding patch:", patch.Namespace, patch.CommitHash, comment, newhash)
|
|
||||||
notdone.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddNotDonePatches(notdone *forgepb.Patches, pset *forgepb.Set, full bool) {
|
|
||||||
for patch := range pset.Patches.IterAll() {
|
|
||||||
comment := cleanSubject(patch.Comment)
|
|
||||||
|
|
||||||
if found := notdone.FindByCommitHash(patch.CommitHash); found != nil {
|
|
||||||
log.Info("duplicate notdone", patch.Namespace, "patch:", patch.NewHash, "commithash:", patch.CommitHash, comment)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("could not find repo", patch.Namespace)
|
|
||||||
if full {
|
|
||||||
notdone.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if patch.NewHash != "" {
|
|
||||||
log.Info("already applied patch", patch.Namespace, ": newhash:", patch.NewHash, "commithash:", patch.CommitHash, comment)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
os.Chdir(repo.GetFullPath())
|
|
||||||
newhash, err := findCommitByHash(patch.StartHash, comment)
|
|
||||||
if err != nil {
|
|
||||||
// this patch has not been applied yet
|
|
||||||
log.Info("patch: not found hash:", patch.Namespace, patch.CommitHash, comment, err)
|
|
||||||
notdone.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
newhash, err = findCommitBySubject(comment)
|
|
||||||
if err == nil {
|
|
||||||
patch.NewHash = newhash
|
|
||||||
log.Info("patch: found hash:", patch.Namespace, "commit patch", patch.CommitHash, "new hash", newhash, "start hash", patch.StartHash, comment)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// this patch has not been applied yet
|
|
||||||
log.Info("patch: not found hash:", patch.Namespace, patch.CommitHash, comment, newhash, err)
|
|
||||||
notdone.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func findExpired() *forgepb.Patches {
|
|
||||||
var pset *forgepb.Patches
|
|
||||||
for found := range me.forge.Patchsets.IterAll() {
|
|
||||||
if found.Name == "forge auto commit" {
|
|
||||||
pset = found.Patches
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if pset == nil {
|
|
||||||
log.Info("failed to find 'forge auto commit'")
|
|
||||||
return forgepb.NewPatches()
|
|
||||||
}
|
|
||||||
|
|
||||||
found := forgepb.NewPatches()
|
|
||||||
|
|
||||||
for patch := range pset.IterAll() {
|
|
||||||
comment := cleanSubject(patch.Comment)
|
|
||||||
|
|
||||||
repo := me.forge.FindByGoPath(patch.Namespace)
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("could not find repo", patch.Namespace)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if patch.NewHash != "" {
|
|
||||||
log.Info("already applied patch", patch.Namespace, ": newhash:", patch.NewHash, "commithash:", patch.CommitHash, comment)
|
|
||||||
found.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
os.Chdir(repo.GetFullPath())
|
|
||||||
_, err := findCommitByHash(patch.StartHash, comment)
|
|
||||||
if err == nil {
|
|
||||||
log.Info("found applied patch", patch.Namespace, ": newhash:", patch.NewHash, "commithash:", patch.CommitHash, comment)
|
|
||||||
found.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
|
|
||||||
newhash, err = findCommitBySubject(comment)
|
|
||||||
if err == nil {
|
|
||||||
patch.NewHash = newhash
|
|
||||||
log.Info("patch: found hash:", patch.Namespace, "commit patch", patch.CommitHash, "new hash", newhash, "start hash", patch.StartHash, comment)
|
|
||||||
found.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func findApplied() *forgepb.Patches {
|
|
||||||
var pset *forgepb.Patches
|
|
||||||
for found := range me.forge.Patchsets.IterAll() {
|
|
||||||
if found.Name == "forge auto commit" {
|
|
||||||
pset = found.Patches
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if pset == nil {
|
|
||||||
log.Info("failed to find 'forge auto commit'")
|
|
||||||
return pset
|
|
||||||
}
|
|
||||||
|
|
||||||
found := forgepb.NewPatches()
|
|
||||||
|
|
||||||
for patch := range pset.IterAll() {
|
|
||||||
cmd := []string{"git", "merge-base", "--is-ancestor", patch.NewHash, "devel"}
|
|
||||||
repo := me.forge.Repos.FindByNamespace(patch.Namespace)
|
|
||||||
_, err := repo.RunStrict(cmd)
|
|
||||||
if err != nil {
|
|
||||||
// log.Info("NOT APPLIED", patch.Namespace, result, err)
|
|
||||||
// log.Info("NOT APPLIED newhash:", patch.NewHash, "commithash:", patch.CommitHash, "patch", patch.Namespace)
|
|
||||||
} else {
|
|
||||||
// log.Info("APPLIED newhash:", patch.NewHash, "commithash:", patch.CommitHash, "patch", patch.Namespace)
|
|
||||||
found.Append(patch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return found
|
|
||||||
}
|
|
254
main.go
254
main.go
|
@ -1,15 +1,11 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
// An app to submit patches for the 30 GO GUI repos
|
// An app to submit patches for the 30 GO GUI repos
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.wit.com/lib/gui/prep"
|
"go.wit.com/dev/alexflint/arg"
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
"go.wit.com/lib/protobuf/forgepb"
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
|
@ -19,212 +15,62 @@ import (
|
||||||
var VERSION string
|
var VERSION string
|
||||||
var BUILDTIME string
|
var BUILDTIME string
|
||||||
|
|
||||||
// this optionally can store the GUI plugins
|
|
||||||
//
|
|
||||||
//go:embed resources/*
|
|
||||||
var resources embed.FS
|
|
||||||
|
|
||||||
// used for shell auto completion
|
|
||||||
var ARGNAME string = "forge"
|
|
||||||
|
|
||||||
// using this for now. triggers config save
|
|
||||||
var configSave bool
|
|
||||||
|
|
||||||
func getVersion(repo *gitpb.Repo, name string) string {
|
|
||||||
cmd := []string{"git", "describe", "--tags", "--always", name}
|
|
||||||
result, _ := repo.RunQuiet(cmd)
|
|
||||||
output := strings.Join(result.Stdout, "\n")
|
|
||||||
log.Info("cmd =", cmd, output)
|
|
||||||
|
|
||||||
return strings.TrimSpace(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
me = new(mainType)
|
me = new(mainType)
|
||||||
me.myGui = prep.Gui() // prepares the GUI package for go-args
|
me.pp = arg.MustParse(&argv)
|
||||||
me.auto = prep.Bash3(&argv) // this line should be: prep.Bash(&argv)
|
|
||||||
|
|
||||||
// me.auto = prep.Bash3(argv.DoAutoComplete, &argv) // this line should be: prep.Bash(&argv)
|
// load the ~/.config/forge/ config
|
||||||
// arg.MustParse(&argv) // these three lines are becoming terrible syntax
|
me.forge = forgepb.Init()
|
||||||
// me.auto = prep.MustParse(&argv) // try to make this work?
|
me.found = new(gitpb.Repos)
|
||||||
|
|
||||||
me.forge = forgepb.Init() // init forge.pb
|
|
||||||
me.forge.ScanRepoDir() // looks for new dirs, checks existing repos for changes
|
|
||||||
|
|
||||||
// initialize patches
|
|
||||||
doPatchInit()
|
|
||||||
|
|
||||||
// first find the repos or gopaths to operate on
|
// first find the repos or gopaths to operate on
|
||||||
if argv.Config != nil {
|
if argv.Config {
|
||||||
doConfig()
|
if findConfig() {
|
||||||
okExit("")
|
me.forge.ConfigPrintTable()
|
||||||
}
|
os.Exit(0)
|
||||||
|
|
||||||
if argv.Commit != nil {
|
|
||||||
doCommit()
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.BuildForge {
|
|
||||||
buildForge()
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Checkout != nil {
|
|
||||||
if err := doCheckout(); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
}
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Build != "" {
|
|
||||||
if err := doBuild(); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Install != "" {
|
|
||||||
if err := doInstall(); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Clean != nil {
|
|
||||||
if err := doClean(); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
me.forge.ConfigSave()
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Help != nil {
|
|
||||||
doHelp()
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Dirty != nil {
|
|
||||||
doDirty()
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Tag != nil {
|
|
||||||
doTag()
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Normal != nil {
|
|
||||||
if argv.Normal.On != nil {
|
|
||||||
if me.forge.Config.Mode == forgepb.ForgeMode_NORMAL {
|
|
||||||
log.Info("you are already in the normal state")
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
setForgeMode(forgepb.ForgeMode_NORMAL)
|
|
||||||
log.Info("normal mode on")
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Normal.Off != nil {
|
|
||||||
if me.forge.Config.Mode != forgepb.ForgeMode_NORMAL {
|
|
||||||
log.Info("you were aleady not in the normal state")
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
setForgeMode(forgepb.ForgeMode_DEVEL)
|
|
||||||
log.Info("normal mode off")
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if doNormal() {
|
|
||||||
log.Infof("all %d repos are on your user branch. It is safe to write code now.\n", me.forge.Repos.Len())
|
|
||||||
if me.forge.Config.Mode != forgepb.ForgeMode_NORMAL {
|
|
||||||
log.Infof("Forge has set the mode to 'Normal'\n")
|
|
||||||
setForgeMode(forgepb.ForgeMode_NORMAL)
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// if you are in "normal" mode, always run normal every time to catch accidental errors
|
|
||||||
// for example, if you accidentally changed branches from your user branch
|
|
||||||
if me.forge.Config.Mode == forgepb.ForgeMode_NORMAL {
|
|
||||||
if doNormal() {
|
|
||||||
log.Infof("all your %d repos are in a normal stete for development\n", me.forge.Repos.Len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Merge != nil {
|
|
||||||
if err := doMerge(); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Pull != nil {
|
|
||||||
doPull()
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.List != nil {
|
|
||||||
found := argv.List.findRepos()
|
|
||||||
// print out the repos
|
|
||||||
if argv.List.Full {
|
|
||||||
me.forge.PrintHumanTableFull(found)
|
|
||||||
} else {
|
|
||||||
me.forge.PrintHumanTable(found)
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if argv.Patch != nil {
|
|
||||||
if err := doPatch(); err != nil {
|
|
||||||
badExit(err)
|
|
||||||
}
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the gui unless the user performed some other
|
|
||||||
// basically, if you run just 'forge' it should open the GUI
|
|
||||||
|
|
||||||
if argv.Gui != nil {
|
|
||||||
// if opening the GUI, always check git for dirty repos
|
|
||||||
me.forge.CheckDirty()
|
|
||||||
|
|
||||||
me.myGui.Start() // loads the GUI toolkit
|
|
||||||
doGui() // start making our forge GUI
|
|
||||||
debug() // sits here forever
|
|
||||||
}
|
|
||||||
// got to the end with nothing to do (?)
|
|
||||||
if dumpWorkRepos() {
|
|
||||||
// found some repos at least
|
|
||||||
} else {
|
} else {
|
||||||
// every repo is in a really clean state. no extra files anywhere
|
findRepos()
|
||||||
// no dirty repos, no repos that need to be published
|
|
||||||
// nothing different between user and master branch version. not common
|
|
||||||
log.Info("All of your git repositories appear to be in perfect shape")
|
|
||||||
}
|
}
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep this small
|
log.Info("found", me.found.Len(), "repos. found", len(me.foundPaths), "paths from .config/forge")
|
||||||
func doHelp() {
|
|
||||||
log.Info("")
|
|
||||||
log.Info("forge -h : to see the available options")
|
|
||||||
log.Info("forge --bash : will create a bash autocomplete file")
|
|
||||||
log.Info("forge : with no arguements, forge tries to load a GO GUI plugin")
|
|
||||||
log.Info(" : there are two GUI plugins. terminal & GTK")
|
|
||||||
log.Info("")
|
|
||||||
log.Info("forge list : shows a table of all your repos")
|
|
||||||
log.Info("forge checkout : checks out all your repos to the same branch")
|
|
||||||
log.Info(" : the default is your user branch")
|
|
||||||
log.Info("forge clean : reverts all repos to the master branch")
|
|
||||||
log.Info("forge dirty : show all repos git reports as dirty")
|
|
||||||
log.Info("")
|
|
||||||
okExit("")
|
|
||||||
}
|
|
||||||
|
|
||||||
func doHelpPatches() {
|
// now, do something to all of them (or just print out a table of them)
|
||||||
log.Info("TODO: ?")
|
var done bool = false
|
||||||
okExit("")
|
if argv.DoScan {
|
||||||
|
doScan()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if argv.DoRedoGoMod {
|
||||||
|
doRedoGoMod()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if argv.DoGitPull {
|
||||||
|
doGitPull()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if argv.DoList {
|
||||||
|
// print out the repos
|
||||||
|
doCobol()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if argv.Fix {
|
||||||
|
// print out the repos
|
||||||
|
doFix()
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the gui at the very end
|
||||||
|
if argv.DoGui {
|
||||||
|
doGui()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
if !done {
|
||||||
|
// if nothing was selected, print out a table of them on STDOUT
|
||||||
|
doCobol()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.wit.com/lib/gadgets"
|
||||||
|
"go.wit.com/lib/gui/repolist"
|
||||||
|
"go.wit.com/log"
|
||||||
|
|
||||||
|
"go.wit.com/gui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type repoWindow struct {
|
||||||
|
win *gadgets.BasicWindow
|
||||||
|
box *gui.Node
|
||||||
|
|
||||||
|
// the top box of the repolist window
|
||||||
|
topbox *gui.Node
|
||||||
|
|
||||||
|
View *repolist.RepoList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repoWindow) Hidden() bool {
|
||||||
|
return r.win.Hidden()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repoWindow) Show() {
|
||||||
|
r.win.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repoWindow) Hide() {
|
||||||
|
r.win.Hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repoWindow) Disable() {
|
||||||
|
r.box.Disable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repoWindow) Enable() {
|
||||||
|
r.box.Enable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// you can only have one of these
|
||||||
|
func makeRepoView() *repoWindow {
|
||||||
|
if me.repos != nil {
|
||||||
|
return me.repos
|
||||||
|
}
|
||||||
|
r := new(repoWindow)
|
||||||
|
r.win = gadgets.RawBasicWindow("All git repositories in ~/go/src/")
|
||||||
|
r.win.Make()
|
||||||
|
|
||||||
|
r.box = r.win.Box().NewBox("bw vbox", false)
|
||||||
|
// me.reposwin.Draw()
|
||||||
|
r.win.Custom = func() {
|
||||||
|
log.Warn("GOT HERE: main() gadgets.NewBasicWindow() close")
|
||||||
|
log.Warn("Should I do something special here?")
|
||||||
|
}
|
||||||
|
|
||||||
|
r.topbox = r.repoMenu()
|
||||||
|
|
||||||
|
r.View = repolist.InitBox(me.forge, r.box)
|
||||||
|
r.View.Enable()
|
||||||
|
|
||||||
|
r.View.ScanRepositories()
|
||||||
|
|
||||||
|
/*
|
||||||
|
r.View = repolist.AutotypistView(r.box)
|
||||||
|
|
||||||
|
showncount := r.View.MirrorShownCount()
|
||||||
|
r.topbox.Append(showncount)
|
||||||
|
duration := r.View.MirrorScanDuration()
|
||||||
|
r.topbox.Append(duration)
|
||||||
|
*/
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repoWindow) repoMenu() *gui.Node {
|
||||||
|
// reposbox.SetExpand(false)
|
||||||
|
group1 := r.box.NewGroup("Run on all repos:")
|
||||||
|
|
||||||
|
hbox := group1.Box()
|
||||||
|
// hbox.Horizontal()
|
||||||
|
hbox.Vertical()
|
||||||
|
|
||||||
|
box2 := hbox.Box().Vertical()
|
||||||
|
/*
|
||||||
|
box2.NewButton("merge all user to devel", func() {
|
||||||
|
r.Disable()
|
||||||
|
if !r.mergeAllUserToDevel() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Enable()
|
||||||
|
})
|
||||||
|
|
||||||
|
box2.NewButton("merge all devel to main", func() {
|
||||||
|
r.Disable()
|
||||||
|
if !r.mergeAllDevelToMain() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Enable()
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
box2.NewButton("merge it all", func() {
|
||||||
|
r.Disable()
|
||||||
|
if !r.mergeAllUserToDevel() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !r.mergeAllDevelToMain() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Enable()
|
||||||
|
})
|
||||||
|
|
||||||
|
box2.NewButton("show apps", func() {
|
||||||
|
loop := me.repos.View.ReposSortByName()
|
||||||
|
for loop.Scan() {
|
||||||
|
repo := loop.Repo()
|
||||||
|
rtype := repo.Status.RepoType()
|
||||||
|
switch rtype {
|
||||||
|
case "'binary'":
|
||||||
|
// log.Info(repo.Status.Path(), "compile here. Show()")
|
||||||
|
repo.Show()
|
||||||
|
case "'library'":
|
||||||
|
// log.Info(repo.Status.Path(), "library here. Hide()")
|
||||||
|
repo.Hide()
|
||||||
|
default:
|
||||||
|
log.Info(repo.Status.Path(), "unknown type", rtype)
|
||||||
|
// repo.Hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
box2.NewButton("scan now", func() {
|
||||||
|
log.Info("re-scanning now")
|
||||||
|
i, s := me.repos.View.ScanRepositories()
|
||||||
|
log.Info("re-scanning done", i, "repos in", s)
|
||||||
|
})
|
||||||
|
|
||||||
|
return box2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repoWindow) mergeAllDevelToMain() bool {
|
||||||
|
log.Info("merge all here")
|
||||||
|
loop := me.repos.View.ReposSortByName()
|
||||||
|
for loop.Scan() {
|
||||||
|
repo := loop.Repo()
|
||||||
|
if repo.ReadOnly() {
|
||||||
|
log.Info("skipping readonly", repo.Name(), repo.State())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if repo.State() != "merge to main" {
|
||||||
|
log.Info("skipping. not merge to main", repo.Name(), repo.State())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if repo.CheckDirty() {
|
||||||
|
log.Info("skipping dirty", repo.Name(), repo.State())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Info("repo:", repo.Name(), repo.State())
|
||||||
|
repo.NewScan()
|
||||||
|
if repo.Status.MergeDevelToMaster() {
|
||||||
|
log.Warn("THINGS SEEM OK fullAutomation() returned true.")
|
||||||
|
} else {
|
||||||
|
log.Warn("last repo:", repo.Name())
|
||||||
|
log.Warn("THINGS FAILED fullAutomation() returned false")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
repo.NewScan()
|
||||||
|
}
|
||||||
|
log.Warn("EVERYTHING WORKED")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repoWindow) mergeAllUserToDevel() bool {
|
||||||
|
log.Info("merge all here")
|
||||||
|
loop := me.repos.View.ReposSortByName()
|
||||||
|
for loop.Scan() {
|
||||||
|
repo := loop.Repo()
|
||||||
|
if repo.ReadOnly() {
|
||||||
|
log.Info("skipping readonly", repo.Name(), repo.State())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if repo.State() != "merge to devel" {
|
||||||
|
log.Info("skipping. not merge to devel", repo.Name(), repo.State())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if repo.CheckDirty() {
|
||||||
|
log.Info("skipping dirty", repo.Name(), repo.State())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Info("repo:", repo.Name(), repo.State())
|
||||||
|
repo.NewScan()
|
||||||
|
if repo.Status.MergeUserToDevel() {
|
||||||
|
log.Warn("THINGS SEEM OK fullAutomation() returned true.")
|
||||||
|
} else {
|
||||||
|
log.Warn("last repo:", repo.Status.Path())
|
||||||
|
log.Warn("THINGS FAILED fullAutomation() returned false")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
repo.NewScan()
|
||||||
|
}
|
||||||
|
log.Warn("EVERYTHING WORKED")
|
||||||
|
return true
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
# this file is intended to be used to customize settings on what
|
|
||||||
# git repos you have write access to. That is, where you can run 'git push'
|
|
||||||
#
|
|
||||||
# add entries to this using 'forge config'
|
|
||||||
|
|
||||||
ForgeConfigs: {
|
|
||||||
goPath: "go.wit.com"
|
|
||||||
writable: true
|
|
||||||
directory: true
|
|
||||||
}
|
|
||||||
repos: {
|
|
||||||
goPath: "go.wit.com/apps/zookeeper"
|
|
||||||
debName: "zookeeper-go"
|
|
||||||
}
|
|
||||||
xterm: "xterm"
|
|
||||||
xtermArgv: "-bg"
|
|
||||||
xtermArgv: "black"
|
|
||||||
xtermArgv: "-fg"
|
|
||||||
xtermArgv: "white"
|
|
||||||
xtermArgv: "-geometry"
|
|
||||||
xtermArgv: "140x32"
|
|
||||||
xtermArgv: "-e"
|
|
67
structs.go
67
structs.go
|
@ -1,13 +1,11 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go.wit.com/dev/alexflint/arg"
|
||||||
"go.wit.com/gui"
|
"go.wit.com/gui"
|
||||||
"go.wit.com/lib/gadgets"
|
"go.wit.com/lib/gadgets"
|
||||||
"go.wit.com/lib/gui/prep"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
"go.wit.com/lib/protobuf/forgepb"
|
||||||
|
"go.wit.com/lib/protobuf/gitpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
var me *mainType
|
var me *mainType
|
||||||
|
@ -20,32 +18,53 @@ func (b *mainType) Enable() {
|
||||||
b.mainbox.Enable()
|
b.mainbox.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the server to connect to
|
|
||||||
func myServer() string {
|
|
||||||
return me.forge.GetForgeURL()
|
|
||||||
}
|
|
||||||
|
|
||||||
// this app's variables
|
// this app's variables
|
||||||
type mainType struct {
|
type mainType struct {
|
||||||
// pp *arg.Parser // for parsing the command line args. Yay to alexflint!
|
pp *arg.Parser // for parsing the command line args. Yay to alexf lint!
|
||||||
auto *prep.Auto // more experiments for bash handling
|
|
||||||
forge *forgepb.Forge // for holding the forge protobuf files
|
forge *forgepb.Forge // for holding the forge protobuf files
|
||||||
myGui *prep.GuiPrep // for initializing the GUI toolkits
|
myGui *gui.Node // the gui toolkit handle
|
||||||
|
found *gitpb.Repos // stores the list of repos to process things on
|
||||||
foundPaths []string // stores gopaths to act on (when doing go-clone)
|
foundPaths []string // stores gopaths to act on (when doing go-clone)
|
||||||
configSave bool // if the config file should be saved after finishing
|
configSave bool // if the config file should be saved after finishing
|
||||||
urlbase string // base URL
|
|
||||||
|
// our view of the repositories
|
||||||
|
repos *repoWindow
|
||||||
|
|
||||||
mainWindow *gadgets.BasicWindow
|
mainWindow *gadgets.BasicWindow
|
||||||
mainbox *gui.Node // the main box. enable/disable this
|
|
||||||
autoDryRun *gui.Node // checkbox for --dry-run
|
|
||||||
goSrcPwd *gadgets.OneLiner // what is being used as primary directory for your work
|
|
||||||
goSrcEdit *gadgets.BasicEntry // what is being used as primary directory for your work
|
|
||||||
gitAuthor *gadgets.OneLiner // ENV GIT_AUTHOR NAME and EMAIL
|
|
||||||
|
|
||||||
// these hold the branches that the user can switch all the repositories to them
|
// the main box. enable/disable this
|
||||||
reposWinB *gui.Node // button that opens the repos window
|
mainbox *gui.Node
|
||||||
repoAllB *gui.Node // "all" repos button
|
|
||||||
repoDirtyB *gui.Node // "dirty" repos button
|
// the window from the /lib/gui/gowit package
|
||||||
repoDevelMergeB *gui.Node // "merge to devel" repos button
|
lw *gadgets.BasicWindow
|
||||||
repoWritableB *gui.Node // "what repos are writable" repos button
|
|
||||||
|
// #### Sorting options for the repolist
|
||||||
|
autoHidePerfect *gui.Node
|
||||||
|
autoHideReadOnly *gui.Node
|
||||||
|
|
||||||
|
// checkbox for --dry-run
|
||||||
|
autoDryRun *gui.Node
|
||||||
|
|
||||||
|
// checkbox to enable intermittent scanning
|
||||||
|
// if checked, it will check all your repos for changes
|
||||||
|
autoScanReposCB *gui.Node
|
||||||
|
|
||||||
|
// what is being used as your home dir
|
||||||
|
userHomePwd *gadgets.OneLiner
|
||||||
|
|
||||||
|
// what is being used as ~/go/src
|
||||||
|
goSrcPwd *gadgets.OneLiner
|
||||||
|
|
||||||
|
// displays a summary of all the repos
|
||||||
|
// has total dirty, total read-only
|
||||||
|
// total patches, etc
|
||||||
|
summary *patchSummary
|
||||||
|
|
||||||
|
// when switch to user or devel branches, autocreate them
|
||||||
|
autoCreateBranches *gui.Node
|
||||||
|
|
||||||
|
// these hold the branches that the user can switch all
|
||||||
|
// the repositories to them
|
||||||
|
newBranch *gui.Node
|
||||||
|
setBranchB *gui.Node
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"go.wit.com/gui"
|
||||||
|
"go.wit.com/lib/gadgets"
|
||||||
|
"go.wit.com/lib/gui/repolist"
|
||||||
|
"go.wit.com/lib/gui/shell"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
type patch struct {
|
||||||
|
ref string
|
||||||
|
giturl string
|
||||||
|
comment string
|
||||||
|
rs *repostatus.RepoStatus
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
type patchSummary struct {
|
||||||
|
grid *gui.Node
|
||||||
|
updateB *gui.Node
|
||||||
|
docsB *gui.Node
|
||||||
|
gitPushB *gui.Node
|
||||||
|
gitPullB *gui.Node
|
||||||
|
checkB *gui.Node
|
||||||
|
|
||||||
|
// stats
|
||||||
|
totalOL *gadgets.OneLiner
|
||||||
|
totalGoOL *gadgets.OneLiner
|
||||||
|
dirtyOL *gadgets.OneLiner
|
||||||
|
readonlyOL *gadgets.OneLiner
|
||||||
|
totalPatchesOL *gadgets.OneLiner
|
||||||
|
totalUserRepos *gui.Node
|
||||||
|
totalDevelRepos *gui.Node
|
||||||
|
totalMasterRepos *gui.Node
|
||||||
|
totalUserPatches *gui.Node
|
||||||
|
totalDevelPatches *gui.Node
|
||||||
|
totalMasterPatches *gui.Node
|
||||||
|
|
||||||
|
// patch set generation
|
||||||
|
unknownOL *gadgets.BasicEntry
|
||||||
|
unknownSubmitB *gui.Node
|
||||||
|
reason *gadgets.BasicEntry
|
||||||
|
submitB *gui.Node
|
||||||
|
allp []*repolist.Patch
|
||||||
|
}
|
||||||
|
|
||||||
|
func submitPatchesBox(box *gui.Node) *patchSummary {
|
||||||
|
s := new(patchSummary)
|
||||||
|
group1 := box.NewGroup("Submit Patches Summary")
|
||||||
|
s.grid = group1.RawGrid()
|
||||||
|
|
||||||
|
s.grid.NewButton("Update Patch Counts", func() {
|
||||||
|
var repocount, patchcount int
|
||||||
|
// broken after move to forge protobuf
|
||||||
|
loop := me.forge.Repos.SortByGoPath()
|
||||||
|
for loop.Scan() {
|
||||||
|
repo := loop.Next()
|
||||||
|
if repo.GetReadOnly() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i, _ := repo.GetMasterPatches()
|
||||||
|
patchcount += i
|
||||||
|
if i > 0 {
|
||||||
|
repocount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.totalMasterPatches.SetText(strconv.Itoa(patchcount) + " patches")
|
||||||
|
s.totalMasterRepos.SetText(strconv.Itoa(repocount) + " go repos")
|
||||||
|
|
||||||
|
repocount = 0
|
||||||
|
patchcount = 0
|
||||||
|
// broken after move to forge protobuf
|
||||||
|
loop = me.forge.Repos.SortByGoPath()
|
||||||
|
for loop.Scan() {
|
||||||
|
repo := loop.Next()
|
||||||
|
if repo.GetReadOnly() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i, _ := repo.GetUserPatches()
|
||||||
|
patchcount += i
|
||||||
|
if i > 0 {
|
||||||
|
repocount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.totalUserPatches.SetText(strconv.Itoa(patchcount) + " patches")
|
||||||
|
s.totalUserRepos.SetText(strconv.Itoa(repocount) + " go repos")
|
||||||
|
})
|
||||||
|
|
||||||
|
/* this used to be the way and should probably be revisited
|
||||||
|
s.grid.NewButton("Make Patches", func() {
|
||||||
|
for i, p := range s.allp {
|
||||||
|
log.Info(i, p.Ref, p.RS.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
s.totalOL = gadgets.NewOneLiner(s.grid, "Total")
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
s.totalGoOL = gadgets.NewOneLiner(s.grid, "Total GO")
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
s.dirtyOL = gadgets.NewOneLiner(s.grid, "dirty")
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
s.readonlyOL = gadgets.NewOneLiner(s.grid, "read-only")
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
// s.grid = group1.RawGrid()
|
||||||
|
s.grid.NewLabel("")
|
||||||
|
s.grid.NewLabel("")
|
||||||
|
s.grid.NewLabel("user to devel")
|
||||||
|
s.grid.NewLabel("devel to master")
|
||||||
|
s.grid.NewLabel("master to last tag")
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
s.grid.NewLabel("total modified")
|
||||||
|
s.grid.NewLabel("")
|
||||||
|
s.totalUserRepos = s.grid.NewLabel("x go repos")
|
||||||
|
s.totalDevelRepos = s.grid.NewLabel("")
|
||||||
|
s.totalMasterRepos = s.grid.NewLabel("x go repos")
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
s.totalPatchesOL = gadgets.NewOneLiner(s.grid, "total commits")
|
||||||
|
s.totalUserPatches = s.grid.NewLabel("x patches")
|
||||||
|
s.totalDevelPatches = s.grid.NewLabel("")
|
||||||
|
s.totalMasterPatches = s.grid.NewLabel("x patches")
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
s.grid.NewLabel("")
|
||||||
|
s.grid.NewLabel("")
|
||||||
|
s.grid.NewButton("merge from user", func() {
|
||||||
|
log.Info("this should make a patchset of your patches")
|
||||||
|
})
|
||||||
|
s.grid.NewButton("merge from devel", func() {
|
||||||
|
log.Info("this probably should not exist")
|
||||||
|
})
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
group1 = box.NewGroup("Create GUI Patch Set")
|
||||||
|
s.grid = group1.RawGrid()
|
||||||
|
s.reason = gadgets.NewBasicEntry(s.grid, "patch name:")
|
||||||
|
s.reason.Custom = func() {
|
||||||
|
if s.reason.String() != "" {
|
||||||
|
s.submitB.Enable()
|
||||||
|
} else {
|
||||||
|
s.submitB.Disable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.submitB = s.grid.NewButton("Create Patch Set", func() {
|
||||||
|
dirname := "submit-patchset.quilt"
|
||||||
|
patchdir := filepath.Join(me.userHomePwd.String(), dirname)
|
||||||
|
if shell.Exists(patchdir) {
|
||||||
|
log.Info("patchset dir already exists", patchdir)
|
||||||
|
shell.PathRun(me.userHomePwd.String(), []string{"rm", "-rf", dirname})
|
||||||
|
}
|
||||||
|
shell.Mkdir(patchdir)
|
||||||
|
if !shell.Exists(patchdir) {
|
||||||
|
log.Info("something went wrong making", patchdir)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
me.repos.View.MakePatchset(patchdir)
|
||||||
|
})
|
||||||
|
s.submitB = s.grid.NewButton("Submit quilt", func() {
|
||||||
|
log.Info("do a submit here")
|
||||||
|
})
|
||||||
|
// disable these until there are not dirty repos
|
||||||
|
s.reason.Disable()
|
||||||
|
s.submitB.Disable()
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
s.unknownOL.Disable()
|
||||||
|
s.unknownSubmitB.Disable()
|
||||||
|
s.grid.NextRow()
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// does not run any commands
|
||||||
|
func (s *patchSummary) Update() {
|
||||||
|
var total, totalgo, dirty, readonly int
|
||||||
|
var userT, develT, masterT int
|
||||||
|
// var userP, develP, masterP int
|
||||||
|
// broken after move to forge protobuf
|
||||||
|
loop := me.forge.Repos.SortByGoPath()
|
||||||
|
for loop.Scan() {
|
||||||
|
repo := loop.Next()
|
||||||
|
total += 1
|
||||||
|
if repo.IsDirty() {
|
||||||
|
dirty += 1
|
||||||
|
}
|
||||||
|
if repo.GetReadOnly() {
|
||||||
|
readonly += 1
|
||||||
|
// don't count these in any further stats
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// compute which GUI repos are out of sync with master
|
||||||
|
userV := repo.GetUserVersion()
|
||||||
|
develV := repo.GetDevelVersion()
|
||||||
|
masterV := repo.GetMasterVersion()
|
||||||
|
lastV := repo.GetLastTagVersion()
|
||||||
|
if userV != develV {
|
||||||
|
userT += 1
|
||||||
|
}
|
||||||
|
if develV != masterV {
|
||||||
|
log.Info("develV != masterV", develV, masterV, repo.GetGoPath())
|
||||||
|
develT += 1
|
||||||
|
}
|
||||||
|
if masterV != lastV {
|
||||||
|
masterT += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.totalOL.SetText(strconv.Itoa(total) + " repos")
|
||||||
|
s.totalGoOL.SetText(strconv.Itoa(totalgo) + " repos")
|
||||||
|
s.dirtyOL.SetText(strconv.Itoa(dirty) + " repos")
|
||||||
|
s.readonlyOL.SetText(strconv.Itoa(readonly) + " repos")
|
||||||
|
|
||||||
|
s.totalUserRepos.SetText(strconv.Itoa(userT) + " repos")
|
||||||
|
s.totalDevelRepos.SetText(strconv.Itoa(develT) + " repos")
|
||||||
|
s.totalMasterRepos.SetText(strconv.Itoa(masterT) + " repos")
|
||||||
|
|
||||||
|
if dirty == 0 {
|
||||||
|
s.reason.Enable()
|
||||||
|
s.submitB.Enable()
|
||||||
|
s.unknownOL.Enable()
|
||||||
|
s.unknownSubmitB.Enable()
|
||||||
|
} else {
|
||||||
|
s.reason.Disable()
|
||||||
|
s.submitB.Enable()
|
||||||
|
s.unknownOL.Enable()
|
||||||
|
s.unknownSubmitB.Enable()
|
||||||
|
}
|
||||||
|
}
|
118
windowHowto.go
118
windowHowto.go
|
@ -1,118 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// An app to submit patches for the 30 GO GUI repos
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"go.wit.com/lib/fhelp"
|
|
||||||
"go.wit.com/lib/gadgets"
|
|
||||||
"go.wit.com/lib/gui/shell"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func makeHowtoWin() *gadgets.GenericWindow {
|
|
||||||
howtoWin := gadgets.NewGenericWindow("Howto", "forge -- a GUI tool for git repostories")
|
|
||||||
tmp := `A good way to see how forge works is to download forge
|
|
||||||
|
|
||||||
This will 'git clone' a few things (~50 repos):
|
|
||||||
`
|
|
||||||
howtoWin.Group.NewLabel(tmp)
|
|
||||||
grid := howtoWin.Group.RawGrid()
|
|
||||||
grid.NewLabel("forge")
|
|
||||||
grid.NewLabel("the sources for forge")
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
grid.NewLabel("autogenpb")
|
|
||||||
grid.NewLabel("generates needed code for working with the protobuf files")
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
grid.NewLabel("go-clone")
|
|
||||||
grid.NewLabel("recursively 'git clone' dependencies based on go.mod")
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
grid.NewLabel("the GUI")
|
|
||||||
grid.NewLabel("GO plugins for libcurses and GTK")
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
grid.NewLabel("") // a stupid way to add padding
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
// howtoWin.Group.NewLabel("Working dir: " + me.forge.Config.ReposDir)
|
|
||||||
|
|
||||||
grid = howtoWin.Group.RawGrid()
|
|
||||||
grid.NewButton("Download into "+me.forge.Config.ReposDir, func() {
|
|
||||||
howtoWin.Disable()
|
|
||||||
defer howtoWin.Enable()
|
|
||||||
downloadForge()
|
|
||||||
})
|
|
||||||
grid.NewButton("Build forge & GUI GO plugins", func() {
|
|
||||||
howtoWin.Disable()
|
|
||||||
defer howtoWin.Enable()
|
|
||||||
buildForge()
|
|
||||||
})
|
|
||||||
return howtoWin
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadForge() {
|
|
||||||
log.Info("download here")
|
|
||||||
if path, err := fhelp.CheckCmd("go-clone"); err != nil {
|
|
||||||
log.Info("go-clone missing", path, err)
|
|
||||||
cmd := []string{"go", "install", "go.wit.com/apps/go-clone@latest"}
|
|
||||||
shell.RunRealtime(cmd)
|
|
||||||
}
|
|
||||||
if _, err := fhelp.CheckCmd("autogenpb"); err != nil {
|
|
||||||
cmd := []string{"go", "install", "go.wit.com/apps/autogenpb@latest"}
|
|
||||||
shell.RunRealtime(cmd)
|
|
||||||
}
|
|
||||||
if _, err := fhelp.CheckCmd("go-mod-clean"); err != nil {
|
|
||||||
cmd := []string{"go", "install", "go.wit.com/apps/go-mod-clean@latest"}
|
|
||||||
shell.RunRealtime(cmd)
|
|
||||||
}
|
|
||||||
if path, err := fhelp.CheckCmd("go-clone"); err != nil {
|
|
||||||
log.Info("can't prep build. you probably need ~/go/bin in your PATH", path, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var basecmd []string
|
|
||||||
var cmd []string
|
|
||||||
if me.forge.IsGoWork() {
|
|
||||||
log.Info("Using go.work directory")
|
|
||||||
basecmd = []string{"go-clone", "--work", "--recursive"}
|
|
||||||
} else {
|
|
||||||
basecmd = []string{"go-clone", "--recursive"}
|
|
||||||
}
|
|
||||||
// log.Info("Running:", cmd)
|
|
||||||
// shell.RunRealtime(cmd)
|
|
||||||
|
|
||||||
cmd = append(basecmd, "go.wit.com/apps/go-mod-clean")
|
|
||||||
log.Info("Running:", cmd)
|
|
||||||
shell.RunRealtime(cmd)
|
|
||||||
os.Exit(-1)
|
|
||||||
|
|
||||||
cmd = append(basecmd, "go.wit.com/apps/forge")
|
|
||||||
log.Info("Running:", cmd)
|
|
||||||
shell.RunRealtime(cmd)
|
|
||||||
|
|
||||||
cmd = append(basecmd, "go.wit.com/toolkits/gocui")
|
|
||||||
log.Info("Running:", cmd)
|
|
||||||
shell.RunRealtime(cmd)
|
|
||||||
|
|
||||||
cmd = append(basecmd, "go.wit.com/toolkits/andlabs")
|
|
||||||
log.Info("Running:", cmd)
|
|
||||||
shell.RunRealtime(cmd)
|
|
||||||
|
|
||||||
// should, after go-clone, be able to go build
|
|
||||||
/*
|
|
||||||
repo := me.forge.FindByGoPath("go.wit.com/lib/proto/forgepb")
|
|
||||||
if repo != nil {
|
|
||||||
repo.RunVerbose([]string{"autogenpb"})
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildForge() {
|
|
||||||
log.Info("buildForge() not done yet")
|
|
||||||
}
|
|
217
windowPatches.go
217
windowPatches.go
|
@ -1,217 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"go.wit.com/gui"
|
|
||||||
"go.wit.com/lib/gadgets"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stdPatchTableWin struct {
|
|
||||||
sync.Mutex
|
|
||||||
win *gadgets.GenericWindow // the machines gui window
|
|
||||||
box *gui.Node // the machines gui parent box widget
|
|
||||||
TB *forgepb.PatchesTable // the gui table buffer
|
|
||||||
update bool // if the window should be updated
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *stdPatchTableWin) Toggle() {
|
|
||||||
if w == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if w.win == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.win.Toggle()
|
|
||||||
}
|
|
||||||
|
|
||||||
func makePatchesWin(patches *forgepb.Patches) *stdPatchTableWin {
|
|
||||||
dwin := new(stdPatchTableWin)
|
|
||||||
dwin.win = gadgets.NewGenericWindow("current patches", "patching options")
|
|
||||||
dwin.win.Custom = func() {
|
|
||||||
log.Info("test delete window here")
|
|
||||||
dwin.win.Hide()
|
|
||||||
// dwin = nil
|
|
||||||
}
|
|
||||||
grid := dwin.win.Group.RawGrid()
|
|
||||||
|
|
||||||
grid.NewLabel(fmt.Sprintf("%d", patches.Len()))
|
|
||||||
grid.NewLabel(fmt.Sprintf("total patches"))
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
repomap := make(map[string]int)
|
|
||||||
all := patches.All()
|
|
||||||
for all.Scan() {
|
|
||||||
patch := all.Next()
|
|
||||||
repomap[patch.Namespace] += 1
|
|
||||||
}
|
|
||||||
grid.NewLabel(fmt.Sprintf("%d", len(repomap)))
|
|
||||||
grid.NewLabel(fmt.Sprintf("total repos"))
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
grid.NewButton("Apply All", func() {
|
|
||||||
var count int
|
|
||||||
all := patches.SortByFilename()
|
|
||||||
for all.Scan() {
|
|
||||||
p := all.Next()
|
|
||||||
applyPatchNew(p)
|
|
||||||
}
|
|
||||||
log.Info("ALL PATCHES WORKED! count =", count)
|
|
||||||
})
|
|
||||||
|
|
||||||
// make a box at the bottom of the window for the protobuf table
|
|
||||||
dwin.box = dwin.win.Bottom.Box().SetProgName("TBOX")
|
|
||||||
|
|
||||||
if patches != nil {
|
|
||||||
dwin.doPatchesTable(patches)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dwin
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyPatchNew(p *forgepb.Patch) error {
|
|
||||||
rn := p.Namespace
|
|
||||||
repo := me.forge.FindByGoPath(rn)
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("Could not figure out repo path", rn)
|
|
||||||
return log.Errorf("%s namespace?\n", rn)
|
|
||||||
}
|
|
||||||
if _, err := applyAndTrackPatch(repo, p); err != nil {
|
|
||||||
cmd := []string{"git", "am", "--abort"}
|
|
||||||
err := repo.RunVerbose(cmd)
|
|
||||||
log.Info("warn user of git am error", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dwin *stdPatchTableWin) doPatchesTable(currentPatches *forgepb.Patches) {
|
|
||||||
dwin.Lock()
|
|
||||||
defer dwin.Unlock()
|
|
||||||
if dwin.TB != nil {
|
|
||||||
dwin.TB.Delete()
|
|
||||||
dwin.TB = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// display the protobuf
|
|
||||||
dwin.TB = AddPatchesPB(dwin.box, currentPatches)
|
|
||||||
f := func(p *forgepb.Patch) {
|
|
||||||
log.Info("do something with patch", p.Filename)
|
|
||||||
}
|
|
||||||
dwin.TB.Custom(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// used by the PB table
|
|
||||||
func applyPatchLabel(p *forgepb.Patch) string {
|
|
||||||
rn := p.Namespace
|
|
||||||
if repo := me.forge.FindByGoPath(rn); repo == nil {
|
|
||||||
// log.Info("Could not figure out repo path", rn)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if p.NewHash == "" {
|
|
||||||
return "git am"
|
|
||||||
}
|
|
||||||
return "done"
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyPatchClick(p *forgepb.Patch) {
|
|
||||||
if err := applyPatchNew(p); err != nil {
|
|
||||||
log.Info("git am failed on file", p.Filename, "with error", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("ran: git am", p.Filename, "ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
// define what rows to have in the protobuf table
|
|
||||||
func AddPatchesPB(tbox *gui.Node, pb *forgepb.Patches) *forgepb.PatchesTable {
|
|
||||||
t := pb.NewTable("PatchesPB")
|
|
||||||
t.NewUuid()
|
|
||||||
t.SetParent(tbox)
|
|
||||||
|
|
||||||
gitam := t.AddButtonFunc("apply", applyPatchLabel)
|
|
||||||
gitam.Custom = applyPatchClick
|
|
||||||
|
|
||||||
t.AddCommitHash()
|
|
||||||
t.AddNamespace()
|
|
||||||
// t.AddFilename()
|
|
||||||
t.AddStringFunc("file", func(p *forgepb.Patch) string {
|
|
||||||
_, fname := filepath.Split(p.Filename)
|
|
||||||
return fname
|
|
||||||
})
|
|
||||||
t.AddCommitHash()
|
|
||||||
|
|
||||||
t.ShowTable()
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyPatch(repo *gitpb.Repo, filename string) error {
|
|
||||||
cmd := []string{"git", "am", filename}
|
|
||||||
err := repo.RunVerbose(cmd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func savePatch(p *forgepb.Patch) (string, error) {
|
|
||||||
_, filen := filepath.Split(p.Filename)
|
|
||||||
tmpname := filepath.Join("/tmp", filen)
|
|
||||||
log.Info("saving as", tmpname, p.Filename)
|
|
||||||
raw, err := os.OpenFile(tmpname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
raw.Write(p.Data)
|
|
||||||
raw.Close()
|
|
||||||
|
|
||||||
return tmpname, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyAndTrackPatch(repo *gitpb.Repo, p *forgepb.Patch) (string, error) {
|
|
||||||
_, filen := filepath.Split(p.Filename)
|
|
||||||
tmpname := filepath.Join("/tmp", filen)
|
|
||||||
log.Info("saving as", tmpname, p.Filename)
|
|
||||||
raw, err := os.OpenFile(tmpname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
raw.Write(p.Data)
|
|
||||||
raw.Close()
|
|
||||||
|
|
||||||
cmd := []string{"git", "am", tmpname}
|
|
||||||
err = repo.RunVerbose(cmd)
|
|
||||||
if err != nil {
|
|
||||||
log.Info("git am failed. run 'git am --abort' here")
|
|
||||||
return "", log.Errorf("git am failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Try to find hash value now")
|
|
||||||
|
|
||||||
p.NewHash = "fixme applyAndTrack"
|
|
||||||
if setNewHash(p, p.NewHash) {
|
|
||||||
log.Info("setting NewHash worked", p.NewHash)
|
|
||||||
}
|
|
||||||
me.forge.SavePatchsets()
|
|
||||||
|
|
||||||
return p.NewHash, log.Errorf("did not lookup new hash")
|
|
||||||
}
|
|
||||||
|
|
||||||
func setNewHash(p *forgepb.Patch, hash string) bool {
|
|
||||||
for pset := range me.forge.Patchsets.IterAll() {
|
|
||||||
for patch := range pset.Patches.IterAll() {
|
|
||||||
if patch.CommitHash == hash {
|
|
||||||
patch.NewHash = hash
|
|
||||||
log.Info("found patch in repo")
|
|
||||||
me.forge.SavePatchsets()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// An app to submit patches for the 30 GO GUI repos
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.wit.com/lib/gadgets"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Publish Window
|
|
||||||
func makePublishWindow() *gadgets.GenericWindow {
|
|
||||||
pubWin := gadgets.NewGenericWindow("publish code", "tasks for merging, versioning and publishing code")
|
|
||||||
|
|
||||||
grid := pubWin.Group.RawGrid()
|
|
||||||
|
|
||||||
grid.NewButton("merge all patches to master", func() {
|
|
||||||
/*
|
|
||||||
pubWin.Disable()
|
|
||||||
defer pubWin.Enable()
|
|
||||||
|
|
||||||
if err := doAllCheckoutDevel(); err != nil {
|
|
||||||
log.Info("checkout error:", err)
|
|
||||||
} else {
|
|
||||||
log.Info("checkout was ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
mergeUserToDevel(true)
|
|
||||||
|
|
||||||
if err := doAllCheckoutMaster(); err != nil {
|
|
||||||
log.Info("checkout error:", err)
|
|
||||||
} else {
|
|
||||||
log.Info("checkout was ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
mergeDevelToMaster(true)
|
|
||||||
*/
|
|
||||||
})
|
|
||||||
|
|
||||||
return pubWin
|
|
||||||
}
|
|
|
@ -1,393 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// An app to submit patches for the 30 GO GUI repos
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/lib/debugger"
|
|
||||||
"go.wit.com/lib/gadgets"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func makeReposWin() *stdReposTableWin {
|
|
||||||
rwin := new(stdReposTableWin)
|
|
||||||
win := gadgets.NewGenericWindow("find errors and try to fix them", "types of errors of some sort or another")
|
|
||||||
rwin.win = win
|
|
||||||
grid := win.Group.RawGrid()
|
|
||||||
|
|
||||||
// win.Top.NewGroup("misc (works in progress)")
|
|
||||||
|
|
||||||
// grid = win.Top.RawGrid()
|
|
||||||
grid = win.Group.RawGrid()
|
|
||||||
var found *gitpb.Repos
|
|
||||||
var txt string
|
|
||||||
|
|
||||||
found = develBehindMasterProblem()
|
|
||||||
txt = fmt.Sprintf("devel is behind master (%d)", found.Len())
|
|
||||||
grid.NewButton(txt, func() {
|
|
||||||
win := gadgets.RawBasicWindow("devel branches that are out of sync with master")
|
|
||||||
win.Make()
|
|
||||||
win.Show()
|
|
||||||
win.Custom = func() {
|
|
||||||
// sets the hidden flag to false so Toggle() works
|
|
||||||
win.Hide()
|
|
||||||
}
|
|
||||||
box := win.Box().NewBox("bw vbox", false)
|
|
||||||
|
|
||||||
found := develBehindMasterProblem()
|
|
||||||
group := box.NewGroup("test buttons")
|
|
||||||
hbox := group.Box().Horizontal()
|
|
||||||
hbox.NewButton("git merge master devel", func() {
|
|
||||||
all := found.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
mname := repo.GetMasterBranchName()
|
|
||||||
dname := repo.GetDevelBranchName()
|
|
||||||
if dname != repo.GetCurrentBranchName() {
|
|
||||||
log.Info("Repo is not on the devel branch", repo.GetGoPath())
|
|
||||||
}
|
|
||||||
cmd := []string{"git", "merge", mname}
|
|
||||||
log.Info(repo.GetGoPath(), cmd)
|
|
||||||
repo.RunVerbose(cmd)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
hbox.NewButton("test", func() {
|
|
||||||
})
|
|
||||||
|
|
||||||
t := makeDevelBehindMaster(found)
|
|
||||||
t.SetParent(box)
|
|
||||||
t.ShowTable()
|
|
||||||
})
|
|
||||||
|
|
||||||
found = remoteUserBranchProblem()
|
|
||||||
txt = fmt.Sprintf("user branch is remote (%d)", found.Len())
|
|
||||||
grid.NewButton(txt, func() {
|
|
||||||
win := gadgets.RawBasicWindow("repos that seem to have remote user branches")
|
|
||||||
win.Make()
|
|
||||||
win.Show()
|
|
||||||
win.Custom = func() {
|
|
||||||
// sets the hidden flag to false so Toggle() works
|
|
||||||
win.Hide()
|
|
||||||
}
|
|
||||||
box := win.Box().NewBox("bw vbox", false)
|
|
||||||
|
|
||||||
found := remoteUserBranchProblem()
|
|
||||||
group := box.NewGroup("test buttons")
|
|
||||||
hbox := group.Box().Horizontal()
|
|
||||||
hbox.NewButton("git branch delete", func() {
|
|
||||||
win.Disable()
|
|
||||||
defer win.Enable()
|
|
||||||
all := found.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
brname := repo.GetUserBranchName()
|
|
||||||
// git push origin --delete jcarr
|
|
||||||
os.Setenv("GIT_TERMINAL_PROMPT", "0")
|
|
||||||
cmd := []string{"git", "push", "origin", "--delete", brname}
|
|
||||||
log.Info("You may want to run:", repo.GetGoPath(), cmd)
|
|
||||||
repo.RunVerbose(cmd)
|
|
||||||
os.Unsetenv("GIT_TERMINAL_PROMPT")
|
|
||||||
|
|
||||||
// git branch --delete --remote origin/jcarr
|
|
||||||
cmd = []string{"git", "branch", "--delete", "--remote", "origin/" + brname}
|
|
||||||
log.Info(repo.GetGoPath(), cmd)
|
|
||||||
repo.RunVerbose(cmd)
|
|
||||||
repo.ReloadCheck()
|
|
||||||
}
|
|
||||||
me.forge.SetConfigSave(true)
|
|
||||||
me.forge.ConfigSave()
|
|
||||||
})
|
|
||||||
|
|
||||||
t := makeStandardReposGrid(found)
|
|
||||||
t.SetParent(box)
|
|
||||||
t.ShowTable()
|
|
||||||
})
|
|
||||||
|
|
||||||
rwin.boxTB = win.Bottom.Box()
|
|
||||||
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
found = develRemoteProblem()
|
|
||||||
txt = fmt.Sprintf("remote devel != local devel (%d)", found.Len())
|
|
||||||
grid.NewButton(txt, func() {
|
|
||||||
found := develRemoteProblem()
|
|
||||||
makeStandardReposWindow(txt, found)
|
|
||||||
})
|
|
||||||
|
|
||||||
found = masterRemoteProblem()
|
|
||||||
txt = fmt.Sprintf("remote master != local master (%d)", found.Len())
|
|
||||||
grid.NewButton(txt, func() {
|
|
||||||
found := masterRemoteProblem()
|
|
||||||
makeStandardReposWindow(txt, found)
|
|
||||||
})
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
makeHackModeWindow(rwin)
|
|
||||||
return rwin
|
|
||||||
}
|
|
||||||
|
|
||||||
// table of devel errors behind master
|
|
||||||
func makeDevelBehindMaster(pb *gitpb.Repos) *gitpb.ReposTable {
|
|
||||||
t := pb.NewTable("testDirty")
|
|
||||||
t.NewUuid()
|
|
||||||
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
|
|
||||||
return r.GetGoPath()
|
|
||||||
})
|
|
||||||
sf.Custom = func(r *gitpb.Repo) {
|
|
||||||
log.Info("merge master into devel here", r.GetGoPath())
|
|
||||||
}
|
|
||||||
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
|
|
||||||
return repo.NewestTime()
|
|
||||||
})
|
|
||||||
t.AddMasterVersion()
|
|
||||||
t.AddDevelVersion()
|
|
||||||
t.AddState()
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// default window for active running droplets
|
|
||||||
func (rwin *stdReposTableWin) doReposTable(pb *gitpb.Repos) {
|
|
||||||
rwin.Lock()
|
|
||||||
defer rwin.Unlock()
|
|
||||||
if rwin.TB != nil {
|
|
||||||
rwin.TB.Delete()
|
|
||||||
rwin.TB = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rwin.pb = pb
|
|
||||||
|
|
||||||
t := makeStandardReposGrid(pb)
|
|
||||||
t.SetParent(rwin.boxTB)
|
|
||||||
t.ShowTable()
|
|
||||||
rwin.TB = t
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeHackModeWindow(stdwin *stdReposTableWin) {
|
|
||||||
group := stdwin.win.Top.NewGroup("This is a work in progress")
|
|
||||||
grid := group.RawGrid()
|
|
||||||
grid.NewButton("git pull", func() {
|
|
||||||
log.Info("todo: run git pull on each repo")
|
|
||||||
})
|
|
||||||
|
|
||||||
me.repoDevelMergeB = grid.NewButton("merge", func() {
|
|
||||||
found := findMergeToDevel()
|
|
||||||
_, box := makeStandardReposWindow("repos to merge from user to devel", found)
|
|
||||||
hbox := box.Box().Horizontal()
|
|
||||||
hbox.NewButton("merge all", func() {
|
|
||||||
stdwin.win.Disable()
|
|
||||||
defer stdwin.win.Enable()
|
|
||||||
all := found.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
if repo.CheckDirty() {
|
|
||||||
log.Info("repo is dirty", repo.GetGoPath())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Info("Starting merge on", repo.GetGoPath())
|
|
||||||
if repo.CheckoutDevel() {
|
|
||||||
log.Info("checkout devel failed", repo.GetGoPath())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := repo.MergeToDevel(); err != nil {
|
|
||||||
log.Info("merge from user failed", repo.GetGoPath(), err)
|
|
||||||
// log.Info(strings.Join(r.Stdout, "\n"))
|
|
||||||
// log.Info(strings.Join(r.Stderr, "\n"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if repo.CheckoutMaster() {
|
|
||||||
log.Info("checkout master failed", repo.GetGoPath())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := repo.MergeToMaster(); err != nil {
|
|
||||||
log.Info("merge from devel failed", repo.GetGoPath(), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
grid.NextRow()
|
|
||||||
|
|
||||||
group2 := stdwin.win.Top.NewGroup("Merge")
|
|
||||||
grid = group2.RawGrid()
|
|
||||||
|
|
||||||
/*
|
|
||||||
grid.NewButton("merge to devel", func() {
|
|
||||||
stdwin.win.Disable()
|
|
||||||
defer stdwin.win.Enable()
|
|
||||||
|
|
||||||
mergeUserToDevel(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
grid.NewButton("merge to master", func() {
|
|
||||||
stdwin.win.Disable()
|
|
||||||
defer stdwin.win.Enable()
|
|
||||||
|
|
||||||
mergeDevelToMaster(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
grid.NewButton("merge all", func() {
|
|
||||||
stdwin.win.Disable()
|
|
||||||
defer stdwin.win.Enable()
|
|
||||||
|
|
||||||
if err := doAllCheckoutDevel(); err != nil {
|
|
||||||
log.Info("checkout error:", err)
|
|
||||||
} else {
|
|
||||||
log.Info("checkout was ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
mergeUserToDevel(true)
|
|
||||||
|
|
||||||
if err := doAllCheckoutMaster(); err != nil {
|
|
||||||
log.Info("checkout error:", err)
|
|
||||||
} else {
|
|
||||||
log.Info("checkout was ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
mergeDevelToMaster(true)
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
grid.NewButton("show dirty repos on win.Bottom", func() {
|
|
||||||
log.Info("try to show dirty repos on bottom")
|
|
||||||
found := me.forge.FindDirty()
|
|
||||||
stdwin.doReposTable(found)
|
|
||||||
})
|
|
||||||
|
|
||||||
group3 := stdwin.win.Top.NewGroup("work in progress")
|
|
||||||
grid = group3.RawGrid()
|
|
||||||
|
|
||||||
grid.NewButton("forge ConfigSave()", func() {
|
|
||||||
me.forge.ConfigSave()
|
|
||||||
})
|
|
||||||
|
|
||||||
grid.NewButton("debugger()", func() {
|
|
||||||
debugger.DebugWindow()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func develBehindMasterProblem() *gitpb.Repos {
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
if repo.GetDevelVersion() == repo.GetMasterVersion() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func remoteUserBranchProblem() *gitpb.Repos {
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
username := repo.GetUserBranchName()
|
|
||||||
if repo.IsBranchRemote(username) {
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func develRemoteProblem() *gitpb.Repos {
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
brname := repo.GetDevelBranchName()
|
|
||||||
if !repo.IsBranchRemote(brname) {
|
|
||||||
// log.Info("repo does not have remote devel branch", repo.GetGoPath())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lhash := repo.GetLocalHash(brname)
|
|
||||||
rhash := repo.GetRemoteHash(brname)
|
|
||||||
// log.Info(lhash, rhash, repo.GetGoPath())
|
|
||||||
if lhash == "" || rhash == "" {
|
|
||||||
// something is wrong if either of these are blank
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if lhash == rhash {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func masterRemoteProblem() *gitpb.Repos {
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
brname := repo.GetMasterBranchName()
|
|
||||||
if !repo.IsBranchRemote(brname) {
|
|
||||||
// log.Info("repo does not have remote devel branch", repo.GetGoPath())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lhash := repo.GetLocalHash(brname)
|
|
||||||
rhash := repo.GetRemoteHash(brname)
|
|
||||||
// log.Info(lhash, rhash, repo.GetGoPath())
|
|
||||||
if lhash == "" || rhash == "" {
|
|
||||||
// something is wrong if either of these are blank
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if lhash == rhash {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeWritableWindow(pb *gitpb.Repos) (*gadgets.GenericWindow, *gitpb.ReposTable) {
|
|
||||||
win := gadgets.NewGenericWindow("Repos You have write access to", "Configure")
|
|
||||||
t := pb.NewTable("testForgeRepos")
|
|
||||||
t.NewUuid()
|
|
||||||
|
|
||||||
grid := win.Group.RawGrid()
|
|
||||||
grid.NewButton("git pull", func() {
|
|
||||||
log.Info("todo: run git pull on each repo")
|
|
||||||
})
|
|
||||||
|
|
||||||
/*
|
|
||||||
grid.NewButton("do repos.ReScan()", func() {
|
|
||||||
t.Update()
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
tbox := win.Bottom.Box()
|
|
||||||
t.SetParent(tbox)
|
|
||||||
|
|
||||||
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
|
|
||||||
return r.GetGoPath()
|
|
||||||
})
|
|
||||||
sf.Custom = func(r *gitpb.Repo) {
|
|
||||||
log.Info("do button click on", r.GetGoPath())
|
|
||||||
}
|
|
||||||
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
|
|
||||||
return repo.NewestTime()
|
|
||||||
})
|
|
||||||
t.AddMasterVersion()
|
|
||||||
t.AddDevelVersion()
|
|
||||||
t.AddUserVersion()
|
|
||||||
t.AddCurrentBranchName()
|
|
||||||
t.AddState()
|
|
||||||
t.ShowTable()
|
|
||||||
return win, t
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// An app to submit patches for the 30 GO GUI repos
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.wit.com/gui"
|
|
||||||
"go.wit.com/lib/gadgets"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stdReposTableWin struct {
|
|
||||||
sync.Mutex
|
|
||||||
win *gadgets.GenericWindow // the machines gui window
|
|
||||||
boxTB *gui.Node // the machines gui parent box widget
|
|
||||||
TB *gitpb.ReposTable // the gui table buffer
|
|
||||||
pb *gitpb.Repos // the current repos protobuf
|
|
||||||
update bool // if the window should be updated
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *stdReposTableWin) Toggle() {
|
|
||||||
if w == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if w.win == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.win.Toggle()
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeWindowForPB() *gadgets.GenericWindow {
|
|
||||||
win := gadgets.NewGenericWindow("Forge Repos Protobuf View", "Display Git Repositories")
|
|
||||||
|
|
||||||
return win
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeReposWinNew() *gadgets.GenericWindow {
|
|
||||||
insertWin := makeWindowForPB()
|
|
||||||
insertWin.Win.Custom = func() {
|
|
||||||
log.Info("test delete window here")
|
|
||||||
}
|
|
||||||
grid := insertWin.Group.RawGrid()
|
|
||||||
|
|
||||||
var t *gitpb.ReposTable
|
|
||||||
grid.NewButton("dirty", func() {
|
|
||||||
if t != nil {
|
|
||||||
t.Delete()
|
|
||||||
t = nil
|
|
||||||
}
|
|
||||||
found := me.forge.FindDirty()
|
|
||||||
|
|
||||||
// display the protobuf
|
|
||||||
t = addWindowPB(insertWin, found)
|
|
||||||
f := func(repo *gitpb.Repo) {
|
|
||||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
|
||||||
}
|
|
||||||
t.Custom(f)
|
|
||||||
log.Info("table has uuid", t.GetUuid())
|
|
||||||
})
|
|
||||||
|
|
||||||
grid.NewButton("to publish", func() {
|
|
||||||
if t != nil {
|
|
||||||
t.Delete()
|
|
||||||
t = nil
|
|
||||||
}
|
|
||||||
found := findReposWithPatches()
|
|
||||||
me.forge.PrintHumanTable(found)
|
|
||||||
|
|
||||||
// make the window for the first time
|
|
||||||
t = addWindowPB(insertWin, found)
|
|
||||||
f := func(repo *gitpb.Repo) {
|
|
||||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
|
||||||
}
|
|
||||||
t.Custom(f)
|
|
||||||
log.Info("table has uuid", t.GetUuid())
|
|
||||||
})
|
|
||||||
|
|
||||||
grid.NewButton("favorites", func() {
|
|
||||||
if t != nil {
|
|
||||||
t.Delete()
|
|
||||||
t = nil
|
|
||||||
}
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
if !me.forge.Config.IsFavorite(repo.GetGoPath()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make the window for the first time
|
|
||||||
t = addWindowPB(insertWin, found)
|
|
||||||
f := func(repo *gitpb.Repo) {
|
|
||||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
|
||||||
}
|
|
||||||
t.Custom(f)
|
|
||||||
log.Info("table has uuid", t.GetUuid())
|
|
||||||
})
|
|
||||||
|
|
||||||
grid.NewButton("writeable", func() {
|
|
||||||
if t != nil {
|
|
||||||
t.Delete()
|
|
||||||
t = nil
|
|
||||||
}
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make the window for the first time
|
|
||||||
t = addWindowPB(insertWin, found)
|
|
||||||
f := func(repo *gitpb.Repo) {
|
|
||||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
|
||||||
}
|
|
||||||
t.Custom(f)
|
|
||||||
log.Info("table has uuid", t.GetUuid())
|
|
||||||
})
|
|
||||||
|
|
||||||
grid.NewButton("all", func() {
|
|
||||||
if t != nil {
|
|
||||||
t.Delete()
|
|
||||||
t = nil
|
|
||||||
}
|
|
||||||
found := new(gitpb.Repos)
|
|
||||||
all := me.forge.Repos.SortByFullPath()
|
|
||||||
for all.Scan() {
|
|
||||||
repo := all.Next()
|
|
||||||
found.AppendByFullPath(repo)
|
|
||||||
|
|
||||||
}
|
|
||||||
// display the protobuf
|
|
||||||
t = addWindowPB(insertWin, found)
|
|
||||||
f := func(repo *gitpb.Repo) {
|
|
||||||
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
|
|
||||||
}
|
|
||||||
t.Custom(f)
|
|
||||||
log.Info("table has uuid", t.GetUuid())
|
|
||||||
})
|
|
||||||
|
|
||||||
return insertWin
|
|
||||||
}
|
|
||||||
|
|
||||||
func addWindowPB(win *gadgets.GenericWindow, pb *gitpb.Repos) *gitpb.ReposTable {
|
|
||||||
t := pb.NewTable("testForgeRepos")
|
|
||||||
t.NewUuid()
|
|
||||||
tbox := win.Bottom.Box().SetProgName("TBOX")
|
|
||||||
t.SetParent(tbox)
|
|
||||||
|
|
||||||
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
|
|
||||||
return r.GetGoPath()
|
|
||||||
})
|
|
||||||
sf.Custom = func(r *gitpb.Repo) {
|
|
||||||
log.Info("do button click on", r.GetGoPath())
|
|
||||||
}
|
|
||||||
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
|
|
||||||
return repo.NewestTime()
|
|
||||||
})
|
|
||||||
t.AddMasterVersion()
|
|
||||||
// hmm := t.AddMasterVersion()
|
|
||||||
// hmm.SetTitle("Master")
|
|
||||||
t.AddDevelVersion()
|
|
||||||
t.AddUserVersion()
|
|
||||||
t.AddCurrentBranchName()
|
|
||||||
t.AddState()
|
|
||||||
f := func(repo *gitpb.Repo) string {
|
|
||||||
log.Info("repo =", repo.GetGoPath(), repo.GetCurrentVersion())
|
|
||||||
return repo.GetGoPath()
|
|
||||||
}
|
|
||||||
t.AddButtonFunc("cur version", f)
|
|
||||||
t.ShowTable()
|
|
||||||
return t
|
|
||||||
}
|
|
|
@ -1,147 +0,0 @@
|
||||||
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by the GPL 3.0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"go.wit.com/lib/gadgets"
|
|
||||||
"go.wit.com/lib/protobuf/forgepb"
|
|
||||||
"go.wit.com/lib/protobuf/gitpb"
|
|
||||||
"go.wit.com/log"
|
|
||||||
|
|
||||||
"go.wit.com/gui"
|
|
||||||
)
|
|
||||||
|
|
||||||
type repoPatchWindow struct {
|
|
||||||
once sync.Once // only init() the window once
|
|
||||||
win *gadgets.BasicWindow // the patches window
|
|
||||||
stack *gui.Node // the top box set as vertical
|
|
||||||
shelf *gui.Node // the first box in the stack, set as horizontal
|
|
||||||
grid *gui.Node // the list of available patches
|
|
||||||
// summary *patchSummary // summary of current patches
|
|
||||||
setgrid *gui.Node // the list of each patchset
|
|
||||||
pset *forgepb.Set // the patchset in question
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: autogenerate these or make them standared 'gui' package functions
|
|
||||||
// make this an go interface somehow
|
|
||||||
|
|
||||||
// is the window hidden right now?
|
|
||||||
func (w *repoPatchWindow) Hidden() bool {
|
|
||||||
return w.win.Hidden()
|
|
||||||
}
|
|
||||||
|
|
||||||
// switches between the window being visable or hidden on the desktop
|
|
||||||
func (w *repoPatchWindow) Toggle() {
|
|
||||||
if w.Hidden() {
|
|
||||||
w.Show()
|
|
||||||
} else {
|
|
||||||
w.Hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hides the window completely
|
|
||||||
func (w *repoPatchWindow) Show() {
|
|
||||||
w.win.Show()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *repoPatchWindow) Hide() {
|
|
||||||
w.win.Hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
// should be the first box/widget in the window
|
|
||||||
// greys out the window to the user
|
|
||||||
func (w *repoPatchWindow) Disable() {
|
|
||||||
w.stack.Disable()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *repoPatchWindow) Enable() {
|
|
||||||
w.stack.Enable()
|
|
||||||
}
|
|
||||||
|
|
||||||
// you can only have one of these
|
|
||||||
func makeRepoPatchWindow(repo *gitpb.Repo, fset []*forgepb.Patch) *repoPatchWindow {
|
|
||||||
pw := new(repoPatchWindow)
|
|
||||||
|
|
||||||
// sync.Once()
|
|
||||||
pw.win = gadgets.RawBasicWindow("Patcheset for " + repo.GetGoPath())
|
|
||||||
pw.win.Make()
|
|
||||||
|
|
||||||
pw.stack = pw.win.Box().NewBox("bw vbox", false)
|
|
||||||
// me.reposwin.Draw()
|
|
||||||
pw.win.Custom = func() {
|
|
||||||
// sets the hidden flag to false so Toggle() works
|
|
||||||
pw.win.Hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
grid := pw.stack.NewGrid("", 0, 0)
|
|
||||||
|
|
||||||
grid.NewLabel(repo.GetGoPath())
|
|
||||||
grid.NewLabel(repo.GetCurrentBranchName())
|
|
||||||
|
|
||||||
g := pw.stack.NewGroup("PatchSet List")
|
|
||||||
|
|
||||||
// make a grid and a header
|
|
||||||
filegrid := g.NewGrid("", 0, 0)
|
|
||||||
filegrid.NewLabel("patch name")
|
|
||||||
filegrid.NewLabel("Applied in current branch?")
|
|
||||||
filegrid.NewLabel("original hash")
|
|
||||||
filegrid.NewLabel("new hash")
|
|
||||||
filegrid.NewButton("git squash", func() {
|
|
||||||
})
|
|
||||||
filegrid.NewButton("git am", func() {
|
|
||||||
})
|
|
||||||
filegrid.NextRow()
|
|
||||||
|
|
||||||
for _, p := range fset {
|
|
||||||
filegrid.NewLabel(p.Comment)
|
|
||||||
filegrid.NewLabel("yes?")
|
|
||||||
filegrid.NewLabel(p.CommitHash)
|
|
||||||
filegrid.NewLabel(p.NewHash)
|
|
||||||
filegrid.NewCheckbox("").SetChecked(true)
|
|
||||||
filegrid.NewCheckbox("").SetChecked(true)
|
|
||||||
filegrid.NextRow()
|
|
||||||
}
|
|
||||||
// add the patches to the grid
|
|
||||||
// pw.addPatchset(filegrid, pset)
|
|
||||||
return pw
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *repoPatchWindow) addPatchset(grid *gui.Node, pset *forgepb.Set) {
|
|
||||||
repomap := make(map[*gitpb.Repo][]*forgepb.Patch)
|
|
||||||
repohash := make(map[*gitpb.Repo]string)
|
|
||||||
|
|
||||||
// sort patches by repo namespace
|
|
||||||
all := pset.Patches.SortByFilename()
|
|
||||||
for all.Scan() {
|
|
||||||
p := all.Next()
|
|
||||||
s := p.Namespace
|
|
||||||
repo := me.forge.FindByGoPath(s)
|
|
||||||
if repo == nil {
|
|
||||||
log.Info("COULD NOT FIND", s)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
repomap[repo] = append(repomap[repo], p)
|
|
||||||
repohash[repo] = p.StartHash
|
|
||||||
}
|
|
||||||
|
|
||||||
for repo, patches := range repomap {
|
|
||||||
log.Info(repo.GetGoPath())
|
|
||||||
grid.NewLabel(repo.GetGoPath())
|
|
||||||
|
|
||||||
for i, p := range patches {
|
|
||||||
log.Info(i, p.Filename)
|
|
||||||
grid.NewLabel(p.Comment)
|
|
||||||
grid.NewLabel("in current branch?")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
hash := repohash[repo]
|
|
||||||
grid.NewLabel(hash)
|
|
||||||
grid.NewButton("View", func() {
|
|
||||||
log.Info("todo: show patches for repo", repo.GetGoPath())
|
|
||||||
})
|
|
||||||
grid.NextRow()
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue