// Exists checks for codebase existence by using the basic pkg rev Exists() // routine to see if the specified codebase (at any specified rev, or from // the default VCS rev) can be found. If so it'll return the fullest URI // it can to that codebase pkg repo, otherwise an error is returned. // Config file and env var settings can help to find the codebase, ie: // * DVLN_CODEBASE_PATH in env or codebase_path in cfg file // - > space separated list of paths to prepend to the codebase name // - > eg: "github.com/dvln git+ssh://host.com/path/to/clones /some/local/dir" // - - note: 'hub' and 'hub:<uri>' reserved for future user/central dvln hub // - > all values are *always* forward slash regardless of platform // - > 'dvln' as the codebase would result in looking, in order, here: // - > "github.com/dvln/dvln[.git]" // - > "git+ssh://host.com/clone/path/dvln[.git]" // - > "/some/local/dir/dvln[.git]" // - > "dvln" // - > final fallback will be on the individual dvln itself // Based on the above existence checks it'll return: // - string: URI: full path (on filesystem) or URL (if remote), "" if not found // - locality: where it exists (LocalDir, RemoteURL, NonExistent) // - error: any error that is detected in scanning for the workspace func (cb *Defn) Exists(codebaseVerSel string) (string, Locality, error) { //eriknow, make some choices around codebase existence checks... // should be a challenge, that's for sure var err error exists := false if codebaseVerSel != "" { exists, err = file.Exists(codebaseVerSel) } wkspcRootDir := "" if !exists { wkspcRootDir = globs.GetString("wkspcRootDir") } if wkspcRootDir == "" { wkspcRootDir = globs.GetString("dvlnCfgDir") } if codebaseVerSel == "" || !exists || err != nil { if err == nil { out.Debugln("No codebase file to read, skipping (considered normal)") } else { out.Debugf("No codebase file to read, skipping (abnormal, unexpected err: %s)\n", err) } return "", NonExistent, err } return codebaseVerSel, LocalDir, nil }
// GitHookInstalled is used to check if a given hook is installed as // specified, it does nothing more, Params: // h (*GitHookMgr): the hook mgr structure (find location of repo/etc) // path (string): where is the hook we wish to install? // name (string): what is the "git name" for the hook? // link (bool): is hook a symlink to hookPath, or full copy/install? // Returns boolean, true if hook is installed as specified, false otherwise func GitHookInstalled(h *GitHookMgr, path, name string, link bool) bool { cachedPathHashes := make(map[string]string) repoPath, _, err := h.Exists(LocalPath) hookInstalled := false hookInstallPath := "" if err == nil && repoPath != "" { // if the local path exists... hookInstallPath = filepath.Join(repoPath, ".git", "hooks", name) if isBareRepo(repoPath) { hookInstallPath = filepath.Join(repoPath, "hooks", name) } if link { // if client wants a link, see if link is there already... fileInfo, err := os.Lstat(hookInstallPath) if err != nil { return false // if not there then installed is false } if fileInfo.Mode()&os.ModeSymlink == 0 { return false // if not a symlink then installed is false } originFile, err := os.Readlink(hookInstallPath) if err != nil { return false // if cannot read link, installed is false } if originFile != path { return false // target is not what we wanted, installed is false } hookInstalled = true } else { // user wants copy of file, see if there and sha matches.. if there, err := file.Exists(hookInstallPath); err != nil || !there { return false // err checking existence|not there, not installed } installed, err := ioutil.ReadFile(hookInstallPath) hasher := sha256.New() hasher.Write(installed) installedFileHash := hex.EncodeToString(hasher.Sum(nil)) if err != nil { return false // failed to read file, assume not installed } wantedFileHash := "" if cachedHash, ok := cachedPathHashes[path]; ok { wantedFileHash = cachedHash // only gen the hash of the target file once per pass } else { wanted, err2 := ioutil.ReadFile(path) if err2 != nil { return false // failed to read file, assume not installed } hasher = sha256.New() hasher.Write(wanted) wantedFileHash = hex.EncodeToString(hasher.Sum(nil)) } if installedFileHash != wantedFileHash { return false // sha's differ, assume rev we want not installed } hookInstalled = true } } return hookInstalled }
// GitHookInstall is used to install a hook into a git clone, params: // h (*GitHookMgr): the hook mgr structure (find location of repo/etc) // path (string): where is the hook we wish to install? // name (string): what is the "git name" for the hook? // link (bool): is hook a symlink to hookPath, or full copy/install? // Returns full path/name to git hook installed along w/any error seen func GitHookInstall(h *GitHookMgr, path, name string, link bool) (string, error) { repoPath, _, err := h.Exists(LocalPath) hookInstallPath := "" if err == nil && repoPath != "" { // if the local path exists... hookInstallPath = filepath.Join(repoPath, ".git", "hooks", name) if isBareRepo(repoPath) { hookInstallPath = filepath.Join(repoPath, "hooks", name) } if there, err := file.Exists(hookInstallPath); err == nil && there { err = os.Remove(hookInstallPath) if err != nil { return "", out.WrapErr(err, "Failed to remove previously installed hook", 4510) } } if there, err := file.Exists(path); err != nil || !there { if err != nil { return "", out.WrapErr(err, "Hook install failed checking source hook existence", 4511) } return "", out.NewErrf(4512, "Hook install failed, hook source path does not exist:\n path: %s", path) } oldUmask := syscall.Umask(0) defer syscall.Umask(oldUmask) if link { // if symlink desired, try and create that err = os.Symlink(path, hookInstallPath) if err != nil { err = out.WrapErrf(err, 4513, "Hook install failed, failed to set up symlink:\n linktgt: %s\n link: %s\n", path, hookInstallPath) } } else { // otherwise try and copy in the hook file _, err = file.CopyFileSetPerms(path, hookInstallPath, 0775) if err != nil { err = out.WrapErrf(err, 4514, "Hook install failed, failed to copy hook file:\n hook source path %s\n hook install path: %s\n", path, hookInstallPath) } } } return hookInstallPath, err }
// findGitDirs expects to be pointed at a git workspace, either // bare or standard. It'll find the gitdir and worktree dirs // and return them, if it fails it'll return non-nil err. Params: // path (string): path to the git workspace // Returns: // gitDir (string): path to git metadata location // workTreeDir (string): working tree, "" if bare clone // err (error): a valid error if unable to find a git repo func findGitDirs(path string) (string, string, error) { gitDir := filepath.Join(path, ".git") // see if std git clone var err error var exists bool if exists, err = dir.Exists(gitDir); exists && err == nil { return gitDir, path, nil } gitRefsDir := filepath.Join(path, "refs") if exists, err = dir.Exists(gitRefsDir); exists && err == nil { gitConfigFile := filepath.Join(path, "config") if exists, err = file.Exists(gitConfigFile); exists && err == nil { return path, "", nil } } if err == nil { return "", "", out.WrapErrf(ErrNoExist, 4500, "Unable to find valid git clone under path: %s", path) } return "", "", out.WrapErrf(ErrNoExist, 4500, "Unable to find valid git clone under path: %s\n existence err: %s", path, err) }
// This tests non-bare git repo's with the various bits of git functionality within the // VCS package... at least those items applicable to non-bare git repo's which is pretty // much all features func TestGit(t *testing.T) { tempDir, err := ioutil.TempDir("", "go-vcs-git-tests") if err != nil { t.Fatal(err) } defer func() { err = os.RemoveAll(tempDir) if err != nil { t.Error(err) } }() testClone := filepath.Join(tempDir, "VCSTestRepo") mirror := true gitGetter, err := NewGitGetter("https://github.com/dvln/git-test-repo", "", testClone, !mirror) if err != nil { t.Fatalf("Unable to instantiate new Git VCS reader, Err: %s", err) } if gitGetter.Vcs() != Git { t.Error("Git is detecting the wrong type") } // Check the basic getters. if gitGetter.Remote() != "https://github.com/dvln/git-test-repo" { t.Error("Remote not set properly") } if gitGetter.LocalRepoPath() != testClone { t.Error("Local disk location not set properly") } // Do an initial mirror clone. results, err := gitGetter.Get() if err != nil { t.Fatalf("Unable to clone Git repo using VCS reader Get(). Err was: %s, details:\n%s", err, results) } // Verify Git repo exists in the workspace path, _, err := gitGetter.Exists(LocalPath) if err != nil { t.Fatalf("Existence check failed on git repo: %s", err) } if path == "" { t.Error("Problem seeing if Git repo exists in workspace") } // Test internal lookup mechanism used outside of Git specific functionality. ltype, err := DetectVcsFromFS(testClone) if err != nil { t.Error("detectVcsFromFS unable to detect Git repo") } if ltype != Git { t.Errorf("detectVcsFromFS detected %s instead of Git type", ltype) } // Test NewReader on existing checkout. This should simply provide a working // instance without error based on looking at the local directory. gitReader, err := NewReader("https://github.com/dvln/git-test-repo", testClone) if err != nil { t.Fatal(err) } if gitReader == nil { t.Fatal("gitReader interface was unexpectedly nil") } // See if the new git VCS reader was instantiated in the workspace path, _, err = gitReader.Exists(LocalPath) if err != nil { t.Errorf("Existence check failed on git repo: %s", err) } if path == "" { t.Error("The git reader was not correctly instantiated in the workspace") } // Perform an update operation gitUpdater, err := NewUpdater("https://github.com/dvln/git-test-repo", "origin", testClone, !mirror, RebaseFalse, nil) if err != nil { t.Fatal(err) } results, err = gitUpdater.Update() if err != nil { t.Fatalf("Failed to run git update, error: %s, results:\n%s", err, results) } // Set the version (checkout) using a short sha1 that should exist results, err = gitReader.RevSet("3f690c9") if err != nil { t.Fatalf("Unable to update Git repo version. Err was: %s, results:\n%s", err, results) } // Use RevRead to verify we are on the right version. v, _, err := gitReader.RevRead(CoreRev) if string(v[0].Core()) != "3f690c91af378fbd09628f9833abb1c3d6828c5e" { t.Errorf("Error checking checked out Git version, found: \"%s\"\n", string(v[0].Core())) } if err != nil { t.Error(err) } // Verify that we can set the version something other than short hash _, err = gitReader.RevSet("master") if err != nil { t.Errorf("Unable to update Git repo version. Err was %s", err) } _, err = gitReader.RevSet("28d488c8deda544076f56b279824657fa691ef01") if err != nil { t.Errorf("Unable to update Git repo version. Err was %s", err) } v, _, err = gitReader.RevRead(CoreRev) if string(v[0].Core()) != "28d488c8deda544076f56b279824657fa691ef01" { t.Errorf("Error checking checked out Git version, found: \"%s\"\n", string(v[0].Core())) } if err != nil { t.Error(err) } // Install a git hook, verify existence, then remove it, verify gone gitHookMgr, err := NewHookMgr(testClone) if err != nil { t.Fatalf("Failed to set up a new git hook manager, err:\n %s\n", err) } // First do a run with an invalid source path, insure that fails (this // is not a bare repo so that will not be found as it doesn't exist) hookSrc := filepath.Join(testClone, "hooks", "pre-push.sample") link := true hookLinkPath, err := gitHookMgr.Install(hookSrc, "pre-push", link) if err == nil { t.Fatal("Hook install of link should have failed as target is not there") } // Now do a run with the real non-bare path that should exist... hookSrc = filepath.Join(testClone, ".git", "hooks", "pre-push.sample") hookLinkPath, err = gitHookMgr.Install(hookSrc, "pre-push", link) if err != nil { t.Fatalf("Failed to install git hook symlink, error:\n%s\n", err) } fileInfo, err := os.Lstat(hookLinkPath) if err != nil { t.Fatalf("Should have set up a hook symlink but os.Lstat failed, err: %s\n", err) } if fileInfo.Mode()&os.ModeSymlink == 0 { t.Fatal("Should have created a symlink but file created was not a symlink") } originFile, err := os.Readlink(hookLinkPath) if err != nil { t.Fatalf("Should have created a symlink but failed to resolve symlink, err: %s", err) } if originFile != hookSrc { t.Fatalf("Installed hook symlink not pointing correctly:\n found: %s\n need: %s\n", originFile, hookSrc) } err = gitHookMgr.Remove("pre-push") if err != nil { t.Fatalf("Failed to remove hook symlink, err: %s", err) } if there, err := file.Exists(hookLinkPath); err != nil || there { t.Fatalf("Removal of hook symlink seems to have failed (err: %s, there: %s)\n", err, there) } // Now lets try a copy type hook install and removal hookCopyPath, err := gitHookMgr.Install(hookSrc, "pre-push", !link) if err != nil { t.Fatalf("Failed to install (copy) git hook\n src:%s\n tgt:%s\n err: %s\n", hookSrc, hookCopyPath, err) } fileInfo, err = os.Stat(hookCopyPath) if err != nil { t.Fatalf("Should have just copied file but os.Stat failed, err: %s\n", err) } srcFileInfo, err := os.Stat(hookSrc) if err != nil { t.Fatalf("Should have copied a symlink but os.Stat failed, err: %s\n", err) } if fileInfo.Size() != srcFileInfo.Size() { t.Fatal("File size of copied file not matching source") } err = gitHookMgr.Remove("pre-push") if err != nil { t.Fatalf("Failed to remove hook file, err: %s", err) } if there, err := file.Exists(hookCopyPath); err != nil || there { t.Fatalf("Removal of hook file seems to have failed (err: %s, there: %s)\n", err, there) } }