Azure / Azure/azure-dev

azd up: postdeploy/predeploy hook output silently discarded (incomplete fix of #8237); long-running postdeploy looks like a hang

Open
#9,073 0 comments 1 reaction 2 assignees Claimed by @jongio View on GitHub
area/hooks area/ux bug
Dominant language
Go
Stars
569
Forks
364
Avg merge
2d 19h
Merged PRs (30d)
136

Description

### Output from `azd version`

`azd version 1.27.1 (commit 257908961d465e98c1a6574eae9624a7954b21cf)`

### Summary

During `azd up`, output from **deploy-phase** command hooks — `postdeploy` (and `predeploy`) — is silently discarded. The hooks **execute** (side effects happen), but produce **zero** visible output and no progress indication. For a long-running `postdeploy` hook, `azd up` appears to **hang** after the deploy table with nothing on screen.

This is the same class of bug as #8237, whose fix was **incomplete**: it restored visibility for **provision-phase** hooks (`preprovision`/`postprovision`) only. Deploy-phase hooks are still swallowed.

### Root cause

1. The deploy progress tracker pauses the previewer for the whole graph run: `startDeployTicker` → `PausePreviewer()` (`cli/azd/internal/cmd/up_graph.go:517-518`). It is only resumed *after* the graph finishes: `RunWithResult` (`up_graph.go:580`) → `stopTicker()`/`ResumePreviewer()` (`up_graph.go:583-585`).
2. While suppressed, `ShowPreviewer` returns `io.Discard` (`cli/azd/pkg/input/console.go:345-348`).
3. Non-interactive command hooks route stdout to that writer: `execCtx.StdOut = h.console.ShowPreviewer(...)` (`cli/azd/pkg/ext/hooks_runner.go:329-338`). So the hook's stdout is `io.Discard`.
4. `cmdhook-postdeploy` is the **last** graph step (`up_graph.go:473-485`) and runs **inside** `RunWithResult` — i.e., while the previewer is still suppressed. Result: its output is discarded.

Why #8237's fix misses this: the fix keeps the previewer un-paused during the *provision + hook* phases (see the comment at `up_graph.go:509-510`, "preprovision/postprovision hook output remains visible (fixes #8237)"). But the ticker pauses the previewer as soon as the first `publish`/`deploy` step starts, and `predeploy`/`postdeploy` hooks run within that paused window.

Additionally, the deploy tracker only updates for `package-`/`publish-`/`deploy-` steps (`up_graph.go:542-577`), not `cmdhook-*` steps, so there isn't even a spinner/row indicating the hook is running.

### Impact

- No output for `postdeploy`/`predeploy` hooks in `azd up` (they work fine under standalone `azd deploy`).
- **Long-running** `postdeploy` hooks (e.g. a DB-bootstrap/migration Container Apps job that polls to completion) make `azd up` look hung — deploy table reads `Done`, then a blank screen for minutes with no indication anything is happening.

### To reproduce

```yaml
# azure.yaml
hooks:
postdeploy:
posix:
shell: sh
run: scripts/postdeploy.sh # e.g. echo lines + a long sleep
```

1. `azd up` (with at least one service so the deploy tracker/ticker starts).
2. Deploy table shows all services `Done`; the `postdeploy` hook then runs with **no** visible output and no progress indicator.
3. Contrast: `preprovision` output *is* visible (it runs before the ticker pauses the previewer), and the same `postdeploy` hook *is* visible under standalone `azd deploy`.

### Expected

Deploy-phase hook output should be visible during `azd up`, matching standalone `azd deploy` and the provision-phase hooks — at minimum a clear "Running postdeploy hook…" progress indicator so long-running hooks don't look like a hang.

### Proposed fix (options)

- Since `postdeploy` is the final step and all `publish`/`deploy` steps are already complete when it runs, finalize the deploy table and `ResumePreviewer()` **before** executing `cmdhook-postdeploy`, so its output streams like provision-phase hooks.
- More generally, wrap deploy-phase command-hook steps so their output routes to the console's default writer (not the suppressed previewer) and/or briefly resume the previewer around the hook.
- Regardless of output routing, add a progress row/spinner for `cmdhook-*` steps in the deploy tracker so a running hook is always visible.

### Workaround

Set `interactive: true` on the hook (e.g. `postdeploy`). Interactive hooks bypass the `ShowPreviewer` path (`hooks_runner.go:326-338`) and stream directly to the terminal, so output is visible (though it may interleave with the deploy table).

### Related

- #8237 (closed) — original "hook output silently discarded during `azd up` due to PausePreviewer"; its fix covered provision-phase hooks only. This issue tracks the remaining deploy-phase (`predeploy`/`postdeploy`) case.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.