Skip to content

fix(paths): recover the profile a skipped or failed migration left behind and prune update staging from the cache - #88

Merged
MotherSphere merged 2 commits into
mainfrom
fix/migration-fallback-and-staging-prune
Oct 9, 2026
Merged

MotherSphere merged 2 commits into
mainfrom
fix/migration-fallback-and-staging-prune

Conversation

@MotherSphere

@MotherSphere MotherSphere commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

What changed

Migration fallback (FS-7). When both the rename and the copy fallback of a legacy directory failed, migrate_legacy_paths removed the half copy and Colony carried on with an empty directory. The error log even said so. Now relocate() returns the directory to use. On failure, the legacy path goes into a OnceLock that colony_data_dir() and colony_cache_dir() use for the rest of the session, and the next start tries the move again.

  • When the Windows Roaming to Local config move fails, the cache stays in the old tree as well. On Windows the new cache root sits inside the new config directory, so creating it would make the next start treat the move as done.

  • When the cache move fails, the per-repo cache moves are skipped. They run on the next start where the cache move succeeds.

  • If the copy fallback fails and the half copy cannot be removed (for example a scanner holds a file open), it is renamed aside to <dir>.incomplete-<timestamp>, so the next start really retries. If even that fails, an error tells the user to delete it by hand.

Recovery for profiles left behind by v0.10.2 to v0.10.4 (FS-7). Those releases created the new config directory for the log file before the move ran, so on Windows the Roaming to Local move was skipped and Colony started on an empty profile. "The target exists" no longer means "moved": the config move writes an explicit .migrated marker in the new config directory. When the target exists without it, every legacy file the target lacks is copied over, never overwriting what was written there since, and the marker is written. The legacy directory is never deleted. Once marked, the move is never redone, so a file removed afterwards stays removed.

Migration runs before the log file is created. On main the log was created first. On Windows, creating it made %LOCALAPPDATA%\Colony\Colony exist, so the Roaming move was always skipped. On Linux it made ~/.cache/Colony/Colony exist, so the config/cache move was always skipped. The migration's own log lines are captured in memory and written to the log file and stderr once the log is open.

Update staging sweep. prune_staging looked in <config>/update-staging, but download_launcher_asset writes to <cache>/update-staging. Both places are swept now. The config one catches leftovers from older versions. The whole directory is emptied: the path of a downloaded update exists only in memory, so an update that was never applied can't be reached after a restart. That includes its binary and its signatures.

Log location (FS-5). colony.log moves from the cache root to <data>/Colony/Colony/colony.log and is still truncated on every run. --help prints this path without creating it.

Config overrides (FS-2). config.rs now reads dirs::config_local_dir()/colony/ first. On Windows it then reads the Roaming dirs::config_dir()/colony/ as a fallback. On Linux and macOS the two are the same path, and it is listed once.

Dead scan entry (FS-6). The ${HOME}/.local/share/colony/applications entry is removed from config/colony.toml and from scan.rs. Nothing has written to that path since the colony-ui layout. Scanned apps are classified as external directly: the entries Colony writes for its own apps carry X-Colony-Managed and are skipped, and no scan directory lies under the install root, so the old directory check could never match. The French comments in colony.toml are translated.

Tests. The update tests and the persistence end-to-end test had separate ENV_LOCKs even though they set the same XDG variables. They now share one XDG_TEST_LOCK.

Docs. The log path is updated in docs/architecture.md, docs/faq.md, docs/tutorial.md, CONTRIBUTING.md and the bug report template. The FAQ's categories.json override path is corrected to what the code reads (~/.config/colony/, ~/Library/Application Support/colony/ on macOS, %LOCALAPPDATA%\colony\ on Windows).

Verification

cargo test --bin colony: 156 passed. New and extended tests:

  • a_move_an_earlier_version_skipped_is_finished_without_overwriting: a target holding only cache/colony.log and a fresh preferences/preferences.json, plus a legacy profile. The missing legacy files arrive, the fresh file is kept, the marker is written, and a second run does not bring back a file removed since.
  • a_completed_config_move_is_marked.
  • a_failed_config_move_resolves_both_dirs_to_the_legacy_tree (unix): the Windows shape with the cache inside the config directory. The migration decision returns both legacy directories, nothing creates the new config directory, and resolve() with those fallbacks returns the legacy tree without consulting the layout.
  • a_failed_cache_move_defers_the_per_repo_moves (unix).
  • a_failed_move_keeps_using_the_legacy_directory (unix): makes the rename fail with a read-only parent and the copy fail with an unreadable file. It checks that the legacy directory is returned with its contents intact and that the half copy is removed.
  • The end-to-end migration test now also runs prune_staging against the migrated layout. It checks that the cache-side staging and a config-side leftover are both removed, and that the reclaimed byte count is exact.
  • the_local_override_wins_over_roaming: Local comes first, and Roaming appears once, right after it.
  • The existing relocate tests now assert the returned directory.

…e staging from the cache

When both the rename and the copy fallback of a legacy directory failed,
migrate_legacy_paths removed the half copy and let Colony continue on an
empty new directory. relocate() now returns the directory to use, and a
failure records the legacy path in a OnceLock that colony_data_dir() and
colony_cache_dir() honour for the rest of the session; the next start
retries the move.

The migration now runs before the log file is created. On Windows the
log's data directory is the config directory the first move targets, and
creating it first made the move skip for good. Its log lines are captured
in memory and written to the log once it is open.

The boot sweep looked for update-staging in the config root while the
launcher download writes it to the cache. Both are swept now, and the
whole directory is emptied: a staged update that was never applied is
unreachable after a restart.

colony.log moves from the cache root to the data directory. External
config overrides are read from the Local config directory first, with the
Roaming one kept as a fallback on Windows. The dead
~/.local/share/colony/applications entry is gone; origin classification
uses the shared install root instead.
v0.10.2 to v0.10.4 created the new config directory for the log file
before the migration ran, so on Windows the Roaming to Local move was
skipped and the profile stayed behind. The config move is now tracked by
a .migrated marker: when the target exists without it, the files it
lacks are copied over, never overwriting what was written there since.

A half copy that cannot be removed after a failed move is renamed aside,
so the next start retries instead of running on it. The migration
decisions are factored out of the OnceLock setters and tested on
explicit paths, scanned apps are classified as external directly, and
the FAQ lists the macOS override path.
@MotherSphere MotherSphere changed the title fix(paths): keep the old profile when migration fails and prune update staging from the cache fix(paths): recover the profile a skipped or failed migration left behind and prune update staging from the cache Oct 9, 2026
@MotherSphere
MotherSphere merged commit 6fa151d into main Oct 9, 2026
5 checks passed
@MotherSphere
MotherSphere deleted the fix/migration-fallback-and-staging-prune branch October 9, 2026 12:30
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