Skip to main content

ralph/migration/
registry.rs

1//! Migration registry containing all defined migrations.
2//!
3//! Responsibilities:
4//! - Define the static list of all migrations in chronological order.
5//! - Provide a central place to register new migrations.
6//!
7//! Not handled here:
8//! - Migration execution logic (see `super::mod.rs`).
9//! - Individual migration implementations (see `config_migrations.rs`, `file_migrations.rs`).
10//!
11//! Invariants/assumptions:
12//! - Migrations are ordered chronologically (oldest first).
13//! - Each migration has a unique ID that never changes.
14//! - New migrations are appended to the end of the list.
15
16use super::{Migration, MigrationType};
17
18/// The static registry of all migrations.
19///
20/// Add new migrations to the end of this list. Each migration should have:
21/// - A unique ID (convention: `<type>_<description>_<YYYY>_<MM>`)
22/// - A clear description of what it does
23/// - The appropriate MigrationType
24///
25/// Example:
26/// ```rust,ignore
27/// use ralph::migration::{Migration, MigrationType};
28///
29/// pub static MIGRATIONS: &[Migration] = &[
30///     Migration {
31///         id: "config_key_rename_2026_02",
32///         description: "Rename agent.runner_cli to agent.runner_options",
33///         migration_type: MigrationType::ConfigKeyRename {
34///             old_key: "agent.runner_cli",
35///             new_key: "agent.runner_options",
36///         },
37///     },
38/// ];
39/// ```
40pub static MIGRATIONS: &[Migration] = &[
41    Migration {
42        id: "config_key_rename_parallel_worktree_root_2026_02",
43        description: "Rename parallel.worktree_root to parallel.workspace_root",
44        migration_type: MigrationType::ConfigKeyRename {
45            old_key: "parallel.worktree_root",
46            new_key: "parallel.workspace_root",
47        },
48    },
49    Migration {
50        id: "config_key_remove_agent_update_task_before_run_2026_02",
51        description: "Remove deprecated agent.update_task_before_run key",
52        migration_type: MigrationType::ConfigKeyRemove {
53            key: "agent.update_task_before_run",
54        },
55    },
56    Migration {
57        id: "config_key_remove_agent_fail_on_prerun_update_error_2026_02",
58        description: "Remove deprecated agent.fail_on_prerun_update_error key",
59        migration_type: MigrationType::ConfigKeyRemove {
60            key: "agent.fail_on_prerun_update_error",
61        },
62    },
63    Migration {
64        id: "file_rename_queue_json_to_jsonc_2026_02",
65        description: "Migrate queue.json to queue.jsonc for JSONC comment support and remove legacy queue.json",
66        migration_type: MigrationType::FileRename {
67            old_path: ".ralph/queue.json",
68            new_path: ".ralph/queue.jsonc",
69        },
70    },
71    Migration {
72        id: "file_rename_done_json_to_jsonc_2026_02",
73        description: "Migrate done.json to done.jsonc for JSONC comment support and remove legacy done.json",
74        migration_type: MigrationType::FileRename {
75            old_path: ".ralph/done.json",
76            new_path: ".ralph/done.jsonc",
77        },
78    },
79    Migration {
80        id: "file_rename_config_json_to_jsonc_2026_02",
81        description: "Migrate config.json to config.jsonc for JSONC comment support and remove legacy config.json",
82        migration_type: MigrationType::FileRename {
83            old_path: ".ralph/config.json",
84            new_path: ".ralph/config.jsonc",
85        },
86    },
87    Migration {
88        id: "file_cleanup_legacy_queue_json_after_jsonc_2026_02",
89        description: "Remove legacy queue.json when queue.jsonc already exists",
90        migration_type: MigrationType::FileRename {
91            old_path: ".ralph/queue.json",
92            new_path: ".ralph/queue.jsonc",
93        },
94    },
95    Migration {
96        id: "file_cleanup_legacy_done_json_after_jsonc_2026_02",
97        description: "Remove legacy done.json when done.jsonc already exists",
98        migration_type: MigrationType::FileRename {
99            old_path: ".ralph/done.json",
100            new_path: ".ralph/done.jsonc",
101        },
102    },
103    Migration {
104        id: "config_ci_gate_rewrite_2026_03",
105        description: "Rewrite legacy agent.ci_gate_command/ci_gate_enabled into structured agent.ci_gate config",
106        migration_type: MigrationType::ConfigCiGateRewrite,
107    },
108    Migration {
109        id: "file_cleanup_legacy_config_json_after_jsonc_2026_02",
110        description: "Remove legacy config.json when config.jsonc already exists",
111        migration_type: MigrationType::FileRename {
112            old_path: ".ralph/config.json",
113            new_path: ".ralph/config.jsonc",
114        },
115    },
116];
117
118/// Get a migration by its ID.
119pub fn get_migration_by_id(id: &str) -> Option<&'static Migration> {
120    MIGRATIONS.iter().find(|m| m.id == id)
121}
122
123/// Get all migration IDs.
124pub fn get_all_migration_ids() -> Vec<&'static str> {
125    MIGRATIONS.iter().map(|m| m.id).collect()
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn migration_ids_are_unique() {
134        let ids: Vec<&str> = MIGRATIONS.iter().map(|m| m.id).collect();
135        let unique_ids: std::collections::HashSet<&str> = ids.iter().cloned().collect();
136
137        assert_eq!(ids.len(), unique_ids.len(), "Migration IDs must be unique");
138    }
139
140    #[test]
141    fn get_migration_by_id_finds_existing() {
142        assert!(get_migration_by_id("config_key_rename_parallel_worktree_root_2026_02").is_some());
143        assert!(get_migration_by_id("nonexistent").is_none());
144    }
145
146    #[test]
147    fn get_all_migration_ids_returns_correct_count() {
148        let ids = get_all_migration_ids();
149        assert_eq!(ids.len(), MIGRATIONS.len());
150    }
151}