Skip to content

Az Migrate runbook enhancements - #10325

Open
krdhruva wants to merge 7 commits into
Azure:mainfrom
krdhruva:krdhruva_migrate_runbook_enh
Open

krdhruva wants to merge 7 commits into
Azure:mainfrom
krdhruva:krdhruva_migrate_runbook_enh

Conversation

@krdhruva

@krdhruva krdhruva commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Validation — ❌ Action needed

Breaking Changes
❌ 1
❌Azure CLI Extensions Breaking Change Test
❌migrate
rule cmd_name rule_message suggest_message
1010 - ParaPropUpdate migrate runbook definition show cmd migrate runbook definition show update parameter workstream_id: updated property options from ['--workstream-id'] to ['--step-group-id'] please change property options from ['--step-group-id'] to ['--workstream-id'] for parameter workstream_id of cmd migrate runbook definition show
1007 - ParaRemove migrate runbook definition step add cmd migrate runbook definition step add removed parameter depends_on please add back parameter depends_on for cmd migrate runbook definition step add
1010 - ParaPropUpdate migrate runbook definition step add cmd migrate runbook definition step add update parameter workstream_id: updated property options from ['--workstream-id'] to ['--step-group-id'] please change property options from ['--step-group-id'] to ['--workstream-id'] for parameter workstream_id of cmd migrate runbook definition step add
1007 - ParaRemove migrate runbook definition step update cmd migrate runbook definition step update removed parameter depends_on please add back parameter depends_on for cmd migrate runbook definition step update
1007 - ParaRemove migrate runbook definition visualize cmd migrate runbook definition visualize removed parameter open_file please add back parameter open_file for cmd migrate runbook definition visualize
1012 - SubgroupRemove migrate runbook definition workstream sub group migrate runbook definition workstream removed please confirm sub group migrate runbook definition workstream removed
1007 - ParaRemove migrate runbook execution parameter download cmd migrate runbook execution parameter download removed parameter file please add back parameter file for cmd migrate runbook execution parameter download
1010 - ParaPropUpdate migrate runbook execution show cmd migrate runbook execution show update parameter interval: updated property default from 5 to 60 please change property default from 60 to 5 for parameter interval of cmd migrate runbook execution show
1007 - ParaRemove migrate runbook execution step approve cmd migrate runbook execution step approve removed parameter all_ready please add back parameter all_ready for cmd migrate runbook execution step approve
1007 - ParaRemove migrate runbook execution visualize cmd migrate runbook execution visualize removed parameter open_file please add back parameter open_file for cmd migrate runbook execution visualize
1010 - ParaPropUpdate migrate runbook execution visualize cmd migrate runbook execution visualize update parameter interval: updated property default from 5 to 60 please change property default from 60 to 5 for parameter interval of cmd migrate runbook execution visualize
1007 - ParaRemove migrate runbook parameter download cmd migrate runbook parameter download removed parameter file please add back parameter file for cmd migrate runbook parameter download
⚠️ 1006 - ParaAdd migrate runbook definition step add cmd migrate runbook definition step add added parameter depends_on_mapped_entities
⚠️ 1006 - ParaAdd migrate runbook definition step add cmd migrate runbook definition step add added parameter depends_on_per_entity
⚠️ 1006 - ParaAdd migrate runbook definition step add cmd migrate runbook definition step add added parameter depends_on_whole_step
⚠️ 1006 - ParaAdd migrate runbook definition step update cmd migrate runbook definition step update added parameter depends_on_mapped_entities
⚠️ 1006 - ParaAdd migrate runbook definition step update cmd migrate runbook definition step update added parameter depends_on_per_entity
⚠️ 1006 - ParaAdd migrate runbook definition step update cmd migrate runbook definition step update added parameter depends_on_whole_step
⚠️ 1011 - SubgroupAdd migrate runbook definition step-group sub group migrate runbook definition step-group added
⚠️ 1006 - ParaAdd migrate runbook definition visualize cmd migrate runbook definition visualize added parameter no_open
⚠️ 1001 - CmdAdd migrate runbook execution parameter configure cmd migrate runbook execution parameter configure added
⚠️ 1006 - ParaAdd migrate runbook execution parameter download cmd migrate runbook execution parameter download added parameter directory
⚠️ 1006 - ParaAdd migrate runbook execution start cmd migrate runbook execution start added parameter no_visualize
⚠️ 1006 - ParaAdd migrate runbook execution visualize cmd migrate runbook execution visualize added parameter no_open
⚠️ 1006 - ParaAdd migrate runbook generate cmd migrate runbook generate added parameter no_visualize
⚠️ 1001 - CmdAdd migrate runbook parameter configure cmd migrate runbook parameter configure added
⚠️ 1006 - ParaAdd migrate runbook parameter download cmd migrate runbook parameter download added parameter directory
⚠️ 1006 - ParaAdd migrate runbook regenerate cmd migrate runbook regenerate added parameter no_visualize

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

  • [x ] Have you run azdev style <YOUR_EXT> locally? (pip install azdev required)
  • Have you run python scripts/ci/test_index.py -q locally? (pip install azdev required)
  • My extension version conforms to the Extension version schema

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.json automatically.
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.

@azure-client-tools-bot-prd

Copy link
Copy Markdown

Hi krdhruva,
Please write the description of changes which can be perceived by customers into HISTORY.rst.
If you want to release a new extension version, please update the version in pyproject.toml (or setup.py, if the extension has not migrated yet) as well.

@yonzhan

Copy link
Copy Markdown
Collaborator

Az Migrate

@krdhruva
krdhruva marked this pull request as ready for review September 11, 2026 03:09
Copilot AI lite review requested due to automatic review settings September 11, 2026 03:09
@krdhruva

Copy link
Copy Markdown
Contributor Author

This cli changes are not yet released to customers. We are taking breaking changes as per improvement identified. No external customers are impacted.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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-open option, but required=True makes a false return from webbrowser.open an error after the HTML has already been written. On headless agents and Linux sessions without a registered browser, execution parameter configure therefore 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 runbookInputs envelope, this export path writes edited stepInputs at the JSON root instead of inside runbookInputs. 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-id and uploads with migrate runbook parameter upload, so following the page edits the runbook rather than the execution. Pass the editor kind/execution id into the template and render migrate runbook execution parameter upload for 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.json member is preserved by the archive extraction path, but definition.download still classifies files by checking whether the filename contains input. A downloaded parameters.json is therefore returned and logged as a definition, 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/--open parameter from execution visualization and changes the default launch behavior. Existing automation using migrate runbook execution visualize --open will be rejected, while non-interactive callers without --no-open can 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 --file option. 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 --interval default 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 provide entities plus entitiesCompleted (the visualization view model already handles that shape), so execution show --watch prints 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_order is 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.json and derived-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.

Comment thread src/migrate/azext_migrate/shared/files.py
Comment on lines 129 to 131
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')
Comment on lines 215 to +217
if isinstance(params, dict) and isinstance(
params.get('runbookInputs'), dict):
runbook_inputs = params['runbookInputs']
params.get('inputs'), dict):
runbook_inputs = params['inputs']
Comment on lines 239 to 241
paths = files.extract_definition_files(zip_bytes, destination)
result = []
for path in paths:
Comment on lines +243 to +244
'no_open', options_list=['--no-open'], action='store_true',
help='Write the HTML file but do not open it in a browser.')
Comment on lines 296 to +299
'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.')
Comment on lines +315 to 317
'directory', options_list=['--directory'],
help='Output directory for the downloaded parameter files '
'(default: current directory).')
Comment on lines 114 to 117
return isinstance(parsed, dict) and (
'runbookInputs' in parsed
'inputs' in parsed
or 'stepInputs' in parsed
or 'schema' in parsed)
Comment on lines 271 to 274
if isinstance(parsed, dict) and isinstance(
parsed.get('runbookInputs'), dict):
return parsed['runbookInputs']
parsed.get('inputs'), dict):
return parsed['inputs']
return parsed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants