Conversation
…service counts, api-version 2026-08-01-preview, archive diagnostics, coverage tooling
|
Hi krdhruva, |
|
Az Migrate |
|
This cli changes are not yet released to customers. We are taking breaking changes as per improvement identified. No external customers are impacted. |
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved archive compatibility regressions and breaking CLI changes affect definition, parameter, and execution workflows.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Enhances Az Migrate runbook artifact handling, offline editors, execution progress views, and dependency visualizations.
Changes:
- Adds richer visualization and parameter configuration experiences.
- Updates artifact extraction, CLI behavior, API handling, and progress reporting.
- Adds tests, coverage tooling, packaging, and lint configuration.
File summaries
| File | Description |
|---|---|
src/migrate/setup.py |
Packages configuration templates. |
src/migrate/scripts/runbook_coverage.py |
Adds runbook coverage tooling. |
src/migrate/linter_exclusions.yml |
Adds targeted lint exclusions. |
src/migrate/azext_migrate/tests/latest/runbook/test_runbook_scenario.py |
Updates scenario coverage. |
src/migrate/azext_migrate/shared/files.py |
Classifies and extracts runbook artifacts. |
src/migrate/azext_migrate/shared/constants.py |
Updates service API versions. |
src/migrate/azext_migrate/shared/arm_client.py |
Improves polling and pagination. |
src/migrate/azext_migrate/runbook/visualize/viewmodel.py |
Builds richer visualization models. |
src/migrate/azext_migrate/runbook/visualize/templates/runbook.html.tmpl |
Updates visualization HTML. |
src/migrate/azext_migrate/runbook/visualize/renderer.py |
Renders offline visualizations. |
src/migrate/azext_migrate/runbook/visualize/graph.py |
Builds dependency graphs. |
src/migrate/azext_migrate/runbook/validators.py |
Provides runbook validation logic. |
src/migrate/azext_migrate/runbook/transformers.py |
Enhances execution projections and progress. |
src/migrate/azext_migrate/runbook/params.py |
Updates runbook CLI arguments. |
src/migrate/azext_migrate/runbook/models.py |
Updates request payload models. |
src/migrate/azext_migrate/runbook/deps.py |
Centralizes dependency handling. |
src/migrate/azext_migrate/runbook/constants.py |
Defines artifact and execution constants. |
src/migrate/azext_migrate/runbook/configure/templates/configure.html.tmpl |
Provides the offline parameter editor. |
src/migrate/azext_migrate/runbook/configure/renderer.py |
Builds editor metadata. |
src/migrate/azext_migrate/runbook/configure/__init__.py |
Initializes configuration functionality. |
src/migrate/azext_migrate/runbook/commands.py |
Defines runbook command support. |
src/migrate/azext_migrate/runbook/cmds/runbook.py |
Adds visualization controls to generation. |
src/migrate/azext_migrate/runbook/cmds/parameter.py |
Adds parameter archive and configuration flows. |
src/migrate/azext_migrate/runbook/cmds/execution.py |
Adds execution visualization and watching. |
src/migrate/azext_migrate/runbook/cmds/execution_step.py |
Updates approval actions. |
src/migrate/azext_migrate/runbook/cmds/execution_parameter.py |
Adds execution parameter configuration. |
src/migrate/azext_migrate/runbook/cmds/definition.py |
Handles definition downloads and visualization. |
src/migrate/azext_migrate/runbook/cmds/definition_workstream.py |
Handles workstream updates. |
src/migrate/azext_migrate/runbook/cmds/definition_step.py |
Handles step updates. |
src/migrate/azext_migrate/runbook/_help.py |
Updates command help. |
src/migrate/AGENTS.md |
Provides repository guidance. |
src/migrate/.coveragerc |
Configures coverage reporting. |
Review details
Suppressed comments (10)
src/migrate/azext_migrate/runbook/cmds/execution_parameter.py:134
- This editor has no
--no-openoption, butrequired=Truemakes a false return fromwebbrowser.openan error after the HTML has already been written. On headless agents and Linux sessions without a registered browser,execution parameter configuretherefore fails instead of returning the generated editor; add the same opt-out as the visualization commands or keep browser launch best-effort.
files.open_in_browser(path, required=True)
src/migrate/azext_migrate/runbook/configure/templates/configure.html.tmpl:1175
- When the source file uses the documented
runbookInputsenvelope, this export path writes editedstepInputsat the JSON root instead of insiderunbookInputs. The saved file then no longer matches the parameters contract shown by the editor's upload command.
if (root.inputs && typeof root.inputs === 'object') { root.inputs.stepInputs = DOC.stepInputs; }
else { root.stepInputs = DOC.stepInputs; }
src/migrate/azext_migrate/runbook/configure/templates/configure.html.tmpl:1099
- The shared page always displays the runbook-level upload command. When this template is used by
execution parameter configure, the generated command omits--execution-idand uploads withmigrate runbook parameter upload, so following the page edits the runbook rather than the execution. Pass the editor kind/execution id into the template and rendermigrate runbook execution parameter uploadfor this caller.
`az migrate runbook parameter upload -g ${m.resourceGroup} -p ${m.project} -n ${m.runbook} --file ${filePath}`;
src/migrate/azext_migrate/runbook/constants.py:45
- The renamed
parameters.jsonmember is preserved by the archive extraction path, butdefinition.downloadstill classifies files by checking whether the filename containsinput. A downloadedparameters.jsonis therefore returned and logged as adefinition, which gives callers the wrong result kind. Please update the result classifier to recognize both canonical parameter names or use content classification.
# The service renamed the user-parameters artifact member inputs.json ->
# parameters.json (2026-09). The CLI reads BOTH: downloads are content-
# classified (name-agnostic, see shared/files.py) and written under their
# source member name, so only the File-mode upload path is name-sensitive.
RUNBOOK_PARAMETERS_FILE = "parameters.json"
# Recognised user-parameter file names (new canonical first).
RUNBOOK_PARAMETER_FILE_NAMES = (RUNBOOK_PARAMETERS_FILE, RUNBOOK_INPUT_FILE)
src/migrate/azext_migrate/runbook/params.py:265
- This removes the existing
open_file/--openparameter from execution visualization and changes the default launch behavior. Existing automation usingmigrate runbook execution visualize --openwill be rejected, while non-interactive callers without--no-opencan now fail after rendering. Please keep the compatibility parameter or provide an explicit alias with compatible semantics.
'no_open', options_list=['--no-open'], action='store_true',
help='Write the HTML file but do not open it in a browser.')
src/migrate/azext_migrate/runbook/params.py:332
- The execution-parameter download command also removes its existing
--fileoption. This breaks existing callers independently of the new directory download behavior; please preserve the old option as a compatibility path instead of deleting it.
'directory', options_list=['--directory'],
help='Output directory for the downloaded parameter files '
'(default: current directory).')
src/migrate/azext_migrate/runbook/params.py:276
- Execution visualization likewise changes the established
--intervaldefault from 5 to 60 seconds. This is a user-visible breaking behavior for watch mode and is already flagged by validation; please restore 5 or intentionally version/document the breaking change.
'interval', options_list=['--interval'], type=int,
help='Refresh interval in seconds for --watch (default: 60).')
src/migrate/azext_migrate/runbook/transformers.py:232
- The live table formatter only computes progress from
entityExecutions. Execution status documents can instead provideentitiesplusentitiesCompleted(the visualization view model already handles that shape), soexecution show --watchprints no workload progress for those steps even though the data contains the count. Add the explicit aggregate fallback here, matching_progress_text.
def _workload_progress(step):
"""Summarize per-entity progress from ``entityExecutions``.
Falls back to an explicit ``workloadProgress`` scalar when present.
"""
progress = step.get('workloadProgress')
if progress is not None:
return progress
entities = step.get('entityExecutions')
if not entities:
return None
total = len(entities)
completed = 0
for entity in entities:
value = (entity or {}).get('status') or (entity or {}).get('state')
if str(value or '').lower() in ENTITY_COMPLETED_STATES:
completed += 1
return '%d/%d completed' % (completed, total)
src/migrate/azext_migrate/runbook/visualize/graph.py:199
- The returned
group_orderis built from a display-name key, so two distinct workstreams with the same display name produce only one lane entry and their nodes are rendered together. This also makes workstream dependency routing ambiguous. Key the grouping by stable workstream id and use the name only as the displayed label.
return Graph(title, nodes, edges, group_order=group_order,
group_deps=group_deps)
src/migrate/azext_migrate/shared/files.py:44
- The comment says both legacy and post-rename derived-input names are excluded, but the tuple omits
derived-input.jsonandderived-inputs.json. If a legacy archive contains one of those before the user file, content classification can select computed inputs as the parameters file and expose/download the wrong data.
_DERIVED_INPUTS_NAMES = (
'system-derived-inputs.json', 'system-derived-parameters.json')
- Files reviewed: 32/33 changed files
- Comments generated: 14
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| return (isinstance(parsed, dict) | ||
| and 'runbookSpec' not in parsed | ||
| and 'spec' not in parsed | ||
| and not _looks_like_parameters(parsed)) |
| """ | ||
| if not isinstance(doc, dict): | ||
| return doc | ||
| payload = doc.get('spec') |
| if isinstance(params, dict) and isinstance( | ||
| params.get('runbookInputs'), dict): | ||
| runbook_inputs = params['runbookInputs'] | ||
| params.get('inputs'), dict): | ||
| runbook_inputs = params['inputs'] |
| paths = files.extract_definition_files(zip_bytes, destination) | ||
| result = [] | ||
| for path in paths: |
| 'no_open', options_list=['--no-open'], action='store_true', | ||
| help='Write the HTML file but do not open it in a browser.') |
| 'entities', options_list=['--entities'], nargs='*', | ||
| help='Space-separated entity ids to approve (partial approval ' | ||
| 'steps only).') | ||
| c.argument( | ||
| 'all_ready', options_list=['--all-ready'], action='store_true', | ||
| help='Approve every currently ready entity (partial approval ' | ||
| 'steps only).') | ||
| 'steps only). Omit to approve every currently ready ' | ||
| 'entity for the step.') |
| 'directory', options_list=['--directory'], | ||
| help='Output directory for the downloaded parameter files ' | ||
| '(default: current directory).') |
| return isinstance(parsed, dict) and ( | ||
| 'runbookInputs' in parsed | ||
| 'inputs' in parsed | ||
| or 'stepInputs' in parsed | ||
| or 'schema' in parsed) |
| if isinstance(parsed, dict) and isinstance( | ||
| parsed.get('runbookInputs'), dict): | ||
| return parsed['runbookInputs'] | ||
| parsed.get('inputs'), dict): | ||
| return parsed['inputs'] | ||
| return parsed |
🤖 PR Validation — ❌ Action needed
❌Azure CLI Extensions Breaking Change Test
Improves the offline HTML views produced by az migrate runbook (execution visualize and parameter configure) so users get clearer progress detail, an accurate dependency graph, and reliable feedback when copying or saving
This checklist is used to make sure that common guidelines for a pull request are followed.
Related command
General Guidelines
azdev style <YOUR_EXT>locally? (pip install azdevrequired)python scripts/ci/test_index.py -qlocally? (pip install azdevrequired)For new extensions:
About Extension Publish
There is a pipeline to automatically build, upload and publish extension wheels.
Once your pull request is merged into main branch, a new pull request will be created to update
src/index.jsonautomatically.You only need to update the version information in file setup.py and historical information in file HISTORY.rst in your PR but do not modify
src/index.json.