// 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 ( "embed" "fmt" "os" "strings" "time" "go.wit.com/dev/alexflint/arg" "go.wit.com/gui" "go.wit.com/lib/fhelp" "go.wit.com/lib/gui/shell" "go.wit.com/lib/protobuf/forgepb" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" ) // sent via -ldflags var VERSION 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() { me = new(mainType) gui.InitArg() me.pp = arg.MustParse(&argv) if argv.Bash { fhelp.DoBash(ARGNAME) os.Exit(0) } if len(argv.BashAuto) != 0 { argv.doBashAuto() os.Exit(0) } me.urlbase = argv.URL if me.urlbase == "" { me.urlbase = "https://go.wit.com/" } if os.Getenv("FORGE_URL") != "" { me.urlbase = os.Getenv("FORGE_URL") log.Info("got forge url", me.urlbase) } me.urlbase = strings.Trim(me.urlbase, "/") // track down why trailing '/' makes http POST not work // internally debugging can be triggered here before Init() if argv.Debug != nil { doDebug() okExit("") } if forgepb.FirstTimeUser() { log.Info("You are running forge for the first time here") } // load the ~/.config/forge/ config me.forge = forgepb.Init() // initialize patches doPatchInit() // first find the repos or gopaths to operate on if argv.Config != nil { doConfig() okExit("") } if argv.Commit != nil { doCommit() okExit("") } if argv.BuildForge { buildForge() okExit("") } if argv.Checkout != nil { me.forge.Config.Mode = forgepb.ForgeMode_MASTER 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 { me.forge.Config.Mode = forgepb.ForgeMode_CLEAN if argv.Clean.Repo != "" { log.Info("only looking at repo:", argv.Clean.Repo) okExit("") } if argv.Clean.GitReset != nil { doGitReset() okExit("reset") } 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.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("") } me.forge.Config.Mode = forgepb.ForgeMode_NORMAL me.forge.Config.ConfigSave() 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("") } me.forge.Config.Mode = forgepb.ForgeMode_MASTER me.forge.Config.ConfigSave() 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") me.forge.Config.Mode = forgepb.ForgeMode_NORMAL me.forge.ConfigSave() } 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 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("") } badExit(fmt.Errorf("You must choose which branch to merge to (devel or master)")) } 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("") } // if the user doesn't want to open the GUI and // nothing else was specified to be done, // then just list the table to stdout if gui.NoGui() { found := doFind() me.forge.PrintHumanTable(found) 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() doGui() } // got to the end with nothing to do (?) if dumpWorkRepos() { // found some repos at least } else { // every repo is in a really clean state. no extra files anywhere // 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 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() { log.Info("TODO: ?") okExit("") }