func TestExists(t *testing.T) { s := runutil.NewSequence(nil, os.Stdin, os.Stdout, os.Stderr, false, true) f, err := s.TempFile("", "file-exists") if err != nil { t.Fatal(err) } name := f.Name() f.Close() defer os.RemoveAll(name) newName := name + "x" err = s.AssertFileExists(name).Rename(name, newName).Done() if err != nil { t.Fatal(err) } defer os.RemoveAll(newName) err = s.AssertFileExists(name).Last("exit 1") if !runutil.IsNotExist(err) { t.Fatal(err) } err = s.AssertFileExists(newName).Last("sh", "-c", "exit 33") if got, want := err.Error(), "exit status 33"; !strings.Contains(got, want) { t.Errorf("got %v, does not contain %v", got, want) } err = s.AssertDirExists(newName).Last("sh", "-c", "exit 33") if !runutil.IsNotExist(err) { t.Fatal(err) } dir, err := s.TempDir("", "dir-exists") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) newDir := dir + "x" err = s.AssertDirExists(dir).Rename(dir, newDir).Done() if err != nil { t.Fatal(err) } defer os.RemoveAll(newDir) err = s.AssertDirExists(dir).Last("exit 1") if !runutil.IsNotExist(err) { t.Fatal(err) } err = s.AssertDirExists(newDir).Last("sh", "-c", "exit 33") if got, want := err.Error(), "exit status 33"; !strings.Contains(got, want) { t.Errorf("got %v, does not contain %v", got, want) } err = s.AssertFileExists(newDir).Last("sh", "-c", "exit 33") if !runutil.IsNotExist(err) { t.Fatal(err) } }
// hostCredentials returns credentials for the given Gerrit host. The // function uses best effort to scan common locations where the // credentials could exist. func hostCredentials(seq runutil.Sequence, hostUrl *url.URL) (_ *credentials, e error) { // Look for the host credentials in the .netrc file. netrcPath := filepath.Join(os.Getenv("HOME"), ".netrc") file, err := seq.Open(netrcPath) if err != nil { if !runutil.IsNotExist(err) { return nil, err } } else { defer collect.Error(func() error { return file.Close() }, &e) credsMap, err := parseNetrcFile(file) if err != nil { return nil, err } creds, ok := credsMap[hostUrl.Host] if ok { return creds, nil } } // Look for the host credentials in the git cookie file. args := []string{"config", "--get", "http.cookiefile"} var stdout, stderr bytes.Buffer if err := seq.Capture(&stdout, &stderr).Last("git", args...); err == nil { cookieFilePath := strings.TrimSpace(stdout.String()) file, err := seq.Open(cookieFilePath) if err != nil { if !runutil.IsNotExist(err) { return nil, err } } else { defer collect.Error(func() error { return file.Close() }, &e) credsMap, err := parseGitCookieFile(file) if err != nil { return nil, err } creds, ok := credsMap[hostUrl.Host] if ok { return creds, nil } // Account for site-wide credentials. Namely, the git cookie // file can contain credentials of the form ".<name>", which // should match any host "*.<name>". for host, creds := range credsMap { if strings.HasPrefix(host, ".") && strings.HasSuffix(hostUrl.Host, host) { return creds, nil } } } } return nil, fmt.Errorf("cannot find credentials for %q", hostUrl.String()) }
// checkDependents makes sure that all CLs in the sequence of // dependent CLs leading to (but not including) the current branch // have been exported to Gerrit. func checkDependents(jirix *jiri.X) (e error) { originalBranch, err := gitutil.New(jirix.NewSeq()).CurrentBranchName() if err != nil { return err } branches, err := getDependentCLs(jirix, originalBranch) if err != nil { return err } for i := 1; i < len(branches); i++ { file, err := getCommitMessageFileName(jirix, branches[i]) if err != nil { return err } if _, err := jirix.NewSeq().Stat(file); err != nil { if !runutil.IsNotExist(err) { return err } return fmt.Errorf(`Failed to export the branch %q to Gerrit because its ancestor %q has not been exported to Gerrit yet. The following steps are needed before the operation can be retried: $ git checkout %v $ jiri cl mail $ git checkout %v # retry the original command `, originalBranch, branches[i], branches[i], originalBranch) } } return nil }
// assertFilesDoNotExist asserts that the files do not exist. func assertFilesDoNotExist(t *testing.T, jirix *jiri.X, files []string) { s := jirix.NewSeq() for _, file := range files { if _, err := s.Stat(file); err != nil && !runutil.IsNotExist(err) { t.Fatalf("%v", err) } else if err == nil { t.Fatalf("expected file %v to not exist but it did", file) } } }
func cleanupRmAll(jirix *jiri.X, db *profiles.DB, root jiri.RelPath) error { s := jirix.NewSeq() if err := s.AssertFileExists(db.Path()).Remove(db.Path()).Done(); err != nil && !runutil.IsNotExist(err) { return err } else { if err := s.AssertDirExists(db.Path()).RemoveAll(db.Path()).Done(); err != nil && !runutil.IsNotExist(err) { return err } } d := root.Abs(jirix) err := s.AssertDirExists(d). Run("chmod", "-R", "u+w", d). RemoveAll(d). Done() if err == nil || runutil.IsNotExist(err) { fmt.Fprintf(jirix.Stdout(), "success\n") return nil } else { fmt.Fprintf(jirix.Stdout(), "%v\n", err) } return err }
// MergeInProgress returns a boolean flag that indicates if a merge // operation is in progress for the current repository. func (g *Git) MergeInProgress() (bool, error) { repoRoot, err := g.TopLevel() if err != nil { return false, err } mergeFile := filepath.Join(repoRoot, ".git", "MERGE_HEAD") if _, err := g.s.Stat(mergeFile); err != nil { if runutil.IsNotExist(err) { return false, nil } return false, err } return true, nil }
func setProjectState(jirix *jiri.X, state *ProjectState, checkDirty bool, ch chan<- error) { var err error switch state.Project.Protocol { case "git": scm := gitutil.New(jirix.NewSeq(), gitutil.RootDirOpt(state.Project.Path)) var branches []string branches, state.CurrentBranch, err = scm.GetBranches() if err != nil { ch <- err return } for _, branch := range branches { file := filepath.Join(state.Project.Path, jiri.ProjectMetaDir, branch, ".gerrit_commit_message") hasFile := true if _, err := jirix.NewSeq().Stat(file); err != nil { if !runutil.IsNotExist(err) { ch <- err return } hasFile = false } state.Branches = append(state.Branches, BranchState{ Name: branch, HasGerritMessage: hasFile, }) } if checkDirty { state.HasUncommitted, err = scm.HasUncommittedChanges() if err != nil { ch <- err return } state.HasUntracked, err = scm.HasUntrackedFiles() if err != nil { ch <- err return } } default: ch <- UnsupportedProtocolErr(state.Project.Protocol) return } ch <- nil }
func getDependentCLs(jirix *jiri.X, branch string) ([]string, error) { file, err := getDependencyPathFileName(jirix, branch) if err != nil { return nil, err } data, err := jirix.NewSeq().ReadFile(file) var branches []string if err != nil { if !runutil.IsNotExist(err) { return nil, err } if branch != remoteBranchFlag { branches = []string{remoteBranchFlag} } } else { branches = strings.Split(strings.TrimSpace(string(data)), "\n") } return branches, nil }
// confirmFlagChanges asks users to confirm if any of the // presubmit and autosubmit flags changes. func (review *review) confirmFlagChanges() (bool, error) { file, err := getCommitMessageFileName(review.jirix, review.CLOpts.Branch) if err != nil { return false, err } bytes, err := review.jirix.NewSeq().ReadFile(file) if err != nil { if runutil.IsNotExist(err) { return true, nil } return false, err } content := string(bytes) changes := []string{} // Check presubmit label change. prevPresubmitType := string(gerrit.PresubmitTestTypeAll) matches := presubmitTestLabelRE.FindStringSubmatch(content) if matches != nil { prevPresubmitType = matches[1] } if presubmitFlag != prevPresubmitType { changes = append(changes, fmt.Sprintf("- presubmit=%s to presubmit=%s", prevPresubmitType, presubmitFlag)) } // Check autosubmit label change. prevAutosubmit := autosubmitLabelRE.MatchString(content) if autosubmitFlag != prevAutosubmit { changes = append(changes, fmt.Sprintf("- autosubmit=%v to autosubmit=%v", prevAutosubmit, autosubmitFlag)) } if len(changes) > 0 { fmt.Printf("Changes:\n%s\n", strings.Join(changes, "\n")) fmt.Print("Are you sure you want to make the above changes? y/N:") var response string if _, err := fmt.Scanf("%s\n", &response); err != nil || response != "y" { return false, nil } } return true, nil }
func runImport(jirix *jiri.X, args []string) error { if len(args) != 2 { return jirix.UsageErrorf("wrong number of arguments") } // Initialize manifest. var manifest *project.Manifest if !flagImportOverwrite { m, err := project.ManifestFromFile(jirix, jirix.JiriManifestFile()) if err != nil && !runutil.IsNotExist(err) { return err } manifest = m } if manifest == nil { manifest = &project.Manifest{} } // There's not much error checking when writing the .jiri_manifest file; // errors will be reported when "jiri update" is run. manifest.Imports = append(manifest.Imports, project.Import{ Manifest: args[0], Name: flagImportName, Protocol: flagImportProtocol, Remote: args[1], RemoteBranch: flagImportRemoteBranch, Root: flagImportRoot, }) // Write output to stdout or file. outFile := flagImportOut if outFile == "" { outFile = jirix.JiriManifestFile() } if outFile == "-" { bytes, err := manifest.ToBytes() if err != nil { return err } _, err = os.Stdout.Write(bytes) return err } return manifest.ToFile(jirix, outFile) }
// updateReviewMessage writes the commit message to the given file. func (review *review) updateReviewMessage(file string) error { git := gitutil.New(review.jirix.NewSeq()) if err := git.CheckoutBranch(review.reviewBranch); err != nil { return err } newMessage, err := git.LatestCommitMessage() if err != nil { return err } // update MultiPart metadata. mpart := review.readMultiPart() newMessage = multiPartRE.ReplaceAllLiteralString(newMessage, mpart) s := review.jirix.NewSeq() // For the initial commit where the commit message file doesn't exist, // add/remove labels after users finish editing the commit message. // // This behavior is consistent with how Change-ID is added for the // initial commit so we don't confuse users. if _, err := s.Stat(file); err != nil { if runutil.IsNotExist(err) { newMessage = review.processLabelsAndCommitFile(newMessage) if err := git.CommitAmendWithMessage(newMessage); err != nil { return err } } else { return err } } topLevel, err := git.TopLevel() if err != nil { return err } newMetadataDir := filepath.Join(topLevel, jiri.ProjectMetaDir, review.CLOpts.Branch) if err := s.MkdirAll(newMetadataDir, os.FileMode(0755)). WriteFile(file, []byte(newMessage), 0644).Done(); err != nil { return err } return nil }
func runSnapshotList(jirix *jiri.X, args []string) error { snapshotDir, err := getSnapshotDir(jirix) if err != nil { return err } if len(args) == 0 { // Identify all known snapshot labels, using a // heuristic that looks for all symbolic links <foo> // in the snapshot directory that point to a file in // the "labels/<foo>" subdirectory of the snapshot // directory. fileInfoList, err := ioutil.ReadDir(snapshotDir) if err != nil { return fmt.Errorf("ReadDir(%v) failed: %v", snapshotDir, err) } for _, fileInfo := range fileInfoList { if fileInfo.Mode()&os.ModeSymlink != 0 { path := filepath.Join(snapshotDir, fileInfo.Name()) dst, err := filepath.EvalSymlinks(path) if err != nil { return fmt.Errorf("EvalSymlinks(%v) failed: %v", path, err) } if strings.HasSuffix(filepath.Dir(dst), filepath.Join("labels", fileInfo.Name())) { args = append(args, fileInfo.Name()) } } } } // Check that all labels exist. var notexist []string for _, label := range args { labelDir := filepath.Join(snapshotDir, "labels", label) switch _, err := jirix.NewSeq().Stat(labelDir); { case runutil.IsNotExist(err): notexist = append(notexist, label) case err != nil: return err } } if len(notexist) > 0 { return fmt.Errorf("snapshot labels %v not found", notexist) } // Print snapshots for all labels. sort.Strings(args) for _, label := range args { // Scan the snapshot directory "labels/<label>" printing // all snapshots. labelDir := filepath.Join(snapshotDir, "labels", label) fileInfoList, err := ioutil.ReadDir(labelDir) if err != nil { return fmt.Errorf("ReadDir(%v) failed: %v", labelDir, err) } fmt.Fprintf(jirix.Stdout(), "snapshots of label %q:\n", label) for _, fileInfo := range fileInfoList { fmt.Fprintf(jirix.Stdout(), " %v\n", fileInfo.Name()) } } return nil }
func cleanupBranch(jirix *jiri.X, branch string) error { git := gitutil.New(jirix.NewSeq()) if err := git.CheckoutBranch(branch); err != nil { return err } if !forceFlag { trackingBranch := "origin/" + remoteBranchFlag if err := git.Merge(trackingBranch); err != nil { return err } files, err := git.ModifiedFiles(trackingBranch, branch) if err != nil { return err } if len(files) != 0 { return fmt.Errorf("unmerged changes in\n%s", strings.Join(files, "\n")) } } if err := git.CheckoutBranch(remoteBranchFlag); err != nil { return err } if err := git.DeleteBranch(branch, gitutil.ForceOpt(true)); err != nil { return err } reviewBranch := branch + "-REVIEW" if git.BranchExists(reviewBranch) { if err := git.DeleteBranch(reviewBranch, gitutil.ForceOpt(true)); err != nil { return err } } // Delete branch metadata. topLevel, err := git.TopLevel() if err != nil { return err } s := jirix.NewSeq() // Remove the branch from all dependency paths. metadataDir := filepath.Join(topLevel, jiri.ProjectMetaDir) fileInfos, err := s.RemoveAll(filepath.Join(metadataDir, branch)). ReadDir(metadataDir) if err != nil { return err } for _, fileInfo := range fileInfos { if !fileInfo.IsDir() { continue } file, err := getDependencyPathFileName(jirix, fileInfo.Name()) if err != nil { return err } data, err := s.ReadFile(file) if err != nil { if !runutil.IsNotExist(err) { return err } continue } branches := strings.Split(string(data), "\n") for i, tmpBranch := range branches { if branch == tmpBranch { data := []byte(strings.Join(append(branches[:i], branches[i+1:]...), "\n")) if err := s.WriteFile(file, data, os.FileMode(0644)).Done(); err != nil { return err } break } } } return nil }
// run implements checks that the review passes all local checks // and then mails it to Gerrit. func (review *review) run() (e error) { git := gitutil.New(review.jirix.NewSeq()) if uncommittedFlag { changes, err := git.FilesWithUncommittedChanges() if err != nil { return err } if len(changes) != 0 { return uncommittedChangesError(changes) } } if review.CLOpts.Branch == remoteBranchFlag { return fmt.Errorf("cannot do a review from the %q branch.", remoteBranchFlag) } stashed, err := git.Stash() if err != nil { return err } defer collect.Error(func() error { return review.cleanup(stashed) }, &e) wd, err := os.Getwd() if err != nil { return fmt.Errorf("Getwd() failed: %v", err) } topLevel, err := git.TopLevel() if err != nil { return err } s := review.jirix.NewSeq() if err := s.Chdir(topLevel).Done(); err != nil { return err } defer collect.Error(func() error { return review.jirix.NewSeq().Chdir(wd).Done() }, &e) file, err := getCommitMessageFileName(review.jirix, review.CLOpts.Branch) if err != nil { return err } message := messageFlag if message == "" { // Message was not passed in flag. Attempt to read it from file. data, err := s.ReadFile(file) if err != nil { if !runutil.IsNotExist(err) { return err } } else { message = string(data) } } // Add/remove labels to/from the commit message before asking users // to edit it. We do this only when this is not the initial commit // where the message is empty. // // For the initial commit, the labels will be processed after the // message is edited by users, which happens in the // updateReviewMessage method. if message != "" { message = review.processLabelsAndCommitFile(message) } if err := review.createReviewBranch(message); err != nil { return err } if err := review.updateReviewMessage(file); err != nil { return err } if err := review.send(); err != nil { return err } if setTopicFlag { if err := review.setTopic(); err != nil { return err } } return nil }