Skip to content

feat(doctor): add local setup checks - #554

Merged
jongio merged 5 commits into
mainfrom
idea/local-setup-doctor
Jul 29, 2026
Merged

feat(doctor): add local setup checks#554
jongio merged 5 commits into
mainfrom
idea/local-setup-doctor

Conversation

@jongio

@jongio jongio commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Adds azd app doctor as a read-only setup checklist. It checks project discovery, azure.yaml parsing, service project paths, required tools, declared ports, and dashboard state with text and JSON output.

Closes #462

Validation: go test ./src/cmd/app/commands -run 'TestDoctor'

@jongio jongio added the idea Feature idea from the idea pipeline label Jul 24, 2026
@jongio jongio self-assigned this Jul 24, 2026
@github-actions

github-actions Bot commented Jul 24, 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 24, 2026
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.20379% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.91%. Comparing base (0f7326a) to head (2bc149f).

Files with missing lines Patch % Lines
cli/src/cmd/app/commands/doctor.go 87.20% 21 Missing and 6 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #554      +/-   ##
==========================================
+ Coverage   62.73%   62.91%   +0.18%     
==========================================
  Files         232      233       +1     
  Lines       30627    30838     +211     
==========================================
+ Hits        19214    19402     +188     
- Misses      10099    10117      +18     
- Partials     1314     1319       +5     
Flag Coverage Δ
unittests 62.91% <87.20%> (+0.18%) ⬆️

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/doctor.go 87.20% <87.20%> (ø)

... and 2 files with indirect coverage changes

🚀 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

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

🚀 Test This PR

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

🌐 Website Preview

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

One-Line Install (Recommended)

PowerShell (Windows):

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

Bash (macOS/Linux):

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

Uninstall

When you're done testing:

PowerShell (Windows):

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

Bash (macOS/Linux):

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

Build Info:

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

@wbreza wbreza left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — feat(doctor): add local setup checks

Nice, self-contained read-only checklist that closes #462. The command structure, severity model, deterministic sorting, and text/JSON split are clean. I found one High and two Medium correctness issues (all around false-positive FAILs that make the command exit non-zero on valid setups), plus a couple of minor/improvement notes. Inline comments below.

Findings

🔴 High

  • Declaring ports: forces a Docker requirement (doctorToolChecks, L137). The condition len(svc.Ports) > 0 requires docker for any service that declares a host port — but explicit host ports are a first-class, common feature for non-container services (see service.ParsePortSpec non-docker semantics and DetectPort priority 1). On a machine without Docker this yields a tool.available FAIL and a non-zero exit for a perfectly valid project. This also contradicts the port.declared warning that encourages declaring ports. Docker should be required only for container/image services (svc.Image != "" or host == "container"), not merely because ports are declared.

🟠 Medium

  • Hand-rolled port parsing diverges from the canonical parser (doctorHostPort, L225). The repo already has service.ParsePortSpec(spec, isDocker). For a container service with a single-port spec (e.g. ports: ["8080"]), the runtime treats it as container-only with an auto-assigned host port (HostPort=0), so two such services never conflict — but doctorHostPort treats 8080 as a declared host port and will emit a false port.unique/port.valid FAIL. Protocol-only differences (3000/tcp vs 3000/udp) are likewise collapsed. Reuse ParsePortSpec (deriving isDocker from host/image) and skip uniqueness when HostPort == 0.
  • Python detection defaults to python (doctorDetectPythonTool, L209). On macOS/Linux where only python3 is on PATH — or when the service runs from a venv (see venv_integration_test.go, which prefers python3) — this reports a tool.available FAIL and non-zero exit even though azd app run would succeed. Probe python3 as well (or align with the existing venv/runner detection).

🟡 Low

  • svc.Project used without TrimSpace (doctorToolChecks, L116), inconsistent with doctorServicePathChecks which trims. A whitespace-only project would produce a bogus svcDir here.

💡 Improvement Opportunities (non-blocking)

  • All tool checks share checkId: "tool.available" (L151), so JSON consumers can't tell which tool failed without parsing the message string. Consider a dedicated field (reuse Service/add Tool).
  • Patch coverage is ~28% (codecov). Core paths — runDoctor, doctorToolChecks, doctorHostPort edge cases (IPv6, /tcp, single vs mapped), doctorDashboardCheck, and the render/JSON output — are untested. A few more table-driven cases would lock in the behavior above.

Comment thread cli/src/cmd/app/commands/doctor.go Outdated
Comment thread cli/src/cmd/app/commands/doctor.go Outdated
Comment thread cli/src/cmd/app/commands/doctor.go Outdated
Comment thread cli/src/cmd/app/commands/doctor.go Outdated
Comment thread cli/src/cmd/app/commands/doctor.go Outdated
jongio and others added 2 commits July 27, 2026 22:20
Closes #462

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

Copilot-Session: 340b4dd3-b4b3-4f1a-9163-66b16d96fa81
Declaring `ports:` no longer forces a docker requirement: a new
doctorNeedsDocker helper mirrors service.DetectServiceRuntime and requires
Docker only for services actually started as containers.

Replace the hand-rolled doctorHostPort parser with service.ParsePortSpec so
container auto-assigned host ports (HostPort=0) are skipped for uniqueness and
protocol-only differences no longer collapse into a spurious conflict. An
explicit 1..65535 guard is kept because ParsePortSpec does not range-check.

Align Python detection with detector.DetectPythonPackageManager, treat a venv as
satisfying the interpreter requirement, and accept python or python3 so
python3-only systems no longer report a false failure.

Trim service project paths consistently in both doctorToolChecks and
doctorServicePathChecks, and add a Tool field so JSON consumers can identify
which tool a tool.available check probed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 125d6b2a-9edd-42a3-85da-e95cbab6f5e6
@jongio
jongio force-pushed the idea/local-setup-doctor branch from 8cf8268 to 8f0d36c Compare July 28, 2026 05:23
github-actions Bot added a commit that referenced this pull request Jul 28, 2026
@jongio

jongio commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Patch coverage is ~28% (codecov). Core paths — runDoctor, doctorToolChecks, doctorHostPort edge cases (IPv6, /tcp, single vs mapped), doctorDashboardCheck, and the render/JSON output — are untested. A few more table-driven cases would lock in the behavior above.

Addressed in 8f0d36ce — added doctor_ports_test.go and doctor_tools_test.go. Per-function coverage for doctor.go is now:

function coverage
runDoctorChecks 100%
doctorPortChecks 100%
doctorNeedsDocker 100%
doctorVenvPython 100%
doctorDetectNodePackageManager / doctorDetectJavaTool 100%
renderDoctorChecks 100%
sortDoctorChecks / countDoctorSeverity 100%
doctorPythonRequirement 85.7%
doctorToolChecks 79.5%
doctorServicePathChecks 78.9%
doctorDashboardCheck 75%
runDoctor 62.5%
NewDoctorCommand 100%

The table-driven port cases cover the edge cases you listed — IPv6 ([::1]:3000:8080), bind-IP (127.0.0.1:3000:8080), /tcp vs /udp, single vs mapped, container auto-assign, out-of-range, zero, negative, and unparseable — and each of the four correctness findings has a regression test that fails against the previous implementation.

There's also an end-to-end TestRunDoctorChecksEndToEnd that drives runDoctorChecks and runDoctor against a real temp project, exercising the dashboard check, the render path, JSON marshalling, fail-first ordering, and the non-zero exit.

doctorHostPort no longer appears in the list because it was deleted in favor of service.ParsePortSpec (see the inline thread).

@jongio
jongio requested a review from wbreza July 28, 2026 05:27

@wbreza wbreza left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — feat(doctor): add local setup checks ✅ Approve

All five findings from my previous review (SHA 8cf8268) are resolved in 8f0d36ce, with correct fixes and comprehensive regression tests. Nice work.

Prior findings — all resolved

  • 🔴 High — ports: forcing Docker → Fixed. New doctorNeedsDocker helper mirrors service.DetectServiceRuntime: the len(svc.Ports) > 0 clause is gone; Docker is required only for real container services (IsContainerService() / host: container), gated by a RunsAsLocalProcess() short-circuit so a container-image service opting into a local command:/process is correctly exempt. Using IsContainerService() over a raw Image != "" check also catches docker.image: services. Locked by TestDoctorNeedsDocker (8 cases) + TestDoctorToolChecksDockerRequirement.
  • 🟠 Medium — duplicate port parser → Fixed. doctorPortChecks now calls service.ParsePortSpec(mapping, isDocker), skips uniqueness when HostPort == 0 (container auto-assign), and keys conflicts on port/protocol so tcp/udp coexist. doctor_ports_test.go covers container-auto-assign, protocol coexistence, explicit-mapping conflict, IPv6/bind-IP, and range/parse failures.
  • 🟠 Medium — python vs python3 → Fixed. doctorPythonRequirement accepts python OR python3 as candidates, returns no requirement when a venv interpreter is present, and defers to detector.DetectPythonPackageManager for uv/poetry/pipenv. Covered by TestDoctorPythonRequirement* and TestDoctorVenvPython.
  • 🟡 Low — untrimmed svc.Project → Fixed (strings.TrimSpace in doctorToolChecks), verified by TestDoctorToolChecksTrimsProjectPath.
  • 💡 Improvement — tool identity in JSON → Addressed. New Tool field on doctorCheck names the probed executable; covered by TestDoctorToolCheckJSONIncludesTool.

Patch coverage is also substantially improved — the added doctor_ports_test.go, doctor_tools_test.go, and expanded doctor_test.go now exercise the tool, port, Python/venv, render, sort, and end-to-end paths.

Verification

CI is green on HEAD (8f0d36c): build, Lint, CodeQL/Analyze, Go vulnerability check, and pnpm audit all pass. (Preflight Checks was still in progress at review time — non-blocking; all substantive gates passed.)

No new Critical/High/Medium issues. Approving.

Resolves main.go command registration conflict by keeping all command registrations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
github-actions Bot added a commit that referenced this pull request Jul 28, 2026

@wbreza wbreza left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at edc86fb · HEAD edc86fb · in sync

Re-review after a clean merge of main — no changes to the doctor implementation; all five prior findings remain resolved. Approving.

What stood out

  • Merge is clean and scope-preserving — the only new commit (edc86fb) merges main; doctor.go and its three test files are byte-for-byte unchanged since the approved fix (8f0d36c). The sole touch to this PR's authored surface is main.go registering NewOpenCommand() adjacent to NewDoctorCommand(), a conflict-free integration of a feature already on main.
  • Prior findings stay resolved — the High (ports: forcing Docker via doctorNeedsDocker), both Mediums (canonical ParsePortSpec reuse; python/python3 + venv/detector alignment), the Low (TrimSpace(svc.Project)), and the JSON Tool field are all still in place and locked by their regression tests.
  • Green against the updated base — build, Lint, CodeQL/Analyze, Go Vulnerability Check, Build Dashboard, and dependency audits pass on the merged tree, confirming the doctor code still compiles and its tests pass against current main (Preflight Checks pending — non-blocking, same posture as the prior approving round).

No new Critical, High, or Medium issues introduced by the merge.

Resolves the command registration conflict in main.go by keeping both
this branch's NewDoctorCommand and the commands that landed on main.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
github-actions Bot added a commit that referenced this pull request Jul 28, 2026

@wbreza wbreza left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at fd16643 · HEAD fd16643 · in sync

Re-review after a second clean merge of main — no changes to the doctor implementation; all prior findings remain resolved. Approving.

What stood out

  • Merge is clean and scope-preserving — the only new commit (fd16643) merges main; doctor.go and its three test files are byte-for-byte unchanged since the approved fix (8f0d36c). The sole touch to this PR's authored surface is main.go registering NewRemoveCommand() and NewHooksCommand() adjacent to NewDoctorCommand() — a conflict-free integration of two commands already landed on main.
  • Prior findings stay resolved — the High (ports: forcing Docker via doctorNeedsDocker), both Mediums (canonical ParsePortSpec reuse; python/python3 + venv/detector alignment), the Low (TrimSpace(svc.Project)), and the JSON Tool field are all still in place and locked by their regression tests.
  • Compiles against the updated base — build, Go Vulnerability Check, Build Dashboard, and dependency audits pass on the merged tree, confirming the doctor code still compiles cleanly against current main (Lint / Preflight / Analyze pending — non-blocking, same posture as the prior approving rounds).

No new Critical, High, or Medium issues introduced by the merge.

Resolves the command registration conflict in cli/src/cmd/app/main.go.
Both sides added a registration to the same slice: main gained
NewValidateCommand from #553 and this branch adds NewDoctorCommand.
Kept both, with the main side left in place so this branch contributes a
single added line.
github-actions Bot added a commit that referenced this pull request Jul 29, 2026

@wbreza wbreza left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 2bc149f · HEAD 2bc149f · in sync

Re-review after another clean merge of main — no changes to the doctor implementation; all prior findings remain resolved. Approving.

What stood out

  • Merge is clean and scope-preserving — the only new commit (2bc149f) merges main; doctor.go and its three test files are byte-for-byte unchanged since the approved fix (8f0d36c). The sole touch to this PR's authored surface is main.go registering NewConfigCommand(), NewValidateCommand(), and NewPortsCommand() adjacent to NewDoctorCommand() — a conflict-free integration of commands already landed on main.
  • Prior findings stay resolved — the High (ports: forcing Docker via doctorNeedsDocker), both Mediums (canonical ParsePortSpec reuse; python/python3 + venv/detector alignment), the Low (TrimSpace(svc.Project)), and the JSON Tool field are all still in place and locked by their regression tests.
  • Compiles against the updated base — build, Go Vulnerability Check, Build Dashboard, and dependency audits pass on the merged tree, confirming the doctor code still compiles cleanly against current main (Lint / Preflight / Analyze pending — non-blocking, same posture as the prior approving rounds).

No new Critical, High, or Medium issues introduced by the merge.

@jongio
jongio merged commit 6fb260f into main Jul 29, 2026
20 checks passed
github-actions Bot added a commit that referenced this pull request Jul 29, 2026
jongio added a commit that referenced this pull request Jul 29, 2026
PR #554 merged the doctor command into main with 945 lines of code and
zero documentation. Merging main into this branch made the docs gate fail
with exactly three non-skippable findings: command-undocumented,
command-missing-overview, and command-missing-detail-doc.

This is the first time the gate has caught a real regression on freshly
merged code rather than on a synthetic negative test.

Fixes those three findings:

- Add cli/docs/commands/doctor.md, written from doctor.go. Covers the
  severity ordering, the eleven check IDs, how required tools are derived
  per language, port-conflict semantics, and the exit codes.
- Add the azd app doctor section and Commands Overview row to
  cli/docs/cli-reference.md. The Flags table documents --output as a
  parseable row rather than prose, which the website generator requires.
- Generate web/src/pages/reference/cli/doctor.astro and its index card.

Docs gate now passes at 35 commands. The website validator reads 31
commands and generates 33 pages.
@jongio jongio mentioned this pull request Jul 29, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

idea Feature idea from the idea pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a local setup doctor command

2 participants