Пример #1
0
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)
	}
}
Пример #2
0
// 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())
}
Пример #3
0
// 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
}
Пример #4
0
// 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)
		}
	}
}
Пример #5
0
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
}
Пример #6
0
// 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
}
Пример #7
0
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
}
Пример #8
0
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
}
Пример #9
0
// 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
}
Пример #10
0
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)
}
Пример #11
0
// 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
}
Пример #12
0
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
}
Пример #13
0
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
}
Пример #14
0
// 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
}