Skip to content

#1885 fix: run every pending migration in version order and record it - #1886

Open
RAprogramm wants to merge 5 commits into
HyDE-Project:devfrom
RAprogramm:1885
Open

#1885 fix: run every pending migration in version order and record it#1886
RAprogramm wants to merge 5 commits into
HyDE-Project:devfrom
RAprogramm:1885

Conversation

@RAprogramm

@RAprogramm RAprogramm commented Jul 30, 2026

Copy link
Copy Markdown

Closes #1885

Problem

The installer ran exactly one migration — the lexicographically highest filename — and kept no record of what had been applied. Three consequences:

  • Scripts/migrations/ holds v25.8.2.sh, v25.9.1.sh, v26.4.3.sh, v26.7.4.sh, and only the last one ever executed, so anyone skipping a release never got the ones in between.
  • sort -r is lexicographic, so v26.10.1 would have sorted below v26.4.3 and the newest migration would have been the one skipped.
  • With no record of applied migrations, adding v26.8.x.sh retires v26.7.4.sh for everyone — including users who have not upgraded yet and still need it to stop the superseded shell helpers from shadowing their replacements.

Change

The runner moved out of install.sh into run_pending_migrations in Scripts/global_fn.sh, which takes the migration directory and a record file. It runs every migration absent from that record, ordered with sort -V, and appends a name only after that migration exits zero. A failure stays a warning, as before, but is not recorded, so it is retried on the next run.

The record lives at ${XDG_STATE_HOME}/hyde/migration/applied.

Existing migrations are all safe to re-run — three only print notices or reload state, and v26.7.4.sh skips any file whose backup already exists — so an installed base with no record simply replays them once.

Tests

tests/test_migrations.sh (new) covers version ordering with v26.10.1 after v26.4.3, recording of successes, a non-zero migration staying unrecorded, no re-run on a second pass, and silence on a missing directory.

Full suite: 9 cases, 0 failed.

Summary by CodeRabbit

  • Bug Fixes

    • Installations now persist and execute all pending migrations in version order, recording successful runs and keeping failed migrations pending for retry.
    • Updated installer migration output to accurately reflect whether any pending work remains.
    • Installer metafile validation now more robustly handles remote sources and strengthens checks for declared paths and source values.
  • Tests

    • Expanded migration tests to verify retry on failure, correct applied-state recording, and stricter confirmation that the installer invokes the pending-migrations flow.
  • Documentation

    • Refreshed test documentation to reflect the updated remote-vs-local metafile validation rules.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f3e0f6f2-53a6-4361-b16a-cd78eb6d3e31

📥 Commits

Reviewing files that changed from the base of the PR and between 5d42806 and 4fe9ecc.

📒 Files selected for processing (1)
  • tests/test_migrations.sh

📝 Walkthrough

Walkthrough

Migration execution now processes all unapplied scripts in version order and persists successful applications. Dot-metafile validation distinguishes remote sources from local paths and applies stricter source, type, containment, and existence checks.

Changes

Migration tracking

Layer / File(s) Summary
Pending migration runner
Scripts/global_fn.sh, tests/test_migrations.sh
Pending migrations run in version order, successful scripts are recorded, failures remain unapplied, and reruns skip recorded migrations.
Installer integration and release note
Scripts/install.sh, CHANGELOG.md
The installer supplies a persistent state file, and the changelog documents the new migration behavior.

Metafile validation

Layer / File(s) Summary
Source and path validation contracts
tests/python/check_dots.py
Metafile sources are classified as usable or remote, while path values and containment checks use normalized list-based validation.
Validation rules and test documentation
tests/python/check_dots.py, tests/README.md
Validation rejects unusable sources, applies repository checks only to local entries, and documents the expanded rules.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Installer
  participant MigrationRunner as run_pending_migrations
  participant MigrationScript
  participant StateFile
  Installer->>MigrationRunner: provide migration directory and state file
  MigrationRunner->>MigrationScript: execute each unapplied script in version order
  MigrationScript-->>MigrationRunner: success or failure
  MigrationRunner->>StateFile: record successful filename
Loading

Possibly related PRs

Poem

I’m a rabbit with migrations to run,
Sorting each script till the work is done.
I mark every success in a state-file trail,
Skip what’s recorded, and log what may fail.
Local paths stay safely inside,
While remote sources hop outside.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also changes tests/python/check_dots.py and tests/README.md for metafile validation, which is unrelated to migrations. Move the metafile validation changes into a separate PR or remove them from this migration-focused change.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main migration fix and is concise.
Linked Issues check ✅ Passed The changes run all pending migrations in version order, record successes, and retry failures, matching #1885.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Scripts/global_fn.sh`:
- Line 211: Update the migration status logic around the applied counter so the
“No outstanding migrations” message is based on whether any unrecorded migration
was encountered, not whether applied remains zero. Track that encounter
separately while processing pending migrations, and preserve the message only
when none were found, including when encountered migrations fail.

In `@tests/python/check_dots.py`:
- Around line 155-157: Update the path validation logic in the check_dots test
around inside and has_glob to expand each glob pattern and validate every
resolved match with inside(root, match), rejecting matches outside source_root.
Preserve the existing existence-check exemption only for patterns with no
matches; literal paths and unmatched globs must retain their current handling.

In `@tests/test_migrations.sh`:
- Around line 49-53: Extend the migration test around run_pending_migrations to
make v26.11.0.sh write a failure marker, then invoke the migration runner again
and assert the order log contains two entries, proving the failed migration is
retried. Also verify v26.11.0 remains absent from the state file after the
second invocation, while preserving the existing check that successful
migrations are not rerun.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 56dcd812-1609-4417-85eb-036db3c6083c

📥 Commits

Reviewing files that changed from the base of the PR and between bab9fcf and 2727b63.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • Scripts/global_fn.sh
  • Scripts/install.sh
  • tests/README.md
  • tests/python/check_dots.py
  • tests/test_migrations.sh

Comment thread Scripts/global_fn.sh Outdated
Comment on lines +155 to +157
if not inside(root, relative):
fail(f"{where} points outside its source_root with path {relative!r}")
elif not has_glob(relative) and not (root / relative).exists():

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Validate resolved glob matches, not just the literal pattern.

inside(root, relative) resolves * literally. A path such as linked/*, where linked is a symlink outside source_root, passes Line 155 and then bypasses Line 157’s existence check. Expand matched globs and apply containment validation to every match; only unmatched-glob existence checks should remain exempt.

Proposed fix
-                        elif not has_glob(relative) and not (root / relative).exists():
+                        elif has_glob(relative):
+                            for match in root.glob(relative):
+                                if not inside(root, str(match.relative_to(root))):
+                                    fail(f"{where} glob escapes its source_root with path {relative!r}")
+                        elif not (root / relative).exists():
                             fail(f"{where} points at a missing path {relative!r}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not inside(root, relative):
fail(f"{where} points outside its source_root with path {relative!r}")
elif not has_glob(relative) and not (root / relative).exists():
if not inside(root, relative):
fail(f"{where} points outside its source_root with path {relative!r}")
if has_glob(relative):
for match in root.glob(relative):
if not inside(root, str(match.relative_to(root))):
fail(f"{where} glob escapes its source_root with path {relative!r}")
elif not (root / relative).exists():
fail(f"{where} points at a missing path {relative!r}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/python/check_dots.py` around lines 155 - 157, Update the path
validation logic in the check_dots test around inside and has_glob to expand
each glob pattern and validate every resolved match with inside(root, match),
rejecting matches outside source_root. Preserve the existing existence-check
exemption only for patterns with no matches; literal paths and unmatched globs
must retain their current handling.

Comment thread tests/test_migrations.sh
Comment on lines +49 to +53
: >"$order_log"
run_pending_migrations "$migration_dir" "$state_file" >/dev/null 2>&1

[ -s "$order_log" ] &&
fail "already applied migrations ran a second time: $(tr '\n' ' ' <"$order_log")"

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert that failed migrations are retried.

The second invocation only verifies successful migrations did not rerun. Make v26.11.0.sh write a failure marker and assert two entries after the second invocation; also recheck that it remains absent from the state file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_migrations.sh` around lines 49 - 53, Extend the migration test
around run_pending_migrations to make v26.11.0.sh write a failure marker, then
invoke the migration runner again and assert the order log contains two entries,
proving the failed migration is retried. Also verify v26.11.0 remains absent
from the state file after the second invocation, while preserving the existing
check that successful migrations are not rerun.

@RAprogramm

Copy link
Copy Markdown
Author

Three follow-ups in 5d42806, from re-reviewing the runner.

Migrations no longer inherit the loop's stdin. sh "$migrationDir/$file" took fd 0 from the while read loop's process substitution, so a migration that reads stdin consumed the names of every migration queued after it — they never ran, were never recorded, and produced no message. The call now redirects from /dev/null. The test covers it: the middle migration of four runs cat >/dev/null, and the two after it still have to run in order.

Honest reporting. The "no outstanding migrations" line was keyed on the count of successes, so a run where a migration was found and failed printed both the failure warning and "no outstanding migrations". It now keys on whether anything was pending, and the test asserts that message cannot appear alongside a pending migration.

Test diagnostics. Sourcing global_fn.sh pulls in its set -e, so a failed cat of the order log aborted the test before its own fail message printed — the suite reported a failure with no reason. That read is now guarded, and the test additionally asserts install.sh actually calls the function, which it did not check before.

Suite: 9 cases, 0 failed.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_migrations.sh`:
- Around line 66-67: Strengthen the assertion in the migration test around
run_pending_migrations so it verifies an actual invocation from
Scripts/install.sh rather than any matching text. Match the command in an
executable position with appropriate shell syntax, or run the installer using a
stubbed migration runner and assert that the runner is called.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b337e0c5-42e1-415b-bf3f-7c92589f2e80

📥 Commits

Reviewing files that changed from the base of the PR and between 2727b63 and 5d42806.

📒 Files selected for processing (2)
  • Scripts/global_fn.sh
  • tests/test_migrations.sh

Comment thread tests/test_migrations.sh Outdated
@RAprogramm

Copy link
Copy Markdown
Author

Both test findings addressed.

Failed migrations are asserted to retry. v26.11.0.sh now appends to a marker file before exiting non-zero, and the test requires exactly two entries after two passes — so a change that recorded a failure as applied, or swallowed the retry, fails the test. Previously the second pass only proved the successful ones did not rerun.

The installer check is no longer a substring match. It now requires run_pending_migrations to appear where a command goes, ^[[:space:]]*run_pending_migrations[[:space:]]+, so a mention in a comment or an unrelated string no longer satisfies it.

The first finding — reporting "no outstanding" when every pending migration failed — was already fixed in 5d42806 before this review landed: the message is keyed on whether anything was pending, not on the success count, and the test asserts the two cannot coexist.

Suite: 9 cases, 0 failed.

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.

1 participant