backend/migrations/
m20250805230408_fix_automerge_storage.rs1use sqlx::{PgConnection, Postgres};
2use sqlx_migrator::Migration;
3use sqlx_migrator::Operation;
4use sqlx_migrator::error::Error;
5use sqlx_migrator::vec_box;
6use std::env;
7use std::path::{Path, PathBuf};
8use std::process::Command;
9use std::process::ExitStatus;
10
11pub(crate) struct FixAutomergeStorage;
12#[async_trait::async_trait]
13impl Migration<Postgres> for FixAutomergeStorage {
14 fn app(&self) -> &str {
15 "backend"
16 }
17 fn name(&self) -> &str {
18 "20250805230408_fix_automerge_storage"
19 }
20 fn parents(&self) -> Vec<Box<dyn Migration<Postgres>>> {
21 vec![]
22 }
23 fn operations(&self) -> Vec<Box<dyn Operation<Postgres>>> {
24 vec_box![MigrationOperation]
25 }
26
27 fn is_atomic(&self) -> bool {
28 false
29 }
30}
31
32struct MigrationOperation;
33#[async_trait::async_trait]
34impl Operation<Postgres> for MigrationOperation {
35 async fn up(&self, _: &mut PgConnection) -> Result<(), Error> {
36 if env::var_os("INVOCATION_ID").is_some() {
37 run_automerge_migration_during_deployment()?;
38 } else {
39 run_automerge_migration_during_development()?;
40 }
41
42 Ok(())
43 }
44
45 async fn down(&self, _: &mut PgConnection) -> Result<(), Error> {
46 Ok(())
47 }
48}
49
50fn run_automerge_migration_during_development()
51-> Result<(), Box<dyn std::error::Error + Send + Sync>> {
52 let cwd = env::current_dir().map_err(|e| format!("Failed to get current directory: {e}"))?;
53
54 let git_root = find_git_root(&cwd).ok_or("No .git root found")?;
55
56 let automerge_server_dir = git_root.join("packages").join("automerge-doc-server");
57
58 let status = Command::new("npm")
59 .args(["run", "main", "--", "--migrate", "fix_automerge_storage"])
60 .current_dir(&automerge_server_dir)
61 .status()?;
62
63 check_status(status, "`npm run migrate-storage`")?;
64
65 Ok(())
66}
67
68fn run_automerge_migration_during_deployment()
69-> Result<(), Box<dyn std::error::Error + Send + Sync>> {
70 let status = Command::new("automerge-doc-server")
71 .args(["--migrate", "fix_automerge_storage"])
72 .status()
73 .map_err(|e| format!("Failed to run `automerge-doc-server`: {e}"))?;
74
75 check_status(status, "`automerge-doc-server --migrate automerge_storage`")?;
76
77 Ok(())
78}
79
80fn check_status(
81 status: ExitStatus,
82 command: &str,
83) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
84 if status.success() {
85 println!("{command} succeeded");
86 Ok(())
87 } else {
88 Err(format!("{command} failed with exit code {:?}", status.code()).into())
89 }
90}
91
92fn find_git_root(start: impl AsRef<Path>) -> Option<PathBuf> {
93 let mut dir = start.as_ref().canonicalize().ok()?;
94
95 loop {
96 if dir.join(".git").is_dir() {
97 return Some(dir);
98 }
99
100 if !dir.pop() {
101 break;
102 }
103 }
104
105 None
106}