func packagesImpl(jirix *jiri.X, cl *packagesFlagValues, args []string) error { mgrs, _, err := availableProfileManagers(jirix, cl.dbPath, args) if err != nil { return err } cl.target.UseCommandLineEnv() root := jiri.NewRelPath(cl.root).Join(profileInstaller) s := jirix.NewSeq() installPackages := cl.installPackages // Never ask a subcommand to install packages. cl.installPackages = false for _, mgr := range mgrs { cmds, err := mgr.packageCmds(jirix, cl, root) if err != nil { return err } for _, cmd := range cmds { if installPackages { if err := s.Verbose(true).Last(cmd[0], cmd[1:]...); err != nil { return err } } else { fmt.Fprintf(jirix.Stdout(), "%s\n", strings.TrimSpace(strings.Join(cmd, " "))) } } } return nil }
// AtomicAction performs an action 'atomically' by keeping track of successfully // completed actions in the supplied completion log and re-running them if they // are not successfully logged therein after deleting the entire contents of the // dir parameter. Consequently it does not make sense to apply AtomicAction to // the same directory in sequence. func AtomicAction(jirix *jiri.X, installFn func() error, dir, message string) error { atomicFn := func() error { completionLogPath := filepath.Join(dir, ".complete") s := jirix.NewSeq() if dir != "" { if exists, _ := s.IsDir(dir); exists { // If the dir exists but the completionLogPath doesn't, then it // means the previous action didn't finish. // Remove the dir so we can perform the action again. if exists, _ := s.IsFile(completionLogPath); !exists { s.RemoveAll(dir).Done() } else { if jirix.Verbose() { fmt.Fprintf(jirix.Stdout(), "AtomicAction: %s already completed in %s\n", message, dir) } return nil } } } if err := installFn(); err != nil { if dir != "" { s.RemoveAll(dir).Done() } return err } return s.WriteFile(completionLogPath, []byte("completed"), DefaultFilePerm).Done() } return jirix.NewSeq().Call(atomicFn, message).Done() }
func installImpl(jirix *jiri.X, cl *installFlagValues, args []string) error { mgrs, db, err := availableProfileManagers(jirix, cl.dbPath, args) if err != nil { return err } cl.target.UseCommandLineEnv() newMgrs := []profileManager{} for _, mgr := range mgrs { name := mgr.mgrName() if !cl.force { installer, profile := profiles.SplitProfileName(name) if p := db.LookupProfileTarget(installer, profile, cl.target); p != nil { fmt.Fprintf(jirix.Stdout(), "%v %v is already installed as %v\n", name, cl.target, p) continue } } newMgrs = append(newMgrs, mgr) } root := jiri.NewRelPath(cl.root).Join(profileInstaller) for _, mgr := range newMgrs { if err := mgr.install(jirix, cl, root); err != nil { return err } } return writeDB(jirix, db, profileInstaller, cl.dbPath) }
// MissingOSPackages returns the subset of the supplied packages that are // missing from the underlying operating system and hence will need to // be installed. func MissingOSPackages(jirix *jiri.X, pkgs []string) ([]string, error) { installedPkgs := map[string]bool{} switch runtime.GOOS { case "linux": if IsFNLHost() { fmt.Fprintf(jirix.Stdout(), "skipping %v on FNL host\n", pkgs) break } var err error installedPkgs, err = linuxList(jirix, pkgs) if err != nil { return nil, err } case "darwin": var err error installedPkgs, err = brewList(jirix) if err != nil { return nil, err } } missing := []string{} for _, pkg := range pkgs { if !installedPkgs[pkg] { missing = append(missing, pkg) } } return missing, nil }
func (ip *inproc) uninstall(jirix *jiri.X, cl *uninstallFlagValues, root jiri.RelPath) error { profile := ip.db.LookupProfile(ip.installer, ip.name) if profile == nil { fmt.Fprintf(jirix.Stdout(), "%s is not installed\n", ip.qname) return nil } mgr := profilesmanager.LookupManager(ip.qname) var targets []*profiles.Target if cl.allTargets { targets = profile.Targets() } else { def, err := targetAtDefaultVersion(mgr, cl.target) if err != nil { return err } targets = []*profiles.Target{&def} } for _, target := range targets { if err := mgr.Uninstall(jirix, ip.db, root, *target); err != nil { logResult(jirix, "Uninstall", mgr, *target, err) return err } logResult(jirix, "Uninstall", mgr, *target, nil) } return nil }
func (ip *inproc) update(jirix *jiri.X, cl *updateFlagValues, root jiri.RelPath) error { profile := ip.db.LookupProfile(ip.installer, ip.name) if profile == nil { // silently ignore uninstalled profile. return nil } mgr := profilesmanager.LookupManager(ip.qname) vi := mgr.VersionInfo() for _, target := range profile.Targets() { if vi.IsTargetOlderThanDefault(target.Version()) { // Check if default target is already installed. defTarget := *target defTarget.SetVersion(vi.Default()) if profiles.FindTarget(profile.Targets(), &defTarget) != nil { // Default target is already installed. Skip. continue } if cl.verbose { fmt.Fprintf(jirix.Stdout(), "Updating %s %s from %q to %s\n", ip.qname, target, target.Version(), vi) } err := mgr.Install(jirix, ip.db, root, defTarget) logResult(jirix, "Update", mgr, defTarget, err) if err != nil { return err } } else { if cl.verbose { fmt.Fprintf(jirix.Stdout(), "%s %s at %q is up to date(%s)\n", ip.qname, target, target.Version(), vi) } } } return nil }
func brewList(jirix *jiri.X) (map[string]bool, error) { var out bytes.Buffer err := jirix.NewSeq().Capture(&out, &out).Last("brew", "list") if err != nil || tool.VerboseFlag { fmt.Fprintf(jirix.Stdout(), "%s", out.String()) } scanner := bufio.NewScanner(&out) pkgs := map[string]bool{} for scanner.Scan() { pkgs[scanner.Text()] = true } return pkgs, err }
// runCLMail is a wrapper that sets up and runs a review instance across // multiple projects. func runCLMail(jirix *jiri.X, _ []string) error { mp, err := initForMultiPart(jirix) if err != nil { return err } if mp.clean { if err := mp.cleanMultiPartMetadata(jirix); err != nil { return err } return nil } if mp.current { return runCLMailCurrent(jirix, []string{}) } // multipart mode if err := mp.writeMultiPartMetadata(jirix); err != nil { mp.cleanMultiPartMetadata(jirix) return err } if err := runCLMailCurrent(jirix, []string{}); err != nil { return err } git := gitutil.New(jirix.NewSeq()) branch, err := git.CurrentBranchName() if err != nil { return err } initialMessage, err := strippedGerritCommitMessage(jirix, branch) if err != nil { return err } s := jirix.NewSeq() tmp, err := s.TempFile("", branch+"-") if err != nil { return err } defer func() { tmp.Close() os.Remove(tmp.Name()) }() if _, err := io.WriteString(tmp, initialMessage); err != nil { return err } // Use Capture to make sure that all output from the subcommands is // sent to stdout/stderr. flags := clMailMultiFlags() flags = append(flags, "--commit-message-body-file="+tmp.Name()) return s.Capture(jirix.Stdout(), jirix.Stderr()).Last("jiri", mp.commandline(mp.currentKey, flags)...) }
func uninstallImpl(jirix *jiri.X, cl *uninstallFlagValues, args []string) error { mgrs, db, err := availableProfileManagers(jirix, cl.dbPath, args) if err != nil { return err } if cl.allTargets && cl.target.IsSet() { fmt.Fprintf(jirix.Stdout(), "ignore target (%v) when used in conjunction with --all-targets\n", cl.target) } root := jiri.NewRelPath(cl.root).Join(profileInstaller) for _, mgr := range mgrs { if err := mgr.uninstall(jirix, cl, root); err != nil { return err } } return writeDB(jirix, db, profileInstaller, cl.dbPath) }
func linuxInstall(jirix *jiri.X, pkgs []string) []string { aptitude, yum, pacman := UsingAptitude(jirix), UsingYum(jirix), UsingPacman(jirix) var cmd []string switch { case aptitude: cmd = append(cmd, "apt-get", "install", "-y") case yum: cmd = append(cmd, "yum", "install", "-y") case pacman: cmd = append(cmd, "pacman", "-S", "--noconfirm") default: fmt.Fprintf(jirix.Stdout(), "no usable package manager found, tested for aptitude, yum and pacman") return nil } return append(cmd, pkgs...) }
// OSPackagesInstallCommands returns the list of commands required to // install the specified packages on the underlying operating system. func OSPackageInstallCommands(jirix *jiri.X, pkgs []string) [][]string { cmds := make([][]string, 0, 1) switch runtime.GOOS { case "linux": if IsFNLHost() { fmt.Fprintf(jirix.Stdout(), "skipping %v on FNL host\n", pkgs) break } if len(pkgs) > 0 { cmds = append(cmds, linuxInstall(jirix, pkgs)) } case "darwin": if len(pkgs) > 0 { return append(cmds, append([]string{"brew", "install"}, pkgs...)) } } return cmds }
func (ip *inproc) cleanup(jirix *jiri.X, cl *cleanupFlagValues, root jiri.RelPath) error { if cl.gc { if cl.verbose { fmt.Fprintf(jirix.Stdout(), "Removing targets older than the default version for %s\n", ip.qname) } if err := cleanupGC(jirix, ip.db, root, cl.verbose, ip.qname); err != nil { return fmt.Errorf("gc: %v", err) } } if cl.rmAll { if cl.verbose { fmt.Fprintf(jirix.Stdout(), "Removing profile manifest and all profile output files\n") } if err := cleanupRmAll(jirix, ip.db, root); err != nil { return err } } return nil }
func runEnv(jirix *jiri.X, args []string) error { if len(envFlags.Profiles) == 0 { return fmt.Errorf("no profiles were specified using --profiles") } rd, err := profilesreader.NewReader(jirix, envFlags.ProfilesMode, envFlags.DBFilename) if err != nil { return err } profileNames := strings.Split(envFlags.Profiles, ",") if err := rd.ValidateRequestedProfilesAndTarget(profileNames, envFlags.Target); err != nil { return err } rd.MergeEnvFromProfiles(envFlags.MergePolicies, envFlags.Target, profileNames...) out := fmtVars(rd.ToMap(), args) if len(out) > 0 { fmt.Fprintln(jirix.Stdout(), out) } return nil }
func cleanupRmAll(jirix *jiri.X, db *profiles.DB, root jiri.RelPath) error { s := jirix.NewSeq() if err := s.AssertFileExists(db.Path()).Remove(db.Path()).Done(); err != nil && !runutil.IsNotExist(err) { return err } else { if err := s.AssertDirExists(db.Path()).RemoveAll(db.Path()).Done(); err != nil && !runutil.IsNotExist(err) { return err } } d := root.Abs(jirix) err := s.AssertDirExists(d). Run("chmod", "-R", "u+w", d). RemoveAll(d). Done() if err == nil || runutil.IsNotExist(err) { fmt.Fprintf(jirix.Stdout(), "success\n") return nil } else { fmt.Fprintf(jirix.Stdout(), "%v\n", err) } return err }
func logResult(jirix *jiri.X, action string, mgr profiles.Manager, target profiles.Target, err error) { fmt.Fprintf(jirix.Stdout(), "%s: %s %s: ", action, profiles.QualifiedProfileName(mgr.Installer(), mgr.Name()), target) if err == nil { fmt.Fprintf(jirix.Stdout(), "success\n") } else { fmt.Fprintf(jirix.Stdout(), "%v\n", err) } }
// 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 }
// ensureAction ensures that the requested profile and target // is installed/uninstalled, installing/uninstalling it if and only if necessary. func ensureAction(jirix *jiri.X, pdb *profiles.DB, action profiles.Action, installer, profile string, root jiri.RelPath, target profiles.Target) error { verb := "" switch action { case profiles.Install: verb = "install" case profiles.Uninstall: verb = "uninstall" default: return fmt.Errorf("unrecognised action %v", action) } if jirix.Verbose() { fmt.Fprintf(jirix.Stdout(), "%s %v %s\n", verb, action, target) } if t := pdb.LookupProfileTarget(installer, profile, target); t != nil { if jirix.Verbose() { fmt.Fprintf(jirix.Stdout(), "%v %v is already %sed as %v\n", profile, target, verb, t) } return nil } mgr := LookupManager(profiles.QualifiedProfileName(installer, profile)) if mgr == nil { return fmt.Errorf("profile %v is not supported", profile) } version, err := mgr.VersionInfo().Select(target.Version()) if err != nil { return err } target.SetVersion(version) if jirix.Verbose() { fmt.Fprintf(jirix.Stdout(), "%s %s %s\n", verb, profile, target.DebugString()) } if action == profiles.Install { return mgr.Install(jirix, pdb, root, target) } return mgr.Uninstall(jirix, pdb, root, target) }
func availableImpl(jirix *jiri.X, cl *availableFlagValues, _ []string) error { if profileInstaller == "" { subcommands := findProfileSubcommands(jirix) if cl.verbose { fmt.Fprintf(jirix.Stdout(), "Available Subcommands: %s\n", strings.Join(subcommands, ", ")) } s := jirix.NewSeq() args := []string{"available"} args = append(args, cl.args()...) out := bytes.Buffer{} for _, sc := range subcommands { if err := s.Capture(&out, nil).Last(sc, args...); err != nil { return err } } if s := strings.TrimSpace(out.String()); s != "" { fmt.Fprintln(jirix.Stdout(), s) } } mgrs := profilesmanager.Managers() if len(mgrs) == 0 { return nil } if cl.verbose { scname := "" if profileInstaller != "" { scname = profileInstaller + ": " } fmt.Fprintf(jirix.Stdout(), "%sAvailable Profiles:\n", scname) for _, name := range mgrs { mgr := profilesmanager.LookupManager(name) vi := mgr.VersionInfo() fmt.Fprintf(jirix.Stdout(), "%s: versions: %s\n", name, vi) } } else { if cl.describe { for _, name := range mgrs { mgr := profilesmanager.LookupManager(name) fmt.Fprintf(jirix.Stdout(), "%s: %s\n", name, strings.Replace(strings.TrimSpace(mgr.Info()), "\n", " ", -1)) } } else { fmt.Fprintf(jirix.Stdout(), "%s\n", strings.Join(mgrs, ", ")) } } return nil }
func runp(jirix *jiri.X, cmd *cmdline.Command, args []string) error { hasUntrackedSet := profilescmdline.IsFlagSet(cmd.ParsedFlags, "has-untracked") hasUncommitedSet := profilescmdline.IsFlagSet(cmd.ParsedFlags, "has-uncommitted") hasGerritSet := profilescmdline.IsFlagSet(cmd.ParsedFlags, "has-gerrit-message") if runpFlags.interactive { runpFlags.collateOutput = false } var keysRE, branchRE *regexp.Regexp var err error if profilescmdline.IsFlagSet(cmd.ParsedFlags, "projects") { re := "" for _, pre := range strings.Split(runpFlags.projectKeys, ",") { re += pre + "|" } re = strings.TrimRight(re, "|") keysRE, err = regexp.Compile(re) if err != nil { return fmt.Errorf("failed to compile projects regexp: %q: %v", runpFlags.projectKeys, err) } } if profilescmdline.IsFlagSet(cmd.ParsedFlags, "has-branch") { branchRE, err = regexp.Compile(runpFlags.hasBranch) if err != nil { return fmt.Errorf("failed to compile has-branch regexp: %q: %v", runpFlags.hasBranch, err) } } for _, f := range []string{"show-key-prefix", "show-name-prefix"} { if profilescmdline.IsFlagSet(cmd.ParsedFlags, f) { if runpFlags.interactive && profilescmdline.IsFlagSet(cmd.ParsedFlags, "interactive") { fmt.Fprintf(jirix.Stderr(), "WARNING: interactive mode being disabled because %s was set\n", f) } runpFlags.interactive = false runpFlags.collateOutput = true break } } git := gitutil.New(jirix.NewSeq()) homeBranch, err := git.CurrentBranchName() if err != nil { // jiri was run from outside of a project. Let's assume we'll // use all projects if none have been specified via the projects flag. if keysRE == nil { keysRE = regexp.MustCompile(".*") } } dirty := false if hasUntrackedSet || hasUncommitedSet { dirty = true } states, err := project.GetProjectStates(jirix, dirty) if err != nil { return err } mapInputs := map[project.ProjectKey]*mapInput{} var keys project.ProjectKeys for key, state := range states { if keysRE != nil { if !keysRE.MatchString(string(key)) { continue } } else { if state.CurrentBranch != homeBranch { continue } } if branchRE != nil { found := false for _, br := range state.Branches { if branchRE.MatchString(br.Name) { found = true break } } if !found { continue } } if hasUntrackedSet && (state.HasUntracked != runpFlags.hasUntracked) { continue } if hasUncommitedSet && (state.HasUncommitted != runpFlags.hasUncommitted) { continue } if hasGerritSet { hasMsg := false for _, br := range state.Branches { if (state.CurrentBranch == br.Name) && br.HasGerritMessage { hasMsg = true break } } if hasMsg != runpFlags.hasGerritMessage { continue } } mapInputs[key] = &mapInput{ ProjectState: state, jirix: jirix, key: key, } keys = append(keys, key) } total := len(mapInputs) index := 1 for _, mi := range mapInputs { mi.index = index mi.total = total index++ } if runpFlags.verbose { fmt.Fprintf(jirix.Stdout(), "Project Names: %s\n", strings.Join(stateNames(mapInputs), " ")) fmt.Fprintf(jirix.Stdout(), "Project Keys: %s\n", strings.Join(stateKeys(mapInputs), " ")) } reader, err := profilesreader.NewReader(jirix, runpFlags.ProfilesMode, runpFlags.DBFilename) runner := &runner{ reader: reader, args: args, } mr := simplemr.MR{} if runpFlags.interactive { // Run one mapper at a time. mr.NumMappers = 1 sort.Sort(keys) } in, out := make(chan *simplemr.Record, len(mapInputs)), make(chan *simplemr.Record, len(mapInputs)) sigch := make(chan os.Signal) signal.Notify(sigch, os.Interrupt) go func() { <-sigch; mr.Cancel() }() go mr.Run(in, out, runner, runner) for _, key := range keys { in <- &simplemr.Record{string(key), []interface{}{mapInputs[key]}} } close(in) <-out return mr.Error() }
// runProjectInfo provides structured info on local projects. func runProjectInfo(jirix *jiri.X, args []string) error { tmpl, err := template.New("info").Parse(formatFlag) if err != nil { return fmt.Errorf("failed to parse template %q: %v", formatFlag, err) } regexps := []*regexp.Regexp{} if len(args) > 0 { regexps = make([]*regexp.Regexp, len(args), len(args)) for i, a := range args { re, err := regexp.Compile(a) if err != nil { return fmt.Errorf("failed to compile regexp %v: %v", a, err) } regexps[i] = re } } dirty := false for _, slow := range []string{"HasUncommitted", "HasUntracked"} { if strings.Contains(formatFlag, slow) { dirty = true break } } var states map[project.ProjectKey]*project.ProjectState var keys project.ProjectKeys if len(args) == 0 { currentProjectKey, err := project.CurrentProjectKey(jirix) if err != nil { return err } state, err := project.GetProjectState(jirix, currentProjectKey, true) if err != nil { // jiri was run from outside of a project so let's // use all available projects. states, err = project.GetProjectStates(jirix, dirty) if err != nil { return err } for key := range states { keys = append(keys, key) } } else { states = map[project.ProjectKey]*project.ProjectState{ currentProjectKey: state, } keys = append(keys, currentProjectKey) } } else { var err error states, err = project.GetProjectStates(jirix, dirty) if err != nil { return err } for key := range states { for _, re := range regexps { if re.MatchString(string(key)) { keys = append(keys, key) break } } } } sort.Sort(keys) for _, key := range keys { state := states[key] out := &bytes.Buffer{} if err = tmpl.Execute(out, state); err != nil { return jirix.UsageErrorf("invalid format") } fmt.Fprintln(jirix.Stdout(), out.String()) } return nil }
func (sc *subcommand) run(jirix *jiri.X, verb string, args []string) error { cl := []string{"profile-" + sc.installer, verb} cl = append(cl, args...) cl = append(cl, sc.qname) return jirix.NewSeq().Capture(jirix.Stdout(), jirix.Stderr()).Last("jiri", cl...) }
func runSnapshotList(jirix *jiri.X, args []string) error { snapshotDir, err := getSnapshotDir(jirix) if err != nil { return err } if len(args) == 0 { // Identify all known snapshot labels, using a // heuristic that looks for all symbolic links <foo> // in the snapshot directory that point to a file in // the "labels/<foo>" subdirectory of the snapshot // directory. fileInfoList, err := ioutil.ReadDir(snapshotDir) if err != nil { return fmt.Errorf("ReadDir(%v) failed: %v", snapshotDir, err) } for _, fileInfo := range fileInfoList { if fileInfo.Mode()&os.ModeSymlink != 0 { path := filepath.Join(snapshotDir, fileInfo.Name()) dst, err := filepath.EvalSymlinks(path) if err != nil { return fmt.Errorf("EvalSymlinks(%v) failed: %v", path, err) } if strings.HasSuffix(filepath.Dir(dst), filepath.Join("labels", fileInfo.Name())) { args = append(args, fileInfo.Name()) } } } } // Check that all labels exist. var notexist []string for _, label := range args { labelDir := filepath.Join(snapshotDir, "labels", label) switch _, err := jirix.NewSeq().Stat(labelDir); { case runutil.IsNotExist(err): notexist = append(notexist, label) case err != nil: return err } } if len(notexist) > 0 { return fmt.Errorf("snapshot labels %v not found", notexist) } // Print snapshots for all labels. sort.Strings(args) for _, label := range args { // Scan the snapshot directory "labels/<label>" printing // all snapshots. labelDir := filepath.Join(snapshotDir, "labels", label) fileInfoList, err := ioutil.ReadDir(labelDir) if err != nil { return fmt.Errorf("ReadDir(%v) failed: %v", labelDir, err) } fmt.Fprintf(jirix.Stdout(), "snapshots of label %q:\n", label) for _, fileInfo := range fileInfoList { fmt.Fprintf(jirix.Stdout(), " %v\n", fileInfo.Name()) } } return nil }
func runList(jirix *jiri.X, args []string) error { if listFlags.Verbose { fmt.Fprintf(jirix.Stdout(), "Profiles Database Path: %s\n", listFlags.DBFilename) } rd, err := profilesreader.NewReader(jirix, listFlags.ProfilesMode, listFlags.DBFilename) if err != nil { return err } profileNames := []string{} for _, a := range args { if a != "" { profileNames = append(profileNames, a) } } if len(args) == 0 { if IsFlagSet(cmdList.ParsedFlags, "profiles") { profileNames = strings.Split(listFlags.Profiles, ",") } else { profileNames = rd.ProfileNames() } } if listFlags.Verbose { fmt.Fprintf(jirix.Stdout(), "Installed Profiles: ") fmt.Fprintf(jirix.Stdout(), "%s\n", strings.Join(rd.ProfileNames(), ", ")) for _, name := range profileNames { profile := rd.LookupProfile(name) if profile == nil { continue } fmt.Fprintf(jirix.Stdout(), "Profile: %s @ %s\n", profile.Name(), profile.Root()) for _, target := range matchingTargets(rd, profile) { fmt.Fprintf(jirix.Stdout(), "\t%s\n", target.DebugString()) } } return nil } if listFlags.info == "" { matchingNames := []string{} for _, name := range profileNames { profile := rd.LookupProfile(name) if profile == nil { continue } if len(matchingTargets(rd, profile)) > 0 { matchingNames = append(matchingNames, name) } } if len(matchingNames) > 0 { fmt.Fprintln(jirix.Stdout(), strings.Join(matchingNames, ", ")) } else { if IsFlagSet(cmdList.ParsedFlags, "target") { return fmt.Errorf("no matching targets for %s", listFlags.Target) } } return nil } // Handle --info found := false for _, name := range profileNames { profile := rd.LookupProfile(name) if profile == nil { continue } targets := matchingTargets(rd, profile) out := &bytes.Buffer{} printHeader := len(profileNames) > 1 || len(targets) > 1 || len(listFlags.info) == 0 for _, target := range targets { if printHeader { out.WriteString(fmtHeader(name, target)) out.WriteString(" ") } r, err := fmtInfo(jirix, listFlags.info, rd, profile, target) if err != nil { return err } out.WriteString(r) if printHeader { out.WriteString("\n") } found = true } fmt.Fprint(jirix.Stdout(), out.String()) } if !found && IsFlagSet(cmdList.ParsedFlags, "target") { return fmt.Errorf("no matching targets for %s", listFlags.Target) } return nil }