diesel_migration_runner/
main.rs

1use std::collections::BTreeMap;
2use std::io::Write;
3
4use camino::Utf8PathBuf;
5use clap::Parser;
6use testcontainers::ImageExt;
7use testcontainers::core::IntoContainerPort;
8use testcontainers::runners::AsyncRunner;
9
10#[derive(clap::Parser)]
11struct Args {
12    #[clap(long, env = "DIESEL_CLI_TOOL")]
13    diesel_cli_tool: Utf8PathBuf,
14
15    #[clap(long, env = "DATABASE_IMAGE_LOAD_TOOL")]
16    database_image_load_tool: Utf8PathBuf,
17
18    #[clap(long, env = "OUTPUT_FILE")]
19    output_file: Utf8PathBuf,
20
21    #[clap(long, env = "RUSTFMT_TOOL")]
22    rustfmt_tool: Utf8PathBuf,
23
24    #[clap(long, env = "RUSTFMT_CONFIG_PATH")]
25    rustfmt_config_path: Utf8PathBuf,
26
27    schema_files: Vec<Utf8PathBuf>,
28}
29
30#[tokio::main]
31async fn main() {
32    let args = Args::parse();
33
34    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
35
36    log::info!("running container load tool");
37
38    let output = tokio::process::Command::new(args.database_image_load_tool)
39        .output()
40        .await
41        .expect("failed to run database image load tool");
42
43    if !output.status.success() {
44        std::io::stderr().write_all(&output.stdout).expect("failed to write stdout");
45        std::io::stderr().write_all(&output.stderr).expect("failed to write stderr");
46        panic!("failed to run database image load tool");
47    }
48
49    let container_digest = String::from_utf8(output.stdout).expect("failed to read stdout");
50    let container_digest = container_digest.lines().next().expect("failed to read container digest");
51    let container_digest = container_digest.trim();
52    let (image, tag) = container_digest.split_once(':').expect("failed to read container digest");
53
54    log::info!("starting container: {image}:{tag}");
55
56    let container = testcontainers::GenericImage::new(image, tag)
57        .with_exposed_port(5432.tcp())
58        .with_wait_for(testcontainers::core::WaitFor::message_on_either_std(
59            "database system is ready to accept connections",
60        ))
61        .with_env_var("POSTGRES_PASSWORD", "scuffle")
62        .start()
63        .await
64        .expect("failed to start container");
65
66    let port = container.get_host_port_ipv4(5432).await.expect("failed to get host port");
67    let url = container.get_host().await.expect("failed to get host");
68
69    tokio::time::sleep(std::time::Duration::from_secs(1)).await;
70
71    let db_url = format!("postgres://postgres:scuffle@{url}:{port}/scuffle");
72    log::info!("database url: {db_url}");
73
74    log::info!("applying migrations");
75    let output = tokio::process::Command::new(&args.diesel_cli_tool)
76        .env("DATABASE_URL", &db_url)
77        .arg("database")
78        .arg("reset")
79        .output()
80        .await
81        .expect("failed to run diesel cli tool");
82
83    if !output.status.success() {
84        std::io::stderr().write_all(&output.stdout).expect("failed to write stdout");
85        std::io::stderr().write_all(&output.stderr).expect("failed to write stderr");
86        panic!("failed to run diesel cli tool");
87    }
88
89    let mut outputs = BTreeMap::new();
90
91    for schema_file in args.schema_files {
92        let output = tokio::process::Command::new(&args.rustfmt_tool)
93            .arg("--config-path")
94            .arg(&args.rustfmt_config_path)
95            .arg(&schema_file)
96            .output()
97            .await
98            .expect("failed to run rustfmt");
99        if !output.status.success() {
100            std::io::stderr().write_all(&output.stdout).expect("failed to write stdout");
101            std::io::stderr().write_all(&output.stderr).expect("failed to write stderr");
102            panic!("failed to run rustfmt");
103        }
104
105        let content = std::fs::read_to_string(&schema_file).expect("failed to read schema file");
106        outputs.insert(schema_file, content);
107    }
108
109    let json = serde_json::to_string_pretty(&outputs).expect("failed to write output file");
110
111    std::fs::write(args.output_file, json).expect("failed to write output file");
112}