Merge "TBR: Revert "jiri: The jiri script must put $JIRI_ROOT/.jiri_root/bin in PATH before invoking the jiri binary.""
diff --git a/bootstrap_jiri_test.go b/bootstrap_jiri_test.go
deleted file mode 100644
index 3bdca29..0000000
--- a/bootstrap_jiri_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "testing"
-
- "v.io/x/lib/gosh"
-)
-
-func TestBootstrapJiri(t *testing.T) {
- sh := gosh.NewShell(gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf, PropagateChildOutput: true})
- defer sh.Cleanup()
-
- bootstrap, err := filepath.Abs("./scripts/bootstrap_jiri")
- if err != nil {
- t.Fatalf("couldn't determine absolute path to bootstrap_jiri script")
- }
- rootDir := filepath.Join(sh.MakeTempDir(), "root")
- stdout, stderr := sh.Cmd(bootstrap, rootDir).StdoutStderr()
- if got, want := stdout, fmt.Sprintf("Please add %s to your PATH.\n", filepath.Join(rootDir, ".jiri_root", "scripts")); got != want {
- t.Errorf("stdout got %q, want %q", got, want)
- }
- if got, want := stderr, ""; got != want {
- t.Errorf("stderr got %q, want %q", got, want)
- }
- if _, err := os.Stat(filepath.Join(rootDir, ".jiri_root", "bin", "jiri")); err != nil {
- t.Error(err)
- }
- if _, err := os.Stat(filepath.Join(rootDir, ".jiri_root", "scripts", "jiri")); err != nil {
- t.Error(err)
- }
-}
-
-func TestBootstrapJiriAlreadyExists(t *testing.T) {
- sh := gosh.NewShell(gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf, PropagateChildOutput: true})
- defer sh.Cleanup()
-
- bootstrap, err := filepath.Abs("./scripts/bootstrap_jiri")
- if err != nil {
- t.Fatalf("couldn't determine absolute path to bootstrap_jiri script")
- }
- rootDir := sh.MakeTempDir()
- c := sh.Cmd(bootstrap, rootDir)
- c.ExitErrorIsOk = true
- stdout, stderr := c.StdoutStderr()
- if c.Err == nil {
- t.Errorf("error got %q, want nil", c.Err)
- }
- if got, want := stdout, ""; got != want {
- t.Errorf("stdout got %q, want %q", got, want)
- }
- if got, want := stderr, rootDir+" already exists"; !strings.Contains(got, want) {
- t.Errorf("stderr got %q, want substr %q", got, want)
- }
-}
diff --git a/cl.go b/cl.go
deleted file mode 100644
index b5a8721..0000000
--- a/cl.go
+++ /dev/null
@@ -1,1120 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "net/url"
- "os"
- "path/filepath"
- "regexp"
- "strings"
-
- "v.io/jiri/collect"
- "v.io/jiri/gerrit"
- "v.io/jiri/gitutil"
- "v.io/jiri/jiri"
- "v.io/jiri/project"
- "v.io/jiri/runutil"
- "v.io/x/lib/cmdline"
-)
-
-const (
- commitMessageFileName = ".gerrit_commit_message"
- dependencyPathFileName = ".dependency_path"
-)
-
-var (
- autosubmitFlag bool
- ccsFlag string
- draftFlag bool
- editFlag bool
- forceFlag bool
- hostFlag string
- messageFlag string
- presubmitFlag string
- remoteBranchFlag string
- reviewersFlag string
- setTopicFlag bool
- topicFlag string
- uncommittedFlag bool
- verifyFlag bool
-)
-
-// Special labels stored in the commit message.
-var (
- // Auto submit label.
- autosubmitLabelRE *regexp.Regexp = regexp.MustCompile("AutoSubmit")
-
- // Change-Ids start with 'I' and are followed by 40 characters of hex.
- changeIDRE *regexp.Regexp = regexp.MustCompile("Change-Id: (I[0123456789abcdefABCDEF]{40})")
-
- // Presubmit test label.
- // PresubmitTest: <type>
- presubmitTestLabelRE *regexp.Regexp = regexp.MustCompile(`PresubmitTest:\s*(.*)`)
-)
-
-// init carries out the package initialization.
-func init() {
- cmdCLCleanup.Flags.BoolVar(&forceFlag, "f", false, `Ignore unmerged changes.`)
- cmdCLCleanup.Flags.StringVar(&remoteBranchFlag, "remote-branch", "master", `Name of the remote branch the CL pertains to, without the leading "origin/".`)
- cmdCLMail.Flags.BoolVar(&autosubmitFlag, "autosubmit", false, `Automatically submit the changelist when feasible.`)
- cmdCLMail.Flags.StringVar(&ccsFlag, "cc", "", `Comma-seperated list of emails or LDAPs to cc.`)
- cmdCLMail.Flags.BoolVar(&draftFlag, "d", false, `Send a draft changelist.`)
- cmdCLMail.Flags.BoolVar(&editFlag, "edit", true, `Open an editor to edit the CL description.`)
- cmdCLMail.Flags.StringVar(&hostFlag, "host", "", `Gerrit host to use. Defaults to gerrit host specified in manifest.`)
- cmdCLMail.Flags.StringVar(&messageFlag, "m", "", `CL description.`)
- cmdCLMail.Flags.StringVar(&presubmitFlag, "presubmit", string(gerrit.PresubmitTestTypeAll),
- fmt.Sprintf("The type of presubmit tests to run. Valid values: %s.", strings.Join(gerrit.PresubmitTestTypes(), ",")))
- cmdCLMail.Flags.StringVar(&remoteBranchFlag, "remote-branch", "master", `Name of the remote branch the CL pertains to, without the leading "origin/".`)
- cmdCLMail.Flags.StringVar(&reviewersFlag, "r", "", `Comma-seperated list of emails or LDAPs to request review.`)
- cmdCLMail.Flags.BoolVar(&setTopicFlag, "set-topic", true, `Set Gerrit CL topic.`)
- cmdCLMail.Flags.StringVar(&topicFlag, "topic", "", `CL topic, defaults to <username>-<branchname>.`)
- cmdCLMail.Flags.BoolVar(&uncommittedFlag, "check-uncommitted", true, `Check that no uncommitted changes exist.`)
- cmdCLMail.Flags.BoolVar(&verifyFlag, "verify", true, `Run pre-push git hooks.`)
- cmdCLSync.Flags.StringVar(&remoteBranchFlag, "remote-branch", "master", `Name of the remote branch the CL pertains to, without the leading "origin/".`)
-}
-
-func getCommitMessageFileName(jirix *jiri.X, branch string) (string, error) {
- topLevel, err := gitutil.New(jirix.NewSeq()).TopLevel()
- if err != nil {
- return "", err
- }
- return filepath.Join(topLevel, jiri.ProjectMetaDir, branch, commitMessageFileName), nil
-}
-
-func getDependencyPathFileName(jirix *jiri.X, branch string) (string, error) {
- topLevel, err := gitutil.New(jirix.NewSeq()).TopLevel()
- if err != nil {
- return "", err
- }
- return filepath.Join(topLevel, jiri.ProjectMetaDir, branch, dependencyPathFileName), 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
-}
-
-// cmdCL represents the "jiri cl" command.
-var cmdCL = &cmdline.Command{
- Name: "cl",
- Short: "Manage project changelists",
- Long: "Manage project changelists.",
- Children: []*cmdline.Command{cmdCLCleanup, cmdCLMail, cmdCLNew, cmdCLSync},
-}
-
-// cmdCLCleanup represents the "jiri cl cleanup" command.
-//
-// TODO(jsimsa): Replace this with a "submit" command that talks to
-// Gerrit to submit the CL and then (optionally) removes it locally.
-var cmdCLCleanup = &cmdline.Command{
- Runner: jiri.RunnerFunc(runCLCleanup),
- Name: "cleanup",
- Short: "Clean up changelists that have been merged",
- Long: `
-Command "cleanup" checks that the given branches have been merged into
-the corresponding remote branch. If a branch differs from the
-corresponding remote branch, the command reports the difference and
-stops. Otherwise, it deletes the given branches.
-`,
- ArgsName: "<branches>",
- ArgsLong: "<branches> is a list of branches to cleanup.",
-}
-
-func cleanupCL(jirix *jiri.X, branches []string) (e error) {
- originalBranch, err := gitutil.New(jirix.NewSeq()).CurrentBranchName()
- if err != nil {
- return err
- }
- stashed, err := gitutil.New(jirix.NewSeq()).Stash()
- if err != nil {
- return err
- }
- if stashed {
- defer collect.Error(func() error { return gitutil.New(jirix.NewSeq()).StashPop() }, &e)
- }
- if err := gitutil.New(jirix.NewSeq()).CheckoutBranch(remoteBranchFlag); err != nil {
- return err
- }
- checkoutOriginalBranch := true
- defer collect.Error(func() error {
- if checkoutOriginalBranch {
- return gitutil.New(jirix.NewSeq()).CheckoutBranch(originalBranch)
- }
- return nil
- }, &e)
- if err := gitutil.New(jirix.NewSeq()).FetchRefspec("origin", remoteBranchFlag); err != nil {
- return err
- }
- s := jirix.NewSeq()
- for _, branch := range branches {
- cleanupFn := func() error { return cleanupBranch(jirix, branch) }
- if err := s.Call(cleanupFn, "Cleaning up branch: %s", branch).Done(); err != nil {
- return err
- }
- if branch == originalBranch {
- checkoutOriginalBranch = false
- }
- }
- return nil
-}
-
-func cleanupBranch(jirix *jiri.X, branch string) error {
- if err := gitutil.New(jirix.NewSeq()).CheckoutBranch(branch); err != nil {
- return err
- }
- if !forceFlag {
- trackingBranch := "origin/" + remoteBranchFlag
- if err := gitutil.New(jirix.NewSeq()).Merge(trackingBranch); err != nil {
- return err
- }
- files, err := gitutil.New(jirix.NewSeq()).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 := gitutil.New(jirix.NewSeq()).CheckoutBranch(remoteBranchFlag); err != nil {
- return err
- }
- if err := gitutil.New(jirix.NewSeq()).DeleteBranch(branch, gitutil.ForceOpt(true)); err != nil {
- return err
- }
- reviewBranch := branch + "-REVIEW"
- if gitutil.New(jirix.NewSeq()).BranchExists(reviewBranch) {
- if err := gitutil.New(jirix.NewSeq()).DeleteBranch(reviewBranch, gitutil.ForceOpt(true)); err != nil {
- return err
- }
- }
- // Delete branch metadata.
- topLevel, err := gitutil.New(jirix.NewSeq()).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
-}
-
-func runCLCleanup(jirix *jiri.X, args []string) error {
- if len(args) == 0 {
- return jirix.UsageErrorf("cleanup requires at least one argument")
- }
- return cleanupCL(jirix, args)
-}
-
-// cmdCLMail represents the "jiri cl mail" command.
-var cmdCLMail = &cmdline.Command{
- Runner: jiri.RunnerFunc(runCLMail),
- Name: "mail",
- Short: "Mail a changelist for review",
- Long: `
-Command "mail" squashes all commits of a local branch into a single
-"changelist" and mails this changelist to Gerrit as a single
-commit. First time the command is invoked, it generates a Change-Id
-for the changelist, which is appended to the commit
-message. Consecutive invocations of the command use the same Change-Id
-by default, informing Gerrit that the incomming commit is an update of
-an existing changelist.
-`,
-}
-
-type changeConflictError struct {
- localBranch string
- message string
- remoteBranch string
-}
-
-func (e changeConflictError) Error() string {
- result := "changelist conflicts with the remote " + e.remoteBranch + " branch\n\n"
- result += "To resolve this problem, run 'git pull origin " + e.remoteBranch + ":" + e.localBranch + "',\n"
- result += "resolve the conflicts identified below, and then try again.\n"
- result += e.message
- return result
-}
-
-type emptyChangeError struct{}
-
-func (_ emptyChangeError) Error() string {
- return "current branch has no commits"
-}
-
-type gerritError string
-
-func (e gerritError) Error() string {
- result := "sending code review failed\n\n"
- result += string(e)
- return result
-}
-
-type noChangeIDError struct{}
-
-func (_ noChangeIDError) Error() string {
- result := "changelist is missing a Change-ID"
- return result
-}
-
-type uncommittedChangesError []string
-
-func (e uncommittedChangesError) Error() string {
- result := "uncommitted local changes in files:\n"
- result += " " + strings.Join(e, "\n ")
- return result
-}
-
-var defaultMessageHeader = `
-# Describe your changelist, specifying what package(s) your change
-# pertains to, followed by a short summary and, in case of non-trivial
-# changelists, provide a detailed description.
-#
-# For example:
-#
-# rpc/stream/proxy: add publish address
-#
-# The listen address is not always the same as the address that external
-# users need to connect to. This CL adds a new argument to proxy.New()
-# to specify the published address that clients should connect to.
-
-# FYI, you are about to submit the following local commits for review:
-#
-`
-
-// currentProject returns the Project containing the current working directory.
-// The current working directory must be inside JIRI_ROOT.
-func currentProject(jirix *jiri.X) (project.Project, error) {
- dir, err := os.Getwd()
- if err != nil {
- return project.Project{}, fmt.Errorf("os.Getwd() failed: %v", err)
- }
-
- // Error if current working dir is not inside jirix.Root.
- if !strings.HasPrefix(dir, jirix.Root) {
- return project.Project{}, fmt.Errorf("'jiri cl mail' must be run from within a project in JIRI_ROOT")
- }
-
- // Walk up the path until we find a project at that path, or hit the jirix.Root.
- for dir != jirix.Root {
- p, err := project.ProjectAtPath(jirix, dir)
- if err != nil {
- dir = filepath.Dir(dir)
- continue
- }
- return p, nil
- }
- return project.Project{}, fmt.Errorf("directory %q is not contained in a project", dir)
-}
-
-// runCLMail is a wrapper that sets up and runs a review instance.
-func runCLMail(jirix *jiri.X, _ []string) error {
- // Sanity checks for the <presubmitFlag> flag.
- if !checkPresubmitFlag() {
- return jirix.UsageErrorf("invalid value for the -presubmit flag. Valid values: %s.",
- strings.Join(gerrit.PresubmitTestTypes(), ","))
- }
-
- p, err := currentProject(jirix)
- if err != nil {
- return err
- }
-
- host := hostFlag
- if host == "" {
- if p.GerritHost == "" {
- return fmt.Errorf("No gerrit host found. Please use the '--host' flag, or add a 'gerrithost' attribute for project %q.", p.Name)
- }
- host = p.GerritHost
- }
- hostUrl, err := url.Parse(host)
- if err != nil {
- return fmt.Errorf("invalid Gerrit host %q: %v", host, err)
- }
- projectRemoteUrl, err := url.Parse(p.Remote)
- if err != nil {
- return fmt.Errorf("invalid project remote: %v", p.Remote, err)
- }
- gerritRemote := *hostUrl
- gerritRemote.Path = projectRemoteUrl.Path
-
- // Create and run the review.
- review, err := newReview(jirix, gerrit.CLOpts{
- Autosubmit: autosubmitFlag,
- Ccs: parseEmails(ccsFlag),
- Draft: draftFlag,
- Edit: editFlag,
- Remote: gerritRemote.String(),
- Host: hostUrl,
- Presubmit: gerrit.PresubmitTestType(presubmitFlag),
- RemoteBranch: remoteBranchFlag,
- Reviewers: parseEmails(reviewersFlag),
- Verify: verifyFlag,
- })
- if err != nil {
- return err
- }
- if confirmed, err := review.confirmFlagChanges(); err != nil {
- return err
- } else if !confirmed {
- return nil
- }
- return review.run()
-}
-
-// parseEmails input a list of comma separated tokens and outputs a
-// list of email addresses. The tokens can either be email addresses
-// or Google LDAPs in which case the suffix @google.com is appended to
-// them to turn them into email addresses.
-func parseEmails(value string) []string {
- var emails []string
- tokens := strings.Split(value, ",")
- for _, token := range tokens {
- if token == "" {
- continue
- }
- if !strings.Contains(token, "@") {
- token += "@google.com"
- }
- emails = append(emails, token)
- }
- return emails
-}
-
-// 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
-}
-
-type review struct {
- jirix *jiri.X
- reviewBranch string
- gerrit.CLOpts
-}
-
-func newReview(jirix *jiri.X, opts gerrit.CLOpts) (*review, error) {
- // Sync all CLs in the sequence of dependent CLs ending in the
- // current branch.
- if err := syncCL(jirix); err != nil {
- return nil, err
- }
-
- // Make sure that all CLs in the above sequence (possibly except for
- // the current branch) have been exported to Gerrit. This is needed
- // to make sure we have commit messages for all but the last CL.
- //
- // NOTE: The alternative here is to prompt the user for multiple
- // commit messages, which seems less user friendly.
- if err := checkDependents(jirix); err != nil {
- return nil, err
- }
-
- branch, err := gitutil.New(jirix.NewSeq()).CurrentBranchName()
- if err != nil {
- return nil, err
- }
- opts.Branch = branch
- if opts.Topic == "" {
- opts.Topic = fmt.Sprintf("%s-%s", os.Getenv("USER"), branch) // use <username>-<branchname> as the default
- }
- if opts.Presubmit == gerrit.PresubmitTestType("") {
- opts.Presubmit = gerrit.PresubmitTestTypeAll // use gerrit.PresubmitTestTypeAll as the default
- }
- if opts.RemoteBranch == "" {
- opts.RemoteBranch = "master" // use master as the default
- }
- return &review{
- jirix: jirix,
- reviewBranch: branch + "-REVIEW",
- CLOpts: opts,
- }, nil
-}
-
-func checkPresubmitFlag() bool {
- for _, t := range gerrit.PresubmitTestTypes() {
- if presubmitFlag == t {
- return true
- }
- }
- return false
-}
-
-// 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
-}
-
-// cleanup cleans up after the review.
-func (review *review) cleanup(stashed bool) error {
- if err := gitutil.New(review.jirix.NewSeq()).CheckoutBranch(review.CLOpts.Branch); err != nil {
- return err
- }
- if gitutil.New(review.jirix.NewSeq()).BranchExists(review.reviewBranch) {
- if err := gitutil.New(review.jirix.NewSeq()).DeleteBranch(review.reviewBranch, gitutil.ForceOpt(true)); err != nil {
- return err
- }
- }
- if stashed {
- if err := gitutil.New(review.jirix.NewSeq()).StashPop(); err != nil {
- return err
- }
- }
- return nil
-}
-
-// createReviewBranch creates a clean review branch from the remote
-// branch this CL pertains to and then iterates over the sequence of
-// dependent CLs leading to the current branch, creating one commit
-// per CL by squashing all commits of each individual CL. The commit
-// message for all but that last CL is derived from their
-// <commitMessageFileName>, while the <message> argument is used as
-// the commit message for the last commit.
-func (review *review) createReviewBranch(message string) (e error) {
- // Create the review branch.
- if err := gitutil.New(review.jirix.NewSeq()).FetchRefspec("origin", review.CLOpts.RemoteBranch); err != nil {
- return err
- }
- if gitutil.New(review.jirix.NewSeq()).BranchExists(review.reviewBranch) {
- if err := gitutil.New(review.jirix.NewSeq()).DeleteBranch(review.reviewBranch, gitutil.ForceOpt(true)); err != nil {
- return err
- }
- }
- upstream := "origin/" + review.CLOpts.RemoteBranch
- if err := gitutil.New(review.jirix.NewSeq()).CreateBranchWithUpstream(review.reviewBranch, upstream); err != nil {
- return err
- }
- if err := gitutil.New(review.jirix.NewSeq()).CheckoutBranch(review.reviewBranch); err != nil {
- return err
- }
- // Register a cleanup handler in case of subsequent errors.
- cleanup := true
- defer collect.Error(func() error {
- if !cleanup {
- return gitutil.New(review.jirix.NewSeq()).CheckoutBranch(review.CLOpts.Branch)
- }
- gitutil.New(review.jirix.NewSeq()).CheckoutBranch(review.CLOpts.Branch, gitutil.ForceOpt(true))
- gitutil.New(review.jirix.NewSeq()).DeleteBranch(review.reviewBranch, gitutil.ForceOpt(true))
- return nil
- }, &e)
-
- // Report an error if the CL is empty.
- if !review.jirix.DryRun() {
- hasDiff, err := gitutil.New(review.jirix.NewSeq()).BranchesDiffer(review.CLOpts.Branch, review.reviewBranch)
- if err != nil {
- return err
- }
- if !hasDiff {
- return emptyChangeError(struct{}{})
- }
- }
-
- // If <message> is empty, replace it with the default message.
- if len(message) == 0 {
- var err error
- message, err = review.defaultCommitMessage()
- if err != nil {
- return err
- }
- }
-
- // Iterate over all dependent CLs leading to (and including) the
- // current branch, creating one commit in the review branch per CL
- // by squashing all commits of each individual CL.
- branches, err := getDependentCLs(review.jirix, review.CLOpts.Branch)
- if err != nil {
- return err
- }
- branches = append(branches, review.CLOpts.Branch)
- if err := review.squashBranches(branches, message); err != nil {
- return err
- }
-
- cleanup = false
- return nil
-}
-
-// squashBranches iterates over the given list of branches, creating
-// one commit per branch in the current branch by squashing all
-// commits of each individual branch.
-//
-// TODO(jsimsa): Consider using "git rebase --onto" to avoid having to
-// deal with merge conflicts.
-func (review *review) squashBranches(branches []string, message string) (e error) {
- for i := 1; i < len(branches); i++ {
- // We want to merge the <branches[i]> branch on top of the review
- // branch, forcing all conflicts to be reviewed in favor of the
- // <branches[i]> branch. Unfortunately, git merge does not offer a
- // strategy that would do that for us. The solution implemented
- // here is based on:
- //
- // http://stackoverflow.com/questions/173919/is-there-a-theirs-version-of-git-merge-s-ours
- if err := gitutil.New(review.jirix.NewSeq()).Merge(branches[i], gitutil.SquashOpt(true), gitutil.StrategyOpt("ours")); err != nil {
- return changeConflictError{
- localBranch: branches[i],
- remoteBranch: review.CLOpts.RemoteBranch,
- message: err.Error(),
- }
- }
- // Fetch the timestamp of the last commit of <branches[i]> and use
- // it to create the squashed commit. This is needed to make sure
- // that the commit hash of the squashed commit stays the same as
- // long as the squashed sequence of commits does not change. If
- // this was not the case, consecutive invocations of "jiri cl mail"
- // could fail if some, but not all, of the dependent CLs submitted
- // to Gerrit have changed.
- output, err := gitutil.New(review.jirix.NewSeq()).Log(branches[i], branches[i]+"^", "%ad%n%cd")
- if err != nil {
- return err
- }
- if len(output) < 1 || len(output[0]) < 2 {
- return fmt.Errorf("unexpected output length: %v", output)
- }
- authorDate := gitutil.AuthorDateOpt(output[0][0])
- committer := gitutil.CommitterDateOpt(output[0][1])
- git := gitutil.New(review.jirix.NewSeq(), authorDate, committer)
- if i < len(branches)-1 {
- file, err := getCommitMessageFileName(review.jirix, branches[i])
- if err != nil {
- return err
- }
- message, err := review.jirix.NewSeq().ReadFile(file)
- if err != nil {
- return err
- }
- if err := git.CommitWithMessage(string(message)); err != nil {
- return err
- }
- } else {
- committer := git.NewCommitter(review.CLOpts.Edit)
- if err := committer.Commit(message); err != nil {
- return err
- }
- }
- tmpBranch := review.reviewBranch + "-" + branches[i] + "-TMP"
- if err := git.CreateBranch(tmpBranch); err != nil {
- return err
- }
- defer collect.Error(func() error {
- return gitutil.New(review.jirix.NewSeq()).DeleteBranch(tmpBranch, gitutil.ForceOpt(true))
- }, &e)
- if err := git.Reset(branches[i]); err != nil {
- return err
- }
- if err := git.Reset(tmpBranch, gitutil.ModeOpt("soft")); err != nil {
- return err
- }
- if err := git.CommitAmend(); err != nil {
- return err
- }
- }
- return nil
-}
-
-// defaultCommitMessage creates the default commit message from the
-// list of commits on the branch.
-func (review *review) defaultCommitMessage() (string, error) {
- commitMessages, err := gitutil.New(review.jirix.NewSeq()).CommitMessages(review.CLOpts.Branch, review.reviewBranch)
- if err != nil {
- return "", err
- }
- // Strip "Change-Id: ..." from the commit messages.
- strippedMessages := changeIDRE.ReplaceAllLiteralString(commitMessages, "")
- // Add comment markers (#) to every line.
- commentedMessages := "# " + strings.Replace(strippedMessages, "\n", "\n# ", -1)
- message := defaultMessageHeader + commentedMessages
- return message, nil
-}
-
-// ensureChangeID makes sure that the last commit contains a Change-Id, and
-// returns an error if it does not.
-func (review *review) ensureChangeID() error {
- latestCommitMessage, err := gitutil.New(review.jirix.NewSeq()).LatestCommitMessage()
- if err != nil {
- return err
- }
- changeID := changeIDRE.FindString(latestCommitMessage)
- if changeID == "" {
- return noChangeIDError(struct{}{})
- }
- return nil
-}
-
-// processLabels adds/removes labels for the given commit message.
-func (review *review) processLabels(message string) string {
- // Find the Change-ID line.
- changeIDLine := changeIDRE.FindString(message)
-
- // Strip existing labels and change-ID.
- message = autosubmitLabelRE.ReplaceAllLiteralString(message, "")
- message = presubmitTestLabelRE.ReplaceAllLiteralString(message, "")
- message = changeIDRE.ReplaceAllLiteralString(message, "")
-
- // Insert labels and change-ID back.
- if review.CLOpts.Autosubmit {
- message += fmt.Sprintf("AutoSubmit\n")
- }
- if review.CLOpts.Presubmit != gerrit.PresubmitTestTypeAll {
- message += fmt.Sprintf("PresubmitTest: %s\n", review.CLOpts.Presubmit)
- }
- if changeIDLine != "" && !strings.HasSuffix(message, "\n") {
- message += "\n"
- }
- message += changeIDLine
-
- return message
-}
-
-// run implements checks that the review passes all local checks
-// and then mails it to Gerrit.
-func (review *review) run() (e error) {
- if uncommittedFlag {
- changes, err := gitutil.New(review.jirix.NewSeq()).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 := gitutil.New(review.jirix.NewSeq()).Stash()
- if err != nil {
- return err
- }
- wd, err := os.Getwd()
- if err != nil {
- return fmt.Errorf("Getwd() failed: %v", err)
- }
- defer collect.Error(func() error { return review.jirix.NewSeq().Chdir(wd).Done() }, &e)
- topLevel, err := gitutil.New(review.jirix.NewSeq()).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.cleanup(stashed) }, &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.processLabels(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
-}
-
-// send mails the current branch out for review.
-func (review *review) send() error {
- if !review.jirix.DryRun() {
- if err := review.ensureChangeID(); err != nil {
- return err
- }
- }
- if err := gerrit.Push(review.jirix.NewSeq(), review.CLOpts); err != nil {
- return gerritError(err.Error())
- }
- return nil
-}
-
-// getChangeID reads the commit message and extracts the change-Id.
-func (review *review) getChangeID() (string, error) {
- file, err := getCommitMessageFileName(review.jirix, review.CLOpts.Branch)
- if err != nil {
- return "", err
- }
- bytes, err := review.jirix.NewSeq().ReadFile(file)
- if err != nil {
- return "", err
- }
- changeID := changeIDRE.FindSubmatch(bytes)
- if changeID == nil || len(changeID) < 2 {
- return "", fmt.Errorf("could not find Change-Id in:\n%s", bytes)
- }
- return string(changeID[1]), nil
-}
-
-// setTopic sets the topic for the CL corresponding to the branch the
-// review was created for.
-func (review *review) setTopic() error {
- changeID, err := review.getChangeID()
- if err != nil {
- return err
- }
- host := review.CLOpts.Host
- if host.Scheme != "http" && host.Scheme != "https" {
- return fmt.Errorf("Cannot set topic for gerrit host %q. Please use a host url with 'https' scheme or run with '--set-topic=false'.", host.String())
- }
- if err := review.jirix.Gerrit(host).SetTopic(changeID, review.CLOpts); err != nil {
- return err
- }
- return nil
-}
-
-// updateReviewMessage writes the commit message to the given file.
-func (review *review) updateReviewMessage(file string) error {
- if err := gitutil.New(review.jirix.NewSeq()).CheckoutBranch(review.reviewBranch); err != nil {
- return err
- }
- newMessage, err := gitutil.New(review.jirix.NewSeq()).LatestCommitMessage()
- if err != nil {
- return err
- }
- 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.processLabels(newMessage)
- if err := gitutil.New(review.jirix.NewSeq()).CommitAmendWithMessage(newMessage); err != nil {
- return err
- }
- } else {
- return err
- }
- }
- topLevel, err := gitutil.New(review.jirix.NewSeq()).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
-}
-
-// cmdCLNew represents the "jiri cl new" command.
-var cmdCLNew = &cmdline.Command{
- Runner: jiri.RunnerFunc(runCLNew),
- Name: "new",
- Short: "Create a new local branch for a changelist",
- Long: fmt.Sprintf(`
-Command "new" creates a new local branch for a changelist. In
-particular, it forks a new branch with the given name from the current
-branch and records the relationship between the current branch and the
-new branch in the %v metadata directory. The information recorded in
-the %v metadata directory tracks dependencies between CLs and is used
-by the "jiri cl sync" and "jiri cl mail" commands.
-`, jiri.ProjectMetaDir, jiri.ProjectMetaDir),
- ArgsName: "<name>",
- ArgsLong: "<name> is the changelist name.",
-}
-
-func runCLNew(jirix *jiri.X, args []string) error {
- if got, want := len(args), 1; got != want {
- return jirix.UsageErrorf("unexpected number of arguments: got %v, want %v", got, want)
- }
- return newCL(jirix, args)
-}
-
-func newCL(jirix *jiri.X, args []string) error {
- topLevel, err := gitutil.New(jirix.NewSeq()).TopLevel()
- if err != nil {
- return err
- }
- originalBranch, err := gitutil.New(jirix.NewSeq()).CurrentBranchName()
- if err != nil {
- return err
- }
-
- // Create a new branch using the current branch.
- newBranch := args[0]
- if err := gitutil.New(jirix.NewSeq()).CreateAndCheckoutBranch(newBranch); err != nil {
- return err
- }
-
- // Register a cleanup handler in case of subsequent errors.
- cleanup := true
- defer func() {
- if cleanup {
- gitutil.New(jirix.NewSeq()).CheckoutBranch(originalBranch, gitutil.ForceOpt(true))
- gitutil.New(jirix.NewSeq()).DeleteBranch(newBranch, gitutil.ForceOpt(true))
- }
- }()
-
- s := jirix.NewSeq()
- // Record the dependent CLs for the new branch. The dependent CLs
- // are recorded in a <dependencyPathFileName> file as a
- // newline-separated list of branch names.
- branches, err := getDependentCLs(jirix, originalBranch)
- if err != nil {
- return err
- }
- branches = append(branches, originalBranch)
- newMetadataDir := filepath.Join(topLevel, jiri.ProjectMetaDir, newBranch)
- if err := s.MkdirAll(newMetadataDir, os.FileMode(0755)).Done(); err != nil {
- return err
- }
- file, err := getDependencyPathFileName(jirix, newBranch)
- if err != nil {
- return err
- }
- if err := s.WriteFile(file, []byte(strings.Join(branches, "\n")), os.FileMode(0644)).Done(); err != nil {
- return err
- }
-
- cleanup = false
- return nil
-}
-
-// cmdCLSync represents the "jiri cl sync" command.
-var cmdCLSync = &cmdline.Command{
- Runner: jiri.RunnerFunc(runCLSync),
- Name: "sync",
- Short: "Bring a changelist up to date",
- Long: fmt.Sprintf(`
-Command "sync" brings the CL identified by the current branch up to
-date with the branch tracking the remote branch this CL pertains
-to. To do that, the command uses the information recorded in the %v
-metadata directory to identify the sequence of dependent CLs leading
-to the current branch. The command then iterates over this sequence
-bringing each of the CLs up to date with its ancestor. The end result
-of this process is that all CLs in the sequence are up to date with
-the branch that tracks the remote branch this CL pertains to.
-
-NOTE: It is possible that the command cannot automatically merge
-changes in an ancestor into its dependent. When that occurs, the
-command is aborted and prints instructions that need to be followed
-before the command can be retried.
-`, jiri.ProjectMetaDir),
-}
-
-func runCLSync(jirix *jiri.X, _ []string) error {
- return syncCL(jirix)
-}
-
-func syncCL(jirix *jiri.X) (e error) {
- stashed, err := gitutil.New(jirix.NewSeq()).Stash()
- if err != nil {
- return err
- }
- if stashed {
- defer collect.Error(func() error { return gitutil.New(jirix.NewSeq()).StashPop() }, &e)
- }
-
- // Register a cleanup handler in case of subsequent errors.
- forceOriginalBranch := true
- originalBranch, err := gitutil.New(jirix.NewSeq()).CurrentBranchName()
- if err != nil {
- return err
- }
- originalWd, err := os.Getwd()
- if err != nil {
- return err
- }
-
- defer func() {
- if forceOriginalBranch {
- gitutil.New(jirix.NewSeq()).CheckoutBranch(originalBranch, gitutil.ForceOpt(true))
- }
- jirix.NewSeq().Chdir(originalWd)
- }()
-
- s := jirix.NewSeq()
- // Switch to an existing directory in master so we can run commands.
- topLevel, err := gitutil.New(jirix.NewSeq()).TopLevel()
- if err != nil {
- return err
- }
- if err := s.Chdir(topLevel).Done(); err != nil {
- return err
- }
-
- // Identify the dependents CLs leading to (and including) the
- // current branch.
- branches, err := getDependentCLs(jirix, originalBranch)
- if err != nil {
- return err
- }
- branches = append(branches, originalBranch)
-
- // Sync from upstream.
- if err := gitutil.New(jirix.NewSeq()).CheckoutBranch(branches[0]); err != nil {
- return err
- }
- if err := gitutil.New(jirix.NewSeq()).Pull("origin", branches[0]); err != nil {
- return err
- }
-
- // Bring all CLs in the sequence of dependent CLs leading to the
- // current branch up to date with the <remoteBranchFlag> branch.
- for i := 1; i < len(branches); i++ {
- if err := gitutil.New(jirix.NewSeq()).CheckoutBranch(branches[i]); err != nil {
- return err
- }
- if err := gitutil.New(jirix.NewSeq()).Merge(branches[i-1]); err != nil {
- return fmt.Errorf(`Failed to automatically merge branch %v into branch %v: %v
-The following steps are needed before the operation can be retried:
-$ git checkout %v
-$ git merge %v
-# resolve all conflicts
-$ git commit -a
-$ git checkout %v
-# retry the original operation
-`, branches[i], branches[i-1], err, branches[i], branches[i-1], originalBranch)
- }
- }
-
- forceOriginalBranch = false
- return nil
-}
diff --git a/cl_test.go b/cl_test.go
deleted file mode 100644
index e92f35b..0000000
--- a/cl_test.go
+++ /dev/null
@@ -1,1033 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "bytes"
- "os"
- "path"
- "path/filepath"
- "runtime"
- "strings"
- "testing"
-
- "v.io/jiri/gerrit"
- "v.io/jiri/gitutil"
- "v.io/jiri/jiri"
- "v.io/jiri/jiritest"
- "v.io/jiri/runutil"
-)
-
-// assertCommitCount asserts that the commit count between two
-// branches matches the expectedCount.
-func assertCommitCount(t *testing.T, jirix *jiri.X, branch, baseBranch string, expectedCount int) {
- got, err := gitutil.New(jirix.NewSeq()).CountCommits(branch, baseBranch)
- if err != nil {
- t.Fatalf("%v", err)
- }
- if want := 1; got != want {
- t.Fatalf("unexpected number of commits: got %v, want %v", got, want)
- }
-}
-
-// assertFileContent asserts that the content of the given file
-// matches the expected content.
-func assertFileContent(t *testing.T, jirix *jiri.X, file, want string) {
- got, err := jirix.NewSeq().ReadFile(file)
- if err != nil {
- t.Fatalf("%v\n", err)
- }
- if string(got) != want {
- t.Fatalf("unexpected content of file %v: got %v, want %v", file, got, want)
- }
-}
-
-// assertFilesExist asserts that the files exist.
-func assertFilesExist(t *testing.T, jirix *jiri.X, files []string) {
- s := jirix.NewSeq()
- for _, file := range files {
- if _, err := s.Stat(file); err != nil {
- if runutil.IsNotExist(err) {
- t.Fatalf("expected file %v to exist but it did not", file)
- }
- t.Fatalf("%v", err)
- }
- }
-}
-
-// 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)
- }
- }
-}
-
-// assertFilesCommitted asserts that the files exist and are committed
-// in the current branch.
-func assertFilesCommitted(t *testing.T, jirix *jiri.X, files []string) {
- assertFilesExist(t, jirix, files)
- for _, file := range files {
- if !gitutil.New(jirix.NewSeq()).IsFileCommitted(file) {
- t.Fatalf("expected file %v to be committed but it is not", file)
- }
- }
-}
-
-// assertFilesNotCommitted asserts that the files exist and are *not*
-// committed in the current branch.
-func assertFilesNotCommitted(t *testing.T, jirix *jiri.X, files []string) {
- assertFilesExist(t, jirix, files)
- for _, file := range files {
- if gitutil.New(jirix.NewSeq()).IsFileCommitted(file) {
- t.Fatalf("expected file %v not to be committed but it is", file)
- }
- }
-}
-
-// assertFilesPushedToRef asserts that the given files have been
-// pushed to the given remote repository reference.
-func assertFilesPushedToRef(t *testing.T, jirix *jiri.X, repoPath, gerritPath, pushedRef string, files []string) {
- chdir(t, jirix, gerritPath)
- assertCommitCount(t, jirix, pushedRef, "master", 1)
- if err := gitutil.New(jirix.NewSeq()).CheckoutBranch(pushedRef); err != nil {
- t.Fatalf("%v", err)
- }
- assertFilesCommitted(t, jirix, files)
- chdir(t, jirix, repoPath)
-}
-
-// assertStashSize asserts that the stash size matches the expected
-// size.
-func assertStashSize(t *testing.T, jirix *jiri.X, want int) {
- got, err := gitutil.New(jirix.NewSeq()).StashSize()
- if err != nil {
- t.Fatalf("%v", err)
- }
- if got != want {
- t.Fatalf("unxpected stash size: got %v, want %v", got, want)
- }
-}
-
-// commitFile commits a file with the specified content into a branch
-func commitFile(t *testing.T, jirix *jiri.X, filename string, content string) {
- s := jirix.NewSeq()
- if err := s.WriteFile(filename, []byte(content), 0644).Done(); err != nil {
- t.Fatalf("%v", err)
- }
- commitMessage := "Commit " + filename
- if err := gitutil.New(jirix.NewSeq()).CommitFile(filename, commitMessage); err != nil {
- t.Fatalf("%v", err)
- }
-}
-
-// commitFiles commits the given files into to current branch.
-func commitFiles(t *testing.T, jirix *jiri.X, filenames []string) {
- // Create and commit the files one at a time.
- for _, filename := range filenames {
- content := "This is file " + filename
- commitFile(t, jirix, filename, content)
- }
-}
-
-// createRepo creates a new repository with the given prefix.
-func createRepo(t *testing.T, jirix *jiri.X, prefix string) string {
- s := jirix.NewSeq()
- repoPath, err := s.TempDir(jirix.Root, "repo-"+prefix)
- if err != nil {
- t.Fatalf("TempDir() failed: %v", err)
- }
- if err := os.Chmod(repoPath, 0777); err != nil {
- t.Fatalf("Chmod(%v) failed: %v", repoPath, err)
- }
- if err := gitutil.New(jirix.NewSeq()).Init(repoPath); err != nil {
- t.Fatalf("%v", err)
- }
- if err := s.MkdirAll(filepath.Join(repoPath, jiri.ProjectMetaDir), os.FileMode(0755)).Done(); err != nil {
- t.Fatalf("%v", err)
- }
- return repoPath
-}
-
-// Simple commit-msg hook that adds a fake Change Id.
-var commitMsgHook string = `#!/bin/sh
-MSG="$1"
-echo "Change-Id: I0000000000000000000000000000000000000000" >> $MSG
-`
-
-// installCommitMsgHook links the gerrit commit-msg hook into a different repo.
-func installCommitMsgHook(t *testing.T, jirix *jiri.X, repoPath string) {
- hookLocation := path.Join(repoPath, ".git/hooks/commit-msg")
- if err := jirix.NewSeq().WriteFile(hookLocation, []byte(commitMsgHook), 0755).Done(); err != nil {
- t.Fatalf("WriteFile(%v) failed: %v", hookLocation, err)
- }
-}
-
-// chdir changes the runtime working directory and traps any errors.
-func chdir(t *testing.T, jirix *jiri.X, path string) {
- if err := jirix.NewSeq().Chdir(path).Done(); err != nil {
- _, file, line, _ := runtime.Caller(1)
- t.Fatalf("%s: %d: Chdir(%v) failed: %v", file, line, path, err)
- }
-}
-
-// createRepoFromOrigin creates a Git repo tracking origin/master.
-func createRepoFromOrigin(t *testing.T, jirix *jiri.X, subpath string, originPath string) string {
- repoPath := createRepo(t, jirix, subpath)
- chdir(t, jirix, repoPath)
- if err := gitutil.New(jirix.NewSeq()).AddRemote("origin", originPath); err != nil {
- t.Fatalf("%v", err)
- }
- if err := gitutil.New(jirix.NewSeq()).Pull("origin", "master"); err != nil {
- t.Fatalf("%v", err)
- }
- return repoPath
-}
-
-// createTestRepos sets up three local repositories: origin, gerrit,
-// and the main test repository which pulls from origin and can push
-// to gerrit.
-func createTestRepos(t *testing.T, jirix *jiri.X) (string, string, string) {
- // Create origin.
- originPath := createRepo(t, jirix, "origin")
- chdir(t, jirix, originPath)
- if err := gitutil.New(jirix.NewSeq()).CommitWithMessage("initial commit"); err != nil {
- t.Fatalf("%v", err)
- }
- // Create test repo.
- repoPath := createRepoFromOrigin(t, jirix, "test", originPath)
- // Add Gerrit remote.
- gerritPath := createRepoFromOrigin(t, jirix, "gerrit", originPath)
- // Switch back to test repo.
- chdir(t, jirix, repoPath)
- return repoPath, originPath, gerritPath
-}
-
-// submit mocks a Gerrit review submit by pushing the Gerrit remote to origin.
-// Actually origin pulls from Gerrit since origin isn't actually a bare git repo.
-// Some of our tests actually rely on accessing .git in origin, so it must be non-bare.
-func submit(t *testing.T, jirix *jiri.X, originPath string, gerritPath string, review *review) {
- cwd, err := os.Getwd()
- if err != nil {
- t.Fatalf("Getwd() failed: %v", err)
- }
- chdir(t, jirix, originPath)
- expectedRef := gerrit.Reference(review.CLOpts)
- if err := gitutil.New(jirix.NewSeq()).Pull(gerritPath, expectedRef); err != nil {
- t.Fatalf("Pull gerrit to origin failed: %v", err)
- }
- chdir(t, jirix, cwd)
-}
-
-// setupTest creates a setup for testing the review tool.
-func setupTest(t *testing.T, installHook bool) (fake *jiritest.FakeJiriRoot, repoPath, originPath, gerritPath string, cleanup func()) {
- oldWD, err := os.Getwd()
- if err != nil {
- t.Fatalf("Getwd() failed: %v", err)
- }
- var cleanupFake func()
- if fake, cleanupFake = jiritest.NewFakeJiriRoot(t); err != nil {
- t.Fatalf("%v", err)
- }
- repoPath, originPath, gerritPath = createTestRepos(t, fake.X)
- if installHook == true {
- for _, path := range []string{repoPath, originPath, gerritPath} {
- installCommitMsgHook(t, fake.X, path)
- }
- }
- chdir(t, fake.X, repoPath)
- cleanup = func() {
- chdir(t, fake.X, oldWD)
- cleanupFake()
- }
- return
-}
-
-func createCLWithFiles(t *testing.T, jirix *jiri.X, branch string, files ...string) {
- if err := newCL(jirix, []string{branch}); err != nil {
- t.Fatalf("%v", err)
- }
- commitFiles(t, jirix, files)
-}
-
-// TestCleanupClean checks that cleanup succeeds if the branch to be
-// cleaned up has been merged with the master.
-func TestCleanupClean(t *testing.T) {
- fake, repoPath, originPath, _, cleanup := setupTest(t, true)
- defer cleanup()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- commitFiles(t, fake.X, []string{"file1", "file2"})
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("master"); err != nil {
- t.Fatalf("%v", err)
- }
- chdir(t, fake.X, originPath)
- commitFiles(t, fake.X, []string{"file1", "file2"})
- chdir(t, fake.X, repoPath)
- if err := cleanupCL(fake.X, []string{branch}); err != nil {
- t.Fatalf("cleanup() failed: %v", err)
- }
- if gitutil.New(fake.X.NewSeq()).BranchExists(branch) {
- t.Fatalf("cleanup failed to remove the feature branch")
- }
-}
-
-// TestCleanupDirty checks that cleanup is a no-op if the branch to be
-// cleaned up has unmerged changes.
-func TestCleanupDirty(t *testing.T) {
- fake, _, _, _, cleanup := setupTest(t, true)
- defer cleanup()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- files := []string{"file1", "file2"}
- commitFiles(t, fake.X, files)
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("master"); err != nil {
- t.Fatalf("%v", err)
- }
- if err := cleanupCL(fake.X, []string{branch}); err == nil {
- t.Fatalf("cleanup did not fail when it should")
- }
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- assertFilesCommitted(t, fake.X, files)
-}
-
-// TestCreateReviewBranch checks that the temporary review branch is
-// created correctly.
-func TestCreateReviewBranch(t *testing.T) {
- fake, _, _, _, cleanup := setupTest(t, true)
- defer cleanup()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- files := []string{"file1", "file2", "file3"}
- commitFiles(t, fake.X, files)
- review, err := newReview(fake.X, gerrit.CLOpts{})
- if err != nil {
- t.Fatalf("%v", err)
- }
- if expected, got := branch+"-REVIEW", review.reviewBranch; expected != got {
- t.Fatalf("Unexpected review branch name: expected %v, got %v", expected, got)
- }
- commitMessage := "squashed commit"
- if err := review.createReviewBranch(commitMessage); err != nil {
- t.Fatalf("%v", err)
- }
- // Verify that the branch exists.
- if !gitutil.New(fake.X.NewSeq()).BranchExists(review.reviewBranch) {
- t.Fatalf("review branch not found")
- }
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(review.reviewBranch); err != nil {
- t.Fatalf("%v", err)
- }
- assertCommitCount(t, fake.X, review.reviewBranch, "master", 1)
- assertFilesCommitted(t, fake.X, files)
-}
-
-// TestCreateReviewBranchWithEmptyChange checks that running
-// createReviewBranch() on a branch with no changes will result in an
-// EmptyChangeError.
-func TestCreateReviewBranchWithEmptyChange(t *testing.T) {
- fake, _, _, _, cleanup := setupTest(t, true)
- defer cleanup()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- review, err := newReview(fake.X, gerrit.CLOpts{Remote: branch})
- if err != nil {
- t.Fatalf("%v", err)
- }
- commitMessage := "squashed commit"
- err = review.createReviewBranch(commitMessage)
- if err == nil {
- t.Fatalf("creating a review did not fail when it should")
- }
- if _, ok := err.(emptyChangeError); !ok {
- t.Fatalf("unexpected error type: %v", err)
- }
-}
-
-// TestSendReview checks the various options for sending a review.
-func TestSendReview(t *testing.T) {
- fake, repoPath, _, gerritPath, cleanup := setupTest(t, true)
- defer cleanup()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- files := []string{"file1"}
- commitFiles(t, fake.X, files)
- {
- // Test with draft = false, no reviewiers, and no ccs.
- review, err := newReview(fake.X, gerrit.CLOpts{Remote: gerritPath})
- if err != nil {
- t.Fatalf("%v", err)
- }
- if err := review.send(); err != nil {
- t.Fatalf("failed to send a review: %v", err)
- }
- expectedRef := gerrit.Reference(review.CLOpts)
- assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
- }
- {
- // Test with draft = true, no reviewers, and no ccs.
- review, err := newReview(fake.X, gerrit.CLOpts{
- Draft: true,
- Remote: gerritPath,
- })
- if err != nil {
- t.Fatalf("%v", err)
- }
- if err := review.send(); err != nil {
- t.Fatalf("failed to send a review: %v", err)
- }
- expectedRef := gerrit.Reference(review.CLOpts)
- assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
- }
- {
- // Test with draft = false, reviewers, and no ccs.
- review, err := newReview(fake.X, gerrit.CLOpts{
- Remote: gerritPath,
- Reviewers: parseEmails("reviewer1,reviewer2@example.org"),
- })
- if err != nil {
- t.Fatalf("%v", err)
- }
- if err := review.send(); err != nil {
- t.Fatalf("failed to send a review: %v", err)
- }
- expectedRef := gerrit.Reference(review.CLOpts)
- assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
- }
- {
- // Test with draft = true, reviewers, and ccs.
- review, err := newReview(fake.X, gerrit.CLOpts{
- Ccs: parseEmails("cc1@example.org,cc2"),
- Draft: true,
- Remote: gerritPath,
- Reviewers: parseEmails("reviewer3@example.org,reviewer4"),
- })
- if err != nil {
- t.Fatalf("%v", err)
- }
- if err := review.send(); err != nil {
- t.Fatalf("failed to send a review: %v", err)
- }
- expectedRef := gerrit.Reference(review.CLOpts)
- assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
- }
-}
-
-// TestSendReviewNoChangeID checks that review.send() correctly errors when
-// not run with a commit hook that adds a Change-Id.
-func TestSendReviewNoChangeID(t *testing.T) {
- // Pass 'false' to setup so it doesn't install the commit-msg hook.
- fake, _, _, gerritPath, cleanup := setupTest(t, false)
- defer cleanup()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- commitFiles(t, fake.X, []string{"file1"})
- review, err := newReview(fake.X, gerrit.CLOpts{Remote: gerritPath})
- if err != nil {
- t.Fatalf("%v", err)
- }
- err = review.send()
- if err == nil {
- t.Fatalf("sending a review did not fail when it should")
- }
- if _, ok := err.(noChangeIDError); !ok {
- t.Fatalf("unexpected error type: %v", err)
- }
-}
-
-// TestEndToEnd checks the end-to-end functionality of the review tool.
-func TestEndToEnd(t *testing.T) {
- fake, repoPath, _, gerritPath, cleanup := setupTest(t, true)
- defer cleanup()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- files := []string{"file1", "file2", "file3"}
- commitFiles(t, fake.X, files)
- review, err := newReview(fake.X, gerrit.CLOpts{Remote: gerritPath})
- if err != nil {
- t.Fatalf("%v", err)
- }
- setTopicFlag = false
- if err := review.run(); err != nil {
- t.Fatalf("run() failed: %v", err)
- }
- expectedRef := gerrit.Reference(review.CLOpts)
- assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
-}
-
-// TestLabelsInCommitMessage checks the labels are correctly processed
-// for the commit message.
-//
-// HACK ALERT: This test runs the review.run() function multiple
-// times. The function ends up pushing a commit to a fake "gerrit"
-// repository created by the setupTest() function. For the real gerrit
-// repository, it is possible to push to the refs/for/change reference
-// multiple times, because it is a special reference that "maps"
-// incoming commits to CL branches based on the commit message
-// Change-Id. The fake "gerrit" repository does not implement this
-// logic and thus the same reference cannot be pushed to multiple
-// times. To overcome this obstacle, the test takes advantage of the
-// fact that the reference name is a function of the reviewers and
-// uses different reviewers for different review runs.
-func TestLabelsInCommitMessage(t *testing.T) {
- fake, repoPath, _, gerritPath, cleanup := setupTest(t, true)
- defer cleanup()
- s := fake.X.NewSeq()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
-
- // Test setting -presubmit=none and autosubmit.
- files := []string{"file1", "file2", "file3"}
- commitFiles(t, fake.X, files)
- review, err := newReview(fake.X, gerrit.CLOpts{
- Autosubmit: true,
- Presubmit: gerrit.PresubmitTestTypeNone,
- Remote: gerritPath,
- Reviewers: parseEmails("run1"),
- })
- if err != nil {
- t.Fatalf("%v", err)
- }
- setTopicFlag = false
- if err := review.run(); err != nil {
- t.Fatalf("%v", err)
- }
- expectedRef := gerrit.Reference(review.CLOpts)
- assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
- // The last three lines of the gerrit commit message file should be:
- // AutoSubmit
- // PresubmitTest: none
- // Change-Id: ...
- file, err := getCommitMessageFileName(review.jirix, review.CLOpts.Branch)
- if err != nil {
- t.Fatalf("%v", err)
- }
- bytes, err := s.ReadFile(file)
- if err != nil {
- t.Fatalf("%v\n", err)
- }
- content := string(bytes)
- lines := strings.Split(content, "\n")
- // Make sure the Change-Id line is the last line.
- if got := lines[len(lines)-1]; !strings.HasPrefix(got, "Change-Id") {
- t.Fatalf("no Change-Id line found: %s", got)
- }
- // Make sure the "AutoSubmit" label exists.
- if autosubmitLabelRE.FindString(content) == "" {
- t.Fatalf("AutoSubmit label doesn't exist in the commit message: %s", content)
- }
- // Make sure the "PresubmitTest" label exists.
- if presubmitTestLabelRE.FindString(content) == "" {
- t.Fatalf("PresubmitTest label doesn't exist in the commit message: %s", content)
- }
-
- // Test setting -presubmit=all but keep autosubmit=true.
- review, err = newReview(fake.X, gerrit.CLOpts{
- Autosubmit: true,
- Remote: gerritPath,
- Reviewers: parseEmails("run2"),
- })
- if err != nil {
- t.Fatalf("%v", err)
- }
- if err := review.run(); err != nil {
- t.Fatalf("%v", err)
- }
- expectedRef = gerrit.Reference(review.CLOpts)
- assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
- bytes, err = s.ReadFile(file)
- if err != nil {
- t.Fatalf("%v\n", err)
- }
- content = string(bytes)
- // Make sure there is no PresubmitTest=none any more.
- match := presubmitTestLabelRE.FindString(content)
- if match != "" {
- t.Fatalf("want no presubmit label line, got: %s", match)
- }
- // Make sure the "AutoSubmit" label still exists.
- if autosubmitLabelRE.FindString(content) == "" {
- t.Fatalf("AutoSubmit label doesn't exist in the commit message: %s", content)
- }
-
- // Test setting autosubmit=false.
- review, err = newReview(fake.X, gerrit.CLOpts{
- Remote: gerritPath,
- Reviewers: parseEmails("run3"),
- })
- if err != nil {
- t.Fatalf("%v", err)
- }
- if err := review.run(); err != nil {
- t.Fatalf("%v", err)
- }
- expectedRef = gerrit.Reference(review.CLOpts)
- assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
- bytes, err = s.ReadFile(file)
- if err != nil {
- t.Fatalf("%v\n", err)
- }
- content = string(bytes)
- // Make sure there is no AutoSubmit label any more.
- match = autosubmitLabelRE.FindString(content)
- if match != "" {
- t.Fatalf("want no AutoSubmit label line, got: %s", match)
- }
-}
-
-// TestDirtyBranch checks that the tool correctly handles unstaged and
-// untracked changes in a working branch with stashed changes.
-func TestDirtyBranch(t *testing.T) {
- fake, _, _, gerritPath, cleanup := setupTest(t, true)
- defer cleanup()
- s := fake.X.NewSeq()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- files := []string{"file1", "file2"}
- commitFiles(t, fake.X, files)
- assertStashSize(t, fake.X, 0)
- stashedFile, stashedFileContent := "stashed-file", "stashed-file content"
- if err := s.WriteFile(stashedFile, []byte(stashedFileContent), 0644).Done(); err != nil {
- t.Fatalf("WriteFile(%v, %v) failed: %v", stashedFile, stashedFileContent, err)
- }
- if err := gitutil.New(fake.X.NewSeq()).Add(stashedFile); err != nil {
- t.Fatalf("%v", err)
- }
- if _, err := gitutil.New(fake.X.NewSeq()).Stash(); err != nil {
- t.Fatalf("%v", err)
- }
- assertStashSize(t, fake.X, 1)
- modifiedFile, modifiedFileContent := "file1", "modified-file content"
- if err := s.WriteFile(modifiedFile, []byte(modifiedFileContent), 0644).Done(); err != nil {
- t.Fatalf("WriteFile(%v, %v) failed: %v", modifiedFile, modifiedFileContent, err)
- }
- stagedFile, stagedFileContent := "file2", "staged-file content"
- if err := s.WriteFile(stagedFile, []byte(stagedFileContent), 0644).Done(); err != nil {
- t.Fatalf("WriteFile(%v, %v) failed: %v", stagedFile, stagedFileContent, err)
- }
- if err := gitutil.New(fake.X.NewSeq()).Add(stagedFile); err != nil {
- t.Fatalf("%v", err)
- }
- untrackedFile, untrackedFileContent := "file3", "untracked-file content"
- if err := s.WriteFile(untrackedFile, []byte(untrackedFileContent), 0644).Done(); err != nil {
- t.Fatalf("WriteFile(%v, %v) failed: %v", untrackedFile, untrackedFileContent, err)
- }
- review, err := newReview(fake.X, gerrit.CLOpts{Remote: gerritPath})
- if err != nil {
- t.Fatalf("%v", err)
- }
- setTopicFlag = false
- if err := review.run(); err == nil {
- t.Fatalf("run() didn't fail when it should")
- }
- assertFilesNotCommitted(t, fake.X, []string{stagedFile})
- assertFilesNotCommitted(t, fake.X, []string{untrackedFile})
- assertFileContent(t, fake.X, modifiedFile, modifiedFileContent)
- assertFileContent(t, fake.X, stagedFile, stagedFileContent)
- assertFileContent(t, fake.X, untrackedFile, untrackedFileContent)
- // As of git 2.4.3 "git stash pop" fails if there are uncommitted
- // changes in the index. So we need to commit them first.
- if err := gitutil.New(fake.X.NewSeq()).Commit(); err != nil {
- t.Fatalf("%v", err)
- }
- assertStashSize(t, fake.X, 1)
- if err := gitutil.New(fake.X.NewSeq()).StashPop(); err != nil {
- t.Fatalf("%v", err)
- }
- assertStashSize(t, fake.X, 0)
- assertFilesNotCommitted(t, fake.X, []string{stashedFile})
- assertFileContent(t, fake.X, stashedFile, stashedFileContent)
-}
-
-// TestRunInSubdirectory checks that the command will succeed when run from
-// within a subdirectory of a branch that does not exist on master branch, and
-// will return the user to the subdirectory after completion.
-func TestRunInSubdirectory(t *testing.T) {
- fake, repoPath, _, gerritPath, cleanup := setupTest(t, true)
- defer cleanup()
- s := fake.X.NewSeq()
- branch := "my-branch"
- if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- subdir := "sub/directory"
- subdirPerms := os.FileMode(0744)
- if err := s.MkdirAll(subdir, subdirPerms).Done(); err != nil {
- t.Fatalf("MkdirAll(%v, %v) failed: %v", subdir, subdirPerms, err)
- }
- files := []string{path.Join(subdir, "file1")}
- commitFiles(t, fake.X, files)
- chdir(t, fake.X, subdir)
- review, err := newReview(fake.X, gerrit.CLOpts{Remote: gerritPath})
- if err != nil {
- t.Fatalf("%v", err)
- }
- setTopicFlag = false
- if err := review.run(); err != nil {
- t.Fatalf("run() failed: %v", err)
- }
- path := path.Join(repoPath, subdir)
- want, err := filepath.EvalSymlinks(path)
- if err != nil {
- t.Fatalf("EvalSymlinks(%v) failed: %v", path, err)
- }
- cwd, err := os.Getwd()
- if err != nil {
- t.Fatalf("%v", err)
- }
- got, err := filepath.EvalSymlinks(cwd)
- if err != nil {
- t.Fatalf("EvalSymlinks(%v) failed: %v", cwd, err)
- }
- if got != want {
- t.Fatalf("unexpected working directory: got %v, want %v", got, want)
- }
- expectedRef := gerrit.Reference(review.CLOpts)
- assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
-}
-
-// TestProcessLabels checks that the processLabels function works as expected.
-func TestProcessLabels(t *testing.T) {
- fake, _, _, _, cleanup := setupTest(t, true)
- defer cleanup()
- testCases := []struct {
- autosubmit bool
- presubmitType gerrit.PresubmitTestType
- originalMessage string
- expectedMessage string
- }{
- {
- presubmitType: gerrit.PresubmitTestTypeNone,
- originalMessage: "",
- expectedMessage: "PresubmitTest: none\n",
- },
- {
- autosubmit: true,
- presubmitType: gerrit.PresubmitTestTypeNone,
- originalMessage: "",
- expectedMessage: "AutoSubmit\nPresubmitTest: none\n",
- },
- {
- presubmitType: gerrit.PresubmitTestTypeNone,
- originalMessage: "review message\n",
- expectedMessage: "review message\nPresubmitTest: none\n",
- },
- {
- autosubmit: true,
- presubmitType: gerrit.PresubmitTestTypeNone,
- originalMessage: "review message\n",
- expectedMessage: "review message\nAutoSubmit\nPresubmitTest: none\n",
- },
- {
- presubmitType: gerrit.PresubmitTestTypeNone,
- originalMessage: `review message
-
-Change-Id: I0000000000000000000000000000000000000000`,
- expectedMessage: `review message
-
-PresubmitTest: none
-Change-Id: I0000000000000000000000000000000000000000`,
- },
- {
- autosubmit: true,
- presubmitType: gerrit.PresubmitTestTypeNone,
- originalMessage: `review message
-
-Change-Id: I0000000000000000000000000000000000000000`,
- expectedMessage: `review message
-
-AutoSubmit
-PresubmitTest: none
-Change-Id: I0000000000000000000000000000000000000000`,
- },
- {
- presubmitType: gerrit.PresubmitTestTypeAll,
- originalMessage: "",
- expectedMessage: "",
- },
- {
- presubmitType: gerrit.PresubmitTestTypeAll,
- originalMessage: "review message\n",
- expectedMessage: "review message\n",
- },
- {
- presubmitType: gerrit.PresubmitTestTypeAll,
- originalMessage: `review message
-
-Change-Id: I0000000000000000000000000000000000000000`,
- expectedMessage: `review message
-
-Change-Id: I0000000000000000000000000000000000000000`,
- },
- }
- for _, test := range testCases {
- review, err := newReview(fake.X, gerrit.CLOpts{
- Autosubmit: test.autosubmit,
- Presubmit: test.presubmitType,
- })
- if err != nil {
- t.Fatalf("%v", err)
- }
- if got := review.processLabels(test.originalMessage); got != test.expectedMessage {
- t.Fatalf("want %s, got %s", test.expectedMessage, got)
- }
- }
-}
-
-// TestCLNew checks the operation of the "jiri cl new" command.
-func TestCLNew(t *testing.T) {
- fake, _, _, _, cleanup := setupTest(t, true)
- defer cleanup()
-
- // Create some dependent CLs.
- if err := newCL(fake.X, []string{"feature1"}); err != nil {
- t.Fatalf("%v", err)
- }
- if err := newCL(fake.X, []string{"feature2"}); err != nil {
- t.Fatalf("%v", err)
- }
-
- // Check that their dependency paths have been recorded correctly.
- testCases := []struct {
- branch string
- data []byte
- }{
- {
- branch: "feature1",
- data: []byte("master"),
- },
- {
- branch: "feature2",
- data: []byte("master\nfeature1"),
- },
- }
- s := fake.X.NewSeq()
- for _, testCase := range testCases {
- file, err := getDependencyPathFileName(fake.X, testCase.branch)
- if err != nil {
- t.Fatalf("%v", err)
- }
- data, err := s.ReadFile(file)
- if err != nil {
- t.Fatalf("%v", err)
- }
- if bytes.Compare(data, testCase.data) != 0 {
- t.Fatalf("unexpected data:\ngot\n%v\nwant\n%v", string(data), string(testCase.data))
- }
- }
-}
-
-// TestDependentClsWithEditDelete exercises a previously observed failure case
-// where if a CL edits a file and a dependent CL deletes it, jiri cl mail after
-// the deletion failed with unrecoverable merge errors.
-func TestDependentClsWithEditDelete(t *testing.T) {
- fake, repoPath, originPath, gerritPath, cleanup := setupTest(t, true)
- defer cleanup()
- chdir(t, fake.X, originPath)
- commitFiles(t, fake.X, []string{"A", "B"})
-
- chdir(t, fake.X, repoPath)
- if err := syncCL(fake.X); err != nil {
- t.Fatalf("%v", err)
- }
- assertFilesExist(t, fake.X, []string{"A", "B"})
-
- createCLWithFiles(t, fake.X, "editme", "C")
- if err := fake.X.NewSeq().WriteFile("B", []byte("Will I dream?"), 0644).Done(); err != nil {
- t.Fatalf("%v", err)
- }
- if err := gitutil.New(fake.X.NewSeq()).Add("B"); err != nil {
- t.Fatalf("%v", err)
- }
- if err := gitutil.New(fake.X.NewSeq()).CommitWithMessage("editing stuff"); err != nil {
- t.Fatalf("git commit failed: %v", err)
- }
- review, err := newReview(fake.X, gerrit.CLOpts{
- Remote: gerritPath,
- Reviewers: parseEmails("run1"), // See hack note about TestLabelsInCommitMessage
- })
- if err != nil {
- t.Fatalf("%v", err)
- }
- setTopicFlag = false
- if err := review.run(); err != nil {
- t.Fatalf("run() failed: %v", err)
- }
-
- if err := newCL(fake.X, []string{"deleteme"}); err != nil {
- t.Fatalf("%v", err)
- }
- if err := gitutil.New(fake.X.NewSeq()).Remove("B", "C"); err != nil {
- t.Fatalf("git rm B C failed: %v", err)
- }
- if err := gitutil.New(fake.X.NewSeq()).CommitWithMessage("deleting stuff"); err != nil {
- t.Fatalf("git commit failed: %v", err)
- }
- review, err = newReview(fake.X, gerrit.CLOpts{
- Remote: gerritPath,
- Reviewers: parseEmails("run2"),
- })
- if err != nil {
- t.Fatalf("%v", err)
- }
- if err := review.run(); err != nil {
- t.Fatalf("run() failed: %v", err)
- }
-
- chdir(t, fake.X, gerritPath)
- expectedRef := gerrit.Reference(review.CLOpts)
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(expectedRef); err != nil {
- t.Fatalf("%v", err)
- }
- assertFilesExist(t, fake.X, []string{"A"})
- assertFilesDoNotExist(t, fake.X, []string{"B", "C"})
-}
-
-// TestParallelDev checks "jiri cl mail" behavior when parallel development has
-// been submitted upstream.
-func TestParallelDev(t *testing.T) {
- fake, repoPath, originPath, gerritAPath, cleanup := setupTest(t, true)
- defer cleanup()
- gerritBPath := createRepoFromOrigin(t, fake.X, "gerritB", originPath)
- chdir(t, fake.X, repoPath)
-
- // Create parallel branches with:
- // * non-conflicting changes in different files
- // * conflicting changes in a file
- createCLWithFiles(t, fake.X, "feature1-A", "A")
-
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("master"); err != nil {
- t.Fatalf("%v", err)
- }
- createCLWithFiles(t, fake.X, "feature1-B", "B")
- commitFile(t, fake.X, "A", "Don't tread on me.")
-
- reviewB, err := newReview(fake.X, gerrit.CLOpts{Remote: gerritBPath})
- if err != nil {
- t.Fatalf("%v", err)
- }
- setTopicFlag = false
- if err := reviewB.run(); err != nil {
- t.Fatalf("run() failed: %v", err)
- }
-
- // Submit B and verify A doesn't revert it.
- submit(t, fake.X, originPath, gerritBPath, reviewB)
-
- // Assert files pushed to origin.
- chdir(t, fake.X, originPath)
- assertFilesExist(t, fake.X, []string{"A", "B"})
- chdir(t, fake.X, repoPath)
-
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("feature1-A"); err != nil {
- t.Fatalf("%v", err)
- }
-
- reviewA, err := newReview(fake.X, gerrit.CLOpts{Remote: gerritAPath})
- if err == nil {
- t.Fatalf("creating a review did not fail when it should")
- }
- // Assert state restored after failed review.
- assertFileContent(t, fake.X, "A", "This is file A")
- assertFilesDoNotExist(t, fake.X, []string{"B"})
-
- // Manual conflict resolution.
- if err := gitutil.New(fake.X.NewSeq()).Merge("master", gitutil.ResetOnFailureOpt(false)); err == nil {
- t.Fatalf("merge applied cleanly when it shouldn't")
- }
- assertFilesNotCommitted(t, fake.X, []string{"A", "B"})
- assertFileContent(t, fake.X, "B", "This is file B")
-
- if err := fake.X.NewSeq().WriteFile("A", []byte("This is file A. Don't tread on me."), 0644).Done(); err != nil {
- t.Fatalf("%v", err)
- }
-
- if err := gitutil.New(fake.X.NewSeq()).Add("A"); err != nil {
- t.Fatalf("%v", err)
- }
- if err := gitutil.New(fake.X.NewSeq()).Add("B"); err != nil {
- t.Fatalf("%v", err)
- }
- if err := gitutil.New(fake.X.NewSeq()).CommitWithMessage("Conflict resolution"); err != nil {
- t.Fatalf("%v", err)
- }
-
- // Retry review.
- reviewA, err = newReview(fake.X, gerrit.CLOpts{Remote: gerritAPath})
- if err != nil {
- t.Fatalf("review failed: %v", err)
- }
-
- if err := reviewA.run(); err != nil {
- t.Fatalf("run() failed: %v", err)
- }
-
- chdir(t, fake.X, gerritAPath)
- expectedRef := gerrit.Reference(reviewA.CLOpts)
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(expectedRef); err != nil {
- t.Fatalf("%v", err)
- }
- assertFilesExist(t, fake.X, []string{"B"})
-}
-
-// TestCLSync checks the operation of the "jiri cl sync" command.
-func TestCLSync(t *testing.T) {
- fake, _, _, _, cleanup := setupTest(t, true)
- defer cleanup()
-
- // Create some dependent CLs.
- if err := newCL(fake.X, []string{"feature1"}); err != nil {
- t.Fatalf("%v", err)
- }
- if err := newCL(fake.X, []string{"feature2"}); err != nil {
- t.Fatalf("%v", err)
- }
-
- // Add the "test" file to the master.
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("master"); err != nil {
- t.Fatalf("%v", err)
- }
- commitFiles(t, fake.X, []string{"test"})
-
- // Sync the dependent CLs.
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("feature2"); err != nil {
- t.Fatalf("%v", err)
- }
- if err := syncCL(fake.X); err != nil {
- t.Fatalf("%v", err)
- }
-
- // Check that the "test" file exists in the dependent CLs.
- for _, branch := range []string{"feature1", "feature2"} {
- if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(branch); err != nil {
- t.Fatalf("%v", err)
- }
- assertFilesExist(t, fake.X, []string{"test"})
- }
-}
diff --git a/cmd.go b/cmd.go
deleted file mode 100644
index 0d4d2ee..0000000
--- a/cmd.go
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// The following enables go generate to generate the doc.go file.
-//go:generate go run $JIRI_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go -env="" .
-
-package main
-
-import (
- "runtime"
-
- "v.io/jiri/tool"
- "v.io/x/lib/cmdline"
-)
-
-func init() {
- runtime.GOMAXPROCS(runtime.NumCPU())
-
- tool.InitializeRunFlags(&cmdRoot.Flags)
-}
-
-func main() {
- cmdline.Main(cmdRoot)
-}
-
-// cmdRoot represents the root of the jiri tool.
-var cmdRoot = &cmdline.Command{
- Name: "jiri",
- Short: "Multi-purpose tool for multi-repo development",
- Long: `
-Command jiri is a multi-purpose tool for multi-repo development.
-`,
- LookPath: true,
- Children: []*cmdline.Command{
- cmdCL,
- cmdContributors,
- cmdImport,
- cmdProfile,
- cmdProject,
- cmdRebuild,
- cmdSnapshot,
- cmdUpdate,
- cmdUpgrade,
- cmdWhich,
- },
- Topics: []cmdline.Topic{
- topicFileSystem,
- topicManifest,
- },
-}
-
-var topicFileSystem = cmdline.Topic{
- Name: "filesystem",
- Short: "Description of jiri file system layout",
- Long: `
-All data managed by the jiri tool is located in the file system under a root
-directory, colloquially called the jiri root directory. The file system layout
-looks like this:
-
- [root] # root directory (name picked by user)
- [root]/.jiri_root # root metadata directory
- [root]/.jiri_root/bin # contains tool binaries (jiri, etc.)
- [root]/.jiri_root/update_history # contains history of update snapshots
- [root]/.manifest # contains jiri manifests
- [root]/[project1] # project directory (name picked by user)
- [root]/[project1]/.jiri # project metadata directory
- [root]/[project1]/.jiri/metadata.v2 # project metadata file
- [root]/[project1]/.jiri/<<cls>> # project per-cl metadata directories
- [root]/[project1]/<<files>> # project files
- [root]/[project2]...
-
-The [root] and [projectN] directory names are picked by the user. The <<cls>>
-are named via jiri cl new, and the <<files>> are named as the user adds files
-and directories to their project. All other names above have special meaning to
-the jiri tool, and cannot be changed; you must ensure your path names don't
-collide with these special names.
-
-There are two ways to run the jiri tool:
-
-1) Shim script (recommended approach). This is a shell script that looks for
-the [root] directory. If the JIRI_ROOT environment variable is set, that is
-assumed to be the [root] directory. Otherwise the script looks for the
-.jiri_root directory, starting in the current working directory and walking up
-the directory chain. The search is terminated successfully when the .jiri_root
-directory is found; it fails after it reaches the root of the file system. Thus
-the shim must be invoked from the [root] directory or one of its subdirectories.
-
-Once the [root] is found, the JIRI_ROOT environment variable is set to its
-location, and [root]/.jiri_root/bin/jiri is invoked. That file contains the
-actual jiri binary.
-
-The point of the shim script is to make it easy to use the jiri tool with
-multiple [root] directories on your file system. Keep in mind that when "jiri
-update" is run, the jiri tool itself is automatically updated along with all
-projects. By using the shim script, you only need to remember to invoke the
-jiri tool from within the appropriate [root] directory, and the projects and
-tools under that [root] directory will be updated.
-
-The shim script is located at [root]/release/go/src/v.io/jiri/scripts/jiri
-
-2) Direct binary. This is the jiri binary, containing all of the actual jiri
-tool logic. The binary requires the JIRI_ROOT environment variable to point to
-the [root] directory.
-
-Note that if you have multiple [root] directories on your file system, you must
-remember to run the jiri binary corresponding to the setting of your JIRI_ROOT
-environment variable. Things may fail if you mix things up, since the jiri
-binary is updated with each call to "jiri update", and you may encounter version
-mismatches between the jiri binary and the various metadata files or other
-logic. This is the reason the shim script is recommended over running the
-binary directly.
-
-The binary is located at [root]/.jiri_root/bin/jiri
-`,
-}
-
-// TODO(toddw): Update the description of manifest files.
-var topicManifest = cmdline.Topic{
- Name: "manifest",
- Short: "Description of manifest files",
- Long: `
-Jiri manifests are revisioned and stored in a "manifest" repository, that is
-available locally in $JIRI_ROOT/.manifest. The manifest uses the following XML
-schema:
-
- <manifest>
- <imports>
- <import name="default"/>
- ...
- </imports>
- <projects>
- <project name="release.go.jiri"
- path="release/go/src/v.io/jiri"
- protocol="git"
- name="https://vanadium.googlesource.com/release.go.jiri"
- revision="HEAD"/>
- ...
- </projects>
- <tools>
- <tool name="jiri" package="v.io/jiri"/>
- ...
- </tools>
- </manifest>
-
-The <import> element can be used to share settings across multiple
-manifests. Import names are interpreted relative to the $JIRI_ROOT/.manifest/v2
-directory. Import cycles are not allowed and if a project or a tool is specified
-multiple times, the last specification takes effect. In particular, the elements
-<project name="foo" exclude="true"/> and <tool name="bar" exclude="true"/> can
-be used to exclude previously included projects and tools.
-
-The tool identifies which manifest to use using the following algorithm. If the
-$JIRI_ROOT/.local_manifest file exists, then it is used. Otherwise, the
-$JIRI_ROOT/.manifest/v2/<manifest>.xml file is used, where <manifest> is the
-value of the -manifest command-line flag, which defaults to "default".
-`,
-}
diff --git a/cmd/jiri/import_test.go b/cmd/jiri/import_test.go
index 7b13b97..6dd60c6 100644
--- a/cmd/jiri/import_test.go
+++ b/cmd/jiri/import_test.go
@@ -138,7 +138,7 @@
opts := gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf}
sh := gosh.NewShell(opts)
defer sh.Cleanup()
- jiriTool := sh.BuildGoPkg("v.io/jiri")
+ jiriTool := sh.BuildGoPkg("v.io/jiri/cmd/jiri")
for _, test := range tests {
if err := testImport(opts, jiriTool, test); err != nil {
t.Errorf("%v: %v", test.Args, err)
diff --git a/cmd/jiri/scripts/bootstrap_jiri b/cmd/jiri/scripts/bootstrap_jiri
index 8f00a2f..3cf0f2c 100755
--- a/cmd/jiri/scripts/bootstrap_jiri
+++ b/cmd/jiri/scripts/bootstrap_jiri
@@ -65,9 +65,9 @@
# Go get the jiri source files, build the jiri binary, and copy the jiri shim
# script from the sources.
-GOPATH="${tmp_dir}" go get -d v.io/jiri
-GOPATH="${tmp_dir}" go build -o "${bin_dir}/jiri" v.io/jiri
-cp "${tmp_dir}/src/v.io/jiri/scripts/jiri" "${scripts_dir}/jiri"
+GOPATH="${tmp_dir}" go get -d v.io/jiri/cmd/jiri
+GOPATH="${tmp_dir}" go build -o "${bin_dir}/jiri" v.io/jiri/cmd/jiri
+cp "${tmp_dir}/src/v.io/jiri/cmd/jiri/scripts/jiri" "${scripts_dir}/jiri"
# Clean up the tmp_dir.
rm -rf "${tmp_dir}"
diff --git a/cmd/jiri/snapshot.go b/cmd/jiri/snapshot.go
index 437cd81..21d572d 100644
--- a/cmd/jiri/snapshot.go
+++ b/cmd/jiri/snapshot.go
@@ -266,19 +266,18 @@
}
// Check that all labels exist.
- failed := false
+ var notexist []string
for _, label := range args {
labelDir := filepath.Join(snapshotDir, "labels", label)
- if _, err := jirix.NewSeq().Stat(labelDir); err != nil {
- if !runutil.IsNotExist(err) {
- return err
- }
- failed = true
- fmt.Fprintf(jirix.Stderr(), "snapshot label %q not found", label)
+ switch _, err := jirix.NewSeq().Stat(labelDir); {
+ case runutil.IsNotExist(err):
+ notexist = append(notexist, label)
+ case err != nil:
+ return err
}
}
- if failed {
- return cmdline.ErrExitCode(2)
+ if len(notexist) > 0 {
+ return fmt.Errorf("snapshot labels %v not found", notexist)
}
// Print snapshots for all labels.
diff --git a/cmd/jiri/upgrade_test.go b/cmd/jiri/upgrade_test.go
index a7c5eab..d8913ed 100644
--- a/cmd/jiri/upgrade_test.go
+++ b/cmd/jiri/upgrade_test.go
@@ -201,7 +201,7 @@
opts := gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf}
sh := gosh.NewShell(opts)
defer sh.Cleanup()
- jiriTool := sh.BuildGoPkg("v.io/jiri")
+ jiriTool := sh.BuildGoPkg("v.io/jiri/cmd/jiri")
for _, test := range tests {
if err := testUpgrade(opts, jiriTool, test); err != nil {
t.Errorf("%v: %v", test.Args, err)
@@ -264,7 +264,7 @@
defer sh.Cleanup()
jiriRoot := sh.MakeTempDir()
sh.Pushd(jiriRoot)
- jiriTool := sh.BuildGoPkg("v.io/jiri")
+ jiriTool := sh.BuildGoPkg("v.io/jiri/cmd/jiri")
localData := `<manifest/>`
jiriData := `<manifest>
<imports>
diff --git a/cmd/jiri/which_test.go b/cmd/jiri/which_test.go
index dbe46f1..a60655a 100644
--- a/cmd/jiri/which_test.go
+++ b/cmd/jiri/which_test.go
@@ -16,7 +16,7 @@
sh := gosh.NewShell(gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf, PropagateChildOutput: true})
defer sh.Cleanup()
- jiriBinary := sh.BuildGoPkg("v.io/jiri")
+ jiriBinary := sh.BuildGoPkg("v.io/jiri/cmd/jiri")
stdout, stderr := sh.Cmd(jiriBinary, []string{"which"}...).StdoutStderr()
if got, want := stdout, fmt.Sprintf("# binary\n%s\n", jiriBinary); got != want {
t.Errorf("stdout got %q, want %q", got, want)
diff --git a/contrib.go b/contrib.go
deleted file mode 100644
index 1fb31e6..0000000
--- a/contrib.go
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "encoding/xml"
- "fmt"
- "path/filepath"
- "regexp"
- "sort"
- "strconv"
- "strings"
-
- "v.io/jiri/collect"
- "v.io/jiri/gitutil"
- "v.io/jiri/jiri"
- "v.io/jiri/project"
- "v.io/jiri/tool"
- "v.io/x/lib/cmdline"
- "v.io/x/lib/set"
-)
-
-const (
- aliasesFileName = "aliases.v1.xml"
-)
-
-var (
- countFlag bool
- aliasesFlag string
-)
-
-func init() {
- cmdContributors.Flags.BoolVar(&countFlag, "n", false, "Show number of contributions.")
- cmdContributors.Flags.StringVar(&aliasesFlag, "aliases", "", "Path to the aliases file.")
-}
-
-// cmdContributors represents the "jiri contributors" command.
-var cmdContributors = &cmdline.Command{
- Runner: jiri.RunnerFunc(runContributors),
- Name: "contributors",
- Short: "List project contributors",
- Long: `
-Lists project contributors. Projects to consider can be specified as
-an argument. If no projects are specified, all projects in the current
-manifest are considered by default.
-`,
- ArgsName: "<projects>",
- ArgsLong: "<projects> is a list of projects to consider.",
-}
-
-type contributor struct {
- count int
- email string
- name string
-}
-
-var (
- contributorRE = regexp.MustCompile("^(.*)\t(.*) <(.*)>$")
-)
-
-type aliasesSchema struct {
- XMLName xml.Name `xml:"aliases"`
- Names []nameSchema `xml:"name"`
- Emails []emailSchema `xml:"email"`
-}
-
-type nameSchema struct {
- Canonical string `xml:"canonical"`
- Aliases []string `xml:"alias"`
-}
-
-type emailSchema struct {
- Canonical string `xml:"canonical"`
- Aliases []string `xml:"alias"`
-}
-
-type aliasMaps struct {
- emails map[string]string
- names map[string]string
-}
-
-func canonicalize(aliases *aliasMaps, email, name string) (string, string) {
- canonicalEmail, canonicalName := email, name
- if email, ok := aliases.emails[email]; ok {
- canonicalEmail = email
- }
- if name, ok := aliases.names[name]; ok {
- canonicalName = name
- }
- return canonicalEmail, canonicalName
-}
-
-func loadAliases(jirix *jiri.X) (*aliasMaps, error) {
- aliasesFile := aliasesFlag
- if aliasesFile == "" {
- dataDir, err := project.DataDirPath(jirix, tool.Name)
- if err != nil {
- return nil, err
- }
- aliasesFile = filepath.Join(dataDir, aliasesFileName)
- }
- bytes, err := jirix.NewSeq().ReadFile(aliasesFile)
- if err != nil {
- return nil, err
- }
- var data aliasesSchema
- if err := xml.Unmarshal(bytes, &data); err != nil {
- return nil, fmt.Errorf("Unmarshal(%v) failed: %v", string(bytes), err)
- }
- aliases := &aliasMaps{
- emails: map[string]string{},
- names: map[string]string{},
- }
- for _, email := range data.Emails {
- for _, alias := range email.Aliases {
- aliases.emails[alias] = email.Canonical
- }
- }
- for _, name := range data.Names {
- for _, alias := range name.Aliases {
- aliases.names[alias] = name.Canonical
- }
- }
- return aliases, nil
-}
-
-func runContributors(jirix *jiri.X, args []string) error {
- localProjects, err := project.LocalProjects(jirix, project.FastScan)
- if err != nil {
- return err
- }
- projectNames := map[string]struct{}{}
- if len(args) != 0 {
- projectNames = set.String.FromSlice(args)
- } else {
- for _, p := range localProjects {
- projectNames[p.Name] = struct{}{}
- }
- }
-
- aliases, err := loadAliases(jirix)
- if err != nil {
- return err
- }
- contributors := map[string]*contributor{}
- for name, _ := range projectNames {
- projects := localProjects.Find(name)
- if len(projects) == 0 {
- continue
- }
-
- for _, project := range projects {
- if err := jirix.NewSeq().Chdir(project.Path).Done(); err != nil {
- return err
- }
- switch project.Protocol {
- case "git":
- lines, err := listCommitters(jirix)
- if err != nil {
- return err
- }
- for _, line := range lines {
- matches := contributorRE.FindStringSubmatch(line)
- if got, want := len(matches), 4; got != want {
- return fmt.Errorf("unexpected length of %v: got %v, want %v", matches, got, want)
- }
- count, err := strconv.Atoi(strings.TrimSpace(matches[1]))
- if err != nil {
- return fmt.Errorf("Atoi(%v) failed: %v", strings.TrimSpace(matches[1]), err)
- }
- c := &contributor{
- count: count,
- email: strings.TrimSpace(matches[3]),
- name: strings.TrimSpace(matches[2]),
- }
- if c.email == "jenkins.veyron@gmail.com" || c.email == "jenkins.veyron.rw@gmail.com" {
- continue
- }
- c.email, c.name = canonicalize(aliases, c.email, c.name)
- if existing, ok := contributors[c.name]; ok {
- existing.count += c.count
- } else {
- contributors[c.name] = c
- }
- }
- }
- }
- }
- names := []string{}
- for name, _ := range contributors {
- names = append(names, name)
- }
- sort.Strings(names)
- for _, name := range names {
- c := contributors[name]
- if countFlag {
- fmt.Fprintf(jirix.Stdout(), "%4d ", c.count)
- }
- fmt.Fprintf(jirix.Stdout(), "%v <%v>\n", c.name, c.email)
- }
- return nil
-}
-
-func listCommitters(jirix *jiri.X) (_ []string, e error) {
- branch, err := gitutil.New(jirix.NewSeq()).CurrentBranchName()
- if err != nil {
- return nil, err
- }
- stashed, err := gitutil.New(jirix.NewSeq()).Stash()
- if err != nil {
- return nil, err
- }
- if stashed {
- defer collect.Error(func() error { return gitutil.New(jirix.NewSeq()).StashPop() }, &e)
- }
- if err := gitutil.New(jirix.NewSeq()).CheckoutBranch("master"); err != nil {
- return nil, err
- }
- defer collect.Error(func() error { return gitutil.New(jirix.NewSeq()).CheckoutBranch(branch) }, &e)
- return gitutil.New(jirix.NewSeq()).Committers()
-}
diff --git a/doc.go b/doc.go
deleted file mode 100644
index a6a93a5..0000000
--- a/doc.go
+++ /dev/null
@@ -1,907 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file was auto-generated via go generate.
-// DO NOT UPDATE MANUALLY
-
-/*
-Command jiri is a multi-purpose tool for multi-repo development.
-
-Usage:
- jiri [flags] <command>
-
-The jiri commands are:
- cl Manage project changelists
- contributors List project contributors
- import Adds imports to .jiri_manifest file
- profile Display information about installed profiles
- project Manage the jiri projects
- rebuild Rebuild all jiri tools
- snapshot Manage project snapshots
- update Update all jiri tools and projects
- upgrade Upgrade jiri to new-style manifests
- which Show path to the jiri tool
- help Display help for commands or topics
-
-The jiri additional help topics are:
- filesystem Description of jiri file system layout
- manifest Description of manifest files
-
-The jiri flags are:
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-The global flags are:
- -metadata=<just specify -metadata to activate>
- Displays metadata for the program and exits.
- -time=false
- Dump timing information to stderr before exiting the program.
-
-Jiri cl - Manage project changelists
-
-Manage project changelists.
-
-Usage:
- jiri cl [flags] <command>
-
-The jiri cl commands are:
- cleanup Clean up changelists that have been merged
- mail Mail a changelist for review
- new Create a new local branch for a changelist
- sync Bring a changelist up to date
-
-The jiri cl flags are:
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri cl cleanup - Clean up changelists that have been merged
-
-Command "cleanup" checks that the given branches have been merged into the
-corresponding remote branch. If a branch differs from the corresponding remote
-branch, the command reports the difference and stops. Otherwise, it deletes the
-given branches.
-
-Usage:
- jiri cl cleanup [flags] <branches>
-
-<branches> is a list of branches to cleanup.
-
-The jiri cl cleanup flags are:
- -f=false
- Ignore unmerged changes.
- -remote-branch=master
- Name of the remote branch the CL pertains to, without the leading "origin/".
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri cl mail - Mail a changelist for review
-
-Command "mail" squashes all commits of a local branch into a single "changelist"
-and mails this changelist to Gerrit as a single commit. First time the command
-is invoked, it generates a Change-Id for the changelist, which is appended to
-the commit message. Consecutive invocations of the command use the same
-Change-Id by default, informing Gerrit that the incomming commit is an update of
-an existing changelist.
-
-Usage:
- jiri cl mail [flags]
-
-The jiri cl mail flags are:
- -autosubmit=false
- Automatically submit the changelist when feasible.
- -cc=
- Comma-seperated list of emails or LDAPs to cc.
- -check-uncommitted=true
- Check that no uncommitted changes exist.
- -d=false
- Send a draft changelist.
- -edit=true
- Open an editor to edit the CL description.
- -host=
- Gerrit host to use. Defaults to gerrit host specified in manifest.
- -m=
- CL description.
- -presubmit=all
- The type of presubmit tests to run. Valid values: none,all.
- -r=
- Comma-seperated list of emails or LDAPs to request review.
- -remote-branch=master
- Name of the remote branch the CL pertains to, without the leading "origin/".
- -set-topic=true
- Set Gerrit CL topic.
- -topic=
- CL topic, defaults to <username>-<branchname>.
- -verify=true
- Run pre-push git hooks.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri cl new - Create a new local branch for a changelist
-
-Command "new" creates a new local branch for a changelist. In particular, it
-forks a new branch with the given name from the current branch and records the
-relationship between the current branch and the new branch in the .jiri metadata
-directory. The information recorded in the .jiri metadata directory tracks
-dependencies between CLs and is used by the "jiri cl sync" and "jiri cl mail"
-commands.
-
-Usage:
- jiri cl new [flags] <name>
-
-<name> is the changelist name.
-
-The jiri cl new flags are:
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri cl sync - Bring a changelist up to date
-
-Command "sync" brings the CL identified by the current branch up to date with
-the branch tracking the remote branch this CL pertains to. To do that, the
-command uses the information recorded in the .jiri metadata directory to
-identify the sequence of dependent CLs leading to the current branch. The
-command then iterates over this sequence bringing each of the CLs up to date
-with its ancestor. The end result of this process is that all CLs in the
-sequence are up to date with the branch that tracks the remote branch this CL
-pertains to.
-
-NOTE: It is possible that the command cannot automatically merge changes in an
-ancestor into its dependent. When that occurs, the command is aborted and prints
-instructions that need to be followed before the command can be retried.
-
-Usage:
- jiri cl sync [flags]
-
-The jiri cl sync flags are:
- -remote-branch=master
- Name of the remote branch the CL pertains to, without the leading "origin/".
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri contributors - List project contributors
-
-Lists project contributors. Projects to consider can be specified as an
-argument. If no projects are specified, all projects in the current manifest are
-considered by default.
-
-Usage:
- jiri contributors [flags] <projects>
-
-<projects> is a list of projects to consider.
-
-The jiri contributors flags are:
- -aliases=
- Path to the aliases file.
- -n=false
- Show number of contributions.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri import
-
-Command "import" adds imports to the $JIRI_ROOT/.jiri_manifest file, which
-specifies manifest information for the jiri tool. The file is created if it
-doesn't already exist, otherwise additional imports are added to the existing
-file.
-
-An <import> element is added to the manifest representing a remote manifest
-import. The manifest file path is relative to the root directory of the remote
-import repository.
-
-Example:
- $ jiri import myfile https://foo.com/bar.git
-
-Run "jiri help manifest" for details on manifests.
-
-Usage:
- jiri import [flags] <manifest> <remote>
-
-<manifest> specifies the manifest file to use.
-
-<remote> specifies the remote manifest repository.
-
-The jiri import flags are:
- -name=
- The name of the remote manifest project, used to disambiguate manifest
- projects with the same remote. Typically empty.
- -out=
- The output file. Uses $JIRI_ROOT/.jiri_manifest if unspecified. Uses stdout
- if set to "-".
- -overwrite=false
- Write a new .jiri_manifest file with the given specification. If it already
- exists, the existing content will be ignored and the file will be
- overwritten.
- -protocol=git
- The version control protocol used by the remote manifest project.
- -remote-branch=master
- The branch of the remote manifest project to track, without the leading
- "origin/".
- -root=
- Root to store the manifest project locally.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri profile - Display information about installed profiles
-
-Display information about installed profiles and their configuration.
-
-Usage:
- jiri profile [flags] <command>
-
-The jiri profile commands are:
- list List available or installed profiles
- env Display profile environment variables
- install Install the given profiles
- uninstall Uninstall the given profiles
- update Install the latest default version of the given profiles
- cleanup Cleanup the locally installed profiles
- available List the available profiles
-
-The jiri profile flags are:
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri profile list - List available or installed profiles
-
-List available or installed profiles.
-
-Usage:
- jiri profile list [flags] [<profiles>]
-
-<profiles> is a list of profiles to list, defaulting to all profiles if none are
-specifically requested.
-
-The jiri profile list flags are:
- -available=false
- print the list of available profiles
- -env=
- specify an environment variable in the form: <var>=[<val>],...
- -info=
- The following fields for use with --profile-info are available:
- SchemaVersion - the version of the profiles implementation.
- Target.InstallationDir - the installation directory of the requested profile.
- Target.CommandLineEnv - the environment variables specified via the command line when installing this profile target.
- Target.Env - the environment variables computed by the profile installation process for this target.
- Target.Command - a command that can be used to create this profile.
- Note: if no --target is specified then the requested field will be displayed for all targets.
- Profile.Description - description of the requested profile.
- Profile.Root - the root directory of the requested profile.
- Profile.Versions - the set of supported versions for this profile.
- Profile.DefaultVersion - the default version of the requested profile.
- Profile.LatestVersion - the latest version available for the requested profile.
- Note: if no profiles are specified then the requested field will be displayed for all profiles.
- -merge-policies=+CCFLAGS,+CGO_CFLAGS,+CGO_CXXFLAGS,+CGO_LDFLAGS,+CXXFLAGS,GOARCH,GOOS,GOPATH:,^GOROOT*,+LDFLAGS,:PATH,VDLPATH:
- specify policies for merging environment variables
- -profiles=base,jiri
- a comma separated list of profiles to use
- -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
- specify the profiles database directory or file.
- -show-profiles-db=false
- print out the profiles database file
- -skip-profiles=false
- if set, no profiles will be used
- -target=<runtime.GOARCH>-<runtime.GOOS>
- specifies a profile target in the following form: <arch>-<os>[@<version>]
- -v=false
- print more detailed information
-
- -color=true
- Use color to format output.
-
-Jiri profile env - Display profile environment variables
-
-List profile specific and target specific environment variables. If the
-requested environment variable name ends in = then only the value will be
-printed, otherwise both name and value are printed, i.e. GOPATH="foo" vs just
-"foo".
-
-If no environment variable names are requested then all will be printed in
-<name>=<val> format.
-
-Usage:
- jiri profile env [flags] [<environment variable names>]
-
-[<environment variable names>] is an optional list of environment variables to
-display
-
-The jiri profile env flags are:
- -env=
- specify an environment variable in the form: <var>=[<val>],...
- -merge-policies=+CCFLAGS,+CGO_CFLAGS,+CGO_CXXFLAGS,+CGO_LDFLAGS,+CXXFLAGS,GOARCH,GOOS,GOPATH:,^GOROOT*,+LDFLAGS,:PATH,VDLPATH:
- specify policies for merging environment variables
- -profiles=base,jiri
- a comma separated list of profiles to use
- -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
- specify the profiles database directory or file.
- -skip-profiles=false
- if set, no profiles will be used
- -target=<runtime.GOARCH>-<runtime.GOOS>
- specifies a profile target in the following form: <arch>-<os>[@<version>]
- -v=false
- print more detailed information
-
- -color=true
- Use color to format output.
-
-Jiri profile install - Install the given profiles
-
-Install the given profiles.
-
-Usage:
- jiri profile install [flags] <profiles>
-
-<profiles> is a list of profiles to install.
-
-The jiri profile install flags are:
- -env=
- specify an environment variable in the form: <var>=[<val>],...
- -force=false
- force install the profile even if it is already installed
- -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
- specify the profiles database directory or file.
- -profiles-dir=.jiri_root/profiles
- the directory, relative to JIRI_ROOT, that profiles are installed in
- -target=<runtime.GOARCH>-<runtime.GOOS>
- specifies a profile target in the following form: <arch>-<os>[@<version>]
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri profile uninstall - Uninstall the given profiles
-
-Uninstall the given profiles.
-
-Usage:
- jiri profile uninstall [flags] <profiles>
-
-<profiles> is a list of profiles to uninstall.
-
-The jiri profile uninstall flags are:
- -all-targets=false
- apply to all targets for the specified profile(s)
- -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
- specify the profiles database directory or file.
- -profiles-dir=.jiri_root/profiles
- the directory, relative to JIRI_ROOT, that profiles are installed in
- -target=<runtime.GOARCH>-<runtime.GOOS>
- specifies a profile target in the following form: <arch>-<os>[@<version>]
- -v=false
- print more detailed information
-
- -color=true
- Use color to format output.
-
-Jiri profile update - Install the latest default version of the given profiles
-
-Install the latest default version of the given profiles.
-
-Usage:
- jiri profile update [flags] <profiles>
-
-<profiles> is a list of profiles to update, if omitted all profiles are updated.
-
-The jiri profile update flags are:
- -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
- specify the profiles database directory or file.
- -profiles-dir=.jiri_root/profiles
- the directory, relative to JIRI_ROOT, that profiles are installed in
- -v=false
- print more detailed information
-
- -color=true
- Use color to format output.
-
-Jiri profile cleanup - Cleanup the locally installed profiles
-
-Cleanup the locally installed profiles. This is generally required when
-recovering from earlier bugs or when preparing for a subsequent change to the
-profiles implementation.
-
-Usage:
- jiri profile cleanup [flags] <profiles>
-
-<profiles> is a list of profiles to cleanup, if omitted all profiles are
-cleaned.
-
-The jiri profile cleanup flags are:
- -gc=false
- uninstall profile targets that are older than the current default
- -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
- specify the profiles database directory or file.
- -profiles-dir=.jiri_root/profiles
- the directory, relative to JIRI_ROOT, that profiles are installed in
- -rewrite-profiles-db=false
- rewrite the profiles database to use the latest schema version
- -rm-all=false
- remove profiles database and all profile generated output files.
- -v=false
- print more detailed information
-
- -color=true
- Use color to format output.
-
-Jiri profile available - List the available profiles
-
-List the available profiles.
-
-Usage:
- jiri profile available [flags]
-
-The jiri profile available flags are:
- -v=false
- print more detailed information
-
- -color=true
- Use color to format output.
-
-Jiri project - Manage the jiri projects
-
-Manage the jiri projects.
-
-Usage:
- jiri project [flags] <command>
-
-The jiri project commands are:
- clean Restore jiri projects to their pristine state
- list List existing jiri projects and branches
- shell-prompt Print a succinct status of projects suitable for shell prompts
- poll Poll existing jiri projects
-
-The jiri project flags are:
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri project clean - Restore jiri projects to their pristine state
-
-Restore jiri projects back to their master branches and get rid of all the local
-branches and changes.
-
-Usage:
- jiri project clean [flags] <project ...>
-
-<project ...> is a list of projects to clean up.
-
-The jiri project clean flags are:
- -branches=false
- Delete all non-master branches.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri project list - List existing jiri projects and branches
-
-Inspect the local filesystem and list the existing projects and branches.
-
-Usage:
- jiri project list [flags]
-
-The jiri project list flags are:
- -branches=false
- Show project branches.
- -nopristine=false
- If true, omit pristine projects, i.e. projects with a clean master branch and
- no other branches.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri project shell-prompt - Print a succinct status of projects suitable for shell prompts
-
-Reports current branches of jiri projects (repositories) as well as an
-indication of each project's status:
- * indicates that a repository contains uncommitted changes
- % indicates that a repository contains untracked files
-
-Usage:
- jiri project shell-prompt [flags]
-
-The jiri project shell-prompt flags are:
- -check-dirty=true
- If false, don't check for uncommitted changes or untracked files. Setting
- this option to false is dangerous: dirty master branches will not appear in
- the output.
- -show-name=false
- Show the name of the current repo.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri project poll - Poll existing jiri projects
-
-Poll jiri projects that can affect the outcome of the given tests and report
-whether any new changes in these projects exist. If no tests are specified, all
-projects are polled by default.
-
-Usage:
- jiri project poll [flags] <test ...>
-
-<test ...> is a list of tests that determine what projects to poll.
-
-The jiri project poll flags are:
- -manifest=
- Name of the project manifest.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri rebuild - Rebuild all jiri tools
-
-Rebuilds all jiri tools and installs the resulting binaries into
-$JIRI_ROOT/.jiri_root/bin. This is similar to "jiri update", but does not update
-any projects before building the tools. The set of tools to rebuild is described
-in the manifest.
-
-Run "jiri help manifest" for details on manifests.
-
-Usage:
- jiri rebuild [flags]
-
-The jiri rebuild flags are:
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri snapshot - Manage project snapshots
-
-The "jiri snapshot" command can be used to manage project snapshots. In
-particular, it can be used to create new snapshots and to list existing
-snapshots.
-
-Usage:
- jiri snapshot [flags] <command>
-
-The jiri snapshot commands are:
- checkout Checkout a project snapshot
- create Create a new project snapshot
- list List existing project snapshots
-
-The jiri snapshot flags are:
- -dir=
- Directory where snapshot are stored. Defaults to $JIRI_ROOT/.snapshot.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri snapshot checkout - Checkout a project snapshot
-
-The "jiri snapshot checkout <snapshot>" command restores local project state to
-the state in the given snapshot manifest.
-
-Usage:
- jiri snapshot checkout [flags] <snapshot>
-
-<snapshot> is the snapshot manifest file.
-
-The jiri snapshot checkout flags are:
- -gc=false
- Garbage collect obsolete repositories.
-
- -color=true
- Use color to format output.
- -dir=
- Directory where snapshot are stored. Defaults to $JIRI_ROOT/.snapshot.
- -v=false
- Print verbose output.
-
-Jiri snapshot create - Create a new project snapshot
-
-The "jiri snapshot create <label>" command captures the current project state in
-a manifest. If the -push-remote flag is provided, the snapshot is committed and
-pushed upstream.
-
-Internally, snapshots are organized as follows:
-
- <snapshot-dir>/
- labels/
- <label1>/
- <label1-snapshot1>
- <label1-snapshot2>
- ...
- <label2>/
- <label2-snapshot1>
- <label2-snapshot2>
- ...
- <label3>/
- ...
- <label1> # a symlink to the latest <label1-snapshot*>
- <label2> # a symlink to the latest <label2-snapshot*>
- ...
-
-NOTE: Unlike the jiri tool commands, the above internal organization is not an
-API. It is an implementation and can change without notice.
-
-Usage:
- jiri snapshot create [flags] <label>
-
-<label> is the snapshot label.
-
-The jiri snapshot create flags are:
- -push-remote=false
- Commit and push snapshot upstream.
- -time-format=2006-01-02T15:04:05Z07:00
- Time format for snapshot file name.
-
- -color=true
- Use color to format output.
- -dir=
- Directory where snapshot are stored. Defaults to $JIRI_ROOT/.snapshot.
- -v=false
- Print verbose output.
-
-Jiri snapshot list - List existing project snapshots
-
-The "snapshot list" command lists existing snapshots of the labels specified as
-command-line arguments. If no arguments are provided, the command lists
-snapshots for all known labels.
-
-Usage:
- jiri snapshot list [flags] <label ...>
-
-<label ...> is a list of snapshot labels.
-
-The jiri snapshot list flags are:
- -color=true
- Use color to format output.
- -dir=
- Directory where snapshot are stored. Defaults to $JIRI_ROOT/.snapshot.
- -v=false
- Print verbose output.
-
-Jiri update - Update all jiri tools and projects
-
-Updates all projects, builds the latest version of all tools, and installs the
-resulting binaries into $JIRI_ROOT/.jiri_root/bin. The sequence in which the
-individual updates happen guarantees that we end up with a consistent set of
-tools and source code. The set of projects and tools to update is described in
-the manifest.
-
-Run "jiri help manifest" for details on manifests.
-
-Usage:
- jiri update [flags]
-
-The jiri update flags are:
- -attempts=1
- Number of attempts before failing.
- -gc=false
- Garbage collect obsolete repositories.
- -manifest=
- Name of the project manifest.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri upgrade - Upgrade jiri to new-style manifests
-
-Upgrades jiri to use new-style manifests.
-
-The old (deprecated) behavior only allowed a single manifest repository, located
-in $JIRI_ROOT/.manifest. The initial manifest file is located as follows:
- 1) Use -manifest flag, if non-empty. If it's empty...
- 2) Use $JIRI_ROOT/.local_manifest file. If it doesn't exist...
- 3) Use $JIRI_ROOT/.manifest/v2/default.
-
-The new behavior allows multiple manifest repositories, by allowing imports to
-specify project attributes describing the remote repository. The -manifest flag
-is no longer allowed to be set; the initial manifest file is always located in
-$JIRI_ROOT/.jiri_manifest. The .local_manifest file is ignored.
-
-During the transition phase, both old and new behaviors are supported. The jiri
-tool uses the existence of the $JIRI_ROOT/.jiri_manifest file as the signal; if
-it exists we run the new behavior, otherwise we run the old behavior.
-
-The new behavior includes a "jiri import" command, which writes or updates the
-.jiri_manifest file. The new bootstrap procedure runs "jiri import", and it is
-intended as a regular command to add imports to your jiri environment.
-
-This upgrade command eases the transition by writing an initial .jiri_manifest
-file for you. If you have an existing .local_manifest file, its contents will
-be incorporated into the new .jiri_manifest file, and it will be renamed to
-.local_manifest.BACKUP. The -revert flag deletes the .jiri_manifest file, and
-restores the .local_manifest file.
-
-Usage:
- jiri upgrade [flags] <kind>
-
-<kind> specifies the kind of upgrade, one of "v23" or "fuchsia".
-
-The jiri upgrade flags are:
- -revert=false
- Revert the upgrade by deleting the $JIRI_ROOT/.jiri_manifest file.
-
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri which - Show path to the jiri tool
-
-Which behaves similarly to the unix commandline tool. It is useful in
-determining whether the jiri binary is being run directly, or run via the jiri
-shim script.
-
-If the binary is being run directly, the output looks like this:
-
- # binary
- /path/to/binary/jiri
-
-If the script is being run, the output looks like this:
-
- # script
- /path/to/script/jiri
-
-Usage:
- jiri which [flags]
-
-The jiri which flags are:
- -color=true
- Use color to format output.
- -v=false
- Print verbose output.
-
-Jiri help - Display help for commands or topics
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-Usage:
- jiri help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The jiri help flags are:
- -style=compact
- The formatting style for help output:
- compact - Good for compact cmdline output.
- full - Good for cmdline output, shows all global flags.
- godoc - Good for godoc processing.
- shortonly - Only output short description.
- Override the default by setting the CMDLINE_STYLE environment variable.
- -width=<terminal width>
- Format output to this target width in runes, or unlimited if width < 0.
- Defaults to the terminal width if available. Override the default by setting
- the CMDLINE_WIDTH environment variable.
-
-Jiri filesystem - Description of jiri file system layout
-
-All data managed by the jiri tool is located in the file system under a root
-directory, colloquially called the jiri root directory. The file system layout
-looks like this:
-
- [root] # root directory (name picked by user)
- [root]/.jiri_root # root metadata directory
- [root]/.jiri_root/bin # contains tool binaries (jiri, etc.)
- [root]/.jiri_root/update_history # contains history of update snapshots
- [root]/.manifest # contains jiri manifests
- [root]/[project1] # project directory (name picked by user)
- [root]/[project1]/.jiri # project metadata directory
- [root]/[project1]/.jiri/metadata.v2 # project metadata file
- [root]/[project1]/.jiri/<<cls>> # project per-cl metadata directories
- [root]/[project1]/<<files>> # project files
- [root]/[project2]...
-
-The [root] and [projectN] directory names are picked by the user. The <<cls>>
-are named via jiri cl new, and the <<files>> are named as the user adds files
-and directories to their project. All other names above have special meaning to
-the jiri tool, and cannot be changed; you must ensure your path names don't
-collide with these special names.
-
-There are two ways to run the jiri tool:
-
-1) Shim script (recommended approach). This is a shell script that looks for
-the [root] directory. If the JIRI_ROOT environment variable is set, that is
-assumed to be the [root] directory. Otherwise the script looks for the
-.jiri_root directory, starting in the current working directory and walking up
-the directory chain. The search is terminated successfully when the .jiri_root
-directory is found; it fails after it reaches the root of the file system. Thus
-the shim must be invoked from the [root] directory or one of its subdirectories.
-
-Once the [root] is found, the JIRI_ROOT environment variable is set to its
-location, and [root]/.jiri_root/bin/jiri is invoked. That file contains the
-actual jiri binary.
-
-The point of the shim script is to make it easy to use the jiri tool with
-multiple [root] directories on your file system. Keep in mind that when "jiri
-update" is run, the jiri tool itself is automatically updated along with all
-projects. By using the shim script, you only need to remember to invoke the
-jiri tool from within the appropriate [root] directory, and the projects and
-tools under that [root] directory will be updated.
-
-The shim script is located at [root]/release/go/src/v.io/jiri/scripts/jiri
-
-2) Direct binary. This is the jiri binary, containing all of the actual jiri
-tool logic. The binary requires the JIRI_ROOT environment variable to point to
-the [root] directory.
-
-Note that if you have multiple [root] directories on your file system, you must
-remember to run the jiri binary corresponding to the setting of your JIRI_ROOT
-environment variable. Things may fail if you mix things up, since the jiri
-binary is updated with each call to "jiri update", and you may encounter version
-mismatches between the jiri binary and the various metadata files or other
-logic. This is the reason the shim script is recommended over running the
-binary directly.
-
-The binary is located at [root]/.jiri_root/bin/jiri
-
-Jiri manifest - Description of manifest files
-
-Jiri manifests are revisioned and stored in a "manifest" repository, that is
-available locally in $JIRI_ROOT/.manifest. The manifest uses the following XML
-schema:
-
- <manifest>
- <imports>
- <import name="default"/>
- ...
- </imports>
- <projects>
- <project name="release.go.jiri"
- path="release/go/src/v.io/jiri"
- protocol="git"
- name="https://vanadium.googlesource.com/release.go.jiri"
- revision="HEAD"/>
- ...
- </projects>
- <tools>
- <tool name="jiri" package="v.io/jiri"/>
- ...
- </tools>
- </manifest>
-
-The <import> element can be used to share settings across multiple manifests.
-Import names are interpreted relative to the $JIRI_ROOT/.manifest/v2 directory.
-Import cycles are not allowed and if a project or a tool is specified multiple
-times, the last specification takes effect. In particular, the elements <project
-name="foo" exclude="true"/> and <tool name="bar" exclude="true"/> can be used to
-exclude previously included projects and tools.
-
-The tool identifies which manifest to use using the following algorithm. If the
-$JIRI_ROOT/.local_manifest file exists, then it is used. Otherwise, the
-$JIRI_ROOT/.manifest/v2/<manifest>.xml file is used, where <manifest> is the
-value of the -manifest command-line flag, which defaults to "default".
-*/
-package main
diff --git a/gitutil/git.go b/gitutil/git.go
index c6a6675..985017f 100644
--- a/gitutil/git.go
+++ b/gitutil/git.go
@@ -212,7 +212,7 @@
// Committers returns a list of committers for the current repository
// along with the number of their commits.
func (g *Git) Committers() ([]string, error) {
- out, err := g.runOutputNoDryRun("shortlog", "-s", "-n", "-e")
+ out, err := g.runOutput("shortlog", "-s", "-n", "-e")
if err != nil {
return nil, err
}
@@ -227,7 +227,7 @@
args = append(args, "^"+base)
}
args = append(args, "--")
- out, err := g.runOutputNoDryRun(args...)
+ out, err := g.runOutput(args...)
if err != nil {
return 0, err
}
@@ -260,7 +260,7 @@
// CurrentBranchName returns the name of the current branch.
func (g *Git) CurrentBranchName() (string, error) {
- out, err := g.runOutputNoDryRun("rev-parse", "--abbrev-ref", "HEAD")
+ out, err := g.runOutput("rev-parse", "--abbrev-ref", "HEAD")
if err != nil {
return "", err
}
@@ -277,7 +277,7 @@
// CurrentRevisionOfBranch returns the current revision of the given branch.
func (g *Git) CurrentRevisionOfBranch(branch string) (string, error) {
- out, err := g.runOutputNoDryRun("rev-parse", branch)
+ out, err := g.runOutput("rev-parse", branch)
if err != nil {
return "", err
}
@@ -336,7 +336,7 @@
// (e.g. --merged).
func (g *Git) GetBranches(args ...string) ([]string, string, error) {
args = append([]string{"branch"}, args...)
- out, err := g.runOutputNoDryRun(args...)
+ out, err := g.runOutput(args...)
if err != nil {
return nil, "", err
}
@@ -555,7 +555,7 @@
// RemoteUrl gets the url of the remote with the given name.
func (g *Git) RemoteUrl(name string) (string, error) {
configKey := fmt.Sprintf("remote.%s.url", name)
- out, err := g.runOutputNoDryRun("config", "--get", configKey)
+ out, err := g.runOutput("config", "--get", configKey)
if err != nil {
return "", err
}
@@ -630,7 +630,7 @@
// TopLevel returns the top level path of the current repository.
func (g *Git) TopLevel() (string, error) {
// TODO(sadovsky): If g.rootDir is set, perhaps simply return that?
- out, err := g.runOutputNoDryRun("rev-parse", "--show-toplevel")
+ out, err := g.runOutput("rev-parse", "--show-toplevel")
if err != nil {
return "", err
}
@@ -657,7 +657,7 @@
// Version returns the major and minor git version.
func (g *Git) Version() (int, int, error) {
- out, err := g.runOutputNoDryRun("version")
+ out, err := g.runOutput("version")
if err != nil {
return 0, 0, err
}
@@ -709,21 +709,6 @@
return trimOutput(stdout.String()), nil
}
-func (g *Git) runOutputNoDryRun(args ...string) ([]string, error) {
- var stdout, stderr bytes.Buffer
- fn := func(s runutil.Sequence) runutil.Sequence {
- dryrun, _ := s.RunOpts()
- if dryrun {
- return s.DryRun(false).Verbose(true).Capture(&stdout, &stderr)
- }
- return s.Capture(&stdout, &stderr)
- }
- if err := g.runWithFn(fn, args...); err != nil {
- return nil, Error(stdout.String(), stderr.String(), args...)
- }
- return trimOutput(stdout.String()), nil
-}
-
func (g *Git) runInteractive(args ...string) error {
var stderr bytes.Buffer
// In order for the editing to work correctly with
diff --git a/import.go b/import.go
deleted file mode 100644
index 7636f13..0000000
--- a/import.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "os"
-
- "v.io/jiri/jiri"
- "v.io/jiri/project"
- "v.io/jiri/runutil"
- "v.io/x/lib/cmdline"
-)
-
-var (
- // Flags for configuring project attributes for remote imports.
- flagImportName, flagImportProtocol, flagImportRemoteBranch, flagImportRoot string
- // Flags for controlling the behavior of the command.
- flagImportOverwrite bool
- flagImportOut string
-)
-
-func init() {
- cmdImport.Flags.StringVar(&flagImportName, "name", "", `The name of the remote manifest project, used to disambiguate manifest projects with the same remote. Typically empty.`)
- cmdImport.Flags.StringVar(&flagImportProtocol, "protocol", "git", `The version control protocol used by the remote manifest project.`)
- cmdImport.Flags.StringVar(&flagImportRemoteBranch, "remote-branch", "master", `The branch of the remote manifest project to track, without the leading "origin/".`)
- cmdImport.Flags.StringVar(&flagImportRoot, "root", "", `Root to store the manifest project locally.`)
-
- cmdImport.Flags.BoolVar(&flagImportOverwrite, "overwrite", false, `Write a new .jiri_manifest file with the given specification. If it already exists, the existing content will be ignored and the file will be overwritten.`)
- cmdImport.Flags.StringVar(&flagImportOut, "out", "", `The output file. Uses $JIRI_ROOT/.jiri_manifest if unspecified. Uses stdout if set to "-".`)
-}
-
-var cmdImport = &cmdline.Command{
- Runner: jiri.RunnerFunc(runImport),
- Name: "import",
- Short: "Adds imports to .jiri_manifest file",
- Long: `
-Command "import" adds imports to the $JIRI_ROOT/.jiri_manifest file, which
-specifies manifest information for the jiri tool. The file is created if it
-doesn't already exist, otherwise additional imports are added to the existing
-file.
-
-An <import> element is added to the manifest representing a remote manifest
-import. The manifest file path is relative to the root directory of the remote
-import repository.
-
-Example:
- $ jiri import myfile https://foo.com/bar.git
-
-Run "jiri help manifest" for details on manifests.
-`,
- ArgsName: "<manifest> <remote>",
- ArgsLong: `
-<manifest> specifies the manifest file to use.
-
-<remote> specifies the remote manifest repository.
-`,
-}
-
-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)
-}
diff --git a/import_test.go b/import_test.go
deleted file mode 100644
index 7b13b97..0000000
--- a/import_test.go
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "testing"
-
- "v.io/jiri/jiri"
- "v.io/x/lib/gosh"
-)
-
-type importTestCase struct {
- Args []string
- Filename string
- Exist, Want string
- Stdout, Stderr string
-}
-
-func TestImport(t *testing.T) {
- tests := []importTestCase{
- {
- Stderr: `wrong number of arguments`,
- },
- {
- Args: []string{"a"},
- Stderr: `wrong number of arguments`,
- },
- {
- Args: []string{"a", "b", "c"},
- Stderr: `wrong number of arguments`,
- },
- // Remote imports, default append behavior
- {
- Args: []string{"-name=name", "-remote-branch=remotebranch", "-root=root", "foo", "https://github.com/new.git"},
- Want: `<manifest>
- <imports>
- <import manifest="foo" name="name" remote="https://github.com/new.git" remotebranch="remotebranch" root="root"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"foo", "https://github.com/new.git"},
- Want: `<manifest>
- <imports>
- <import manifest="foo" remote="https://github.com/new.git"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"-out=file", "foo", "https://github.com/new.git"},
- Filename: `file`,
- Want: `<manifest>
- <imports>
- <import manifest="foo" remote="https://github.com/new.git"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"-out=-", "foo", "https://github.com/new.git"},
- Stdout: `<manifest>
- <imports>
- <import manifest="foo" remote="https://github.com/new.git"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"foo", "https://github.com/new.git"},
- Exist: `<manifest>
- <imports>
- <import manifest="bar" remote="https://github.com/orig.git"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="bar" remote="https://github.com/orig.git"/>
- <import manifest="foo" remote="https://github.com/new.git"/>
- </imports>
-</manifest>
-`,
- },
- // Remote imports, explicit overwrite behavior
- {
- Args: []string{"-overwrite", "foo", "https://github.com/new.git"},
- Want: `<manifest>
- <imports>
- <import manifest="foo" remote="https://github.com/new.git"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"-overwrite", "-out=file", "foo", "https://github.com/new.git"},
- Filename: `file`,
- Want: `<manifest>
- <imports>
- <import manifest="foo" remote="https://github.com/new.git"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"-overwrite", "-out=-", "foo", "https://github.com/new.git"},
- Stdout: `<manifest>
- <imports>
- <import manifest="foo" remote="https://github.com/new.git"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"-overwrite", "foo", "https://github.com/new.git"},
- Exist: `<manifest>
- <imports>
- <import manifest="bar" remote="https://github.com/orig.git"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="foo" remote="https://github.com/new.git"/>
- </imports>
-</manifest>
-`,
- },
- }
- opts := gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf}
- sh := gosh.NewShell(opts)
- defer sh.Cleanup()
- jiriTool := sh.BuildGoPkg("v.io/jiri")
- for _, test := range tests {
- if err := testImport(opts, jiriTool, test); err != nil {
- t.Errorf("%v: %v", test.Args, err)
- }
- }
-}
-
-func testImport(opts gosh.Opts, jiriTool string, test importTestCase) error {
- sh := gosh.NewShell(opts)
- defer sh.Cleanup()
- tmpDir := sh.MakeTempDir()
- jiriRoot := filepath.Join(tmpDir, "root")
- if err := os.Mkdir(jiriRoot, 0755); err != nil {
- return err
- }
- sh.Pushd(jiriRoot)
- filename := test.Filename
- if filename == "" {
- filename = ".jiri_manifest"
- }
- // Set up manfile for the local file import tests. It should exist in both
- // the tmpDir (for ../manfile tests) and jiriRoot.
- for _, dir := range []string{tmpDir, jiriRoot} {
- if err := ioutil.WriteFile(filepath.Join(dir, "manfile"), nil, 0644); err != nil {
- return err
- }
- }
- // Set up an existing file if it was specified.
- if test.Exist != "" {
- if err := ioutil.WriteFile(filename, []byte(test.Exist), 0644); err != nil {
- return err
- }
- }
- // Run import and check the error.
- sh.Vars[jiri.RootEnv] = jiriRoot
- cmd := sh.Cmd(jiriTool, append([]string{"import"}, test.Args...)...)
- if test.Stderr != "" {
- cmd.ExitErrorIsOk = true
- }
- stdout, stderr := cmd.StdoutStderr()
- if got, want := stdout, test.Stdout; !strings.Contains(got, want) || (got != "" && want == "") {
- return fmt.Errorf("stdout got %q, want substr %q", got, want)
- }
- if got, want := stderr, test.Stderr; !strings.Contains(got, want) || (got != "" && want == "") {
- return fmt.Errorf("stderr got %q, want substr %q", got, want)
- }
- // Make sure the right file is generated.
- if test.Want != "" {
- data, err := ioutil.ReadFile(filename)
- if err != nil {
- return err
- }
- if got, want := string(data), test.Want; got != want {
- return fmt.Errorf("GOT\n%s\nWANT\n%s", got, want)
- }
- }
- return nil
-}
diff --git a/profile.go b/profile.go
deleted file mode 100644
index aa78d3f..0000000
--- a/profile.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "v.io/jiri/jiri"
- "v.io/jiri/profiles/profilescmdline"
- "v.io/x/lib/cmdline"
-)
-
-var cmdProfile = &cmdline.Command{
- Name: "profile",
- Short: "Display information about installed profiles",
- Long: "Display information about installed profiles and their configuration.",
-}
-
-func init() {
- profilescmdline.RegisterReaderCommands(cmdProfile, jiri.ProfilesDBDir)
- profilescmdline.RegisterManagementCommands(cmdProfile, true, "", jiri.ProfilesDBDir, jiri.ProfilesRootDir)
-}
diff --git a/profiles/profilescmdline/internal/i1/doc.go b/profiles/profilescmdline/internal/i1/doc.go
new file mode 100644
index 0000000..07f6370
--- /dev/null
+++ b/profiles/profilescmdline/internal/i1/doc.go
@@ -0,0 +1,239 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated via go generate.
+// DO NOT UPDATE MANUALLY
+
+/*
+Profiles are used to manage external sofware dependencies and offer a balance
+between providing no support at all and a full blown package manager. Profiles
+can be built natively as well as being cross compiled. A profile is a named
+collection of software required for a given system component or application.
+Current example profiles include 'syncbase' which consists of the leveldb and
+snappy libraries or 'android' which consists of all of the android components
+and downloads needed to build android applications. Profiles are built for
+specific targets.
+
+Targets
+
+Profiles generally refer to uncompiled source code that needs to be compiled for
+a specific "target". Targets hence represent compiled code and consist of:
+
+1. An 'architecture' that refers to the CPU to be generate code for
+
+2. An 'operating system' that refers to the operating system to generate code
+for
+
+3. A lexicographically orderd set of supported versions, one of which is
+designated as the default.
+
+4. An 'environment' which is a set of environment variables to use when
+compiling the profile
+
+Targets thus provide the basic support needed for cross compilation.
+
+Targets are versioned and multiple versions may be installed and used
+simultaneously. Versions are ordered lexicographically and each target specifies
+a 'default' version to be used when a specific version is not explicitly
+requested. A request to 'upgrade' the profile will result in the installation of
+the default version of the targets currently installed if that default version
+is not already installed.
+
+The Supported Commands
+
+Profiles, or more correctly, targets for specific profiles may be installed or
+removed. When doing so, the name of the profile is required, but the other
+components of the target are optional and will default to the values of the
+system that the commands are run on (so-called native builds) and the default
+version for that target. Once a profile is installed it may be referred to by
+its tag for subsequent removals.
+
+The are also update and cleanup commands. Update installs the default version of
+the requested profile or for all profiles for the already installed targets.
+Cleanup will uninstall targets whose version is older than the default.
+
+Finally, there are commands to list the available and installed profiles and to
+access the environment variables specified and stored in each profile
+installation and a command (recreate) to generate a list of commands that can be
+run to recreate the currently installed profiles.
+
+The Profiles Database
+
+The profiles packages manages a database that tracks the installed profiles and
+their configurations. Other command line tools and packages are expected to read
+information about the currently installed profiles from this database via the
+profiles package. The profile command line tools support displaying the database
+(via the list command) or for specifying an alternate version of the file (via
+the -profiles-db flag) which is generally useful for debugging.
+
+Adding Profiles
+
+Profiles are intended to be provided as go packages that register themselves
+with the profile command line tools via the *v.io/jiri/profiles* package. They
+must implement the interfaces defined by that package and be imported (e.g.
+import _ "myprofile") by the command line tools that are to use them.
+
+Usage:
+ jiri profile-i1 [flags] <command>
+
+The jiri profile-i1 commands are:
+ install Install the given profiles
+ uninstall Uninstall the given profiles
+ update Install the latest default version of the given profiles
+ cleanup Cleanup the locally installed profiles
+ available List the available profiles
+ help Display help for commands or topics
+
+The jiri profile-i1 flags are:
+ -color=true
+ Use color to format output.
+ -v=false
+ Print verbose output.
+
+The global flags are:
+ -metadata=<just specify -metadata to activate>
+ Displays metadata for the program and exits.
+ -time=false
+ Dump timing information to stderr before exiting the program.
+
+Jiri profile-i1 install - Install the given profiles
+
+Install the given profiles.
+
+Usage:
+ jiri profile-i1 install [flags] <profiles>
+
+<profiles> is a list of profiles to install.
+
+The jiri profile-i1 install flags are:
+ -env=
+ specify an environment variable in the form: <var>=[<val>],...
+ -force=false
+ force install the profile even if it is already installed
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+ specify the profiles database directory or file.
+ -profiles-dir=.jiri_root/profiles
+ the directory, relative to JIRI_ROOT, that profiles are installed in
+ -target=<runtime.GOARCH>-<runtime.GOOS>
+ specifies a profile target in the following form: <arch>-<os>[@<version>]
+
+ -color=true
+ Use color to format output.
+ -v=false
+ Print verbose output.
+
+Jiri profile-i1 uninstall - Uninstall the given profiles
+
+Uninstall the given profiles.
+
+Usage:
+ jiri profile-i1 uninstall [flags] <profiles>
+
+<profiles> is a list of profiles to uninstall.
+
+The jiri profile-i1 uninstall flags are:
+ -all-targets=false
+ apply to all targets for the specified profile(s)
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+ specify the profiles database directory or file.
+ -profiles-dir=.jiri_root/profiles
+ the directory, relative to JIRI_ROOT, that profiles are installed in
+ -target=<runtime.GOARCH>-<runtime.GOOS>
+ specifies a profile target in the following form: <arch>-<os>[@<version>]
+ -v=false
+ print more detailed information
+
+ -color=true
+ Use color to format output.
+
+Jiri profile-i1 update - Install the latest default version of the given profiles
+
+Install the latest default version of the given profiles.
+
+Usage:
+ jiri profile-i1 update [flags] <profiles>
+
+<profiles> is a list of profiles to update, if omitted all profiles are updated.
+
+The jiri profile-i1 update flags are:
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+ specify the profiles database directory or file.
+ -profiles-dir=.jiri_root/profiles
+ the directory, relative to JIRI_ROOT, that profiles are installed in
+ -v=false
+ print more detailed information
+
+ -color=true
+ Use color to format output.
+
+Jiri profile-i1 cleanup - Cleanup the locally installed profiles
+
+Cleanup the locally installed profiles. This is generally required when
+recovering from earlier bugs or when preparing for a subsequent change to the
+profiles implementation.
+
+Usage:
+ jiri profile-i1 cleanup [flags] <profiles>
+
+<profiles> is a list of profiles to cleanup, if omitted all profiles are
+cleaned.
+
+The jiri profile-i1 cleanup flags are:
+ -gc=false
+ uninstall profile targets that are older than the current default
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+ specify the profiles database directory or file.
+ -profiles-dir=.jiri_root/profiles
+ the directory, relative to JIRI_ROOT, that profiles are installed in
+ -rewrite-profiles-db=false
+ rewrite the profiles database to use the latest schema version
+ -rm-all=false
+ remove profiles database and all profile generated output files.
+ -v=false
+ print more detailed information
+
+ -color=true
+ Use color to format output.
+
+Jiri profile-i1 available - List the available profiles
+
+List the available profiles.
+
+Usage:
+ jiri profile-i1 available [flags]
+
+The jiri profile-i1 available flags are:
+ -v=false
+ print more detailed information
+
+ -color=true
+ Use color to format output.
+
+Jiri profile-i1 help - Display help for commands or topics
+
+Help with no args displays the usage of the parent command.
+
+Help with args displays the usage of the specified sub-command or help topic.
+
+"help ..." recursively displays help for all commands and topics.
+
+Usage:
+ jiri profile-i1 help [flags] [command/topic ...]
+
+[command/topic ...] optionally identifies a specific sub-command or help topic.
+
+The jiri profile-i1 help flags are:
+ -style=compact
+ The formatting style for help output:
+ compact - Good for compact cmdline output.
+ full - Good for cmdline output, shows all global flags.
+ godoc - Good for godoc processing.
+ shortonly - Only output short description.
+ Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+ Format output to this target width in runes, or unlimited if width < 0.
+ Defaults to the terminal width if available. Override the default by setting
+ the CMDLINE_WIDTH environment variable.
+*/
+package main
diff --git a/profiles/profilescmdline/internal/i2/doc.go b/profiles/profilescmdline/internal/i2/doc.go
new file mode 100644
index 0000000..b202251
--- /dev/null
+++ b/profiles/profilescmdline/internal/i2/doc.go
@@ -0,0 +1,239 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated via go generate.
+// DO NOT UPDATE MANUALLY
+
+/*
+Profiles are used to manage external sofware dependencies and offer a balance
+between providing no support at all and a full blown package manager. Profiles
+can be built natively as well as being cross compiled. A profile is a named
+collection of software required for a given system component or application.
+Current example profiles include 'syncbase' which consists of the leveldb and
+snappy libraries or 'android' which consists of all of the android components
+and downloads needed to build android applications. Profiles are built for
+specific targets.
+
+Targets
+
+Profiles generally refer to uncompiled source code that needs to be compiled for
+a specific "target". Targets hence represent compiled code and consist of:
+
+1. An 'architecture' that refers to the CPU to be generate code for
+
+2. An 'operating system' that refers to the operating system to generate code
+for
+
+3. A lexicographically orderd set of supported versions, one of which is
+designated as the default.
+
+4. An 'environment' which is a set of environment variables to use when
+compiling the profile
+
+Targets thus provide the basic support needed for cross compilation.
+
+Targets are versioned and multiple versions may be installed and used
+simultaneously. Versions are ordered lexicographically and each target specifies
+a 'default' version to be used when a specific version is not explicitly
+requested. A request to 'upgrade' the profile will result in the installation of
+the default version of the targets currently installed if that default version
+is not already installed.
+
+The Supported Commands
+
+Profiles, or more correctly, targets for specific profiles may be installed or
+removed. When doing so, the name of the profile is required, but the other
+components of the target are optional and will default to the values of the
+system that the commands are run on (so-called native builds) and the default
+version for that target. Once a profile is installed it may be referred to by
+its tag for subsequent removals.
+
+The are also update and cleanup commands. Update installs the default version of
+the requested profile or for all profiles for the already installed targets.
+Cleanup will uninstall targets whose version is older than the default.
+
+Finally, there are commands to list the available and installed profiles and to
+access the environment variables specified and stored in each profile
+installation and a command (recreate) to generate a list of commands that can be
+run to recreate the currently installed profiles.
+
+The Profiles Database
+
+The profiles packages manages a database that tracks the installed profiles and
+their configurations. Other command line tools and packages are expected to read
+information about the currently installed profiles from this database via the
+profiles package. The profile command line tools support displaying the database
+(via the list command) or for specifying an alternate version of the file (via
+the -profiles-db flag) which is generally useful for debugging.
+
+Adding Profiles
+
+Profiles are intended to be provided as go packages that register themselves
+with the profile command line tools via the *v.io/jiri/profiles* package. They
+must implement the interfaces defined by that package and be imported (e.g.
+import _ "myprofile") by the command line tools that are to use them.
+
+Usage:
+ jiri profile-i2 [flags] <command>
+
+The jiri profile-i2 commands are:
+ install Install the given profiles
+ uninstall Uninstall the given profiles
+ update Install the latest default version of the given profiles
+ cleanup Cleanup the locally installed profiles
+ available List the available profiles
+ help Display help for commands or topics
+
+The jiri profile-i2 flags are:
+ -color=true
+ Use color to format output.
+ -v=false
+ Print verbose output.
+
+The global flags are:
+ -metadata=<just specify -metadata to activate>
+ Displays metadata for the program and exits.
+ -time=false
+ Dump timing information to stderr before exiting the program.
+
+Jiri profile-i2 install - Install the given profiles
+
+Install the given profiles.
+
+Usage:
+ jiri profile-i2 install [flags] <profiles>
+
+<profiles> is a list of profiles to install.
+
+The jiri profile-i2 install flags are:
+ -env=
+ specify an environment variable in the form: <var>=[<val>],...
+ -force=false
+ force install the profile even if it is already installed
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+ specify the profiles database directory or file.
+ -profiles-dir=.jiri_root/profiles
+ the directory, relative to JIRI_ROOT, that profiles are installed in
+ -target=<runtime.GOARCH>-<runtime.GOOS>
+ specifies a profile target in the following form: <arch>-<os>[@<version>]
+
+ -color=true
+ Use color to format output.
+ -v=false
+ Print verbose output.
+
+Jiri profile-i2 uninstall - Uninstall the given profiles
+
+Uninstall the given profiles.
+
+Usage:
+ jiri profile-i2 uninstall [flags] <profiles>
+
+<profiles> is a list of profiles to uninstall.
+
+The jiri profile-i2 uninstall flags are:
+ -all-targets=false
+ apply to all targets for the specified profile(s)
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+ specify the profiles database directory or file.
+ -profiles-dir=.jiri_root/profiles
+ the directory, relative to JIRI_ROOT, that profiles are installed in
+ -target=<runtime.GOARCH>-<runtime.GOOS>
+ specifies a profile target in the following form: <arch>-<os>[@<version>]
+ -v=false
+ print more detailed information
+
+ -color=true
+ Use color to format output.
+
+Jiri profile-i2 update - Install the latest default version of the given profiles
+
+Install the latest default version of the given profiles.
+
+Usage:
+ jiri profile-i2 update [flags] <profiles>
+
+<profiles> is a list of profiles to update, if omitted all profiles are updated.
+
+The jiri profile-i2 update flags are:
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+ specify the profiles database directory or file.
+ -profiles-dir=.jiri_root/profiles
+ the directory, relative to JIRI_ROOT, that profiles are installed in
+ -v=false
+ print more detailed information
+
+ -color=true
+ Use color to format output.
+
+Jiri profile-i2 cleanup - Cleanup the locally installed profiles
+
+Cleanup the locally installed profiles. This is generally required when
+recovering from earlier bugs or when preparing for a subsequent change to the
+profiles implementation.
+
+Usage:
+ jiri profile-i2 cleanup [flags] <profiles>
+
+<profiles> is a list of profiles to cleanup, if omitted all profiles are
+cleaned.
+
+The jiri profile-i2 cleanup flags are:
+ -gc=false
+ uninstall profile targets that are older than the current default
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+ specify the profiles database directory or file.
+ -profiles-dir=.jiri_root/profiles
+ the directory, relative to JIRI_ROOT, that profiles are installed in
+ -rewrite-profiles-db=false
+ rewrite the profiles database to use the latest schema version
+ -rm-all=false
+ remove profiles database and all profile generated output files.
+ -v=false
+ print more detailed information
+
+ -color=true
+ Use color to format output.
+
+Jiri profile-i2 available - List the available profiles
+
+List the available profiles.
+
+Usage:
+ jiri profile-i2 available [flags]
+
+The jiri profile-i2 available flags are:
+ -v=false
+ print more detailed information
+
+ -color=true
+ Use color to format output.
+
+Jiri profile-i2 help - Display help for commands or topics
+
+Help with no args displays the usage of the parent command.
+
+Help with args displays the usage of the specified sub-command or help topic.
+
+"help ..." recursively displays help for all commands and topics.
+
+Usage:
+ jiri profile-i2 help [flags] [command/topic ...]
+
+[command/topic ...] optionally identifies a specific sub-command or help topic.
+
+The jiri profile-i2 help flags are:
+ -style=compact
+ The formatting style for help output:
+ compact - Good for compact cmdline output.
+ full - Good for cmdline output, shows all global flags.
+ godoc - Good for godoc processing.
+ shortonly - Only output short description.
+ Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+ Format output to this target width in runes, or unlimited if width < 0.
+ Defaults to the terminal width if available. Override the default by setting
+ the CMDLINE_WIDTH environment variable.
+*/
+package main
diff --git a/profiles/profilescmdline/manager_test.go b/profiles/profilescmdline/manager_test.go
index 38cd5d7..b00eccc 100644
--- a/profiles/profilescmdline/manager_test.go
+++ b/profiles/profilescmdline/manager_test.go
@@ -76,7 +76,7 @@
buildInstallersOnce.Do(func() {
sh := newShell(t)
prefix := "v.io/jiri/profiles/profilescmdline/internal/"
- sh.BuildGoPkg("v.io/jiri", "-o", "jiri")
+ sh.BuildGoPkg("v.io/jiri/cmd/jiri", "-o", "jiri")
sh.BuildGoPkg(prefix+"i1", "-o", "jiri-profile-i1")
sh.BuildGoPkg(prefix+"i2", "-o", "jiri-profile-i2")
buildInstallersBindir = sh.Opts.BinDir
@@ -87,7 +87,7 @@
func buildJiri(t *testing.T) string {
buildJiriOnce.Do(func() {
sh := newShell(t)
- sh.BuildGoPkg("v.io/jiri", "-o", "jiri")
+ sh.BuildGoPkg("v.io/jiri/cmd/jiri", "-o", "jiri")
buildJiriBindir = sh.Opts.BinDir
})
return buildJiriBindir
diff --git a/project.go b/project.go
deleted file mode 100644
index ea94798..0000000
--- a/project.go
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "encoding/json"
- "fmt"
- "path/filepath"
- "sort"
- "strings"
-
- "v.io/jiri/jiri"
- "v.io/jiri/project"
- "v.io/jiri/tool"
- "v.io/jiri/util"
- "v.io/x/lib/cmdline"
- "v.io/x/lib/set"
-)
-
-var (
- branchesFlag bool
- cleanupBranchesFlag bool
- noPristineFlag bool
- checkDirtyFlag bool
- showNameFlag bool
-)
-
-func init() {
- cmdProjectClean.Flags.BoolVar(&cleanupBranchesFlag, "branches", false, "Delete all non-master branches.")
- cmdProjectList.Flags.BoolVar(&branchesFlag, "branches", false, "Show project branches.")
- cmdProjectList.Flags.BoolVar(&noPristineFlag, "nopristine", false, "If true, omit pristine projects, i.e. projects with a clean master branch and no other branches.")
- cmdProjectShellPrompt.Flags.BoolVar(&checkDirtyFlag, "check-dirty", true, "If false, don't check for uncommitted changes or untracked files. Setting this option to false is dangerous: dirty master branches will not appear in the output.")
- cmdProjectShellPrompt.Flags.BoolVar(&showNameFlag, "show-name", false, "Show the name of the current repo.")
-
- tool.InitializeProjectFlags(&cmdProjectPoll.Flags)
-
-}
-
-// cmdProject represents the "jiri project" command.
-var cmdProject = &cmdline.Command{
- Name: "project",
- Short: "Manage the jiri projects",
- Long: "Manage the jiri projects.",
- Children: []*cmdline.Command{cmdProjectClean, cmdProjectList, cmdProjectShellPrompt, cmdProjectPoll},
-}
-
-// cmdProjectClean represents the "jiri project clean" command.
-var cmdProjectClean = &cmdline.Command{
- Runner: jiri.RunnerFunc(runProjectClean),
- Name: "clean",
- Short: "Restore jiri projects to their pristine state",
- Long: "Restore jiri projects back to their master branches and get rid of all the local branches and changes.",
- ArgsName: "<project ...>",
- ArgsLong: "<project ...> is a list of projects to clean up.",
-}
-
-func runProjectClean(jirix *jiri.X, args []string) (e error) {
- localProjects, err := project.LocalProjects(jirix, project.FullScan)
- if err != nil {
- return err
- }
- var projects project.Projects
- if len(args) > 0 {
- for _, arg := range args {
- p, err := localProjects.FindUnique(arg)
- if err != nil {
- fmt.Fprintf(jirix.Stderr(), "Error finding local project %q: %v.\n", p.Name, err)
- } else {
- projects[p.Key()] = p
- }
- }
- } else {
- projects = localProjects
- }
- if err := project.CleanupProjects(jirix, projects, cleanupBranchesFlag); err != nil {
- return err
- }
- return nil
-}
-
-// cmdProjectList represents the "jiri project list" command.
-var cmdProjectList = &cmdline.Command{
- Runner: jiri.RunnerFunc(runProjectList),
- Name: "list",
- Short: "List existing jiri projects and branches",
- Long: "Inspect the local filesystem and list the existing projects and branches.",
-}
-
-// runProjectList generates a listing of local projects.
-func runProjectList(jirix *jiri.X, _ []string) error {
- states, err := project.GetProjectStates(jirix, noPristineFlag)
- if err != nil {
- return err
- }
- var keys project.ProjectKeys
- for key := range states {
- keys = append(keys, key)
- }
- sort.Sort(keys)
-
- for _, key := range keys {
- state := states[key]
- if noPristineFlag {
- pristine := len(state.Branches) == 1 && state.CurrentBranch == "master" && !state.HasUncommitted && !state.HasUntracked
- if pristine {
- continue
- }
- }
- fmt.Fprintf(jirix.Stdout(), "name=%q remote=%q path=%q\n", state.Project.Name, state.Project.Remote, state.Project.Path)
- if branchesFlag {
- for _, branch := range state.Branches {
- s := " "
- if branch.Name == state.CurrentBranch {
- s += "* "
- }
- s += branch.Name
- if branch.HasGerritMessage {
- s += " (exported to gerrit)"
- }
- fmt.Fprintf(jirix.Stdout(), "%v\n", s)
- }
- }
- }
- return nil
-}
-
-// cmdProjectShellPrompt represents the "jiri project shell-prompt" command.
-var cmdProjectShellPrompt = &cmdline.Command{
- Runner: jiri.RunnerFunc(runProjectShellPrompt),
- Name: "shell-prompt",
- Short: "Print a succinct status of projects suitable for shell prompts",
- Long: `
-Reports current branches of jiri projects (repositories) as well as an
-indication of each project's status:
- * indicates that a repository contains uncommitted changes
- % indicates that a repository contains untracked files
-`,
-}
-
-func runProjectShellPrompt(jirix *jiri.X, args []string) error {
- states, err := project.GetProjectStates(jirix, checkDirtyFlag)
- if err != nil {
- return err
- }
- var keys project.ProjectKeys
- for key := range states {
- keys = append(keys, key)
- }
- sort.Sort(keys)
-
- // Get the key of the current project.
- currentProjectKey, err := project.CurrentProjectKey(jirix)
- if err != nil {
- return err
- }
- var statuses []string
- for _, key := range keys {
- state := states[key]
- status := ""
- if checkDirtyFlag {
- if state.HasUncommitted {
- status += "*"
- }
- if state.HasUntracked {
- status += "%"
- }
- }
- short := state.CurrentBranch + status
- long := filepath.Base(states[key].Project.Name) + ":" + short
- if key == currentProjectKey {
- if showNameFlag {
- statuses = append([]string{long}, statuses...)
- } else {
- statuses = append([]string{short}, statuses...)
- }
- } else {
- pristine := state.CurrentBranch == "master"
- if checkDirtyFlag {
- pristine = pristine && !state.HasUncommitted && !state.HasUntracked
- }
- if !pristine {
- statuses = append(statuses, long)
- }
- }
- }
- fmt.Println(strings.Join(statuses, ","))
- return nil
-}
-
-// cmdProjectPoll represents the "jiri project poll" command.
-var cmdProjectPoll = &cmdline.Command{
- Runner: jiri.RunnerFunc(runProjectPoll),
- Name: "poll",
- Short: "Poll existing jiri projects",
- Long: `
-Poll jiri projects that can affect the outcome of the given tests
-and report whether any new changes in these projects exist. If no
-tests are specified, all projects are polled by default.
-`,
- ArgsName: "<test ...>",
- ArgsLong: "<test ...> is a list of tests that determine what projects to poll.",
-}
-
-// runProjectPoll generates a description of changes that exist
-// remotely but do not exist locally.
-func runProjectPoll(jirix *jiri.X, args []string) error {
- projectSet := map[string]struct{}{}
- if len(args) > 0 {
- config, err := util.LoadConfig(jirix)
- if err != nil {
- return err
- }
- // Compute a map from tests to projects that can change the
- // outcome of the test.
- testProjects := map[string][]string{}
- for _, project := range config.Projects() {
- for _, test := range config.ProjectTests([]string{project}) {
- testProjects[test] = append(testProjects[test], project)
- }
- }
- for _, arg := range args {
- projects, ok := testProjects[arg]
- if !ok {
- return fmt.Errorf("failed to find any projects for test %q", arg)
- }
- set.String.Union(projectSet, set.String.FromSlice(projects))
- }
- }
- update, err := project.PollProjects(jirix, projectSet)
- if err != nil {
- return err
- }
-
- // Remove projects with empty changes.
- for project := range update {
- if changes := update[project]; len(changes) == 0 {
- delete(update, project)
- }
- }
-
- // Print update if it is not empty.
- if len(update) > 0 {
- bytes, err := json.MarshalIndent(update, "", " ")
- if err != nil {
- return fmt.Errorf("MarshalIndent() failed: %v", err)
- }
- fmt.Fprintf(jirix.Stdout(), "%s\n", bytes)
- }
- return nil
-}
diff --git a/rebuild.go b/rebuild.go
deleted file mode 100644
index dbf30bc..0000000
--- a/rebuild.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
-
- "v.io/jiri/collect"
- "v.io/jiri/jiri"
- "v.io/jiri/project"
- "v.io/x/lib/cmdline"
-)
-
-// cmdRebuild represents the "jiri rebuild" command.
-var cmdRebuild = &cmdline.Command{
- Runner: jiri.RunnerFunc(runRebuild),
- Name: "rebuild",
- Short: "Rebuild all jiri tools",
- Long: `
-Rebuilds all jiri tools and installs the resulting binaries into
-$JIRI_ROOT/.jiri_root/bin. This is similar to "jiri update", but does not update
-any projects before building the tools. The set of tools to rebuild is described
-in the manifest.
-
-Run "jiri help manifest" for details on manifests.
-`,
-}
-
-func runRebuild(jirix *jiri.X, args []string) (e error) {
- projects, tools, err := project.LoadManifest(jirix)
- if err != nil {
- return err
- }
-
- // Create a temporary directory in which tools will be built.
- tmpDir, err := jirix.NewSeq().TempDir("", "tmp-jiri-rebuild")
- if err != nil {
- return fmt.Errorf("TempDir() failed: %v", err)
- }
-
- // Make sure we cleanup the temp directory.
- defer collect.Error(func() error { return jirix.NewSeq().RemoveAll(tmpDir).Done() }, &e)
-
- // Paranoid sanity checking.
- if _, ok := tools[project.JiriName]; !ok {
- return fmt.Errorf("tool %q not found", project.JiriName)
- }
-
- // Build and install tools.
- if err := project.BuildTools(jirix, projects, tools, tmpDir); err != nil {
- return err
- }
- return project.InstallTools(jirix, tmpDir)
-}
diff --git a/runutil/.api b/runutil/.api
index e24cb58..4f9083e 100644
--- a/runutil/.api
+++ b/runutil/.api
@@ -20,7 +20,6 @@
pkg runutil, method (Sequence) Create(string) (*os.File, error)
pkg runutil, method (Sequence) Dir(string) Sequence
pkg runutil, method (Sequence) Done() error
-pkg runutil, method (Sequence) DryRun(bool) Sequence
pkg runutil, method (Sequence) Env(map[string]string) Sequence
pkg runutil, method (Sequence) Error() error
pkg runutil, method (Sequence) Fprintf(io.Writer, string, ...interface{}) Sequence
diff --git a/runutil/executor.go b/runutil/executor.go
index 69fd5a9..c8cebce 100644
--- a/runutil/executor.go
+++ b/runutil/executor.go
@@ -25,7 +25,6 @@
type opts struct {
color bool
dir string
- dryRun bool
env map[string]string
stdin io.Reader
stdout io.Writer
@@ -38,7 +37,7 @@
opts opts
}
-func newExecutor(env map[string]string, stdin io.Reader, stdout, stderr io.Writer, color, dryRun, verbose bool) *executor {
+func newExecutor(env map[string]string, stdin io.Reader, stdout, stderr io.Writer, color, verbose bool) *executor {
if color {
term := os.Getenv("TERM")
switch term {
@@ -50,7 +49,6 @@
indent: 0,
opts: opts{
color: color,
- dryRun: dryRun,
env: env,
stdin: stdin,
stdout: stdout,
@@ -115,28 +113,8 @@
}
// call executes the given Go standard library function,
-// encapsulated as a closure, respecting the "dry run" option.
+// encapsulated as a closure.
func (e *executor) call(fn func() error, format string, args ...interface{}) error {
- if opts := e.opts; opts.dryRun {
- opts.verbose = true
- return e.function(opts, func() error { return nil }, format, args...)
- }
- return e.function(e.opts, fn, format, args...)
-}
-
-// alwaysRun executes the given Go standard library function, encapsulated as a
-// closure, but translating "dry run" into "verbose" for this particular
-// command so that the command can execute and thus allow subsequent
-// commands to complete. It is generally used for testing/making files/directories
-// that affect subsequent behaviour.
-func (e *executor) alwaysRun(fn func() error, format string, args ...interface{}) error {
- if opts := e.opts; opts.dryRun {
- // Disable the dry run option as this function has no effect and
- // doing so results in more informative "dry run" output.
- opts.dryRun = false
- opts.verbose = true
- return e.function(opts, fn, format, args...)
- }
return e.function(e.opts, fn, format, args...)
}
@@ -168,7 +146,7 @@
command.Stdout = opts.stdout
command.Stderr = opts.stderr
command.Env = envvar.MapToSlice(opts.env)
- if opts.verbose || opts.dryRun {
+ if opts.verbose {
args := []string{}
for _, arg := range command.Args {
// Quote any arguments that contain '"', ''', '|', or ' '.
@@ -180,10 +158,6 @@
}
e.printf(e.opts.stdout, strings.Replace(strings.Join(args, " "), "%", "%%", -1))
}
- if opts.dryRun {
- e.printf(e.opts.stdout, "OK")
- return nil, nil
- }
if wait {
if timeout == 0 {
diff --git a/runutil/executor_test.go b/runutil/executor_test.go
index c02ca43..634a647 100644
--- a/runutil/executor_test.go
+++ b/runutil/executor_test.go
@@ -40,7 +40,7 @@
func TestCommandOK(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, true)
if err := e.run(forever, e.opts, "go", "run", "./testdata/ok_hello.go"); err != nil {
t.Fatalf(`Command("go run ./testdata/ok_hello.go") failed: %v`, err)
}
@@ -51,7 +51,7 @@
func TestCommandFail(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, true)
if err := e.run(forever, e.opts, "go", "run", "./testdata/fail_hello.go"); err == nil {
t.Fatalf(`Command("go run ./testdata/fail_hello.go") did not fail when it should`)
}
@@ -62,7 +62,7 @@
func TestCommandWithOptsOK(t *testing.T) {
var cmdOut, runOut bytes.Buffer
- e := newExecutor(nil, os.Stdin, &runOut, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &runOut, ioutil.Discard, false, true)
opts := e.opts
opts.stdout = &cmdOut
if err := e.run(forever, opts, "go", "run", "./testdata/ok_hello.go"); err != nil {
@@ -78,7 +78,7 @@
func TestCommandWithOptsFail(t *testing.T) {
var cmdOut, runOut bytes.Buffer
- e := newExecutor(nil, os.Stdin, &runOut, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &runOut, ioutil.Discard, false, true)
opts := e.opts
opts.stdout = &cmdOut
if err := e.run(forever, opts, "go", "run", "./testdata/fail_hello.go"); err == nil {
@@ -94,7 +94,7 @@
func TestTimedCommandOK(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, true)
if err := e.run(10*time.Second, e.opts, "go", "run", "./testdata/fast_hello.go"); err != nil {
t.Fatalf(`TimedCommand("go run ./testdata/fast_hello.go") failed: %v`, err)
}
@@ -105,7 +105,7 @@
func TestTimedCommandFail(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, true)
bin, err := buildTestProgram(e, "slow_hello")
if bin != "" {
defer os.RemoveAll(filepath.Dir(bin))
@@ -125,7 +125,7 @@
func TestTimedCommandWithOptsOK(t *testing.T) {
var cmdOut, runOut bytes.Buffer
- e := newExecutor(nil, os.Stdin, &runOut, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &runOut, ioutil.Discard, false, true)
opts := e.opts
opts.stdout = &cmdOut
if err := e.run(10*time.Second, opts, "go", "run", "./testdata/fast_hello.go"); err != nil {
@@ -141,7 +141,7 @@
func TestTimedCommandWithOptsFail(t *testing.T) {
var cmdOut, runOut bytes.Buffer
- e := newExecutor(nil, os.Stdin, &runOut, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &runOut, ioutil.Discard, false, true)
bin, err := buildTestProgram(e, "slow_hello")
if bin != "" {
defer os.RemoveAll(filepath.Dir(bin))
@@ -166,7 +166,7 @@
func TestFunctionOK(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, true)
fn := func() error {
cmd := exec.Command("go", "run", "./testdata/ok_hello.go")
cmd.Stdout = &out
@@ -182,7 +182,7 @@
func TestFunctionFail(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, true)
fn := func() error {
cmd := exec.Command("go", "run", "./testdata/fail_hello.go")
cmd.Stdout = &out
@@ -201,7 +201,7 @@
func TestFunctionWithOptsOK(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, false)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false)
opts := e.opts
opts.verbose = true
fn := func() error {
@@ -222,7 +222,7 @@
func TestFunctionWithOptsFail(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, false)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false)
opts := e.opts
opts.verbose = true
fn := func() error {
@@ -243,7 +243,7 @@
func TestOutput(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, true)
e.output(e.opts, []string{"hello", "world"})
if got, want := removeTimestamps(t, &out), ">> hello\n>> world\n"; got != want {
t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
@@ -252,7 +252,7 @@
func TestOutputWithOpts(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, false)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false)
opts := e.opts
opts.verbose = true
e.output(opts, []string{"hello", "world"})
@@ -263,7 +263,7 @@
func TestNested(t *testing.T) {
var out bytes.Buffer
- e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, false, true)
+ e := newExecutor(nil, os.Stdin, &out, ioutil.Discard, false, true)
fn := func() error {
e.output(e.opts, []string{"hello", "world"})
return nil
diff --git a/runutil/sequence.go b/runutil/sequence.go
index 12d5014..3a930ae 100644
--- a/runutil/sequence.go
+++ b/runutil/sequence.go
@@ -94,7 +94,6 @@
defaultStdout, defaultStderr io.Writer
dirs []string
verbosity *bool
- dryRun *bool
cmdDir string
timeout time.Duration
serializedWriterLock sync.Mutex
@@ -110,7 +109,7 @@
}
s := Sequence{
&sequence{
- r: newExecutor(env, stdin, stdout, stderr, color, dryRun, verbose),
+ r: newExecutor(env, stdin, stdout, stderr, color, verbose),
defaultStdin: stdin,
},
}
@@ -122,7 +121,7 @@
// create this sequence.
func (s Sequence) RunOpts() (dryRun bool, verbose bool) {
opts := s.getOpts()
- return opts.dryRun, opts.verbose
+ return false, opts.verbose
}
// Capture arranges for the next call to Run or Last to write its stdout and
@@ -202,17 +201,6 @@
return s
}
-// DryRun arranges for the next call to Run, Call, Start or Last to use the
-// specified dry run value. This will be cleared and not used for any calls
-// to Run, Call or Last beyond the next one.
-func (s Sequence) DryRun(dryRun bool) Sequence {
- if s.err != nil {
- return s
- }
- s.dryRun = &dryRun
- return s
-}
-
// internal getOpts that doesn't override stdin, stdout, stderr
func (s Sequence) getOpts() opts {
var opts opts
@@ -334,7 +322,7 @@
// reset all state except s.err
func (s Sequence) reset() {
s.stdin, s.stdout, s.stderr, s.env = nil, nil, nil, nil
- s.opts, s.verbosity, s.dryRun = nil, nil, nil
+ s.opts, s.verbosity = nil, nil
s.cmdDir = ""
s.reading = false
s.timeout = 0
@@ -421,9 +409,6 @@
if s.verbosity != nil {
opts.verbose = *s.verbosity
}
- if s.dryRun != nil {
- opts.dryRun = *s.dryRun
- }
opts.dir = s.cmdDir
s.setOpts(opts)
if h != nil {
@@ -485,9 +470,6 @@
if s.verbosity != nil {
opts.verbose = *s.verbosity
}
- if s.dryRun != nil {
- opts.dryRun = *s.dryRun
- }
opts.dir = s.cmdDir
s.setOpts(opts)
if h != nil {
@@ -640,7 +622,7 @@
if len(s.dirs) > 0 {
cwd := s.dirs[0]
s.dirs = nil
- err := s.r.alwaysRun(func() error {
+ err := s.r.call(func() error {
return os.Chdir(cwd)
}, fmt.Sprintf("sequence done popd %q", cwd))
if err != nil {
@@ -668,7 +650,7 @@
return s
}
s.dirs = append(s.dirs, cwd)
- err = s.r.alwaysRun(func() error {
+ err = s.r.call(func() error {
return os.Chdir(dir)
}, fmt.Sprintf("pushd %q", dir))
s.setError(err, "Pushd("+dir+")")
@@ -688,7 +670,7 @@
}
last := s.dirs[len(s.dirs)-1]
s.dirs = s.dirs[:len(s.dirs)-1]
- err := s.r.alwaysRun(func() error {
+ err := s.r.call(func() error {
return os.Chdir(last)
}, fmt.Sprintf("popd %q", last))
s.setError(err, "Popd() -> "+last)
@@ -696,12 +678,12 @@
}
// Chdir is a wrapper around os.Chdir that handles options such as
-// "verbose" or "dry run".
+// "verbose".
func (s Sequence) Chdir(dir string) Sequence {
if s.err != nil {
return s
}
- err := s.r.alwaysRun(func() error {
+ err := s.r.call(func() error {
return os.Chdir(dir)
}, fmt.Sprintf("cd %q", dir))
s.setError(err, "Chdir("+dir+")")
@@ -710,7 +692,7 @@
}
// Chmod is a wrapper around os.Chmod that handles options such as
-// "verbose" or "dry run".
+// "verbose".
func (s Sequence) Chmod(dir string, mode os.FileMode) Sequence {
if s.err != nil {
return s
@@ -722,7 +704,7 @@
}
// MkdirAll is a wrapper around os.MkdirAll that handles options such
-// as "verbose" or "dry run".
+// as "verbose".
func (s Sequence) MkdirAll(dir string, mode os.FileMode) Sequence {
if s.err != nil {
return s
@@ -733,7 +715,7 @@
}
// RemoveAll is a wrapper around os.RemoveAll that handles options
-// such as "verbose" or "dry run".
+// such as "verbose".
func (s Sequence) RemoveAll(dir string) Sequence {
if s.err != nil {
return s
@@ -744,7 +726,7 @@
}
// Remove is a wrapper around os.Remove that handles options
-// such as "verbose" or "dry run".
+// such as "verbose".
func (s Sequence) Remove(file string) Sequence {
if s.err != nil {
return s
@@ -755,7 +737,7 @@
}
// Rename is a wrapper around os.Rename that handles options such as
-// "verbose" or "dry run".
+// "verbose".
func (s Sequence) Rename(src, dst string) Sequence {
if s.err != nil {
return s
@@ -785,7 +767,7 @@
}
// Symlink is a wrapper around os.Symlink that handles options such as
-// "verbose" or "dry run".
+// "verbose".
func (s Sequence) Symlink(src, dst string) Sequence {
if s.err != nil {
return s
@@ -796,7 +778,7 @@
}
// Open is a wrapper around os.Open that handles options such as
-// "verbose" or "dry run". Open is a terminating function.
+// "verbose". Open is a terminating function.
func (s Sequence) Open(name string) (f *os.File, err error) {
if s.err != nil {
return nil, s.Done()
@@ -811,7 +793,7 @@
}
// OpenFile is a wrapper around os.OpenFile that handles options such as
-// "verbose" or "dry run". OpenFile is a terminating function.
+// "verbose". OpenFile is a terminating function.
func (s Sequence) OpenFile(name string, flag int, perm os.FileMode) (f *os.File, err error) {
if s.err != nil {
return nil, s.Done()
@@ -826,7 +808,7 @@
}
// Create is a wrapper around os.Create that handles options such as "verbose"
-// or "dry run". Create is a terminating function.
+//. Create is a terminating function.
func (s Sequence) Create(name string) (f *os.File, err error) {
if s.err != nil {
return nil, s.Done()
@@ -842,12 +824,12 @@
}
// ReadDir is a wrapper around ioutil.ReadDir that handles options
-// such as "verbose" or "dry run". ReadDir is a terminating function.
+// such as "verbose". ReadDir is a terminating function.
func (s Sequence) ReadDir(dirname string) (fi []os.FileInfo, err error) {
if s.err != nil {
return nil, s.Done()
}
- s.r.alwaysRun(func() error {
+ s.r.call(func() error {
fi, err = ioutil.ReadDir(dirname)
return err
}, fmt.Sprintf("ls %q", dirname))
@@ -857,13 +839,13 @@
}
// ReadFile is a wrapper around ioutil.ReadFile that handles options
-// such as "verbose" or "dry run". ReadFile is a terminating function.
+// such as "verbose". ReadFile is a terminating function.
func (s Sequence) ReadFile(filename string) (bytes []byte, err error) {
if s.err != nil {
return nil, s.Done()
}
- s.r.alwaysRun(func() error {
+ s.r.call(func() error {
bytes, err = ioutil.ReadFile(filename)
return err
}, fmt.Sprintf("read %q", filename))
@@ -873,7 +855,7 @@
}
// WriteFile is a wrapper around ioutil.WriteFile that handles options
-// such as "verbose" or "dry run".
+// such as "verbose".
func (s Sequence) WriteFile(filename string, data []byte, perm os.FileMode) Sequence {
if s.err != nil {
return s
@@ -901,12 +883,12 @@
}
// Stat is a wrapper around os.Stat that handles options such as
-// "verbose" or "dry run". Stat is a terminating function.
+// "verbose". Stat is a terminating function.
func (s Sequence) Stat(name string) (fi os.FileInfo, err error) {
if s.err != nil {
return nil, s.Done()
}
- s.r.alwaysRun(func() error {
+ s.r.call(func() error {
fi, err = os.Stat(name)
return err
}, fmt.Sprintf("stat %q", name))
@@ -916,12 +898,12 @@
}
// Lstat is a wrapper around os.Lstat that handles options such as
-// "verbose" or "dry run". Lstat is a terminating function.
+// "verbose". Lstat is a terminating function.
func (s Sequence) Lstat(name string) (fi os.FileInfo, err error) {
if s.err != nil {
return nil, s.Done()
}
- s.r.alwaysRun(func() error {
+ s.r.call(func() error {
fi, err = os.Lstat(name)
return err
}, fmt.Sprintf("lstat %q", name))
@@ -931,12 +913,12 @@
}
// Readlink is a wrapper around os.Readlink that handles options such as
-// "verbose" or "dry run". Lstat is a terminating function.
+// "verbose". Lstat is a terminating function.
func (s Sequence) Readlink(name string) (link string, err error) {
if s.err != nil {
return "", s.Done()
}
- s.r.alwaysRun(func() error {
+ s.r.call(func() error {
link, err = os.Readlink(name)
return err
}, fmt.Sprintf("readlink %q", name))
@@ -946,7 +928,7 @@
}
// TempDir is a wrapper around ioutil.TempDir that handles options
-// such as "verbose" or "dry run". TempDir is a terminating function.
+// such as "verbose". TempDir is a terminating function.
func (s Sequence) TempDir(dir, prefix string) (tmpDir string, err error) {
if s.err != nil {
return "", s.Done()
@@ -965,7 +947,7 @@
}
// TempFile is a wrapper around ioutil.TempFile that handles options
-// such as "verbose" or "dry run".
+// such as "verbose".
func (s Sequence) TempFile(dir, prefix string) (f *os.File, err error) {
if s.err != nil {
return nil, s.Done()
@@ -991,7 +973,7 @@
}
var fileInfo os.FileInfo
var err error
- err = s.r.alwaysRun(func() error {
+ err = s.r.call(func() error {
fileInfo, err = os.Stat(dirname)
return err
}, fmt.Sprintf("isdir %q", dirname))
@@ -1014,7 +996,7 @@
}
var fileInfo os.FileInfo
var err error
- err = s.r.alwaysRun(func() error {
+ err = s.r.call(func() error {
fileInfo, err = os.Stat(file)
return err
}, fmt.Sprintf("isfile %q", file))
diff --git a/runutil/sequence_test.go b/runutil/sequence_test.go
index 806f3c7..3bb7421 100644
--- a/runutil/sequence_test.go
+++ b/runutil/sequence_test.go
@@ -358,24 +358,6 @@
}
stdout.Reset()
}
- for _, dryRun := range []bool{false, true} {
- err := seq.DryRun(dryRun).Last("sh", "-c", "echo hello")
- if err != nil {
- t.Fatal(err)
- }
- out := sanitizeTimestampsAndPaths(stdout.String())
- want := `[hh:mm:ss.xx] >> sh -c "echo hello"
-[hh:mm:ss.xx] >> OK
-`
- if !dryRun {
- want += `hello
-`
- }
- if got, want := out, want; got != want {
- t.Errorf("dryRun: %t, got %v, want %v", dryRun, got, want)
- }
- stdout.Reset()
- }
}
func TestSequenceOutputOnError(t *testing.T) {
diff --git a/snapshot.go b/snapshot.go
deleted file mode 100644
index 21d572d..0000000
--- a/snapshot.go
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "sort"
- "strings"
- "time"
-
- "v.io/jiri/collect"
- "v.io/jiri/gitutil"
- "v.io/jiri/jiri"
- "v.io/jiri/project"
- "v.io/jiri/runutil"
- "v.io/x/lib/cmdline"
-)
-
-const (
- defaultSnapshotDir = ".snapshot"
-)
-
-var (
- pushRemoteFlag bool
- snapshotDirFlag string
- snapshotGcFlag bool
- timeFormatFlag string
-)
-
-func init() {
- cmdSnapshot.Flags.StringVar(&snapshotDirFlag, "dir", "", "Directory where snapshot are stored. Defaults to $JIRI_ROOT/.snapshot.")
- cmdSnapshotCheckout.Flags.BoolVar(&snapshotGcFlag, "gc", false, "Garbage collect obsolete repositories.")
- cmdSnapshotCreate.Flags.BoolVar(&pushRemoteFlag, "push-remote", false, "Commit and push snapshot upstream.")
- cmdSnapshotCreate.Flags.StringVar(&timeFormatFlag, "time-format", time.RFC3339, "Time format for snapshot file name.")
-}
-
-var cmdSnapshot = &cmdline.Command{
- Name: "snapshot",
- Short: "Manage project snapshots",
- Long: `
-The "jiri snapshot" command can be used to manage project snapshots.
-In particular, it can be used to create new snapshots and to list
-existing snapshots.
-`,
- Children: []*cmdline.Command{cmdSnapshotCheckout, cmdSnapshotCreate, cmdSnapshotList},
-}
-
-// cmdSnapshotCreate represents the "jiri snapshot create" command.
-var cmdSnapshotCreate = &cmdline.Command{
- Runner: jiri.RunnerFunc(runSnapshotCreate),
- Name: "create",
- Short: "Create a new project snapshot",
- Long: `
-The "jiri snapshot create <label>" command captures the current project state
-in a manifest. If the -push-remote flag is provided, the snapshot is committed
-and pushed upstream.
-
-Internally, snapshots are organized as follows:
-
- <snapshot-dir>/
- labels/
- <label1>/
- <label1-snapshot1>
- <label1-snapshot2>
- ...
- <label2>/
- <label2-snapshot1>
- <label2-snapshot2>
- ...
- <label3>/
- ...
- <label1> # a symlink to the latest <label1-snapshot*>
- <label2> # a symlink to the latest <label2-snapshot*>
- ...
-
-NOTE: Unlike the jiri tool commands, the above internal organization
-is not an API. It is an implementation and can change without notice.
-`,
- ArgsName: "<label>",
- ArgsLong: "<label> is the snapshot label.",
-}
-
-func runSnapshotCreate(jirix *jiri.X, args []string) error {
- if len(args) != 1 {
- return jirix.UsageErrorf("unexpected number of arguments")
- }
- label := args[0]
- snapshotDir, err := getSnapshotDir(jirix)
- if err != nil {
- return err
- }
- snapshotFile := filepath.Join(snapshotDir, "labels", label, time.Now().Format(timeFormatFlag))
-
- if !pushRemoteFlag {
- // No git operations necessary. Just create the snapshot file.
- return createSnapshot(jirix, snapshotDir, snapshotFile, label)
- }
-
- // Attempt to create a snapshot on a clean master branch. If snapshot
- // creation fails, return to the state we were in before.
- createFn := func() error {
- git := gitutil.New(jirix.NewSeq())
- revision, err := git.CurrentRevision()
- if err != nil {
- return err
- }
- if err := createSnapshot(jirix, snapshotDir, snapshotFile, label); err != nil {
- git.Reset(revision)
- git.RemoveUntrackedFiles()
- return err
- }
- return commitAndPushChanges(jirix, snapshotDir, snapshotFile, label)
- }
-
- // Execute the above function in the snapshot directory on a clean master branch.
- p := project.Project{
- Path: snapshotDir,
- Protocol: "git",
- RemoteBranch: "master",
- Revision: "HEAD",
- }
- return project.ApplyToLocalMaster(jirix, project.Projects{p.Key(): p}, createFn)
-}
-
-// getSnapshotDir returns the path to the snapshot directory, creating it if
-// necessary.
-func getSnapshotDir(jirix *jiri.X) (string, error) {
- dir := snapshotDirFlag
- if dir == "" {
- dir = filepath.Join(jirix.Root, defaultSnapshotDir)
- }
-
- if !filepath.IsAbs(dir) {
- cwd, err := os.Getwd()
- if err != nil {
- return "", err
- }
- dir = filepath.Join(cwd, dir)
- }
-
- // Make sure directory exists.
- if err := jirix.NewSeq().MkdirAll(dir, 0755).Done(); err != nil {
- return "", err
- }
- return dir, nil
-}
-
-func createSnapshot(jirix *jiri.X, snapshotDir, snapshotFile, label string) error {
- // Create a snapshot that encodes the current state of master
- // branches for all local projects.
- if err := project.CreateSnapshot(jirix, snapshotFile, ""); err != nil {
- return err
- }
-
- s := jirix.NewSeq()
- // Update the symlink for this snapshot label to point to the
- // latest snapshot.
- symlink := filepath.Join(snapshotDir, label)
- newSymlink := symlink + ".new"
- relativeSnapshotPath := strings.TrimPrefix(snapshotFile, snapshotDir+string(os.PathSeparator))
- return s.RemoveAll(newSymlink).
- Symlink(relativeSnapshotPath, newSymlink).
- Rename(newSymlink, symlink).Done()
-}
-
-// commitAndPushChanges commits changes identified by the given manifest file
-// and label to the containing repository and pushes these changes to the
-// remote repository.
-func commitAndPushChanges(jirix *jiri.X, snapshotDir, snapshotFile, label string) (e error) {
- cwd, err := os.Getwd()
- if err != nil {
- return err
- }
- defer collect.Error(func() error { return jirix.NewSeq().Chdir(cwd).Done() }, &e)
- if err := jirix.NewSeq().Chdir(snapshotDir).Done(); err != nil {
- return err
- }
- relativeSnapshotPath := strings.TrimPrefix(snapshotFile, snapshotDir+string(os.PathSeparator))
- git := gitutil.New(jirix.NewSeq())
- // Pull from master so we are up-to-date.
- if err := git.Pull("origin", "master"); err != nil {
- return err
- }
- if err := git.Add(relativeSnapshotPath); err != nil {
- return err
- }
- if err := git.Add(label); err != nil {
- return err
- }
- name := strings.TrimPrefix(snapshotFile, snapshotDir)
- if err := git.CommitNoVerify(fmt.Sprintf("adding snapshot %q for label %q", name, label)); err != nil {
- return err
- }
- if err := git.Push("origin", "master", gitutil.VerifyOpt(false)); err != nil {
- return err
- }
- return nil
-}
-
-// cmdSnapshotCheckout represents the "jiri snapshot checkout" command.
-var cmdSnapshotCheckout = &cmdline.Command{
- Runner: jiri.RunnerFunc(runSnapshotCheckout),
- Name: "checkout",
- Short: "Checkout a project snapshot",
- Long: `
-The "jiri snapshot checkout <snapshot>" command restores local project state to
-the state in the given snapshot manifest.
-`,
- ArgsName: "<snapshot>",
- ArgsLong: "<snapshot> is the snapshot manifest file.",
-}
-
-func runSnapshotCheckout(jirix *jiri.X, args []string) error {
- if len(args) != 1 {
- return jirix.UsageErrorf("unexpected number of arguments")
- }
- return project.CheckoutSnapshot(jirix, args[0], snapshotGcFlag)
-}
-
-// cmdSnapshotList represents the "jiri snapshot list" command.
-var cmdSnapshotList = &cmdline.Command{
- Runner: jiri.RunnerFunc(runSnapshotList),
- Name: "list",
- Short: "List existing project snapshots",
- Long: `
-The "snapshot list" command lists existing snapshots of the labels
-specified as command-line arguments. If no arguments are provided, the
-command lists snapshots for all known labels.
-`,
- ArgsName: "<label ...>",
- ArgsLong: "<label ...> is a list of snapshot labels.",
-}
-
-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
-}
diff --git a/snapshot_test.go b/snapshot_test.go
deleted file mode 100644
index d24b5a6..0000000
--- a/snapshot_test.go
+++ /dev/null
@@ -1,341 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "bytes"
- "fmt"
- "os"
- "path/filepath"
- "testing"
-
- "v.io/jiri/gitutil"
- "v.io/jiri/jiri"
- "v.io/jiri/jiritest"
- "v.io/jiri/project"
- "v.io/jiri/tool"
-)
-
-func createLabelDir(t *testing.T, jirix *jiri.X, snapshotDir, name string, snapshots []string) {
- if snapshotDir == "" {
- snapshotDir = filepath.Join(jirix.Root, defaultSnapshotDir)
- }
- s := jirix.NewSeq()
- labelDir, perm := filepath.Join(snapshotDir, "labels", name), os.FileMode(0700)
- if err := s.MkdirAll(labelDir, perm).Done(); err != nil {
- t.Fatalf("MkdirAll(%v, %v) failed: %v", labelDir, perm, err)
- }
- for i, snapshot := range snapshots {
- path := filepath.Join(labelDir, snapshot)
- _, err := os.Create(path)
- if err != nil {
- t.Fatalf("%v", err)
- }
- if i == 0 {
- symlinkPath := filepath.Join(snapshotDir, name)
- if err := s.Symlink(path, symlinkPath).Done(); err != nil {
- t.Fatalf("Symlink(%v, %v) failed: %v", path, symlinkPath, err)
- }
- }
- }
-}
-
-func generateOutput(labels []label) string {
- output := ""
- for _, label := range labels {
- output += fmt.Sprintf("snapshots of label %q:\n", label.name)
- for _, snapshot := range label.snapshots {
- output += fmt.Sprintf(" %v\n", snapshot)
- }
- }
- return output
-}
-
-type config struct {
- remote bool
- dir string
-}
-
-type label struct {
- name string
- snapshots []string
-}
-
-func TestList(t *testing.T) {
- resetFlags()
- fake, cleanup := jiritest.NewFakeJiriRoot(t)
- defer cleanup()
-
- snapshotDir1 := "" // Should use default dir.
- snapshotDir2 := filepath.Join(fake.X.Root, "some/other/dir")
-
- // Create a test suite.
- tests := []config{
- config{
- dir: snapshotDir1,
- },
- config{
- dir: snapshotDir2,
- },
- }
- labels := []label{
- label{
- name: "beta",
- snapshots: []string{"beta-1", "beta-2", "beta-3"},
- },
- label{
- name: "stable",
- snapshots: []string{"stable-1", "stable-2", "stable-3"},
- },
- }
-
- for _, test := range tests {
- snapshotDirFlag = test.dir
- // Create the snapshots directory and populate it with the
- // data specified by the test suite.
- for _, label := range labels {
- createLabelDir(t, fake.X, test.dir, label.name, label.snapshots)
- }
-
- // Check that running "jiri snapshot list" with no arguments
- // returns the expected output.
- var stdout bytes.Buffer
- fake.X.Context = tool.NewContext(tool.ContextOpts{Stdout: &stdout})
- if err := runSnapshotList(fake.X, nil); err != nil {
- t.Fatalf("%v", err)
- }
- got, want := stdout.String(), generateOutput(labels)
- if got != want {
- t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v\n", got, want)
- }
-
- // Check that running "jiri snapshot list" with one argument
- // returns the expected output.
- stdout.Reset()
- if err := runSnapshotList(fake.X, []string{"stable"}); err != nil {
- t.Fatalf("%v", err)
- }
- got, want = stdout.String(), generateOutput(labels[1:])
- if got != want {
- t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v\n", got, want)
- }
-
- // Check that running "jiri snapshot list" with
- // multiple arguments returns the expected output.
- stdout.Reset()
- if err := runSnapshotList(fake.X, []string{"beta", "stable"}); err != nil {
- t.Fatalf("%v", err)
- }
- got, want = stdout.String(), generateOutput(labels)
- if got != want {
- t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v\n", got, want)
- }
- }
-}
-
-func checkReadme(t *testing.T, jirix *jiri.X, project, message string) {
- s := jirix.NewSeq()
- if _, err := s.Stat(project); err != nil {
- t.Fatalf("%v", err)
- }
- readmeFile := filepath.Join(project, "README")
- data, err := s.ReadFile(readmeFile)
- if err != nil {
- t.Fatalf("%v", err)
- }
- if got, want := data, []byte(message); bytes.Compare(got, want) != 0 {
- t.Fatalf("unexpected content %v:\ngot\n%s\nwant\n%s\n", project, got, want)
- }
-}
-
-func localProjectName(i int) string {
- return "test-local-project-" + fmt.Sprintf("%d", i+1)
-}
-
-func remoteProjectName(i int) string {
- return "test-remote-project-" + fmt.Sprintf("%d", i+1)
-}
-
-func writeReadme(t *testing.T, jirix *jiri.X, projectDir, message string) {
- s := jirix.NewSeq()
- path, perm := filepath.Join(projectDir, "README"), os.FileMode(0644)
- if err := s.WriteFile(path, []byte(message), perm).Done(); err != nil {
- t.Fatalf("%v", err)
- }
- cwd, err := os.Getwd()
- if err != nil {
- t.Fatalf("%v", err)
- }
- defer jirix.NewSeq().Chdir(cwd)
- if err := s.Chdir(projectDir).Done(); err != nil {
- t.Fatalf("%v", err)
- }
- if err := gitutil.New(jirix.NewSeq()).CommitFile(path, "creating README"); err != nil {
- t.Fatalf("%v", err)
- }
-}
-
-func resetFlags() {
- snapshotDirFlag = ""
- pushRemoteFlag = false
-}
-
-func TestGetSnapshotDir(t *testing.T) {
- resetFlags()
- defer resetFlags()
- fake, cleanup := jiritest.NewFakeJiriRoot(t)
- defer cleanup()
-
- // With all flags at default values, snapshot dir should be default.
- resetFlags()
- got, err := getSnapshotDir(fake.X)
- if err != nil {
- t.Fatalf("getSnapshotDir() failed: %v\n", err)
- }
- if want := filepath.Join(fake.X.Root, defaultSnapshotDir); got != want {
- t.Errorf("unexpected snapshot dir: got %v want %v", got, want)
- }
-
- // With dir flag set to absolute path, snapshot dir should be value of dir
- // flag.
- resetFlags()
- tempDir, err := fake.X.NewSeq().TempDir("", "")
- if err != nil {
- t.Fatalf("TempDir() failed: %v", err)
- }
- defer fake.X.NewSeq().RemoveAll(tempDir).Done()
- snapshotDirFlag = tempDir
- got, err = getSnapshotDir(fake.X)
- if err != nil {
- t.Fatalf("getSnapshotDir() failed: %v\n", err)
- }
- if want := snapshotDirFlag; got != want {
- t.Errorf("unexpected snapshot dir: got %v want %v", got, want)
- }
-
- // With dir flag set to relative path, snapshot dir should absolute path
- // rooted at current working dir.
- resetFlags()
- snapshotDirFlag = "some/relative/path"
- cwd, err := os.Getwd()
- if err != nil {
- t.Fatalf("os.Getwd() failed: %v", err)
- }
- got, err = getSnapshotDir(fake.X)
- if err != nil {
- t.Fatalf("getSnapshotDir() failed: %v\n", err)
- }
- if want := filepath.Join(cwd, snapshotDirFlag); got != want {
- t.Errorf("unexpected snapshot dir: got %v want %v", got, want)
- }
-}
-
-// TestCreate tests creating and checking out a snapshot.
-func TestCreate(t *testing.T) {
- resetFlags()
- defer resetFlags()
- fake, cleanup := jiritest.NewFakeJiriRoot(t)
- defer cleanup()
- s := fake.X.NewSeq()
-
- // Setup the initial remote and local projects.
- numProjects, remoteProjects := 2, []string{}
- for i := 0; i < numProjects; i++ {
- if err := fake.CreateRemoteProject(remoteProjectName(i)); err != nil {
- t.Fatalf("%v", err)
- }
- if err := fake.AddProject(project.Project{
- Name: remoteProjectName(i),
- Path: localProjectName(i),
- Remote: fake.Projects[remoteProjectName(i)],
- }); err != nil {
- t.Fatalf("%v", err)
- }
- }
-
- // Create initial commits in the remote projects and use UpdateUniverse()
- // to mirror them locally.
- for i := 0; i < numProjects; i++ {
- writeReadme(t, fake.X, fake.Projects[remoteProjectName(i)], "revision 1")
- }
- if err := project.UpdateUniverse(fake.X, true); err != nil {
- t.Fatalf("%v", err)
- }
-
- // Create a snapshot.
- var stdout bytes.Buffer
- fake.X.Context = tool.NewContext(tool.ContextOpts{Stdout: &stdout})
- if err := runSnapshotCreate(fake.X, []string{"test-local"}); err != nil {
- t.Fatalf("%v", err)
- }
-
- // Remove the local project repositories.
- for i, _ := range remoteProjects {
- localProject := filepath.Join(fake.X.Root, localProjectName(i))
- if err := s.RemoveAll(localProject).Done(); err != nil {
- t.Fatalf("%v", err)
- }
- }
-
- // Check that invoking the UpdateUniverse() with the snapshot restores the
- // local repositories.
- snapshotDir := filepath.Join(fake.X.Root, defaultSnapshotDir)
- snapshotFile := filepath.Join(snapshotDir, "test-local")
- localX := fake.X.Clone(tool.ContextOpts{
- Manifest: &snapshotFile,
- })
- if err := project.UpdateUniverse(localX, true); err != nil {
- t.Fatalf("%v", err)
- }
- for i, _ := range remoteProjects {
- localProject := filepath.Join(fake.X.Root, localProjectName(i))
- checkReadme(t, fake.X, localProject, "revision 1")
- }
-}
-
-// TestCreatePushRemote checks that creating a snapshot with the -push-remote
-// flag causes the snapshot to be committed and pushed upstream.
-func TestCreatePushRemote(t *testing.T) {
- resetFlags()
- defer resetFlags()
-
- fake, cleanup := jiritest.NewFakeJiriRoot(t)
- defer cleanup()
-
- fake.EnableRemoteManifestPush()
- defer fake.DisableRemoteManifestPush()
-
- manifestDir := filepath.Join(fake.X.Root, ".manifest")
- snapshotDir := filepath.Join(manifestDir, "snapshot")
- label := "test"
-
- git := gitutil.New(fake.X.NewSeq(), gitutil.RootDirOpt(manifestDir))
- commitCount, err := git.CountCommits("master", "")
- if err != nil {
- t.Fatalf("git.CountCommits(\"master\", \"\") failed: %v", err)
- }
-
- // Create snapshot with -push-remote flag set to true.
- snapshotDirFlag = snapshotDir
- pushRemoteFlag = true
- if err := runSnapshotCreate(fake.X, []string{label}); err != nil {
- t.Fatalf("%v", err)
- }
-
- // Check that repo has one new commit.
- newCommitCount, err := git.CountCommits("master", "")
- if err != nil {
- t.Fatalf("git.CountCommits(\"master\", \"\") failed: %v", err)
- }
- if got, want := newCommitCount, commitCount+1; got != want {
- t.Errorf("unexpected commit count: got %v want %v", got, want)
- }
-
- // Check that new label is commited.
- labelFile := filepath.Join(snapshotDir, "labels", label)
- if !git.IsFileCommitted(labelFile) {
- t.Errorf("expected file %v to be committed but it was not", labelFile)
- }
-}
diff --git a/update.go b/update.go
deleted file mode 100644
index 9dccf97..0000000
--- a/update.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "v.io/jiri/jiri"
- "v.io/jiri/project"
- "v.io/jiri/retry"
- "v.io/jiri/tool"
- "v.io/x/lib/cmdline"
-)
-
-var (
- gcFlag bool
- attemptsFlag int
-)
-
-func init() {
- tool.InitializeProjectFlags(&cmdUpdate.Flags)
-
- cmdUpdate.Flags.BoolVar(&gcFlag, "gc", false, "Garbage collect obsolete repositories.")
- cmdUpdate.Flags.IntVar(&attemptsFlag, "attempts", 1, "Number of attempts before failing.")
-}
-
-// cmdUpdate represents the "jiri update" command.
-var cmdUpdate = &cmdline.Command{
- Runner: jiri.RunnerFunc(runUpdate),
- Name: "update",
- Short: "Update all jiri tools and projects",
- Long: `
-Updates all projects, builds the latest version of all tools, and installs the
-resulting binaries into $JIRI_ROOT/.jiri_root/bin. The sequence in which the
-individual updates happen guarantees that we end up with a consistent set of
-tools and source code. The set of projects and tools to update is described in
-the manifest.
-
-Run "jiri help manifest" for details on manifests.
-`,
-}
-
-func runUpdate(jirix *jiri.X, _ []string) error {
- seq := jirix.NewSeq()
- // Create the $JIRI_ROOT/.jiri_root directory if it doesn't already exist.
- //
- // TODO(toddw): Remove this logic after the transition to .jiri_root is done.
- // The bootstrapping logic should create this directory, and jiri should fail
- // if the directory doesn't exist.
- if err := seq.MkdirAll(jirix.RootMetaDir(), 0755).Done(); err != nil {
- return err
- }
-
- // Update all projects to their latest version.
- // Attempt <attemptsFlag> times before failing.
- updateFn := func() error { return project.UpdateUniverse(jirix, gcFlag) }
- if err := retry.Function(jirix.Context, updateFn, retry.AttemptsOpt(attemptsFlag)); err != nil {
- return err
- }
- if err := project.WriteUpdateHistorySnapshot(jirix, ""); err != nil {
- return err
- }
-
- // Only attempt the bin dir transition after the update has succeeded, to
- // avoid messy partial states.
- return project.TransitionBinDir(jirix)
-}
diff --git a/upgrade.go b/upgrade.go
deleted file mode 100644
index da6947f..0000000
--- a/upgrade.go
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "os"
- "path/filepath"
-
- "v.io/jiri/jiri"
- "v.io/jiri/project"
- "v.io/jiri/runutil"
- "v.io/x/lib/cmdline"
-)
-
-// TODO(toddw): Remove the upgrade command after the transition to new-style
-// manifests is complete.
-
-var flagUpgradeRevert bool
-
-func init() {
- cmdUpgrade.Flags.BoolVar(&flagUpgradeRevert, "revert", false, `Revert the upgrade by deleting the $JIRI_ROOT/.jiri_manifest file.`)
-}
-
-var cmdUpgrade = &cmdline.Command{
- Runner: jiri.RunnerFunc(runUpgrade),
- Name: "upgrade",
- Short: "Upgrade jiri to new-style manifests",
- Long: `
-Upgrades jiri to use new-style manifests.
-
-The old (deprecated) behavior only allowed a single manifest repository, located
-in $JIRI_ROOT/.manifest. The initial manifest file is located as follows:
- 1) Use -manifest flag, if non-empty. If it's empty...
- 2) Use $JIRI_ROOT/.local_manifest file. If it doesn't exist...
- 3) Use $JIRI_ROOT/.manifest/v2/default.
-
-The new behavior allows multiple manifest repositories, by allowing imports to
-specify project attributes describing the remote repository. The -manifest flag
-is no longer allowed to be set; the initial manifest file is always located in
-$JIRI_ROOT/.jiri_manifest. The .local_manifest file is ignored.
-
-During the transition phase, both old and new behaviors are supported. The jiri
-tool uses the existence of the $JIRI_ROOT/.jiri_manifest file as the signal; if
-it exists we run the new behavior, otherwise we run the old behavior.
-
-The new behavior includes a "jiri import" command, which writes or updates the
-.jiri_manifest file. The new bootstrap procedure runs "jiri import", and it is
-intended as a regular command to add imports to your jiri environment.
-
-This upgrade command eases the transition by writing an initial .jiri_manifest
-file for you. If you have an existing .local_manifest file, its contents will
-be incorporated into the new .jiri_manifest file, and it will be renamed to
-.local_manifest.BACKUP. The -revert flag deletes the .jiri_manifest file, and
-restores the .local_manifest file.
-`,
- ArgsName: "<kind>",
- ArgsLong: `
-<kind> specifies the kind of upgrade, one of "v23" or "fuchsia".
-`,
-}
-
-func runUpgrade(jirix *jiri.X, args []string) error {
- localFile := filepath.Join(jirix.Root, ".local_manifest")
- backupFile := localFile + ".BACKUP"
- if flagUpgradeRevert {
- // Restore .local_manifest.BACKUP if it exists.
- switch _, err := jirix.NewSeq().Stat(backupFile); {
- case err != nil && !runutil.IsNotExist(err):
- return err
- case err == nil:
- if err := jirix.NewSeq().Rename(backupFile, localFile).Done(); err != nil {
- return fmt.Errorf("couldn't restore %v to %v: %v", backupFile, localFile, err)
- }
- }
- // Deleting the .jiri_manifest file reverts to the old behavior.
- return jirix.NewSeq().Remove(jirix.JiriManifestFile()).Done()
- }
- if len(args) != 1 {
- return jirix.UsageErrorf("must specify upgrade kind")
- }
- kind := args[0]
- var argRemote, argName, argManifest string
- switch kind {
- case "v23":
- argRemote = "https://vanadium.googlesource.com/manifest"
- argName, argManifest = "manifest", "public"
- case "fuchsia":
- argRemote = "https://fuchsia.googlesource.com/fnl-start"
- argName, argManifest = "fnl-start", "manifest/fuchsia"
- default:
- return jirix.UsageErrorf("unknown upgrade kind %q", kind)
- }
- // Initialize manifest from .local_manifest.
- hasLocalFile := true
- manifest, err := project.ManifestFromFile(jirix, localFile)
- if err != nil {
- if !runutil.IsNotExist(err) {
- return err
- }
- hasLocalFile = false
- manifest = &project.Manifest{}
- }
- oldImports := manifest.Imports
- manifest.Imports = nil
- for _, oldImport := range oldImports {
- if oldImport.Remote != "" {
- // This is a new-style remote import, carry it over directly.
- manifest.Imports = append(manifest.Imports, oldImport)
- continue
- }
- // This is an old-style import, convert it to the new style.
- oldName := oldImport.Name
- switch {
- case kind == "v23" && oldName == "default":
- oldName = "public"
- case kind == "fuchsia" && oldName == "default":
- oldName = "manifest/fuchsia"
- }
- manifest.Imports = append(manifest.Imports, project.Import{
- Manifest: oldName,
- Name: argName,
- Remote: argRemote,
- })
- }
- if len(manifest.Imports) == 0 {
- manifest.Imports = append(manifest.Imports, project.Import{
- Manifest: argManifest,
- Name: argName,
- Remote: argRemote,
- })
- }
- // Write output to .jiri_manifest file.
- outFile := jirix.JiriManifestFile()
- if _, err := os.Stat(outFile); err == nil {
- return fmt.Errorf("%v already exists", outFile)
- }
- if err := manifest.ToFile(jirix, outFile); err != nil {
- return err
- }
- // Backup .local_manifest file, if it exists.
- if hasLocalFile {
- if err := jirix.NewSeq().Rename(localFile, backupFile).Done(); err != nil {
- return fmt.Errorf("couldn't backup %v to %v: %v", localFile, backupFile, err)
- }
- }
- return nil
-}
diff --git a/upgrade_test.go b/upgrade_test.go
deleted file mode 100644
index a7c5eab..0000000
--- a/upgrade_test.go
+++ /dev/null
@@ -1,311 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "v.io/jiri/jiri"
- "v.io/x/lib/gosh"
-)
-
-type upgradeTestCase struct {
- Args []string
- Exist bool
- Local, Want string
- Stderr string
-}
-
-func TestUpgrade(t *testing.T) {
- tests := []upgradeTestCase{
- {
- Stderr: `must specify upgrade kind`,
- },
- {
- Args: []string{"foo"},
- Stderr: `unknown upgrade kind "foo"`,
- },
- // Test v23 upgrades.
- {
- Args: []string{"v23"},
- Exist: true,
- Stderr: `.jiri_manifest already exists`,
- },
- {
- Args: []string{"v23"},
- Want: `<manifest>
- <imports>
- <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"v23"},
- Local: `<manifest>
- <imports>
- <import name="default"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"v23"},
- Local: `<manifest>
- <imports>
- <import name="private"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="private" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"v23"},
- Local: `<manifest>
- <imports>
- <import name="private"/>
- <import name="infrastructure"/>
- <import name="default"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="private" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- <import manifest="infrastructure" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"v23"},
- Local: `<manifest>
- <imports>
- <import name="default"/>
- <import name="infrastructure"/>
- <import name="private"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- <import manifest="infrastructure" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- <import manifest="private" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- </imports>
-</manifest>
-`,
- },
- // Test fuchsia upgrades.
- {
- Args: []string{"fuchsia"},
- Exist: true,
- Stderr: `.jiri_manifest already exists`,
- },
- {
- Args: []string{"fuchsia"},
- Want: `<manifest>
- <imports>
- <import manifest="manifest/fuchsia" name="fnl-start" remote="https://fuchsia.googlesource.com/fnl-start"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"fuchsia"},
- Local: `<manifest>
- <imports>
- <import name="default"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="manifest/fuchsia" name="fnl-start" remote="https://fuchsia.googlesource.com/fnl-start"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"fuchsia"},
- Local: `<manifest>
- <imports>
- <import name="private"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="private" name="fnl-start" remote="https://fuchsia.googlesource.com/fnl-start"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"fuchsia"},
- Local: `<manifest>
- <imports>
- <import name="private"/>
- <import name="infrastructure"/>
- <import name="default"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="private" name="fnl-start" remote="https://fuchsia.googlesource.com/fnl-start"/>
- <import manifest="infrastructure" name="fnl-start" remote="https://fuchsia.googlesource.com/fnl-start"/>
- <import manifest="manifest/fuchsia" name="fnl-start" remote="https://fuchsia.googlesource.com/fnl-start"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"fuchsia"},
- Local: `<manifest>
- <imports>
- <import name="default"/>
- <import name="infrastructure"/>
- <import name="private"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="manifest/fuchsia" name="fnl-start" remote="https://fuchsia.googlesource.com/fnl-start"/>
- <import manifest="infrastructure" name="fnl-start" remote="https://fuchsia.googlesource.com/fnl-start"/>
- <import manifest="private" name="fnl-start" remote="https://fuchsia.googlesource.com/fnl-start"/>
- </imports>
-</manifest>
-`,
- },
- }
- opts := gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf}
- sh := gosh.NewShell(opts)
- defer sh.Cleanup()
- jiriTool := sh.BuildGoPkg("v.io/jiri")
- for _, test := range tests {
- if err := testUpgrade(opts, jiriTool, test); err != nil {
- t.Errorf("%v: %v", test.Args, err)
- }
- }
-}
-
-func testUpgrade(opts gosh.Opts, jiriTool string, test upgradeTestCase) error {
- opts.PropagateChildOutput = true
- sh := gosh.NewShell(opts)
- defer sh.Cleanup()
- jiriRoot := sh.MakeTempDir()
- sh.Pushd(jiriRoot)
- // Set up an existing file or local_manifest, if they were specified
- if test.Exist {
- if err := ioutil.WriteFile(".jiri_manifest", []byte("<manifest/>"), 0644); err != nil {
- return err
- }
- }
- if test.Local != "" {
- if err := ioutil.WriteFile(".local_manifest", []byte(test.Local), 0644); err != nil {
- return err
- }
- }
- // Run upgrade and check the error.
- sh.Vars[jiri.RootEnv] = jiriRoot
- cmd := sh.Cmd(jiriTool, append([]string{"upgrade"}, test.Args...)...)
- if test.Stderr != "" {
- cmd.ExitErrorIsOk = true
- }
- _, stderr := cmd.StdoutStderr()
- if got, want := stderr, test.Stderr; !strings.Contains(got, want) || (got != "" && want == "") {
- return fmt.Errorf("stderr got %q, want substr %q", got, want)
- }
- // Make sure the right file is generated.
- if test.Want != "" {
- data, err := ioutil.ReadFile(".jiri_manifest")
- if err != nil {
- return err
- }
- if got, want := string(data), test.Want; got != want {
- return fmt.Errorf("GOT\n%s\nWANT\n%s", got, want)
- }
- }
- // Make sure the .local_manifest file is backed up.
- if test.Local != "" && test.Stderr == "" {
- data, err := ioutil.ReadFile(".local_manifest.BACKUP")
- if err != nil {
- return fmt.Errorf("local manifest backup got error: %v", err)
- }
- if got, want := string(data), test.Local; got != want {
- return fmt.Errorf("local manifest backup GOT\n%s\nWANT\n%s", got, want)
- }
- }
- return nil
-}
-
-func TestUpgradeRevert(t *testing.T) {
- sh := gosh.NewShell(gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf})
- defer sh.Cleanup()
- jiriRoot := sh.MakeTempDir()
- sh.Pushd(jiriRoot)
- jiriTool := sh.BuildGoPkg("v.io/jiri")
- localData := `<manifest/>`
- jiriData := `<manifest>
- <imports>
- <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
- </imports>
-</manifest>
-`
- // Set up an existing local_manifest.
- if err := ioutil.WriteFile(".local_manifest", []byte(localData), 0644); err != nil {
- t.Errorf("couldn't write local manifest: %v", err)
- }
- // Run a regular upgrade first, and make sure files are as expected.
- sh.Vars[jiri.RootEnv] = jiriRoot
- sh.Cmd(jiriTool, "upgrade", "v23").Run()
- gotJiri, err := ioutil.ReadFile(".jiri_manifest")
- if err != nil {
- t.Errorf("couldn't read jiri manifest: %v", err)
- }
- if got, want := string(gotJiri), jiriData; got != want {
- t.Errorf("jiri manifest GOT\n%s\nWANT\n%s", got, want)
- }
- gotBackup, err := ioutil.ReadFile(".local_manifest.BACKUP")
- if err != nil {
- t.Errorf("couldn't read local manifest backup: %v", err)
- }
- if got, want := string(gotBackup), localData; got != want {
- t.Errorf("local manifest backup GOT\n%s\nWANT\n%s", got, want)
- }
- // Now run a revert, and make sure files are as expected.
- sh.Cmd(jiriTool, "upgrade", "-revert").Run()
- if _, err := os.Stat(".jiri_manifest"); !os.IsNotExist(err) {
- t.Errorf(".jiri_manifest still exists after revert: %v", err)
- }
- if _, err := os.Stat(".local_manifest.BACKUP"); !os.IsNotExist(err) {
- t.Errorf(".local_manifest.BACKUP still exists after revert: %v", err)
- }
- gotLocal, err := ioutil.ReadFile(".local_manifest")
- if err != nil {
- t.Errorf("couldn't read local manifest: %v", err)
- }
- if got, want := string(gotLocal), localData; got != want {
- t.Errorf("local manifest GOT\n%s\nWANT\n%s", got, want)
- }
-}
diff --git a/which.go b/which.go
deleted file mode 100644
index 9dee05c..0000000
--- a/which.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
-
- "v.io/x/lib/cmdline"
-)
-
-var cmdWhich = &cmdline.Command{
- Runner: cmdline.RunnerFunc(runWhich),
- Name: "which",
- Short: "Show path to the jiri tool",
- Long: `
-Which behaves similarly to the unix commandline tool. It is useful in
-determining whether the jiri binary is being run directly, or run via the jiri
-shim script.
-
-If the binary is being run directly, the output looks like this:
-
- # binary
- /path/to/binary/jiri
-
-If the script is being run, the output looks like this:
-
- # script
- /path/to/script/jiri
-`,
-}
-
-func runWhich(env *cmdline.Env, args []string) error {
- if len(args) == 0 {
- fmt.Fprintln(env.Stdout, "# binary")
- path, err := exec.LookPath(os.Args[0])
- if err != nil {
- return err
- }
- abs, err := filepath.Abs(path)
- if err != nil {
- return err
- }
- fmt.Fprintln(env.Stdout, abs)
- return nil
- }
- // TODO(toddw): Look up the path to each argument. This will only be helpful
- // after the profiles are moved back into the main jiri tool.
- return fmt.Errorf("unexpected arguments")
-}
diff --git a/which_test.go b/which_test.go
deleted file mode 100644
index dbe46f1..0000000
--- a/which_test.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "path/filepath"
- "testing"
-
- "v.io/x/lib/gosh"
-)
-
-func TestWhich(t *testing.T) {
- sh := gosh.NewShell(gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf, PropagateChildOutput: true})
- defer sh.Cleanup()
-
- jiriBinary := sh.BuildGoPkg("v.io/jiri")
- stdout, stderr := sh.Cmd(jiriBinary, []string{"which"}...).StdoutStderr()
- if got, want := stdout, fmt.Sprintf("# binary\n%s\n", jiriBinary); got != want {
- t.Errorf("stdout got %q, want %q", got, want)
- }
- if got, want := stderr, ""; got != want {
- t.Errorf("stderr got %q, want %q", got, want)
- }
-}
-
-// TestWhichScript tests the behavior of "jiri which" for the shim script.
-func TestWhichScript(t *testing.T) {
- sh := gosh.NewShell(gosh.Opts{Fatalf: t.Fatalf, Logf: t.Logf, PropagateChildOutput: true})
- defer sh.Cleanup()
-
- jiriScript, err := filepath.Abs("./scripts/jiri")
- if err != nil {
- t.Fatalf("couldn't determine absolute path to jiri script")
- }
- stdout, stderr := sh.Cmd(jiriScript, "which").StdoutStderr()
- if got, want := stdout, fmt.Sprintf("# script\n%s\n", jiriScript); got != want {
- t.Errorf("stdout got %q, want %q", got, want)
- }
- if got, want := stderr, ""; got != want {
- t.Errorf("stderr got %q, want %q", got, want)
- }
-}