feat(doctor): add local setup checks - #554
Conversation
|
🚀 Website Preview
Preview has been cleaned up as the PR was closed. |
Codecov Report❌ Patch coverage is
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
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
🚀 Test This PRA preview build ( 🌐 Website PreviewLive 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-pr554UninstallWhen 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 554Build Info:
What to Test: |
wbreza
left a comment
There was a problem hiding this comment.
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 conditionlen(svc.Ports) > 0requiresdockerfor any service that declares a host port — but explicit host ports are a first-class, common feature for non-container services (seeservice.ParsePortSpecnon-docker semantics andDetectPortpriority 1). On a machine without Docker this yields atool.availableFAIL and a non-zero exit for a perfectly valid project. This also contradicts theport.declaredwarning that encourages declaring ports. Docker should be required only for container/image services (svc.Image != ""orhost == "container"), not merely because ports are declared.
🟠 Medium
- Hand-rolled port parsing diverges from the canonical parser (
doctorHostPort, L225). The repo already hasservice.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 — butdoctorHostPorttreats8080as a declared host port and will emit a falseport.unique/port.validFAIL. Protocol-only differences (3000/tcpvs3000/udp) are likewise collapsed. ReuseParsePortSpec(derivingisDockerfrom host/image) and skip uniqueness whenHostPort == 0. - Python detection defaults to
python(doctorDetectPythonTool, L209). On macOS/Linux where onlypython3is on PATH — or when the service runs from a venv (seevenv_integration_test.go, which preferspython3) — this reports atool.availableFAIL and non-zero exit even thoughazd app runwould succeed. Probepython3as well (or align with the existing venv/runner detection).
🟡 Low
svc.Projectused withoutTrimSpace(doctorToolChecks, L116), inconsistent withdoctorServicePathCheckswhich trims. A whitespace-onlyprojectwould produce a bogussvcDirhere.
💡 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 (reuseService/addTool). - Patch coverage is ~28% (codecov). Core paths —
runDoctor,doctorToolChecks,doctorHostPortedge 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.
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
8cf8268 to
8f0d36c
Compare
Addressed in
The table-driven port cases cover the edge cases you listed — IPv6 ( There's also an end-to-end
|
wbreza
left a comment
There was a problem hiding this comment.
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. NewdoctorNeedsDockerhelper mirrorsservice.DetectServiceRuntime: thelen(svc.Ports) > 0clause is gone; Docker is required only for real container services (IsContainerService()/host: container), gated by aRunsAsLocalProcess()short-circuit so a container-image service opting into a localcommand:/process is correctly exempt. UsingIsContainerService()over a rawImage != ""check also catchesdocker.image:services. Locked byTestDoctorNeedsDocker(8 cases) +TestDoctorToolChecksDockerRequirement. - 🟠 Medium — duplicate port parser → Fixed.
doctorPortChecksnow callsservice.ParsePortSpec(mapping, isDocker), skips uniqueness whenHostPort == 0(container auto-assign), and keys conflicts onport/protocolsotcp/udpcoexist.doctor_ports_test.gocovers container-auto-assign, protocol coexistence, explicit-mapping conflict, IPv6/bind-IP, and range/parse failures. - 🟠 Medium —
pythonvspython3→ Fixed.doctorPythonRequirementacceptspythonORpython3as candidates, returns no requirement when a venv interpreter is present, and defers todetector.DetectPythonPackageManagerfor uv/poetry/pipenv. Covered byTestDoctorPythonRequirement*andTestDoctorVenvPython. - 🟡 Low — untrimmed
svc.Project→ Fixed (strings.TrimSpaceindoctorToolChecks), verified byTestDoctorToolChecksTrimsProjectPath. - 💡 Improvement — tool identity in JSON → Addressed. New
Toolfield ondoctorChecknames the probed executable; covered byTestDoctorToolCheckJSONIncludesTool.
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>
wbreza
left a comment
There was a problem hiding this comment.
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) mergesmain;doctor.goand its three test files are byte-for-byte unchanged since the approved fix (8f0d36c). The sole touch to this PR's authored surface ismain.goregisteringNewOpenCommand()adjacent toNewDoctorCommand(), a conflict-free integration of a feature already onmain. - Prior findings stay resolved — the High (
ports:forcing Docker viadoctorNeedsDocker), both Mediums (canonicalParsePortSpecreuse;python/python3+ venv/detector alignment), the Low (TrimSpace(svc.Project)), and the JSONToolfield 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>
wbreza
left a comment
There was a problem hiding this comment.
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) mergesmain;doctor.goand its three test files are byte-for-byte unchanged since the approved fix (8f0d36c). The sole touch to this PR's authored surface ismain.goregisteringNewRemoveCommand()andNewHooksCommand()adjacent toNewDoctorCommand()— a conflict-free integration of two commands already landed onmain. - Prior findings stay resolved — the High (
ports:forcing Docker viadoctorNeedsDocker), both Mediums (canonicalParsePortSpecreuse;python/python3+ venv/detector alignment), the Low (TrimSpace(svc.Project)), and the JSONToolfield 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.
wbreza
left a comment
There was a problem hiding this comment.
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) mergesmain;doctor.goand its three test files are byte-for-byte unchanged since the approved fix (8f0d36c). The sole touch to this PR's authored surface ismain.goregisteringNewConfigCommand(),NewValidateCommand(), andNewPortsCommand()adjacent toNewDoctorCommand()— a conflict-free integration of commands already landed onmain. - Prior findings stay resolved — the High (
ports:forcing Docker viadoctorNeedsDocker), both Mediums (canonicalParsePortSpecreuse;python/python3+ venv/detector alignment), the Low (TrimSpace(svc.Project)), and the JSONToolfield 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.
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.
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'