fix(hooks): restore lifecycle hook output visibility during azd up#8263
fix(hooks): restore lifecycle hook output visibility during azd up#8263vhvb1989 wants to merge 3 commits into
Conversation
PausePreviewer() was being called at graph build time in up_graph.go, before any steps execute. This caused ShowPreviewer() to return io.Discard for the entire Run() duration, silencing all lifecycle hook output (preprovision, postprovision, predeploy, postdeploy) during �zd up. The fix moves PausePreviewer() into startDeployTicker (called via tickerOnce.Do when the first publish or deploy step begins). The previewer is now only suppressed during the publish/deploy phase when the progress table is actually rendering — not during the earlier provision and hook phases. Adds a regression test (TestAskerConsole_PausePreviewer_DiscardsHookOutput) documenting the broken behavior and the PreviewerPauser mechanism. Fixes #8237. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
- Move PausePreviewer() before StartTicker() to close race window - Reword test assert message to clarify PausePreviewer behavior vs bug Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
hemarina
left a comment
There was a problem hiding this comment.
Approving — targeted fix, 29 LOC of production change, root cause correctly identified. Moving PausePreviewer() from graph-build time into startDeployTicker (gated by tickerOnce.Do on the first publish-*/deploy-* step) is the right place for it: preprovision/postprovision/predeploy now run with the previewer live. Closure capture of stop, sync.Once ordering, and the post-RunWithResult cleanup at line 565 all look correct.
One thing worth addressing (can be follow-up)
TestAskerConsole_PausePreviewer_DiscardsHookOutput covers the mechanism, not the bug. The assertions only exercise AskerConsole.PausePreviewer / ShowPreviewer / ResumePreviewer — behavior that was never broken. Reverting the up_graph.go change leaves this test green. The test's own comment acknowledges this: "The actual bug (#8237) was that azd up called PausePreviewer too early (before hooks), not that PausePreviewer suppresses output."
For genuine regression coverage, a test should drive UpGraphAction.Run (or a narrower seam) with a graph containing a hook-style step before any deploy step, and assert that ShowPreviewer returns a non-io.Discard writer at hook-execution time. Without that, this exact bug can regress silently next time up_graph.go is refactored.
Suggestions (non-blocking)
-
Resume on panic. Pre-fix code used
defer ps.ResumePreviewer()atRunscope, so it fired even if the executor panicked. New code only resumes insidestopTicker()at line 565. Practical impact is small (the process is dying), but defending against it is a one-liner — e.g.defer func() { if stopTicker != nil { stopTicker() } }()instead of the explicit post-RunWithResultcall. -
Track the
postdeployfollow-up. PR body notespostdeployis still suppressed because it runs as a graph step beforeRunWithResultreturns. Fine scoping decision for this PR — please link a tracked issue (under #8237 or fresh) for the graph-step approach so the remaining 1/4 doesn't get lost.
Confirmed correct
- Root-cause attribution to PR #7776 / early
PausePreviewerinup_graph.go. tickerOnce.Do(startDeployTicker)ensures a single pause regardless of which ofpublish-*/deploy-*fires first.if deployTracker == nil { return }preserves the prior nil-check.stopTickerread afterRunWithResultis safe via theOncehappens-before barrier.elsebranch (noPreviewerPauserimpl) correctly skips the pause/resume wrap.
Summary
Fixes #8237 — lifecycle hook output (
preprovision,postprovision,predeploy) is restored duringazd up.Note:
postdeployhook output is still suppressed becausepostdeployruns as a graph step beforeRunWithResultreturns (while the deploy progress ticker is still active). Restoringpostdeployoutput requires adding a dedicated graph step to stop the ticker between deploy and postdeploy — tracked as a follow-up.Root Cause
Introduced in 1.25.0 by PR #7776 (the exegraph rewrite). In
up_graph.go,PausePreviewer()was called at graph build time, before any steps execute:This set
previewerSuppressed = truefor the entireRun()duration. When lifecycle hooks later calledShowPreviewer()inhooks_runner.go, they receivedio.Discard— silently discarding all hook stdout.Pre-1.25.0:
azd upused aworkflow.Runnerspawningazd provision+azd deployas sub-processes.PausePreviewerdid not exist — hooks always got real writers.Fix
Move
PausePreviewer()intostartDeployTicker, which is called viatickerOnce.Doonly when the first publish or deploy step begins (i.e., when the deploy progress table is actually rendering). The previewer is no longer suppressed during the provision + hook phases.Testing
TestAskerConsole_PausePreviewer_DiscardsHookOutputtopkg/input/console_test.godocumenting thePreviewerPausermechanism and the window during which hooks must not be suppressed.