forge/main.go

287 lines
6.2 KiB
Go

// 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/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 {
argv.doBash()
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.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 %d repos are still normal\n", me.forge.Repos.Len())
}
}
if argv.Dirty != nil {
doDirty()
okExit("")
}
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 argv.Help != nil {
doHelp()
okExit("")
}
// todo: redo this logic using forgepb
if configSave {
me.forge.ConfigSave()
configSave = false
}
// 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 opening the GUI, always check git for dirty repos
me.forge.CheckDirty()
doGui()
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("")
}