diff --git a/README.md b/README.md new file mode 100644 index 0000000..146e600 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +This code is really hacky still diff --git a/clone.go b/clone.go new file mode 100644 index 0000000..39a6aed --- /dev/null +++ b/clone.go @@ -0,0 +1,269 @@ +package repostatus + +import ( + "errors" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "strings" + + "go.wit.com/lib/gui/shell" + "go.wit.com/log" +) + +// guess the paths. returns + +// realpath : the actual path on the filesystem +// goSrcPath : this could be ~/go/src, or where the go.work file is + +// goPath : go.wit.com/lib/gui/repostatus (for example) +// true/false if the repo is a golang repo +func guessPaths(path string) (string, string, string, bool, error) { + var realpath, goSrcDir string + var isGoLang bool = false + + homeDir, err := os.UserHomeDir() + if err != nil { + log.Log(WARN, "Error getting home directory:", err) + return path, realpath, goSrcDir, false, err + } + goSrcDir = filepath.Join(homeDir, "go/src") + + // allow arbitrary paths, otherwise, assume the repo is in ~/go/src + // unless REPO_WORK_PATH was set. to over-ride ~/go/src + // todo, look for go.work + if os.Getenv("REPO_WORK_PATH") == "" { + os.Setenv("REPO_WORK_PATH", goSrcDir) + } else { + goSrcDir = os.Getenv("REPO_WORK_PATH") + } + + // todo: this is dumb + if strings.HasPrefix(path, "/") { + realpath = path + } else if strings.HasPrefix(path, "~") { + tmp := strings.TrimPrefix(path, "~") + realpath = filepath.Join(homeDir, tmp) + } else { + realpath = filepath.Join(goSrcDir, path) + isGoLang = true + } + + if os.Getenv("REPO_AUTO_CLONE") == "true" { + err := Clone(goSrcDir, path) + if err != nil { + // directory doesn't exist. exit with nil and error nil + return path, realpath, goSrcDir, false, errors.New("git clone") + } + } + + if !IsDirectory(realpath) { + log.Log(REPOWARN, "directory doesn't exist", realpath) + // directory doesn't exist. exit with nil and error nil + return path, realpath, goSrcDir, false, errors.New(realpath + " does not exist") + } + + filename := filepath.Join(realpath, ".git/config") + + _, err = os.Open(filename) + if err != nil { + // log.Log(WARN, "Error reading .git/config:", filename, err) + // log.Log(WARN, "TODO: find .git/config in parent directory") + return path, realpath, goSrcDir, false, err + } + return path, realpath, goSrcDir, isGoLang, nil +} + +// attempt to git clone if the go path doesn't exist +// wdir = /home/jcarr/go/src/ +// path = go.wit.com/apps/helloworld +func Clone(wdir string, path string) error { + fullpath := filepath.Join(wdir, path) + if IsDirectory(fullpath) { + // directory already exists + return nil + } + err := os.Chdir(wdir) + if err != nil { + return err + } + + fulldir := filepath.Join(wdir, filepath.Dir(path)) + /* + base := filepath.Base(path) + os.MkdirAll(fulldir, 0750) + err = os.Chdir(fulldir) + if err != nil { + return err + } + */ + + return CloneNew(fulldir, path) + + /* + // TODO: this should go in a config file if needed + // todo: move this to a resources/ text file in go-clone + // go get golang.org/x/term + // is now supposed to be: + // git clone https://go.googlesource.com/term + // if url, err = findGoImport("http://" + path); err != nil { + switch path { + case "golang.org/x/crypto": + path = "go.googlesource.com/crypto" + case "golang.org/x/mod": + path = "go.googlesource.com/mod" + case "golang.org/x/net": + path = "go.googlesource.com/net" + case "golang.org/x/sys": + path = "go.googlesource.com/sys" + case "golang.org/x/sync": + path = "go.googlesource.com/sync" + case "golang.org/x/term": + path = "go.googlesource.com/term" + case "golang.org/x/text": + path = "go.googlesource.com/text" + case "golang.org/x/tools": + path = "go.googlesource.com/tools" + case "golang.org/x/xerrors": + path = "go.googlesource.com/xerrors" + } + */ + + /* + var url string + + // this creates a valid URL for git clone + if url, err = runGoList(path); err != nil { + return err + } + log.Info("URL:", url) + shell.PathRunRealtime(fulldir, []string{"git", "clone", url, base}) + if IsDirectory(fullpath) { + // clone worked + return nil + } + return errors.New("resolve go import") + */ +} + +// does a git clone, if it works, returns true +// workdir = /home/jcarr/go/src/ +// gopath = go.wit.com/apps/helloworld +func CloneNew(workdir, gopath string) error { + fullpath := filepath.Join(workdir, gopath) + dirname := filepath.Base(fullpath) + + basedir := strings.TrimSuffix(fullpath, dirname) + + var err error + url := "http://" + gopath + log.Info("trying git clone") + log.Info("gopath =", gopath) + log.Info("dirname =", dirname) + log.Info("basedir =", basedir) + log.Info("url =", url) + + // try a direct git clone against the gopath + // cloneActual("helloworld", "/home/jcarr/go/src/go.wit.com/apps", "http://go.wit.com/apps/helloworld") + if err = cloneActual(dirname, basedir, url); err == nil { + // git clone worked! + return nil + } + log.Info("direct attempt at git clone failed", url) + + // query the golang package system for the last known location + // NOTE: take time to thank the go developers and google for designing this wonderful system + if url, err = runGoList(gopath); err != nil { + return err + } + if err := cloneActual(dirname, basedir, url); err == nil { + // git clone worked! + return nil + } + log.Info("direct attempt at git clone failed", url) + + // try to parse a redirect + + return errors.New("can not find git sources for gopath " + gopath) +} + +// newdir = helloworld +// basedir = /home/jcarr/go/src/go.wit.com/apps +// giturl = https://gitea.wit.com/gui/helloworld +func cloneActual(newdir, basedir, giturl string) error { + if !IsDirectory(basedir) { + os.MkdirAll(basedir, 0750) + } + err := os.Chdir(basedir) + if err != nil { + log.Warn("chdir failed", basedir, err) + return err + } + + r := shell.PathRunRealtime(basedir, []string{"git", "clone", "--verbose", "--progress", giturl}) + if r.Error != nil { + log.Warn("git clone error", r.Error) + return r.Error + } + + fullpath := filepath.Join(basedir, newdir) + if !IsDirectory(fullpath) { + log.Info("git clone failed", giturl) + return errors.New("git clone failed " + giturl) + } + gitdir := filepath.Join(fullpath, ".git") + if IsDirectory(gitdir) { + log.Info("git clone worked", gitdir) + return nil + } + // git clone didn't really work but did make a directory + log.Info("fullpath is probably empty", fullpath) + panic("crapnuts. rmdir fullpath here?") +} + +// check the server for the current go path to git url mapping +// for example: +// This will check go.wit.com for "go.wit.com/apps/go-clone" +// and return where the URL to do git clone should be +func findGoImport(url string) (string, error) { + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() + + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + tmp := string(bodyBytes) + parts := strings.Split(tmp, "go-import") + if len(parts) < 2 { + return "", errors.New("missing go-import") + } + // this is terrible, it doesn't even look for 'content=' + // but again, this is just a hack for my own code to be + // usuable after the removal in go v1.22 of the go get clone behavior that was in v1.21 + parts = strings.Split(parts[1], "\"") + var newurl string + for { + if len(parts) == 0 { + break + } + tmp := strings.TrimSpace(parts[0]) + fields := strings.Split(tmp, " ") + // log.Info("FIELDS:", fields) + if len(fields) == 3 { + newurl = fields[2] + break + } + parts = parts[1:] + } + if newurl == "" { + return "", errors.New("missing git content string") + } + + return newurl, nil +} diff --git a/new.go b/new.go index 7e4b545..729a52e 100644 --- a/new.go +++ b/new.go @@ -1,15 +1,10 @@ package repostatus import ( - "errors" - "io/ioutil" - "net/http" "os" - "path/filepath" "strings" "go.wit.com/lib/gadgets" - "go.wit.com/lib/gui/shell" "go.wit.com/log" ) @@ -48,185 +43,6 @@ func SetWorkPath(path string) { os.Setenv("REPO_WORK_PATH", path) } -// guess the paths. returns -// realpath : the actual path on the filesystem -// goSrcPath : this could be ~/go/src, or where the go.work file is -// goPath : go.wit.com/lib/gui/repostatus (for example) -// true/false if the repo is a golang repo -func guessPaths(path string) (string, string, string, bool, error) { - var realpath, goSrcDir string - var isGoLang bool = false - - homeDir, err := os.UserHomeDir() - if err != nil { - log.Log(WARN, "Error getting home directory:", err) - return path, realpath, goSrcDir, false, err - } - goSrcDir = filepath.Join(homeDir, "go/src") - - // allow arbitrary paths, otherwise, assume the repo is in ~/go/src - // unless REPO_WORK_PATH was set. to over-ride ~/go/src - // todo, look for go.work - if os.Getenv("REPO_WORK_PATH") == "" { - os.Setenv("REPO_WORK_PATH", goSrcDir) - } else { - goSrcDir = os.Getenv("REPO_WORK_PATH") - } - - if strings.HasPrefix(path, "/") { - realpath = path - } else if strings.HasPrefix(path, "~") { - tmp := strings.TrimPrefix(path, "~") - realpath = filepath.Join(homeDir, tmp) - } else { - realpath = filepath.Join(goSrcDir, path) - isGoLang = true - } - - if os.Getenv("REPO_AUTO_CLONE") == "true" { - err := Clone(goSrcDir, path) - if err != nil { - // directory doesn't exist. exit with nil and error nil - return path, realpath, goSrcDir, false, errors.New("git clone") - } - } - - if !IsDirectory(realpath) { - log.Log(REPOWARN, "directory doesn't exist", realpath) - // directory doesn't exist. exit with nil and error nil - return path, realpath, goSrcDir, false, errors.New(realpath + " does not exist") - } - - filename := filepath.Join(realpath, ".git/config") - - _, err = os.Open(filename) - if err != nil { - // log.Log(WARN, "Error reading .git/config:", filename, err) - // log.Log(WARN, "TODO: find .git/config in parent directory") - return path, realpath, goSrcDir, false, err - } - return path, realpath, goSrcDir, isGoLang, nil -} - -// attempt to git clone if the go path doesn't exist -func Clone(wdir string, path string) error { - fullpath := filepath.Join(wdir, path) - if IsDirectory(fullpath) { - // directory already exists - return nil - } - err := os.Chdir(wdir) - if err != nil { - return err - } - - fulldir := filepath.Join(wdir, filepath.Dir(path)) - base := filepath.Base(path) - os.MkdirAll(fulldir, 0750) - err = os.Chdir(fulldir) - if err != nil { - return err - } - // TODO: attempt to work around things like: - // todo: move this to a resources/ text file in go-clone - // go get golang.org/x/term - // is now supposed to be: - // git clone https://go.googlesource.com/term - // if url, err = findGoImport("http://" + path); err != nil { - switch path { - case "golang.org/x/crypto": - path = "go.googlesource.com/crypto" - case "golang.org/x/mod": - path = "go.googlesource.com/mod" - case "golang.org/x/net": - path = "go.googlesource.com/net" - case "golang.org/x/sys": - path = "go.googlesource.com/sys" - case "golang.org/x/sync": - path = "go.googlesource.com/sync" - case "golang.org/x/term": - path = "go.googlesource.com/term" - case "golang.org/x/text": - path = "go.googlesource.com/text" - case "golang.org/x/tools": - path = "go.googlesource.com/tools" - case "golang.org/x/xerrors": - path = "go.googlesource.com/xerrors" - } - - r := shell.PathRunRealtime(fulldir, []string{"git", "clone", "--verbose", "--progress", "http://" + path}) - if r.Error != nil { - log.Warn("git clone error", r.Error) - log.Error(r.Error) - } - - if IsDirectory(fullpath) { - // clone worked - return nil - } - var url string - - // this creates a valid URL for git clone - if url, err = runGoList(path); err != nil { - return err - } - log.Info("URL:", url) - shell.PathRunRealtime(fulldir, []string{"git", "clone", url, base}) - if IsDirectory(fullpath) { - // clone worked - return nil - } - return errors.New("resolve go import") -} - -// this is a terrible hack and really -// shouldn't be here. Hopefully this can -// be removed and the GO compiler code can -// be involked directly (it appears to currently -// be an internal/ function in the GO sources -// so it can't be imported from outside the compiler -func findGoImport(url string) (string, error) { - resp, err := http.Get(url) - if err != nil { - return "", err - } - defer resp.Body.Close() - - bodyBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - - tmp := string(bodyBytes) - parts := strings.Split(tmp, "go-import") - if len(parts) < 2 { - return "", errors.New("missing go-import") - } - // this is terrible, it doesn't even look for 'content=' - // but again, this is just a hack for my own code to be - // usuable after the removal in go v1.22 of the go get clone behavior that was in v1.21 - parts = strings.Split(parts[1], "\"") - var newurl string - for { - if len(parts) == 0 { - break - } - tmp := strings.TrimSpace(parts[0]) - fields := strings.Split(tmp, " ") - // log.Info("FIELDS:", fields) - if len(fields) == 3 { - newurl = fields[2] - break - } - parts = parts[1:] - } - if newurl == "" { - return "", errors.New("missing git content string") - } - - return newurl, nil -} - func NewRepoStatusWindow(path string) (error, *RepoStatus) { path, realpath, goSrcDir, isGoLang, err := guessPaths(path) if err != nil {