Skip to content

fix: detached runs, non-interactive port prompts, test command reporting - #565

Merged
jongio merged 2 commits into
mainfrom
fix/detach-port-testcmd
Jul 27, 2026
Merged

fix: detached runs, non-interactive port prompts, test command reporting#565
jongio merged 2 commits into
mainfrom
fix/detach-port-testcmd

Conversation

@jongio

@jongio jongio commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Description

Three reports share one theme: azd app works when a human is watching an interactive terminal, and misbehaves the moment nobody is.

#555 azd app run --detach dies immediately on Windows. This turned out to be five independent causes:

  • The detached child stays inside the parent's Job Object. DETACHED_PROCESS does not remove it, so a kill-on-close job takes the child down after CreateProcess succeeds but before it writes anything, which is exactly why run.log existed and was empty. The spawn now requests CREATE_BREAKAWAY_FROM_JOB first and falls back to the old flags on ERROR_ACCESS_DENIED, with a fresh exec.Cmd per attempt.
  • Run state was written only after every service started and after a 10s dashboard wait, so status and stop had no PID. It is now seeded right after cmd.Start().
  • The child re-ran LoadAzdEnvironment, shelling out to azd env get-values against an azd host that was already exiting. First bytes in run.log went from ~1590ms to ~328ms once skipped.
  • Parent and child keyed run state by different directories, so stop looked in a third place.
  • stop removed run state from an unconditional defer, discarding the only PID record when it failed.

#556 The port-conflict prompt turned io.EOF into a fatal error even though a non-interactive fallback already existed. readPromptLine now separates EOF with partial data (honour it) from EOF with nothing (fall through to the documented auto-kill path), and still discards partial data on a genuine read error.

#557 Execution was already correct; the reporting was not. Validation had no idea which type was requested, so a service with both framework: vitest and an explicit test.e2e.command was reported as vitest detected. Validation now takes the requested type, reports the effective command, and reports a type it would silently skip as skipped rather than passed.

Full root-cause analysis, evidence tables, trade-offs and acceptance criteria: docs/specs/detach-port-testcmd/spec.md

Closes #555
Closes #556
Closes #557

Checklist

  • All tests pass
  • Documentation updated
  • CHANGELOG.md updated
  • Follows code style guidelines

Testing

39 packages, 0 failures. mage lint 0 issues. gofmt and go vet clean. Cross-compiles for linux/amd64, darwin/arm64 and windows/amd64. Roughly 840 lines of tests to 460 of source.

Every test that guards a fix was mutation-tested: the fix was temporarily reverted and the test confirmed to fail, then restored, so none of them are tautological.

#556 is covered end to end by pointing stdin at the OS null device, which is a character device that yields EOF instantly and so reproduces the exact reported condition. A pipe cannot, because it fails the interactive guard before reaching the crash site.

Quality Gates

Gate Status Notes
mq (full pipeline) PASS Four parallel audit agents, verified at 99b1fc7
cr (code-review) PASS Quad coverage. CR-001 withdrawn on evidence, CR-002/003/004 fixed
pf (preflight) PASS gofmt, build, vet, lint, full suite
secops (security) PASS Evidence-backed re-run. One real finding (detach marker leaking into every service and hook) fixed
th (test-health) PASS Closed the two coverage gaps on the user-visible fixes

Notes for reviewers

  • A temp-file plus rename for runstate.Write was tried and deliberately reverted. Windows refuses to rename over a file another handle has open, so it converts a rare, self-correcting short read into a hard write failure that loses the PID. A bounded retry still starved under a sustained reader. Evidence is in the spec trade-offs.
  • OS-enforced service teardown is deliberately out of scope. An architecture review found that attaching a kill-on-close job from the shared service start path would kill services started by the short-lived azd app start and tie MCP-started services to the MCP host lifetime. A follow-up issue will capture it.
  • status_test.go restores the status command's package-level flag vars. They leaked across runs and broke go test -count=2 in the very package this PR adds tests to. A second pre-existing -count=2 defect in internal/service is documented in the test plan and left alone to keep this diff scoped.
  • This touches run.go and status_test.go, which several open idea/* PRs also touch (feat(status): add exit-code flag #563, feat(run): add no-deps repeat start option #544, feat(run): add --except to run every service but the named ones #513, feat(status): show uptime in azd app status #502, feat(run): add --env for inline environment overrides #498). No semantic overlap, mechanical conflicts only.

jongio and others added 2 commits July 25, 2026 21:58
…test commands

Three reports share one theme: azd app works when a human is watching an
interactive terminal, and misbehaves the moment nobody is.

#555 azd app run --detach dies immediately on Windows. This turned out to be
five independent causes, each able to produce part of the report:

* The detached child stays inside the parent's Job Object. DETACHED_PROCESS
  does not remove it, so a kill-on-close job takes the child down after
  CreateProcess succeeds but before it writes anything, which is exactly why
  run.log existed and was empty. The spawn now tries CREATE_BREAKAWAY_FROM_JOB
  first and falls back to the previous flags on ERROR_ACCESS_DENIED, building a
  fresh exec.Cmd per attempt since a failed one cannot be restarted.
* Run state was written only after every service started and after a 10s
  dashboard wait, so status and stop had no PID to work with. It is now seeded
  immediately after cmd.Start(), and a persist failure does not fail the run.
* The child re-ran LoadAzdEnvironment, which shells out to azd env get-values
  against an azd host that is already exiting. First bytes in run.log dropped
  from ~1590ms to ~328ms once skipped. The marker that identifies the child is
  now consumed at startup so services, hooks, and nested runs do not inherit it.
* Parent and child keyed run state by different directories, so running from a
  subdirectory produced two state files and stop looked in a third place. cwd
  and azureYamlDir are collapsed into a single projectDir.
* stop removed run state from an unconditional defer, discarding the only PID
  record when it failed. It now branches on manager liveness.

#556 The port-conflict prompt turned io.EOF into a fatal error even though a
non-interactive fallback already existed. readPromptLine now separates EOF with
partial data (honour it, since ReadString returns both when the final answer has
no trailing newline) from EOF with nothing (fall through to the documented
auto-kill path), and still discards partial data on a genuine read error rather
than letting a truncated read select a destructive menu entry.

#557 Execution was already correct on main; the reporting was not. Validation
had no idea which type was requested, so a service with both framework: vitest
and an explicit test.e2e.command was reported as "vitest detected". Validation
now takes the requested type, records the effective command, and reports a type
it would silently skip as skipped rather than passed.

Also restores the status command's package-level flag vars in its tests. Those
leaked across runs and broke go test -count=2 in the very package these changes
add tests to.

Fixes #555
Fixes #556
Fixes #557

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ixes

Covers the user-visible behaviour changes from #555, #556 and #557 under
the existing Unreleased / Fixed heading.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

🚀 Website Preview

Your PR preview was available here.

Preview has been cleaned up as the PR was closed.

github-actions Bot added a commit that referenced this pull request Jul 26, 2026
@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.50273% with 43 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.33%. Comparing base (4a4de62) to head (99b1fc7).

Files with missing lines Patch % Lines
cli/src/cmd/app/commands/run_orchestration.go 0.00% 15 Missing ⚠️
cli/src/internal/testing/orchestrator.go 40.00% 9 Missing ⚠️
cli/src/cmd/app/commands/run_detach.go 88.88% 4 Missing and 2 partials ⚠️
cli/src/cmd/app/commands/stop.go 60.00% 5 Missing and 1 partial ⚠️
...li/src/internal/portmanager/portmanager_prompts.go 86.36% 3 Missing ⚠️
cli/src/internal/testing/validation.go 91.66% 1 Missing and 1 partial ⚠️
cli/src/cmd/app/commands/run.go 0.00% 1 Missing ⚠️
cli/src/cmd/app/commands/test.go 90.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #565      +/-   ##
==========================================
+ Coverage   61.09%   61.33%   +0.24%     
==========================================
  Files         224      224              
  Lines       28989    29074      +85     
==========================================
+ Hits        17711    17833     +122     
+ Misses      10082    10040      -42     
- Partials     1196     1201       +5     
Flag Coverage Δ
unittests 61.33% <76.50%> (+0.24%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
cli/src/cmd/app/commands/run_detach_unix.go 100.00% <100.00%> (ø)
cli/src/cmd/app/commands/run_detach_windows.go 100.00% <100.00%> (ø)
cli/src/internal/runstate/runstate.go 75.51% <100.00%> (ø)
cli/src/internal/testing/types.go 84.21% <100.00%> (+4.90%) ⬆️
cli/src/cmd/app/commands/run.go 11.48% <0.00%> (+0.12%) ⬆️
cli/src/cmd/app/commands/test.go 29.28% <90.00%> (+7.98%) ⬆️
cli/src/internal/testing/validation.go 92.60% <91.66%> (-0.15%) ⬇️
...li/src/internal/portmanager/portmanager_prompts.go 80.18% <86.36%> (+31.25%) ⬆️
cli/src/cmd/app/commands/run_detach.go 82.97% <88.88%> (+6.50%) ⬆️
cli/src/cmd/app/commands/stop.go 59.82% <60.00%> (-0.73%) ⬇️
... and 2 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Test This PR

A preview build (0.20.0-pr565) is ready for testing!

🌐 Website Preview

Live Preview: https://jongio.github.io/azd-app/pr/565/

One-Line Install (Recommended)

PowerShell (Windows):

iex "& { $(irm https://raw.githubusercontent.com/jongio/azd-app/main/cli/scripts/install-pr.ps1) } -PrNumber 565 -Version 0.20.0-pr565"

Bash (macOS/Linux):

curl -fsSL https://raw.githubusercontent.com/jongio/azd-app/main/cli/scripts/install-pr.sh | bash -s 565 0.20.0-pr565

Uninstall

When you're done testing:

PowerShell (Windows):

iex "& { $(irm https://raw.githubusercontent.com/jongio/azd-app/main/cli/scripts/uninstall-pr.ps1) } -PrNumber 565"

Bash (macOS/Linux):

curl -fsSL https://raw.githubusercontent.com/jongio/azd-app/main/cli/scripts/uninstall-pr.sh | bash -s 565

Build Info:

What to Test:
Please review the PR description and test the changes described there.

@jongio
jongio requested a review from wbreza July 26, 2026 17:08
@jongio
jongio merged commit 81c2806 into main Jul 27, 2026
20 checks passed
@jongio
jongio deleted the fix/detach-port-testcmd branch July 27, 2026 14:14
github-actions Bot added a commit that referenced this pull request Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant