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 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}