Compare commits

..

No commits in common. "master" and "v0.6.5" have entirely different histories.

10 changed files with 273 additions and 427 deletions

2
.gitignore vendored
View File

@ -3,4 +3,4 @@ go.mod
go.sum go.sum
/files/* /files/*
/work/* /work/*
go-clone* go-clone

View File

@ -1,38 +1,25 @@
VERSION = $(shell git describe --tags) VERSION = $(shell git describe --tags)
BUILDTIME = $(shell date +%Y.%m.%d_%H%M) BUILDTIME = $(shell date +%Y.%m.%d)
run: install run: build
go-clone --version ./go-clone --version
vet: vet:
@GO111MODULE=off go vet @GO111MODULE=off go vet
@echo this go binary package builds okay @echo this go library package builds okay
no-gui: install build-darwin build-windows no-gui: build
./go-clone --no-gui ./go-clone --no-gui
build: goimports build:
GO111MODULE=off go build -v \ GO111MODULE=off go build \
-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}"
build-darwin: install:
GOOS=darwin GOARCH=amd64 GO111MODULE=off go build -v -o go-clone-darwin.x86 \
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
build-darwin-arm64:
GOOS=darwin GOARCH=arm64 GO111MODULE=off go build -v -o go-clone-darwin.arm \
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
build-windows:
GOOS=windows GOARCH=amd64 GO111MODULE=off go build -v -o go-clone.exe \
-ldflags "-X main.VERSION=v0.7.46 -X main.BUILDTIME=2025.02.22_0643 -X gui.GUIVERSION=v0.7.46"
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}"
goimports: goimports:
reset
goimports -w *.go goimports -w *.go
# // to globally reset paths: # // to globally reset paths:
# // gofmt -w -r "go.wit.com/gui -> go.wit.com/gui/gui" . # // gofmt -w -r "go.wit.com/gui -> go.wit.com/gui/gui" .
@ -43,28 +30,24 @@ redomod:
GO111MODULE= go mod tidy GO111MODULE= go mod tidy
go mod edit -go=1.20 go mod edit -go=1.20
reset:
# clear your terminal
reset
gui-gocui: build gui-gocui: build
reset
./go-clone --gui gocui >/tmp/witgui.log.stderr 2>&1 ./go-clone --gui gocui >/tmp/witgui.log.stderr 2>&1
nocui: build nocui: reset build
./go-clone --gui nocui ./go-clone --gui nocui
clean: clean:
rm -f go.* -rm go-clone
-rm go-clone*
go-mod-clean purge
# this will test the golang.org/x -> googlesource override # this will test the golang.org/x -> googlesource override
git-clone: git-clone:
./go-clone --recursive --go-src --no-work go.wit.com/lib/daemons/virtigod ./go-clone --recursive --go-src --no-work go.wit.com/lib/daemons/virtigod
test-build: build
./go-clone --build go.wit.com/apps/guireleaser
test-install: build
./go-clone --install go.wit.com/apps/guireleaser
debian: debian:
go-deb --no-gui --repo go.wit.com/apps/go-clone go-deb --no-gui --repo go.wit.com/apps/go-clone

55
argv.go
View File

@ -1,10 +1,5 @@
package main package main
import (
"fmt"
"os"
)
/* /*
this parses the command line arguements this parses the command line arguements
@ -15,39 +10,31 @@ var argv args
type args struct { type args struct {
Repo string `arg:"positional" help:"go import path"` Repo string `arg:"positional" help:"go import path"`
AutoWork bool `arg:"--work" default:"false" help:"recreate the go.work file"` AutoWork bool `arg:"--auto-work" default:"false" help:"auto recreate the go.work file"`
DryRun bool `arg:"--dry-run" help:"show what would be run"` DryRun bool `arg:"--dry-run" help:"show what would be run"`
Recursive bool `arg:"--recursive" default:"true" help:"recursively clone all dependencies"` Recursive bool `arg:"--recursive" default:"false" help:"resursively clone all dependencies"`
Build bool `arg:"--build" help:"try to build it after clone"` Pull bool `arg:"--git-pull" default:"false" help:"run 'git pull' on all your repos"`
Install bool `arg:"--install" help:"try to install it after clone"` RedoGoMod bool `arg:"--go-reset" default:"false" help:"remake all the go.sum and go.mod files"`
Ignore bool `arg:"--ignore" default:"false" help:"ignore weird clone errors from non-standard repos"`
// Fetch bool `arg:"--git-fetch" default:"false" help:"run 'git fetch' on all your repos"` // Fetch bool `arg:"--git-fetch" default:"false" help:"run 'git fetch' on all your repos"`
} }
func (a args) Description() string {
return `
go-clone does git clone on go package repositories.
It uses ~/go/src unless it finds a go.work file in a parent directory.
This will clone the sources for go-clone:
go-clone go.wit.com/apps/go-clone
If a go.work file is found, this will auto generate a new go.work file.
The old work file is saved as go.work.last
go-clone --auto-work go.wit.com/apps/go-clone
This will recursively clone a package and all the build requirements:
go-clone --recursive go.wit.com/apps/go-clone
`
}
func (args) Version() string { func (args) Version() string {
return "go-clone " + VERSION + " Built on " + BUILDTIME return "go-clone " + VERSION + " Built on " + BUILDTIME
} }
func (a args) Description() string {
return `
git clone go repositories recursively
Examples:
go-clone go.wit.com/apps/go-clone # 'git clone' go-clone
`
}
func (a args) DoAutoComplete(argv []string) {
switch argv[0] {
case "checkout":
fmt.Println("user devel master ")
case "--recursive":
fmt.Println("true false")
default:
if argv[0] == ARGNAME {
// list the subcommands here
fmt.Println("--dry-run --recursive --work")
}
}
os.Exit(0)
}

View File

@ -1,24 +0,0 @@
package main
import (
"os"
"go.wit.com/lib/gui/shell"
"go.wit.com/log"
)
func build() error {
err := forge.Build(workingRepo, nil)
pwd, _ := os.Getwd()
if err == nil {
log.Info("this totally worked", pwd)
shell.RunEcho([]string{"ls", "-l"})
log.Info("ran ls")
} else {
log.Info("this totally did not work", pwd)
shell.RunEcho([]string{"ls", "-l"})
log.Info("ran ls")
badExit(err)
}
return err
}

196
clone.go
View File

@ -1,196 +0,0 @@
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
/*
# to support distributed 'git bug'
# create a new user:
git bug user new -e "jcarr@wit.com" -n "Jeff Carr"
git pull origin +refs/bugs/\*:refs/bugs/\*
git pull origin +refs/identities/\*:refs/identities/\*
git show-ref | grep refs/bugs/
git log refs/bugs/<some-id>
git config --add remote.origin.fetch '+refs/bugs/*:refs/bugs/*'
git config --add remote.origin.fetch '+refs/identities/*:refs/identities/*'
git config --get-all remote.origin.fetch
[remote "origin"]
url = ...
fetch = +refs/heads/*:refs/remotes/origin/*
fetch = +refs/bugs/*:refs/bugs/*
fetch = +refs/identities/*:refs/identities/*
# remove the caches
rm -rf .git/git-bug
# rebuild the cache with any command
git bug user
*/
// CleanRepoURL removes http://, https://, and .git suffix from the given URL if present.
func CleanRepoURL(url string) string {
// Trim protocol prefix
if strings.HasPrefix(url, "http://") {
url = strings.TrimPrefix(url, "http://")
} else if strings.HasPrefix(url, "https://") {
url = strings.TrimPrefix(url, "https://")
}
// Trim trailing .git
url = strings.TrimSuffix(url, ".git")
return url
}
func clone(gopath string) (*gitpb.Repo, error) {
// if the user defined a repo, attempt to download it now
if gopath == "" {
// nothing to clone
// user probably wants to --recursive on current working dir
return nil, errors.New("gopath was blank")
}
gopath = CleanRepoURL(gopath)
os.Setenv("REPO_AUTO_CLONE", "true")
// pb, _ := forge.NewGoPath(gopath)
check := forge.FindAnyPath(filepath.Join(forge.Config.ReposDir, gopath))
if check != nil {
if check.IsValidDir() {
// repo already exists and is valid
return check, nil
}
}
pb, err := forge.GoClone(gopath)
if err != nil {
log.Info("clone() could not download err:", err)
return nil, err
}
autoWork()
if err := makeValidGoSum(pb); err != nil {
return nil, err
}
log.Info("go-clone clone() onward and upward")
return pb, nil
}
// really only does go.sum things
// so not 'really' recursive
// but that is because go.sum is supposed
// to have everything required in it
func recursiveClone(check *gitpb.Repo) error {
var good int
var bad int
badmap := make(map[string]error)
if check == nil {
return errors.New("repo was nil")
}
log.Info("STARTING RECURSIVE CLONE", check.Namespace)
log.Info("STARTING RECURSIVE CLONE", check.Namespace)
// if just cloned, parse the go.sum file for deps
if check.ParseGoSum() {
} else {
makeValidGoSum(check)
}
check.ReloadForce()
if check.GoDeps == nil {
log.Info("repo godeps == nil", check.Namespace)
return errors.New("no go deps?")
}
// probably this should never be 0 because GoPrimitive should have been true otherwise
if check.GoDeps.Len() == 0 {
log.Info("repo len(godeps) == 0", check.Namespace)
return errors.New("go.sum never parsed?")
}
log.Info("deps for", check.Namespace, "len()", check.GoDeps.Len())
deps := check.GoDeps.SortByGoPath()
for deps.Scan() {
depRepo := deps.Next()
log.Info("download:", depRepo.GetGoPath())
_, err := clone(depRepo.GetGoPath())
if err != nil {
log.Info("recursiveClone() could not download", depRepo.GetGoPath())
log.Info("err:", err)
bad += 1
badmap[depRepo.GetGoPath()] = err
} else {
log.Info("downloaded", depRepo.GetGoPath())
good += 1
}
}
log.Info("got", good, "repos", "failed on", bad, "repos")
if bad != 0 {
log.Info("clone() ERROR len(badmap)", len(badmap))
for gopath, err := range badmap {
log.Info("clone() ERROR", gopath, err)
}
if !argv.Ignore {
return errors.New("clone failed on some repos")
}
}
return nil
}
func makeValidGoSum(check *gitpb.Repo) error {
// attempt to grab the notes
check.RunQuiet([]string{"git", "fetch", "origin", "refs/notes/*:refs/notes/*"})
if err := check.RunVerbose([]string{"forge", "list"}); err != nil {
log.Info("")
log.Info("Do you have go-mod-clean? Otherwise:")
log.Info(" go install go.wit.com/apps/go-mod-clean@latest")
log.Info("")
}
log.Info("try running go-mod-clean")
// update go.sum and go.mod
if err := check.RunVerbose([]string{"go-mod-clean", "lax"}); err != nil {
log.Info("")
log.Info("Do you have go-mod-clean? Otherwise:")
log.Info(" go install go.wit.com/apps/go-mod-clean@latest")
log.Info("")
}
if check.ParseGoSum() {
return nil
}
// if this fails, just use go mod
if err := check.ValidGoSum(); err != nil {
cmd := []string{"go", "mod", "init", check.Namespace}
log.Info("try running", cmd)
if _, err := check.RunQuiet(cmd); err != nil {
log.Info("go mod init failed", err)
}
if _, err := check.RunQuiet([]string{"go", "mod", "tidy"}); err != nil {
log.Info("go mod tidy failed", err)
}
}
if check.ParseGoSum() {
return nil
}
return fmt.Errorf("could not make a valid go.mod")
}

View File

@ -1,8 +1,10 @@
Source: go-clone Source: go-clone
Build-Depends: golang, protoc-gen-go, autogenpb Build-Depends: golang
Package: go-clone Package: go-clone
Maintainer: Jeff Carr <jcarr@wit.com> Maintainer: Jeff Carr <jcarr@wit.com>
Architecture: amd64 Architecture: amd64
Depends: Depends:
Recommends: go-clone Recommends: go-clone
Description: 'git clone' the sources for a go project Description: git clones a go package and it's dependancies
does GO111MODULE=auto go get -v (more or less) only works on git
repositories. This is an experiment. GO should be used instead.

268
main.go
View File

@ -2,11 +2,13 @@ package main
import ( import (
"os" "os"
"path/filepath"
"go.wit.com/dev/alexflint/arg" "go.wit.com/dev/alexflint/arg"
"go.wit.com/lib/gui/prep" "go.wit.com/gui"
"go.wit.com/lib/gui/repolist"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/forgepb" "go.wit.com/lib/protobuf/forgepb"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log" "go.wit.com/log"
) )
@ -14,78 +16,206 @@ import (
var VERSION string var VERSION string
var BUILDTIME string var BUILDTIME string
var ARGNAME string = "go-clone" var rv *repolist.RepoList
var goSrcPath string
var pp *arg.Parser
var forge *forgepb.Forge
var workingRepo *gitpb.Repo
func main() { func main() {
log.Info("go-clone version", VERSION, "built on", BUILDTIME) pp := arg.MustParse(&argv)
// command line parsing & handling
prep.Bash(ARGNAME, argv.DoAutoComplete) // todo: make this: prep.Bash(argv)
pp = arg.MustParse(&argv) // for very new users or users unfamilar with the command line, this may help them
if argv.Repo == "version" || argv.Repo == "help" || argv.Repo == "?" {
forge = forgepb.Init() pp.WriteHelp(os.Stdout)
var err error
// attempt to clone, returns *gitpb.Repo
workingRepo, err = clone(argv.Repo)
if err != nil {
badExit(err)
}
if argv.Recursive {
log.Info("STARTING RECURSIVE CLONE", workingRepo.GetGoPath())
if err := recursiveClone(workingRepo); err != nil {
badExit(err)
}
}
autoWork()
if argv.Build {
log.Info("STARTING BUILD", workingRepo.GetGoPath())
/*
if err := makeValidGoSum(workingRepo); err != nil {
badExit(err)
}
if err := build(); err != nil {
badExit(err)
}
*/
if workingRepo.GetRepoType() == "binary" || workingRepo.GetRepoType() == "plugin" {
log.Info("build will probably fail", workingRepo.GetGoPath(), "is", workingRepo.GetRepoType())
}
if err := forge.Build(workingRepo, nil); err != nil {
log.Warn("BUILD FAILED", workingRepo.GetGoPath(), err)
badExit(err)
}
}
if argv.Install {
log.Info("STARTING INSTALL", workingRepo.GetGoPath())
// can only install binary or plugin go packages
if workingRepo.GetRepoType() == "binary" || workingRepo.GetRepoType() == "plugin" {
log.Info("install will probably fail", workingRepo.GetGoPath(), "is", workingRepo.GetRepoType())
}
if err := forge.Install(workingRepo, nil); err != nil {
log.Warn("INSTALL FAILED", workingRepo.GetGoPath(), err)
badExit(err)
}
}
okExit("")
}
func okExit(thing string) {
if thing != "" {
log.Info(thing, "ok")
}
log.Info("Finished clone on", workingRepo.GetGoPath(), "ok")
forge.ConfigSave()
os.Exit(0) os.Exit(0)
} }
func badExit(err error) { // load the ~/.config/forge/ config
log.Info("Total repositories:", forge.Repos.Len()) forge := forgepb.Init()
log.Info("Finished go-clone with error", err, forge.Config.ReposDir) forge.ConfigPrintTable()
os.Setenv("REPO_WORK_PATH", forge.GetGoSrc())
if argv.Repo == "" {
// if there isn't anything else, just exit here
// if --git-pull, continue
if argv.Pull || argv.RedoGoMod {
// there is more to do
log.Info("onward and upward")
} else {
// user needs to pick something to do
pp.WriteHelp(os.Stdout)
log.Info("give me something to do!")
os.Exit(0)
}
} else {
// the user specified a repo, check if it's already cloned
fullgitdir := filepath.Join(goSrcPath, argv.Repo, ".git")
if shell.IsDir(fullgitdir) {
// if --recursive, continue
// if --git-pull, continue
if argv.Recursive || argv.Pull || argv.RedoGoMod {
log.Info("repo already cloned", filepath.Join(goSrcPath, argv.Repo))
// there is more to do
log.Info("argv.Recursive is", argv.Recursive)
log.Info("argv.Pull is", argv.Pull)
log.Info("argv.RedoGoMod is", argv.RedoGoMod)
log.Info("onward and upward")
} else {
log.Info("repo already cloned", filepath.Join(goSrcPath, argv.Repo))
os.Exit(0)
}
} else {
// need to download this new repo!
log.Info("repo is new. going to clone it to:", filepath.Join(goSrcPath, argv.Repo))
}
}
// testing gui idea
myGui := gui.New()
// myGui.Default()
rv := repolist.Init(forge, myGui)
rv.Enable()
rv.ScanRepositories()
// if the user defined a repo, attempt to download it now
if argv.Repo != "" {
os.Setenv("REPO_AUTO_CLONE", "true")
// pb, _ := forge.NewGoPath(argv.Repo)
pb, err := forge.Clone(argv.Repo)
if err != nil {
log.Info("could not download:", err)
os.Exit(-1) os.Exit(-1)
} }
newr, err := rv.AddRepo(pb)
if err != nil {
log.Info("AddRepo() failed", err)
os.Exit(-1)
}
if argv.Recursive || argv.Pull || argv.RedoGoMod {
log.Info("repo already cloned", filepath.Join(goSrcPath, argv.Repo))
// there is more to do
log.Info("argv.Recursive is", argv.Recursive)
log.Info("argv.Pull is", argv.Pull)
log.Info("argv.RedoGoMod is", argv.RedoGoMod)
log.Info("onward and upward")
} else {
log.Info("repo cloned worked to", filepath.Join(goSrcPath, argv.Repo))
os.Exit(0)
}
// update go.sum and go.mod
// todo: only do this if they don't exist?
// todo: make these git commit metadata
newr.MakeRedoMod()
// double check it actually downloaded
fullgitdir := filepath.Join(goSrcPath, argv.Repo, ".git")
if !shell.IsDir(fullgitdir) {
log.Info("repo cloned failed", filepath.Join(goSrcPath, argv.Repo))
os.Exit(-1)
}
}
os.Setenv("REPO_AUTO_CLONE", "false")
// look recursively in your working directory for git repos
totalcount := forge.Repos.Len()
// if --git-pull, run git pull on everything here
if argv.Pull {
log.Info("Total repositories:", totalcount)
log.Info("Going to run git pull in each one")
log.Sleep(1)
pull := []string{"git", "pull"}
var trycount, errcount int
loop := rv.ReposSortByName()
for loop.Scan() {
repo := loop.Repo()
if argv.DryRun {
log.Info("git pull --dry-run", repo.Status.Path())
} else {
trycount += 1
log.Info("actually run: git pull:", repo.Status.Path())
if result := shell.PathRunRealtime(repo.Status.Path(), pull); result.Error != nil {
log.Info("git pull error:", result.Error)
errcount += 1
}
}
}
log.Info("Total repositories:", totalcount, "Total attempted:", trycount, "Errors:", errcount)
os.Exit(0)
}
// this is experiemental but works for me
if argv.Recursive {
newr := rv.FindByName(argv.Repo)
if newr == nil {
log.Info("how did this repo still not exist?", argv.Repo)
os.Exit(-1)
}
os.Setenv("REPO_AUTO_CLONE", "true")
godep := newr.Status.GetGoDeps()
for gopath, version := range godep {
pb, err := forge.Clone(gopath)
if err != nil {
log.Info("could not download:", err)
os.Exit(-1)
}
repo, err := rv.AddRepo(pb)
if err != nil {
log.Info("git clone failed for", gopath, version)
continue
}
// always do this for now. probably always forever
repo.MakeRedoMod()
}
}
// remake all the go.sum & go.mod in every repo
// todo: make go.sum and go.mod git commit metadata
if argv.RedoGoMod {
loop := rv.ReposSortByName()
for loop.Scan() {
repo := loop.Repo()
repo.MakeRedoMod()
}
}
// remake the go.work file
if argv.AutoWork {
log.Info("About to re-create", goSrcPath+"/go.work")
log.Info("Sleep 3. original go.work saved as go.work.last (hit ctrl-c to cancel)")
log.Sleep(3)
shell.PathRun(goSrcPath, []string{"mv", "go.work", "go.work.last"})
rv.MakeGoWork()
shell.PathRun(goSrcPath, []string{"go", "work", "use"})
log.Info("")
log.Info("original go.work file saved as go.work.last")
log.Info("")
}
log.Info("Total repositories:", totalcount)
log.Info("Finished go-clone for", argv.Repo)
}
/*
func scanForRepos(goSrcPath string) int {
var count int
log.Info("scanning for repo in:", filepath.Join(goSrcPath, argv.Repo))
// rv.NewRepo("go.wit.com/apps/helloworld")
for _, path := range repostatus.ScanGitDirectories(goSrcPath) {
count += 1
gopath := strings.TrimPrefix(path, goSrcPath)
gopath = strings.Trim(gopath, "/")
// log.Info("Also should add:", gopath)
pb, err := forge.Clone(gopath)
if err != nil {
log.Info("could not download:", err)
os.Exit(-1)
}
repo, err := rv.AddRepo(pb)
}
return count
}
*/

41
readWorkFile.go Normal file
View File

@ -0,0 +1,41 @@
package main
/*
func addDir(d string) {
if shell.IsDir(d) {
rv.NewRepo(d)
}
}
func readControlFile(path string) error {
fullname := filepath.Join(path, "go.work")
file, err := os.Open(fullname)
if err != nil {
return err
}
defer file.Close()
// pairs := make(map[string]string)
// var key string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)
partsNew := strings.SplitN(line, ":", 2)
if len(partsNew) > 1 {
continue
}
log.Info(line)
addDir(line)
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}
*/

View File

@ -1,57 +0,0 @@
package main
import (
"bufio"
"errors"
"fmt"
"os"
"strings"
"go.wit.com/log"
)
func showOptions(b bool, s []string) {
fmt.Println("")
for _, line := range s {
fmt.Println(line)
}
fmt.Println("")
if b {
fmt.Println("Enter (Y/n)")
} else {
fmt.Println("Enter (y/N)")
}
}
// if b == true, default is to continue with 'Y'
func simpleStdin(b bool, s []string) {
/*
if argv.Auto {
return
}
*/
err := errors.New("user cancelled via stdin")
showOptions(b, s)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
s := scanner.Text()
s = strings.TrimSpace(s)
s = strings.ToLower(s)
switch s {
case "y":
log.Info("got y")
return
case "n":
log.Info("got n")
badExit(err)
case "":
if b {
return
} else {
badExit(err)
}
default:
badExit(err)
}
}
}

20
work.go
View File

@ -1,20 +0,0 @@
package main
import (
"go.wit.com/lib/gui/shell"
"go.wit.com/log"
)
func autoWork() {
// remake the go.work file
if !argv.AutoWork {
return
}
log.Info("About to re-create", forge.Config.ReposDir+"/go.work")
shell.PathRun(forge.Config.ReposDir, []string{"mv", "go.work", "go.work.last"})
forge.MakeGoWork()
shell.PathRun(forge.Config.ReposDir, []string{"go", "work", "use"})
log.Info("")
log.Info("original go.work file saved as go.work.last")
log.Info("")
}