// the remote helper is untrusted and must be either escaped with %q or by
// calling EscapeControlCharacters().
+func (s *Sync) formatChanges(resp safcm.MsgSyncResp) string {
+ var changes []string
+ if len(resp.FileChanges) > 0 {
+ changes = append(changes,
+ s.formatFileChanges(resp.FileChanges))
+ }
+ if len(resp.PackageChanges) > 0 {
+ changes = append(changes,
+ s.formatPackageChanges(resp.PackageChanges))
+ }
+ if len(resp.ServiceChanges) > 0 {
+ changes = append(changes,
+ s.formatServiceChanges(resp.ServiceChanges))
+ }
+ if len(resp.CommandChanges) > 0 {
+ changes = append(changes,
+ s.formatCommandChanges(resp.CommandChanges))
+ }
+ if len(changes) == 0 {
+ // Notify user that the host was synced successfully
+ return "no changes"
+ }
+
+ x := strings.Join(changes, "\n")
+ // If quiet is used and only commands without output were executed
+ // then don't prepend a newline so that the whole change output of a
+ // host fits in a single line. This makes the output much more
+ // readable with multiple hosts.
+ if strings.Count(x, "\n") == 1 {
+ return x
+ }
+ return "\n" + x
+}
+
func (s *Sync) formatFileChanges(changes []safcm.FileChange) string {
var buf strings.Builder
fmt.Fprintf(&buf, "changed %d file(s):", len(changes))
func (s *Sync) formatCommandChanges(changes []safcm.CommandChange) string {
const indent = " > "
+ // Quiet hides all successful, non-trigger commands which produce no
+ // output. This is useful as many commands will be used to enforce a
+ // certain state (e.g. file not-present, `ainsl`, etc.) and are run on
+ // each sync. Displaying them provides not much useful information.
+ // Instead, quiet shows them only when they produce output (e.g.
+ // `ainsl`, `rm -v`) and thus modify the host's state.
+ var noOutput int
+ if s.config.Quiet {
+ for _, x := range changes {
+ if x.Trigger == "" &&
+ x.Error == "" &&
+ x.Output == "" {
+ noOutput++
+ }
+ }
+ }
+
var buf strings.Builder
- fmt.Fprintf(&buf, "executed %d command(s):", len(changes))
+ fmt.Fprintf(&buf, "executed %d command(s)", len(changes))
+ if noOutput > 0 && !s.config.DryRun {
+ fmt.Fprintf(&buf, ", %d with no output", noOutput)
+ }
+ if noOutput != len(changes) {
+ fmt.Fprintf(&buf, ":")
+ }
if s.config.DryRun {
fmt.Fprintf(&buf, " (dry-run)")
}
fmt.Fprintf(&buf, "\n")
for _, x := range changes {
+ if noOutput > 0 &&
+ x.Trigger == "" && x.Error == "" && x.Output == "" {
+ continue
+ }
+
fmt.Fprintf(&buf, "%s", s.formatTarget(x.Command))
if x.Trigger != "" {
fmt.Fprintf(&buf, ", trigger for %q", x.Trigger)
}
func indentBlock(x string, sep string) string {
- if x == "" {
- return ""
- }
-
lines := strings.Split(x, "\n")
if lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]