Skip to content

Commit 8f0d06c

Browse files
authored
Merge pull request #13 from elastic/fix/docs-builder-false-success
Report what the docs-builder installer actually did
2 parents ecca9bf + f524d44 commit 8f0d06c

3 files changed

Lines changed: 82 additions & 7 deletions

File tree

‎cmd/elastic-docs-utils/main.go‎

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -228,16 +228,55 @@ func installOptionalTools(r *ui.Renderer, vale, docsBuilder, dryRun, force, assu
228228
}
229229
updateDownloadProgress(progress, "docs-builder", completed, total)
230230
}
231+
// The upstream installer exits 0 whether it installs, skips an existing
232+
// binary, or is declined at its overwrite prompt, so its exit code alone
233+
// cannot tell us what happened. Compare the version either side of it.
234+
before := updates.BinaryVersion("docs-builder", "--version")
231235
if err := bootstrap.InstallDocsBuilderWithProgress(force, assumeYes, callback); err != nil {
232236
failures = append(failures, fmt.Errorf("install docs-builder: %w", err))
233237
} else {
234-
r.Success("Installed docs-builder.")
238+
progress.Stop()
239+
level, message := installerOutcome("docs-builder", before, updates.BinaryVersion("docs-builder", "--version"))
240+
switch level {
241+
case outcomeMissing:
242+
r.Warn("%s", message)
243+
case outcomeUnchanged:
244+
r.Info("%s", message)
245+
default:
246+
r.Success("%s", message)
247+
}
235248
}
236249
progress.Stop()
237250
}
238251
return failures
239252
}
240253

254+
type installerOutcomeLevel int
255+
256+
const (
257+
outcomeInstalled installerOutcomeLevel = iota
258+
outcomeUpdated
259+
outcomeUnchanged
260+
outcomeMissing
261+
)
262+
263+
// installerOutcome decides what an upstream installer actually did, from the
264+
// tool's reported version either side of the run. The installers exit 0 whether
265+
// they install, skip an existing binary, or are declined at an overwrite
266+
// prompt, so a zero exit on its own must not be reported as an install.
267+
func installerOutcome(tool, before, after string) (installerOutcomeLevel, string) {
268+
switch {
269+
case after == "":
270+
return outcomeMissing, fmt.Sprintf("The %s installer finished, but no %s binary is on PATH.", tool, tool)
271+
case before != "" && after == before:
272+
return outcomeUnchanged, fmt.Sprintf("%s left unchanged at %s; the installer did not replace it.", tool, after)
273+
case before == "":
274+
return outcomeInstalled, fmt.Sprintf("Installed %s %s.", tool, after)
275+
default:
276+
return outcomeUpdated, fmt.Sprintf("Updated %s %s to %s.", tool, before, after)
277+
}
278+
}
279+
241280
func reportToolFailures(r *ui.Renderer, failures []error) {
242281
for _, failure := range failures {
243282
r.Warn("%v", failure)
@@ -452,16 +491,16 @@ func commandUpdate(r *ui.Renderer, args []string) error {
452491
// A failed component must not stop the remaining ones, and the refreshed
453492
// status below is most useful precisely when something went wrong.
454493
var toolFailures []error
455-
if selected.vale {
456-
toolFailures = append(toolFailures, installOptionalTools(r, true, false, *dryRun, *force, false)...)
457-
} else {
494+
if !selected.vale {
458495
r.Info("Skipping Vale and Elastic Vale rules.")
459496
}
460-
if selected.docsBuilder {
461-
toolFailures = append(toolFailures, installOptionalTools(r, false, true, *dryRun, *force, false)...)
462-
} else {
497+
if !selected.docsBuilder {
463498
r.Info("Skipping docs-builder.")
464499
}
500+
// One call for both tools: installOptionalTools prints its own section
501+
// header, so calling it per tool printed "Installing documentation tools"
502+
// twice and read like two separate phases.
503+
toolFailures = append(toolFailures, installOptionalTools(r, selected.vale, selected.docsBuilder, *dryRun, *force, false)...)
465504
reportToolFailures(r, toolFailures)
466505
if *dryRun {
467506
r.Info("Would refresh documentation tool status after updates.")

‎cmd/elastic-docs-utils/main_test.go‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,31 @@ func TestRenderUpdateStatusShowsHintsForActionableRows(t *testing.T) {
130130
t.Fatalf("current component printed a hint:\n%s", out.String())
131131
}
132132
}
133+
134+
// The upstream installers exit 0 whether they install, skip, or are declined,
135+
// so the reported outcome has to come from the version either side of the run.
136+
func TestInstallerOutcome(t *testing.T) {
137+
cases := []struct {
138+
name string
139+
before, after string
140+
want installerOutcomeLevel
141+
contains string
142+
}{
143+
{"declined leaves version untouched", "1.57.1", "1.57.1", outcomeUnchanged, "left unchanged at 1.57.1"},
144+
{"fresh install", "", "1.58.0", outcomeInstalled, "Installed docs-builder 1.58.0"},
145+
{"real update", "1.57.1", "1.58.0", outcomeUpdated, "1.57.1 to 1.58.0"},
146+
{"binary absent afterwards", "1.57.1", "", outcomeMissing, "no docs-builder binary is on PATH"},
147+
{"absent before and after", "", "", outcomeMissing, "no docs-builder binary is on PATH"},
148+
}
149+
for _, c := range cases {
150+
t.Run(c.name, func(t *testing.T) {
151+
level, msg := installerOutcome("docs-builder", c.before, c.after)
152+
if level != c.want {
153+
t.Fatalf("level = %v, want %v (msg %q)", level, c.want, msg)
154+
}
155+
if !strings.Contains(msg, c.contains) {
156+
t.Fatalf("message %q does not contain %q", msg, c.contains)
157+
}
158+
})
159+
}
160+
}

‎internal/updates/updates.go‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,14 @@ func lookupHint(err error) string {
260260
return "Could not query GitHub to determine the latest version"
261261
}
262262

263+
// BinaryVersion reports the version a locally installed tool prints, or an
264+
// empty string when the tool is absent or prints nothing usable. Callers use it
265+
// to confirm that an installer actually changed the binary, because the
266+
// upstream installers exit 0 whether they install, skip, or are declined.
267+
func BinaryVersion(command string, args ...string) string {
268+
return binaryVersion(command, args...)
269+
}
270+
263271
func binaryVersion(command string, args ...string) string {
264272
path, err := exec.LookPath(command)
265273
if err != nil {

0 commit comments

Comments
 (0)