diesel_migration_runner/
main.rs1use 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 #[clap(long, env = "SCHEMA_FILE")]
28 schema_file: Utf8PathBuf,
29}
30
31#[tokio::main]
32async fn main() {
33 let args = Args::parse();
34
35 env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
36
37 log::info!("running container load tool");
38
39 let output = tokio::process::Command::new(args.database_image_load_tool)
40 .output()
41 .await
42 .expect("failed to run database image load tool");
43
44 if !output.status.success() {
45 std::io::stderr().write_all(&output.stdout).expect("failed to write stdout");
46 std::io::stderr().write_all(&output.stderr).expect("failed to write stderr");
47 panic!("failed to run database image load tool");
48 }
49
50 let container_digest = String::from_utf8(output.stdout).expect("failed to read stdout");
51 let container_digest = container_digest.lines().next().expect("failed to read container digest");
52 let container_digest = container_digest.trim();
53 let (image, tag) = container_digest.split_once(':').expect("failed to read container digest");
54
55 log::info!("starting container: {image}:{tag}");
56
57 let container = testcontainers::GenericImage::new(image, tag)
58 .with_exposed_port(5432.tcp())
59 .with_wait_for(testcontainers::core::WaitFor::message_on_either_std(
60 "database system is ready to accept connections",
61 ))
62 .with_env_var("POSTGRES_PASSWORD", "scuffle")
63 .start()
64 .await
65 .expect("failed to start container");
66
67 let port = container.get_host_port_ipv4(5432).await.expect("failed to get host port");
68 let url = container.get_host().await.expect("failed to get host");
69
70 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
71
72 let db_url = format!("postgres://postgres:scuffle@{url}:{port}/scuffle");
73 log::info!("database url: {db_url}");
74
75 log::info!("applying migrations");
76 let output = tokio::process::Command::new(&args.diesel_cli_tool)
77 .env("DATABASE_URL", &db_url)
78 .arg("database")
79 .arg("reset")
80 .output()
81 .await
82 .expect("failed to run diesel cli tool");
83
84 if !output.status.success() {
85 std::io::stderr().write_all(&output.stdout).expect("failed to write stdout");
86 std::io::stderr().write_all(&output.stderr).expect("failed to write stderr");
87 panic!("failed to run diesel cli tool");
88 }
89
90 let output = tokio::process::Command::new(&args.rustfmt_tool)
91 .arg("--config-path")
92 .arg(&args.rustfmt_config_path)
93 .arg(&args.schema_file)
94 .output()
95 .await
96 .expect("failed to run rustfmt");
97 if !output.status.success() {
98 std::io::stderr().write_all(&output.stdout).expect("failed to write stdout");
99 std::io::stderr().write_all(&output.stderr).expect("failed to write stderr");
100 panic!("failed to run rustfmt");
101 }
102
103 let content = std::fs::read_to_string(&args.schema_file).expect("failed to read schema file");
104 let json =
105 serde_json::to_string(&BTreeMap::from_iter([(args.schema_file, content)])).expect("failed to serialize output");
106
107 std::fs::write(args.output_file, json).expect("failed to write output file");
108}