func runUninstall(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) if err := initCommand(ctx, args); err != nil { return err } if allFlag && targetFlag.IsSet() { return fmt.Errorf("don't specify a target in conjunction with --all") } if allFlag { for _, name := range args { profile := profiles.LookupProfile(name) mgr := profiles.LookupManager(name) if profile == nil || mgr == nil { continue } mgr.SetRoot(rootDir) for _, target := range profile.Targets() { if err := mgr.Uninstall(ctx, *target); err != nil { logResult(ctx, "Uninstall", mgr, *target, err) return err } logResult(ctx, "Uninstall", mgr, *target, nil) } } } else { applyCommand(args, env, ctx, targetFlag, func(mgr profiles.Manager, ctx *tool.Context, target profiles.Target) error { err := mgr.Uninstall(ctx, target) logResult(ctx, "Uninstall", mgr, target, err) return err }) } return profiles.Write(ctx, manifestFlag) }
func runRebuild(env *cmdline.Env, args []string) (e error) { ctx := tool.NewContextFromEnv(env) _, tools, err := project.ReadManifest(ctx) if err != nil { return err } // Create a temporary directory in which tools will be built. tmpDir, err := ctx.Run().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 ctx.Run().RemoveAll(tmpDir) }, &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(ctx, tools, tmpDir); err != nil { return err } return project.InstallTools(ctx, tmpDir) }
func runCLNew(env *cmdline.Env, args []string) error { if got, want := len(args), 1; got != want { return env.UsageErrorf("unexpected number of arguments: got %v, want %v", got, want) } ctx := tool.NewContextFromEnv(env) return newCL(ctx, args) }
func runInstall(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) if err := initCommand(ctx, args); err != nil { return err } names := []string{} if len(args) == 0 { for _, name := range profiles.Managers() { names = append(names, name) } } for _, name := range args { if p := profiles.LookupProfileTarget(name, targetFlag); p != nil { fmt.Fprintf(ctx.Stdout(), "%v %v is already installed as %v\n", name, targetFlag, p) continue } names = append(names, name) } if err := applyCommand(names, env, ctx, targetFlag, func(mgr profiles.Manager, ctx *tool.Context, target profiles.Target) error { err := mgr.Install(ctx, target) logResult(ctx, "Install:", mgr, target, err) return err }); err != nil { return err } return profiles.Write(ctx, manifestFlag) }
func runRecreate(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) if err := profiles.Read(ctx, manifestFlag); err != nil { fmt.Fprintf(ctx.Stderr(), "Failed to read manifest: %v", err) return err } profileNames := args if len(args) == 0 { profileNames = profiles.Profiles() } prefix := "jiri v23-profile install" for _, name := range profileNames { profile := profiles.LookupProfile(name) if profile == nil { return fmt.Errorf("Profile %v is not installed", name) } for _, target := range profile.Targets() { fmt.Fprintf(ctx.Stdout(), "%s --target=%s", prefix, target) cmdEnv := target.CommandLineEnv() if len(cmdEnv.Vars) > 0 { fmt.Fprintf(ctx.Stdout(), " --env=\"%s\"", strings.Join(cmdEnv.Vars, ",")) } fmt.Fprintf(ctx.Stdout(), " %s\n", name) } } return nil }
func runCLCleanup(env *cmdline.Env, args []string) error { if len(args) == 0 { return env.UsageErrorf("cleanup requires at least one argument") } ctx := tool.NewContextFromEnv(env) return cleanupCL(ctx, args) }
// NewX returns a new execution environment, given a cmdline env. // It also prepends $JIRI_ROOT/.jiri_root/bin to the PATH. func NewX(env *cmdline.Env) (*X, error) { ctx := tool.NewContextFromEnv(env) root, err := findJiriRoot(ctx.Timer()) if err != nil { return nil, err } x := &X{ Context: ctx, Root: root, Usage: env.UsageErrorf, } if ctx.Env()[PreservePathEnv] == "" { // Prepend $JIRI_ROOT/.jiri_root/bin to the PATH, so execing a binary will // invoke the one in that directory, if it exists. This is crucial for jiri // subcommands, where we want to invoke the binary that jiri installed, not // whatever is in the user's PATH. // // Note that we must modify the actual os env variable with os.SetEnv and // also the ctx.env, so that execing a binary through the os/exec package // and with ctx.Run both have the correct behavior. newPath := envvar.PrependUniqueToken(ctx.Env()["PATH"], string(os.PathListSeparator), x.BinDir()) ctx.Env()["PATH"] = newPath if err := os.Setenv("PATH", newPath); err != nil { return nil, err } } return x, nil }
func runList(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) if showManifestFlag { data, err := ctx.Run().ReadFile(manifestFlag) if err != nil { return err } fmt.Fprintln(ctx.Stdout(), string(data)) return nil } if verboseFlag { fmt.Fprintf(ctx.Stdout(), "Manifest: %s\n", manifestFlag) } if availableFlag { if verboseFlag { fmt.Fprintf(ctx.Stdout(), "Available Profiles:\n") for _, name := range profiles.Managers() { mgr := profiles.LookupManager(name) vi := mgr.VersionInfo() fmt.Fprintf(ctx.Stdout(), "%s: versions: %s - %s\n", name, vi.Default(), strings.Join(vi.Supported(), " ")) } } else { fmt.Fprintf(ctx.Stdout(), "%s\n", strings.Join(profiles.Managers(), ", ")) } } if err := profiles.Read(ctx, manifestFlag); err != nil { fmt.Fprintf(ctx.Stderr(), "Failed to read manifest: %v", err) return err } profileNames := args if len(args) == 0 { profileNames = profiles.Profiles() } availableNames := []string{} for _, name := range profileNames { if profiles.LookupProfile(name) != nil { availableNames = append(availableNames, name) } } if verboseFlag { fmt.Fprintf(ctx.Stdout(), "Installed Profiles: ") fmt.Fprintf(ctx.Stdout(), "%s\n", strings.Join(profiles.Profiles(), ", ")) for _, name := range availableNames { profile := profiles.LookupProfile(name) fmt.Fprintf(ctx.Stdout(), "Profile: %s @ %s\n", profile.Name, profile.Root) for _, target := range profile.Targets() { fmt.Fprintf(ctx.Stdout(), "\t%s\n", target.DebugString()) } } } else { for _, name := range availableNames { profile := profiles.LookupProfile(name) for _, target := range profile.Targets() { fmt.Fprintf(ctx.Stdout(), "%s %s\n", name, target) } } } return nil }
func runProjectShellPrompt(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) states, err := project.GetProjectStates(ctx, checkDirtyFlag) if err != nil { return err } names := []string{} for name := range states { names = append(names, name) } sort.Strings(names) // Get the name of the current project. currentProjectName, err := project.CurrentProjectName(ctx) if err != nil { return err } var statuses []string for _, name := range names { state := states[name] status := "" if checkDirtyFlag { if state.HasUncommitted { status += "*" } if state.HasUntracked { status += "%" } } short := state.CurrentBranch + status long := filepath.Base(name) + ":" + short if name == currentProjectName { 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 }
// runProjectPoll generates a description of changes that exist // remotely but do not exist locally. func runProjectPoll(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) projectSet := map[string]struct{}{} if len(args) > 0 { config, err := util.LoadConfig(ctx) 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(ctx, 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(env.Stdout, "%s\n", bytes) } return nil }
func runEnv(env *cmdline.Env, args []string) error { if len(profileFlag) == 0 { return fmt.Errorf("no profile was specified using --profile") } ctx := tool.NewContextFromEnv(env) if err := profiles.Read(ctx, manifestFlag); err != nil { return fmt.Errorf("Failed to read manifest: %v", err) } profile := profiles.LookupProfile(profileFlag) if profile == nil { return fmt.Errorf("profile %q is not installed", profileFlag) } target := profiles.FindTarget(profile.Targets(), &targetFlag) if target == nil { return fmt.Errorf("target %q is not installed for profile %q", targetFlag, profileFlag) } vars := envvar.SliceToMap(target.Env.Vars) buf := bytes.Buffer{} if len(args) == 0 { for k, v := range vars { buf.WriteString(fmt.Sprintf("%s=%q ", k, v)) } for k, fn := range pseudoVariables { buf.WriteString(fmt.Sprintf("%s=%q ", k, fn(target))) } } else { for _, arg := range args { name := strings.TrimSuffix(arg, "=") trimmed := name != arg for k, fn := range pseudoVariables { if k == name { buf.WriteString(expr(k, fn(target), trimmed)) } } for k, v := range vars { if k == name { buf.WriteString(expr(k, v, trimmed)) } } } } fmt.Fprintf(ctx.Stdout(), strings.TrimSuffix(buf.String(), " ")+"\n") return nil }
func runSnapshotCreate(env *cmdline.Env, args []string) error { if len(args) != 1 { return env.UsageErrorf("unexpected number of arguments") } label := args[0] ctx := tool.NewContextFromEnv(env) if err := checkSnapshotDir(ctx); err != nil { return err } snapshotDir, err := getSnapshotDir() if err != nil { return err } snapshotFile := filepath.Join(snapshotDir, "labels", label, time.Now().Format(timeFormatFlag)) // Either atomically create a new snapshot that captures the project // state and push the changes to the remote repository (if // applicable), or fail with no effect. createFn := func() error { revision, err := ctx.Git().CurrentRevision() if err != nil { return err } if err := createSnapshot(ctx, snapshotDir, snapshotFile, label); err != nil { // Clean up on all errors. ctx.Git().Reset(revision) ctx.Git().RemoveUntrackedFiles() return err } return nil } // Execute the above function in the snapshot directory. p := project.Project{ Path: snapshotDir, Protocol: "git", Revision: "HEAD", } if err := project.ApplyToLocalMaster(ctx, project.Projects{p.Name: p}, createFn); err != nil { return err } return nil }
func runInfo(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) profileNames := args if len(args) == 0 { profileNames = profiles.Managers() } _, width, err := textutil.TerminalSize() if err != nil { width = 80 } w := textutil.NewUTF8LineWriter(ctx.Stdout(), width) defer w.Flush() for _, name := range profileNames { mgr := profiles.LookupManager(name) if mgr == nil { return fmt.Errorf("profile %q is not available", name) } fmt.Fprintf(w, "%s: %s\n\n", name, mgr.Info()) } return nil }
func runUpdate(env *cmdline.Env, _ []string) error { ctx := tool.NewContextFromEnv(env) // Create a snapshot of the current state of all projects and // write it to the $JIRI_ROOT/.update_history folder. root, err := project.JiriRoot() if err != nil { return err } snapshotFile := filepath.Join(root, ".update_history", time.Now().Format(time.RFC3339)) if err := project.CreateSnapshot(ctx, snapshotFile); err != nil { return err } // Update all projects to their latest version. // Attempt <attemptsFlag> times before failing. updateFn := func() error { return project.UpdateUniverse(ctx, gcFlag) } return retry.Function(ctx, updateFn, retry.AttemptsOpt(attemptsFlag)) }
// runCLMail is a wrapper that sets up and runs a review instance. func runCLMail(env *cmdline.Env, _ []string) error { ctx := tool.NewContextFromEnv(env) // Sanity checks for the <presubmitFlag> flag. if !checkPresubmitFlag() { return env.UsageErrorf("invalid value for the -presubmit flag. Valid values: %s.", strings.Join(gerrit.PresubmitTestTypes(), ",")) } host := hostFlag if host == "" { var err error if host, err = project.GerritHost(ctx); err != nil { return err } } // Create and run the review. review, err := newReview(ctx, gerrit.CLOpts{ Autosubmit: autosubmitFlag, Ccs: parseEmails(ccsFlag), Draft: draftFlag, Edit: editFlag, Host: host, 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() }
func runProjectClean(env *cmdline.Env, args []string) (e error) { ctx := tool.NewContextFromEnv(env) localProjects, err := project.LocalProjects(ctx, project.FullScan) if err != nil { return err } projects := map[string]project.Project{} if len(args) > 0 { for _, arg := range args { if p, ok := localProjects[arg]; ok { projects[p.Name] = p } else { fmt.Fprintf(ctx.Stderr(), "Local project %q not found.\n", p.Name) } } } else { projects = localProjects } if err := project.CleanupProjects(ctx, projects, cleanupBranchesFlag); err != nil { return err } return nil }
// runProjectList generates a listing of local projects. func runProjectList(env *cmdline.Env, _ []string) error { ctx := tool.NewContextFromEnv(env) states, err := project.GetProjectStates(ctx, noPristineFlag) if err != nil { return err } names := []string{} for name := range states { names = append(names, name) } sort.Strings(names) for _, name := range names { state := states[name] if noPristineFlag { pristine := len(state.Branches) == 1 && state.CurrentBranch == "master" && !state.HasUncommitted && !state.HasUntracked if pristine { continue } } fmt.Fprintf(ctx.Stdout(), "project=%q path=%q\n", path.Base(name), 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(ctx.Stdout(), "%v\n", s) } } } return nil }
func runCleanup(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) if len(args) == 0 { args = profiles.Managers() } if err := initCommand(ctx, args); err != nil { return err } for _, n := range args { mgr := profiles.LookupManager(n) vi := mgr.VersionInfo() profile := profiles.LookupProfile(n) for _, target := range profile.Targets() { if vi.IsOlderThanDefault(target.Version()) { err := mgr.Uninstall(ctx, *target) logResult(ctx, "Cleanup", mgr, *target, err) if err != nil { return err } } } } return profiles.Write(ctx, manifestFlag) }
func runUpdate(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) if len(args) == 0 { args = profiles.Managers() } if err := initCommand(ctx, args); err != nil { return err } for _, n := range args { mgr := profiles.LookupManager(n) profile := profiles.LookupProfile(n) if profile == nil { continue } vi := mgr.VersionInfo() mgr.SetRoot(rootDir) for _, target := range profile.Targets() { if vi.IsNewerThanDefault(target.Version()) { if verboseFlag { fmt.Fprintf(ctx.Stdout(), "Updating %s %s from %q to %s\n", n, target, target.Version(), vi) } target.SetVersion(vi.Default()) err := mgr.Install(ctx, *target) logResult(ctx, "Update", mgr, *target, err) if err != nil { return err } } else { if verboseFlag { fmt.Fprintf(ctx.Stdout(), "%s %s at %q is up to date(%s)\n", n, target, target.Version(), vi) } } } } return profiles.Write(ctx, manifestFlag) }
func runContributors(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) projects, err := project.LocalProjects(ctx, project.FastScan) if err != nil { return err } projectNames := map[string]struct{}{} if len(args) != 0 { projectNames = set.String.FromSlice(args) } else { for name, _ := range projects { projectNames[name] = struct{}{} } } aliases, err := loadAliases(ctx) if err != nil { return err } contributors := map[string]*contributor{} for name, _ := range projectNames { project, ok := projects[name] if !ok { continue } if err := ctx.Run().Chdir(project.Path); err != nil { return err } switch project.Protocol { case "git": lines, err := listCommitters(ctx) 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 == "*****@*****.**" || c.email == "*****@*****.**" { 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(env.Stdout, "%4d ", c.count) } fmt.Fprintf(env.Stdout, "%v <%v>\n", c.name, c.email) } return nil }
func runCLSync(env *cmdline.Env, _ []string) error { ctx := tool.NewContextFromEnv(env) return syncCL(ctx) }
func runSnapshotList(env *cmdline.Env, args []string) error { ctx := tool.NewContextFromEnv(env) if err := checkSnapshotDir(ctx); err != nil { return err } snapshotDir, err := getSnapshotDir() 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. failed := false for _, label := range args { labelDir := filepath.Join(snapshotDir, "labels", label) if _, err := ctx.Run().Stat(labelDir); err != nil { if !os.IsNotExist(err) { return err } failed = true fmt.Fprintf(env.Stderr, "snapshot label %q not found", label) } } if failed { return cmdline.ErrExitCode(2) } // 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(env.Stdout, "snapshots of label %q:\n", label) for _, fileInfo := range fileInfoList { fmt.Fprintf(env.Stdout, " %v\n", fileInfo.Name()) } } return nil }