[vm-repair] Fetch scripts from the library's main branch, fix and validate --preview (2.4.2) - #10326
Conversation
Adds a README section listing which NVMe platform-migration scenarios the extension can help with today, so an unsupported combination is not mistaken for a supported one. Only the two read-only readiness detectors are listed as available; recovery is marked not available yet.
|
Hi Edwin Bernal Microsoft (@EdwinBernal1), |
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
🟡 Changes recommended
The Windows example uses an unavailable default map URL, and README wording needs clarification.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Documentation-only update adding NVMe readiness guidance and releasing vm-repair 2.4.2.
Changes:
- Adds Windows and Linux readiness detector examples to CLI help.
- Documents platform-migration scenarios and controller guidance.
- Updates version and changelog metadata.
File summaries
| File | Summary |
|---|---|
src/vm-repair/setup.py |
Updates version from 2.4.1 to 2.4.2. |
src/vm-repair/README.md |
Documents NVMe migration scenarios; wording clarifications requested. |
src/vm-repair/HISTORY.rst |
Adds the 2.4.2 release entry. |
src/vm-repair/azext_vm_repair/_help.py |
Adds readiness examples; the default map URL issue requires correction. |
Review details
Suppressed comments (2)
src/vm-repair/README.md:51
- The implementation does not prefer SCSI for every source VM: it only queries the repair SKU's controller capabilities and pins
SCSIwhen the source controller isNVMe; otherwise it leaves the platform default. This unconditional wording can lead users migrating from a SCSI source to assume the repair VM was forced to SCSI when it was not. Scope the statement to NVMe source VMs.
`az vm repair create` selects a repair VM disk controller that can attach the copied OS disk, and
prefers SCSI whenever the repair VM size supports it, so that existing repair scripts can see the disk.
src/vm-repair/README.md:31
- Attaching the disk to a repair VM only makes offline inspection possible; it does not repair an NVMe boot-driver problem. This is especially misleading because the table below says boot-driver recovery is not available yet. Please describe the disk as being inspected (and repaired only when a supported repair exists).
(`0x7B`) on Windows, or a dracut emergency shell on Linux. The fault is inside the guest OS disk, so it
is repaired by attaching that disk to a repair VM.
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
vm-repair |
Azure/repair-script-library renamed its default branch from master to main. The default map URL and the --preview help text still named master, which only resolves through a rename redirect that GitHub can withdraw; if it were withdrawn, every run-id lookup and list-scripts call would fail. Also corrects two README statements: attaching the OS disk to a repair VM enables inspection rather than repair on its own, and the SCSI repair VM pin applies only when the source VM uses NVMe.
|
Review feedback addressed in 7322aaa. Summary of every point raised: 1. Default map URL still targets
|
There was a problem hiding this comment.
🔵 Needs a closer look
The Linux driver still downloads from master while map resolution uses main; the documented clarifications also remain outstanding.
Review details
Suppressed comments (3)
src/vm-repair/HISTORY.rst:8
- This release note overstates the impact of losing the
masterredirect: commands that pass--previewreplace the default map URL before fetching it, so they would continue to use their fork. Limit the statement to invocations using the default map (or explicitly exclude--preview) so the changelog accurately describes the compatibility impact.
Scripts are now looked up from the ``main`` branch of the repair script library instead of ``master``. The library renamed its default branch, and the old name only kept working through a redirect that can be withdrawn at any time; if that happened, every ``az vm repair run --run-id`` and ``az vm repair list-scripts`` invocation would fail to find any script. The set of available scripts is unchanged.
src/vm-repair/README.md:43
- These detectors are read-only with respect to the attached source OS disk, but they still write logs and evidence files on the repair VM (the Windows script uses the Public desktop and the Linux script uses
/tmp). The unqualified “They make no changes” is broader than the actual behavior; clarify that only the attached source disk is not modified.
`--run-on-repair`. They make no changes.
src/vm-repair/azext_vm_repair/repair_utils.py:23
- This switches map lookups to the canonical
mainbranch, but Linux script execution still downloadshttps://github.com/Azure/repair-script-library/tarball/master/fromazext_vm_repair/scripts/linux-run-driver.sh:13. As a result, the new Linux run-id can resolve frommainand then fail (or remain dependent on the rename redirect) when the driver downloads the script bundle. Update the Linux driver to usemainas well, and keep its branch selection aligned with the map/preview path.
REPAIR_MAP_URL = 'https://raw.githubusercontent.com/Azure/repair-script-library/main/map.json'
- Files reviewed: 7/7 changed files
- Comments generated: 0 new
- Review effort level: Lite
The preview URL is parsed positionally: fork_name and branch_name are taken at fixed offsets from the 'map.json' element. A branch name containing a slash shifts those offsets, so the fork resolves to the repository name and only the last branch segment is used - the command then downloads a map from a different repository without reporting an error. Document the constraint on both --preview arguments.
The Linux run driver downloaded the script bundle from Azure/repair-script-library/tarball/master while the Windows driver already defaulted to main and run-id resolution now uses main, so a run id could resolve from one branch and execute from another. Extends the branch regression test to cover both drivers. Also scopes the changelog claim to invocations that use the default library, since --preview replaces it, and records that the readiness detectors leave the attached source disk untouched but do write an evidence bundle on the repair VM.
|
Round 2 addressed in ceb3480. All three suppressed comments were correct — the Linux driver one is the best catch in the review, and it is a bug my previous commit would have made worse. 1. Linux driver still on
|
| Component | Branch before | Branch now |
|---|---|---|
REPAIR_MAP_URL (run-id resolution) |
main (from 7322aaa) |
main |
win-run-driver.ps1 — $repo_branch default |
main (already) |
main |
linux-run-driver.sh:13 — tarball/master/ |
master |
main |
So on Linux a run id resolved from one branch and then executed from a bundle downloaded from another. Harmless while the redirect keeps them identical, wrong the moment they diverge. Fixed.
I also extended the regression test rather than only pinning the constant — test_run_drivers_agree_with_the_map_url parses both driver scripts and asserts every declared library branch matches the map URL branch, so this specific drift cannot recur silently. Verified the regex catches tarball/master/ before concluding it guards anything.
2. HISTORY.rst overstated the --preview impact — fixed
Correct: _set_repair_map_url(preview) replaces REPAIR_MAP_URL before the fetch, so --preview users were never exposed to the redirect for map resolution. "Every invocation would fail" was wrong. The entry is now scoped to the default library and states explicitly that --preview is unaffected.
3. README — "They make no changes" was too broad — fixed
Verified in the scripts themselves:
win-detect-nvme-readiness.ps1:44-45→$env:PUBLIC\Desktop\...logandnvme-evidence-<timestamp>\, plusreg.exe exportof a temporarily loaded hivelinux-detect-nvme-readiness.sh:38→/tmp/nvme-evidence-<timestamp>
Reworded to say the attached source OS disk is not modified, and that both write a log and evidence bundle on the repair VM.
One related bug found while fixing this — flagging, not fixing here
--preview does not work on Linux at all, independently of the branch issue. custom.py appends repo_fork= / repo_branch= to run_command_params for both platforms, but only win-run-driver.ps1 declares them as parameters. linux-run-driver.sh takes the script path as $1 and forwards $*, so those tokens are never honoured and are passed through to the repair script as stray positional arguments. A Linux --preview run therefore resolves the path from the fork's map.json and then downloads the bundle from Azure/main.
Fixing that means reworking the bash driver's argument handling, which is a functional change with its own test needs and does not belong in this PR. I am happy to open a separate issue or PR for it — let me know which the maintainers prefer.
Testing
43 unit tests (up from 41; 3 pre-existing modules still skip locally for a missing pytest, unrelated). All help entries parse. No repair-script-library/...master references remain anywhere under src/vm-repair.
custom.py appended repo_fork and repo_branch for both platforms, but only win-run-driver.ps1 declared them. linux-run-driver.sh read the script path from \ and forwarded \$*, so the two values were ignored by the driver and passed to the repair script as its first two arguments, shifting every --parameters value. A Linux --preview run therefore resolved the script path from the fork's map.json and then downloaded the bundle from Azure. The driver now consumes the fork and branch positionally and builds the command from the remaining arguments; they are always sent for Linux so the positions stay stable. The map URL is derived from the same fork and branch constants the drivers default to. Verified against five argument shapes, including the custom-script path.
|
Folding the Linux The bug
The fix
While extracting the helper I also fixed a latent bug: the old guard read VerificationThe driver logic change is the risky part, so I exercised it rather than reasoning about it — a harness reproducing the argument handling, run under bash across five shapes:
The last row caught a defect in my own first attempt: with
Note for reviewersThis PR now contains a behaviour fix on a GA Linux path, not just documentation. The blast radius is the run-command parameter list and the bundle URL for Linux runs; Windows behaviour is untouched. No test recordings reference |
The preview URL is read positionally, so a branch name containing a slash shifted the owner to the repository name: --preview .../SomeUser/repair-script-library/blob/feature/nvme/map.json resolved the fork to 'repair-script-library' and made the run driver download a bundle from that organization instead. The operator saw no error and ran scripts from a repository they never named. The URL is now matched against the shape the driver actually requires, and anything else is rejected with InvalidArgumentValueError rather than a bare ValueError traceback.
|
Pushed caba26c, and refreshed the PR title and description — the old body claimed "documentation-only, no behaviour change", which stopped being true two rounds ago. One more fix, and it is the most serious thing found so farWhile adding a test for the single-segment branch constraint documented in dec9a5f, I checked what actually happens when the constraint is violated. It is worse than a footgun: The URL is read positionally, so a slash in the branch name shifts the owner onto the repository name. The operator asked for their own fork, got a bundle from an unrelated GitHub organization — one that is not controlled by Azure and could be registered by anyone — and no error was reported. Those scripts then execute on the repair VM, which is attached to the customer's OS disk.
The URL is now matched against the shape the driver actually requires — Also upgraded the failure from a bare Compatibility: the rejected inputs never worked. A slashed branch silently fetched from the wrong organization, and a non- 49 unit tests now, same 3 pre-existing Still outstanding, deliberately not touched
CIChecks are still pending with no runs recorded; the pipelines bot notes an authorized user must comment |
|
/azp run |
|
Commenter does not have sufficient privileges for PR 10326 in repo Azure/azure-cli-extensions |
There was a problem hiding this comment.
🟡 Changes recommended
Resolve the two moderate preview URL validation issues in custom.py.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
src/vm-repair/azext_vm_repair/custom.py:89
- The new strict parser is only reached through
run()'s_build_repo_params()call.list_scripts()still sends--previewdirectly to_set_repair_map_url()and fetches the map, so URLs with another repository or a slashed branch are still accepted byaz vm repair list-scripts --preview, contrary to the updated help text and validation contract. Validate the preview URL through the same parser before fetching the map in that command too.
def _build_repo_params(preview, is_linux):
- Files reviewed: 9/9 changed files
- Comments generated: 1
- Review effort level: Lite
list-scripts applied the preview URL without any validation, so it still accepted a URL naming another repository or a branch containing a slash. run validated it only when the driver parameters were built, after the map had already been fetched from the unvalidated location. Both commands now parse the URL first and fail before anything is downloaded.
The preview URL validator accepts both /blob/ and /tree/, but _set_repair_map_url only rewrote /blob/, so a /tree/ url passed validation and then produced raw.githubusercontent.com/<fork>/repair-script-library/tree/<branch>/map.json, which 404s before the run id can be resolved. The rewrite now normalizes both forms, and a new test drives the validator and the rewrite together so neither can widen without the other. Also carries a pending refinement to the validation ordering: the check now runs before the command helper is constructed, because the helper reports telemetry from its destructor, which runs at interpreter shutdown when the command aborts early and prints a shutdown traceback over the real error.
There was a problem hiding this comment.
🟡 Changes recommended
Two moderate preview-URL handling issues remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 9/9 changed files
- Comments generated: 2
- Review effort level: Lite
|
/azp run |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The CheckExternalUrls pipeline job rejects new raw.githubusercontent.com URLs and asks that the file be mirrored to the internal CLI storage account. Neither URL here can be: Azure/repair-script-library is the upstream source this command fetches scripts from, and --preview deliberately names an arbitrary user fork. Both lines already pointed at raw GitHub before this PR; the job only flags them because the PR touched the lines. Suppressed with the documented inline comment and a reason.
|
Ran the pipeline's What it flagged
Worth noting for reviewers: neither URL is new to the extension. Why Option 1 does not applyThe tool's preferred fix is mirroring the file to
This is precisely the situation the tool describes as the fallback: "Only if the GitHub URL is required by design (e.g. the upstream repo IS the authoritative source)." AppliedDocumented inline suppression with a reason on each line: # external-url-exempt: Azure/repair-script-library is the upstream source this command fetches scripts from
# external-url-exempt: --preview names a user fork, which cannot be mirrored internallyRe-ran the exact pipeline command afterwards: Status
The remaining |
🤖 PR Validation — ️✔️ All clear
Summary
vm-repair2.4.2. This PR started as documentation for the NVMe platform-migration readiness checks, and grew to include the script-resolution and--previewfixes that testing those checks against a forked library uncovered. The behaviour changes are listed first because they are the part that affects users.Behaviour changes
Scripts are resolved from the library's
mainbranch instead ofmaster.Azure/repair-script-libraryrenamed its default branch.masteronly still resolves through a GitHub rename redirect, which GitHub is free to withdraw; if it were withdrawn,az vm repair run --run-idandaz vm repair list-scriptswould stop finding any script in the default library. Both themap.jsonlookup and the Linux run driver's bundle download were pinned tomaster; both now usemain. The set of available scripts is unchanged, and invocations that pass--previewwere never affected because they replace the library location before it is used.--previewnow works on Linux source VMs. The fork and branch taken from the preview URL were sent to the Linux VM but never consumed by the driver, so the run id was resolved from the fork while the scripts themselves were still downloaded fromAzure. Worse, the two unused values were passed on to the repair script as its first two arguments, shifting every parameter supplied with--parameters. Previewing a forked library now behaves on Linux the way it already did on Windows.Malformed
--previewURLs are rejected instead of silently redirected. The URL used to be parsed positionally (parts[index('map.json') - 4]), sohttps://github.com/SomeUser/repair-script-library/blob/feature/nvme/map.jsonresolved the owner torepair-script-libraryand downloaded scripts from an unrelated GitHub organisation, reporting nothing. A URL is now matched against an explicit pattern: it must name arepair-script-libraryrepository and the branch must be a single path segment.az vm repair runandaz vm repair list-scriptsboth apply the check, and both apply it before the library location is replaced, so a rejected URL is never fetched from.Documentation
az vm repair run -hgains examples for checking whether a Windows or Linux guest is ready to boot from an NVMe controller. Both run the corresponding read-only readiness detector on the linked repair VM.README.mdlists which platform-migration scenarios the extension can currently help with, so that an unsupported combination is not mistaken for a supported one, and states plainly that neither detector modifies the attached source OS disk.--previewhelp text now states the single-path-segment branch requirement.Changes
azext_vm_repair/custom.py—PREVIEW_URL_PATTERN/_parse_preview_url/_build_repo_paramsreplace the positional URL parser;runandlist_scriptsvalidate before calling_set_repair_map_url.azext_vm_repair/repair_utils.py—REPAIR_MAP_URLtargetsmain.azext_vm_repair/scripts/linux-run-driver.sh— consumesrepo_fork/repo_branchand defaults tomain.azext_vm_repair/_params.py,azext_vm_repair/_help.py,README.md— help and docs.azext_vm_repair/tests/latest/test_repair_utils.py— regression tests.setup.py,HISTORY.rst— version and changelog.Version & changelog
setup.pyVERSION: 2.4.1 -> 2.4.2HISTORY.rstentry added: yesTesting
azext_vm_repair/tests/latest/test_repair_utils.py, 14 tests, all passing:RepairMapUrlTest— asserts the map URL and both run drivers agree on one branch, so a run id can no longer resolve from one branch and execute content from another.BuildRepoParamsTest— Linux always receivesrepo_fork/repo_branchpositionally, Windows omits them without--preview, and URLs missingmap.json, naming another repository, or carrying a slashed branch are rejected.PreviewUrlIsValidatedBeforeUseTest—runandlist-scriptsboth raise before_set_repair_map_urlis called, which is the regression guard for the review comment oncustom.py.No live Azure tests were run.
Cross-repo impact (repair-script-library)
None. No run id, script path, or
map.jsonentry changes. The branch rename this PR follows already happened in that repository.Security review
The positional
--previewparser was the one input-handling defect here: a user-supplied URL could redirect script downloads to an arbitrary GitHub owner without any signal. It is now validated against an explicit pattern before use. No shell interpolation of untrusted input is introduced, and no secrets are logged.Backward compatibility
No command, parameter, or default is removed or renamed.
--previewURLs that were previously accepted and resolved to what the caller intended continue to work; the URLs that now fail are the ones that were resolving somewhere other than where they pointed.