Skip to content

Migration script#100

Open
lispandfound wants to merge 11 commits intopegasusfrom
check_script
Open

Migration script#100
lispandfound wants to merge 11 commits intopegasusfrom
check_script

Conversation

@lispandfound
Copy link
Copy Markdown
Contributor

  • add check realisation script
  • simplify migration script
  • fix(check-realisation): correctly store auto response
  • refactor(check_script): rename to migrate
  • refactor(migrate): move utilities out of migrate script
  • feat(migrate): add more refactoring utilities

Copilot AI review requested due to automatic review settings March 23, 2026 03:26
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the workflow's configuration management by introducing a comprehensive migration script. This script streamlines the process of updating realization configuration files, ensuring they conform to new default structures. It centralizes utility functions, provides interactive prompts for handling schema errors and missing defaults, and adds powerful features for copying and cloning realisations, thereby improving the maintainability and adaptability of the system.

Highlights

  • New Migration Utility: A new command-line utility, workflow/scripts/migrate.py, has been introduced to manage and update realization configuration files, offering functionalities for migration, copying, and cloning.
  • Refactored Dictionary Merging: The _merge_defaults function was extracted from workflow/defaults.py and moved to workflow/utils.py as merge_dictionaries, centralizing common utility logic.
  • Interactive Configuration Updates: The migrate.py script provides interactive tools to automatically trim extraneous keys and fill in missing default values in realization JSON files, with user prompting for actions and the ability to store auto-responses.
  • Enhanced Realisation Management: New copy and clone utilities were added to migrate.py, enabling users to copy configuration blocks between realisations and to clone realisations while regenerating seeds.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@lispandfound lispandfound requested a review from sungeunbae March 23, 2026 03:27
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new migration script for realisation configurations and refactors a dictionary merging utility. The refactoring is a good improvement. The new migration script is a valuable addition, but I've found a critical bug that would cause it to crash due to a typo. Additionally, the script's functionality to fill in missing default values, as described in its documentation, appears to be incomplete. I've also included several medium-severity suggestions to improve code clarity, remove dead code, and clean up unused imports.

Comment on lines +389 to +394
)

try:
_ = config.read_from_realisation(realisation)
except realisations.RealisationParseError:
if config not in defaults and config != realisations.Seeds:
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.

critical

There's a typo here. response.AUTO will raise an AttributeError because response is an instance of the Response enum, not the enum class itself. It should be Response.AUTO.

Suggested change
)
try:
_ = config.read_from_realisation(realisation)
except realisations.RealisationParseError:
if config not in defaults and config != realisations.Seeds:
if response in (Response.AUTO, Response.YES):
autofill(
realisation,
default_config,
dry_run=dry_run,
)

Comment on lines +418 to +419
error, _ = extract_error(
config._config_key, config._schema, error
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.

critical

Similar to a previous comment, response.AUTO will raise an AttributeError because response is an instance of the Response enum, not the enum class itself. It should be Response.AUTO.

Suggested change
error, _ = extract_error(
config._config_key, config._schema, error
if response in (Response.AUTO, Response.YES):
trim_keys(realisation, config, extra_keys, dry_run)

@@ -0,0 +1,587 @@
"""Check that realisation can be loaded, if it can't automatically trim extraneous tags and offer to fill in default values."""
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.

high

The script's docstring mentions it will "offer to fill in default values", and the Action enum includes FILL. However, the implementation doesn't seem to handle cases where a schema.SchemaError is due to missing keys. The extract_error function only identifies extraneous keys ("Wrong key(s)"). To fully meet the stated goal, the script should also handle Missing key errors from the schema library, perhaps by prompting the user to fill them from the default configuration.

Comment on lines +223 to +225
return f"Error in {name}: {last_error}", []


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.

medium

The comment ... (keep your existing fuzzy matching logic here) ... is misleading as there is no fuzzy matching logic implemented for suggesting corrections for misspelled keys. This should either be implemented or the comment should be removed to avoid confusion for future maintainers.

Suggested change
return f"Error in {name}: {last_error}", []
unknown_key = match.group(1)
return f"Error in {name}: Unknown key '{unknown_key}'", [unknown_key]

Comment on lines +400 to +403

default_config = defaults.get(config)
error, extra_keys = extract_error(config._config_key, config._schema, error)
console.print(error)
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.

medium

The special handling for realisations.Seeds in this except block is not immediately obvious. Adding a comment explaining why Seeds is excluded from the "Missing required configuration" error would improve code clarity and maintainability. For example, a comment could explain that Seeds can be generated randomly if missing and doesn't rely on a default configuration in the same way.

if extra_keys:
response = auto_response.get((config, Action.TRIM)) or should_trim_keys(
config, extra_keys
)
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.

medium

The variable default_config is assigned here but it is not used in the rest of the except block. This appears to be dead code and can be removed.

@@ -1,5 +1,6 @@
"""Miscellaneous workflow utilities that couldn't go anywhere else."""

import inspect
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.

medium

The inspect module is imported but not used in this file. It should be removed to keep the code clean.

@@ -14,6 +15,7 @@
from shapely import Geometry, Polygon, geometry

from qcore import coordinates
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.

medium

The defaults module is imported but not used in this file. It should be removed to keep the code clean.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new migrate utility to help update/repair realisation JSON files against a chosen defaults version, and refactors deep-merge logic into a shared utility used by defaults loading and migration helpers.

Changes:

  • Introduces workflow/scripts/migrate.py with commands to migrate, copy config blocks, and clone realisations.
  • Moves deep dictionary merge logic into workflow.utils.merge_dictionaries().
  • Updates workflow/defaults.py to use the shared merge utility.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 13 comments.

File Description
workflow/utils.py Adds merge_dictionaries() utility (but also introduces unused imports / circular import risk).
workflow/scripts/migrate.py New Typer CLI for migration/copy/clone workflows; contains several runtime and CLI-behavior bugs to address.
workflow/defaults.py Refactors defaults merging to use utils.merge_dictionaries().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

from qcore import cli
from workflow import realisations, utils
from workflow.defaults import DefaultsVersion
from workflow.realisations import RealisationMetadata, Seeds
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

RealisationMetadata is imported but never used (the code references it via realisations.RealisationMetadata). This will fail linting (ruff F401); please remove the unused import.

Suggested change
from workflow.realisations import RealisationMetadata, Seeds
from workflow.realisations import Seeds

Copilot uses AI. Check for mistakes.
Comment on lines +173 to +179
if dry_run:
console.print(
f"DRY RUN: Would merge with {config.__class__.__name__} defaults in {realisation}"
)
else:
config.write_to_realisation(realisation)

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

autofill() claims it will “merge with defaults”, but config.write_to_realisation() overwrites the entire config block for that _config_key (it does not deep-merge with the existing values). If the goal is to fill missing keys while preserving existing user values, load the current config dict, merge defaults into it appropriately, and write the merged result back.

Copilot uses AI. Check for mistakes.
Comment on lines +211 to +225
last_error = e.autos[-1] if e.autos else str(e)
extraneous_keys = []
assert isinstance(last_error, str)
if "Wrong keys" in last_error:
extraneous_keys = re.findall(r"'(.*?)'", last_error.split(" in {")[0])
error_msg = f"Extraneous keys found: [red]{', '.join(extraneous_keys)}[/red]"
return f"Error in {name}: {error_msg}", extraneous_keys

if match := re.match(r"^Wrong key '(.*?)'", last_error):
unknown_key = match.group(1)
return f"Error in {name}: Unknown key '{unknown_key}'", [unknown_key]

return f"Error in {name}: {last_error}", []


Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

extract_error() can crash on non-string entries because of assert isinstance(last_error, str). Schema errors often carry structured context in autos; please handle non-string values defensively (e.g., last_error = str(last_error)) instead of asserting. Also, the placeholder “keep your existing fuzzy matching logic here” and the unused keys/schema variables should be removed or implemented to avoid dead/incomplete code paths.

Copilot uses AI. Check for mistakes.
Comment on lines +418 to +420
error, _ = extract_error(
config._config_key, config._schema, error
)
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

Same enum bug as above: response.AUTO / response.YES will raise AttributeError because response is an instance. Use Response.AUTO / Response.YES.

Copilot uses AI. Check for mistakes.
Comment on lines +399 to +403
console.print(f"[red]Schema error for {realisation}[/red]")

default_config = defaults.get(config)
error, extra_keys = extract_error(config._config_key, config._schema, error)
console.print(error)
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

This branch never reports missing configs: earlier in the loop you continue when config has no defaults, so config not in defaults will be false here. Also, {config.__class__.__name__} will print type because config is itself a class. Consider reporting missing configs unconditionally (except seeds) and use config.__name__ for the class name.

Copilot uses AI. Check for mistakes.
Comment on lines +511 to +515
"""
with open(realisation_template) as f:
template = json.load(f)

configs = configs or list(template)
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

Docstring references “Has no effect when combined with dry run”, but copy() has no dry_run option. Please remove/adjust this text or add the corresponding parameter/behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +565 to +567
realisations[event].add(realisation_number)

for event, existing_realisations in realisations.items():
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

Path.is_dir is a method; using realisation.is_dir without calling it is always truthy and defeats the directory check. This should be realisation.is_dir().

Copilot uses AI. Check for mistakes.
Comment on lines +407 to +408
)

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

default_config is assigned here but never used, which will be flagged by ruff (F841). Please remove the assignment or use it (e.g., to offer a fill/merge action when resolving schema errors).

Copilot uses AI. Check for mistakes.
@@ -1,5 +1,6 @@
"""Miscellaneous workflow utilities that couldn't go anywhere else."""

import inspect
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

inspect is imported but not used in this module. This will fail linting (ruff F401); please remove the import or use it.

Suggested change
import inspect

Copilot uses AI. Check for mistakes.
from shapely import Geometry, Polygon, geometry

from qcore import coordinates
from workflow import defaults
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

from workflow import defaults is unused here and also introduces a circular import with workflow/defaults.py (which now imports workflow.utils). Please remove this import (or refactor to avoid the cycle if it becomes needed later).

Suggested change
from workflow import defaults

Copilot uses AI. Check for mistakes.
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.

2 participants