Add sqlite for tracking build history.

This commit is contained in:
Tom Alexander
2026-02-14 22:15:09 -05:00
parent 0e0c5dac80
commit 7eb36ce0a4
16 changed files with 442 additions and 30 deletions

166
Cargo.lock generated
View File

@@ -17,6 +17,21 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.21" version = "0.6.21"
@@ -204,12 +219,34 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
version = "1.2.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.4" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.54" version = "4.5.54"
@@ -270,6 +307,12 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.17" version = "0.2.17"
@@ -405,6 +448,12 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.11.1" version = "0.11.1"
@@ -702,6 +751,30 @@ dependencies = [
"tokio-io-timeout", "tokio-io-timeout",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "icu_collections" name = "icu_collections"
version = "2.1.1" version = "2.1.1"
@@ -893,6 +966,7 @@ version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [ dependencies = [
"cc",
"pkg-config", "pkg-config",
"vcpkg", "vcpkg",
] ]
@@ -1177,7 +1251,7 @@ dependencies = [
"libc", "libc",
"redox_syscall 0.5.18", "redox_syscall 0.5.18",
"smallvec", "smallvec",
"windows-link", "windows-link 0.2.1",
] ]
[[package]] [[package]]
@@ -1523,6 +1597,12 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.5" version = "1.4.5"
@@ -1617,6 +1697,7 @@ checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
"chrono",
"crc", "crc",
"crossbeam-queue", "crossbeam-queue",
"either", "either",
@@ -1637,8 +1718,11 @@ dependencies = [
"sha2", "sha2",
"smallvec", "smallvec",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio",
"tokio-stream",
"tracing", "tracing",
"url", "url",
"uuid",
] ]
[[package]] [[package]]
@@ -1675,6 +1759,7 @@ dependencies = [
"sqlx-postgres", "sqlx-postgres",
"sqlx-sqlite", "sqlx-sqlite",
"syn 2.0.114", "syn 2.0.114",
"tokio",
"url", "url",
] ]
@@ -1689,6 +1774,7 @@ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"byteorder", "byteorder",
"bytes", "bytes",
"chrono",
"crc", "crc",
"digest", "digest",
"dotenvy", "dotenvy",
@@ -1717,6 +1803,7 @@ dependencies = [
"stringprep", "stringprep",
"thiserror 2.0.18", "thiserror 2.0.18",
"tracing", "tracing",
"uuid",
"whoami", "whoami",
] ]
@@ -1730,6 +1817,7 @@ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bitflags 2.11.0", "bitflags 2.11.0",
"byteorder", "byteorder",
"chrono",
"crc", "crc",
"dotenvy", "dotenvy",
"etcetera", "etcetera",
@@ -1754,6 +1842,7 @@ dependencies = [
"stringprep", "stringprep",
"thiserror 2.0.18", "thiserror 2.0.18",
"tracing", "tracing",
"uuid",
"whoami", "whoami",
] ]
@@ -1764,6 +1853,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
dependencies = [ dependencies = [
"atoi", "atoi",
"chrono",
"flume", "flume",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@@ -1779,6 +1869,7 @@ dependencies = [
"thiserror 2.0.18", "thiserror 2.0.18",
"tracing", "tracing",
"url", "url",
"uuid",
] ]
[[package]] [[package]]
@@ -2233,6 +2324,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "valuable" name = "valuable"
version = "0.1.1" version = "0.1.1"
@@ -2327,12 +2428,71 @@ dependencies = [
"wasite", "wasite",
] ]
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link 0.1.3",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"
@@ -2366,7 +2526,7 @@ version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.2.1",
] ]
[[package]] [[package]]
@@ -2406,7 +2566,7 @@ version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.2.1",
"windows_aarch64_gnullvm 0.53.1", "windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1", "windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1", "windows_i686_gnu 0.53.1",

View File

@@ -15,7 +15,7 @@ opentelemetry-semantic-conventions = { version = "0.12.0", optional = true }
serde = { version = "1.0.228", default-features = false, features = ["std", "derive"] } serde = { version = "1.0.228", default-features = false, features = ["std", "derive"] }
serde_json = { version = "1.0.149", default-features = false, features = ["std"] } serde_json = { version = "1.0.149", default-features = false, features = ["std"] }
sha2 = { version = "0.10.9", default-features = false, features = ["std"] } sha2 = { version = "0.10.9", default-features = false, features = ["std"] }
sqlx = "0.8.6" sqlx = { version = "0.8.6", default-features = false, features = ["runtime-tokio", "sqlite", "migrate", "macros", "uuid", "chrono"] }
tokio = { version = "1.49.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util", "process"] } tokio = { version = "1.49.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util", "process"] }
toml = { version = "0.9.11", default-features = false, features = ["display", "parse", "serde", "std"] } toml = { version = "0.9.11", default-features = false, features = ["display", "parse", "serde", "std"] }
tracing = { version = "0.1.37", optional = true } tracing = { version = "0.1.37", optional = true }
@@ -28,3 +28,7 @@ url = { version = "2.5.8", default-features = false, features = ["std"] }
inherits = "release" inherits = "release"
lto = true lto = true
strip = "symbols" strip = "symbols"
[profile.dev.package.sqlx-macros]
# Faster compile-time verified macros
opt-level = 3

48
flake.lock generated Normal file
View File

@@ -0,0 +1,48 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1771008912,
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1771125043,
"narHash": "sha256-ldf/s49n6rOAxl7pYLJGGS1N/assoHkCOWdEdLyNZkc=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "4912f951a26dc8142b176be2c2ad834319dc06e8",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

66
flake.nix Normal file
View File

@@ -0,0 +1,66 @@
{
description = "nix_builder development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs = {
nixpkgs.follows = "nixpkgs";
};
};
};
outputs =
{
self,
nixpkgs,
rust-overlay,
}:
let
forAllSystems =
func:
builtins.listToAttrs (
map (system: {
name = system;
value = func system;
}) nixpkgs.lib.systems.flakeExposed
);
in
{
devShells = forAllSystems (
system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit system overlays;
};
rustToolchain = pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
in
{
default = pkgs.mkShell {
nativeBuildInputs = [
pkgs.pkg-config
rustToolchain
];
buildInputs = with pkgs; [
sqlx-cli # For sqlx CLI to manage migrations
sqlite # To access the database (sqlite is bundled into the nix_builder binary but this is for manually accessing the db).
];
shellHook = ''
cat <<EOF
nix_builder dev shell
Create new migrations with:
sqlx migrate add -r <name>
Generate metadata for query!() macros:
cargo sqlx prepare
EOF
'';
};
}
);
};
}

View File

@@ -0,0 +1 @@
DROP TABLE build;

View File

@@ -0,0 +1,7 @@
CREATE TABLE build (
id INTEGER NOT NULL PRIMARY KEY,
start_time INTEGER NOT NULL,
end_time INTEGER,
status INTEGER,
target TEXT NOT NULL
) STRICT;

View File

@@ -1,18 +1,32 @@
use crate::Result;
use crate::cli::parameters::BuildArgs; use crate::cli::parameters::BuildArgs;
use crate::config::Config; use crate::config::Config;
use crate::config::TargetConfig; use crate::config::TargetConfig;
use crate::error::CustomError; use crate::database::db_handle::DbHandle;
use crate::fs_util::assert_directory; use crate::fs_util::assert_directory;
use crate::fs_util::is_git_repo; use crate::fs_util::is_git_repo;
use crate::git_util::git_force_into_state; use crate::git_util::git_force_into_state;
use crate::git_util::git_init_at_rev; use crate::git_util::git_init_at_rev;
use crate::nix_util::nixos_build_target; use crate::nix_util::nixos_build_target;
pub(crate) async fn run_build(args: BuildArgs) -> Result<(), CustomError> { pub(crate) async fn run_build(args: BuildArgs) -> Result<()> {
println!("{:?}", args); println!("{:?}", args);
let config = Config::load_from_file(args.config).await?; let config = Config::load_from_file(args.config).await?;
println!("{:?}", config); println!("{:?}", config);
let database_path = config.get_database_path()?;
let database_parent = database_path
.parent()
.expect("Database should exist in a folder.");
let database_path = database_path.to_string_lossy();
assert_directory!(
database_parent,
"Creating database directory {}",
database_parent.to_string_lossy()
);
let db_handle = DbHandle::new(Some(database_path)).await?;
for target_name in args.target { for target_name in args.target {
let target_config = { let target_config = {
let target_config = config.get_target_config(&target_name)?; let target_config = config.get_target_config(&target_name)?;
@@ -30,15 +44,12 @@ pub(crate) async fn run_build(args: BuildArgs) -> Result<(), CustomError> {
Ok(()) Ok(())
} }
async fn prepare_flake_repo( async fn prepare_flake_repo(config_root: &Config, target_config: &TargetConfig) -> Result<()> {
config_root: &Config,
target_config: &TargetConfig,
) -> Result<(), CustomError> {
let repo_directory = target_config.get_repo_directory(config_root)?; let repo_directory = target_config.get_repo_directory(config_root)?;
assert_directory!( assert_directory!(
&repo_directory, &repo_directory,
"Creating repo directory {}", "Creating repo directory {}",
(&repo_directory).to_string_lossy() repo_directory.to_string_lossy()
); );
if is_git_repo(&repo_directory).await? { if is_git_repo(&repo_directory).await? {
@@ -63,16 +74,13 @@ async fn prepare_flake_repo(
Ok(()) Ok(())
} }
async fn build_target( async fn build_target(config_root: &Config, target_config: &TargetConfig) -> Result<()> {
config_root: &Config,
target_config: &TargetConfig,
) -> Result<(), CustomError> {
let flake_directory = target_config.get_flake_directory(config_root)?; let flake_directory = target_config.get_flake_directory(config_root)?;
let build_directory = target_config.get_build_directory(config_root)?; let build_directory = target_config.get_build_directory(config_root)?;
assert_directory!( assert_directory!(
&build_directory, &build_directory,
"Creating build directory {}", "Creating build directory {}",
(&build_directory).to_string_lossy() build_directory.to_string_lossy()
); );
nixos_build_target(build_directory, flake_directory, target_config.get_attr()?).await?; nixos_build_target(build_directory, flake_directory, target_config.get_attr()?).await?;

View File

@@ -73,4 +73,11 @@ impl Config {
let work_dir = current_dir.join("work"); let work_dir = current_dir.join("work");
Ok(Cow::Owned(work_dir)) Ok(Cow::Owned(work_dir))
} }
/// The path to the sqlite database where run history is stored.
pub(crate) fn get_database_path(&self) -> Result<Cow<'_, Path>, CustomError> {
let output_directory = self.get_output_directory()?;
let database_path = output_directory.join("nix_builder.sqlite");
Ok(Cow::Owned(database_path))
}
} }

80
src/database/db_handle.rs Normal file
View File

@@ -0,0 +1,80 @@
use sqlx::Executor;
use sqlx::Pool;
use sqlx::Sqlite;
use sqlx::migrate::MigrateDatabase;
use sqlx::sqlite::SqlitePoolOptions;
use tracing::info;
use tracing::warn;
use super::migration::run_migrations;
use crate::Result;
pub(crate) struct DbHandle {
pub(crate) conn: Pool<Sqlite>,
}
impl DbHandle {
pub(crate) async fn new<P: AsRef<str>>(db_path: Option<P>) -> Result<DbHandle> {
let db_path = db_path.as_ref().map(|p| p.as_ref());
let options = SqlitePoolOptions::new()
.max_connections(5)
.test_before_acquire(true)
.after_connect(|conn, _meta| {
Box::pin(async move {
// Enforce foreign keys.
conn.execute("PRAGMA foreign_keys = ON;").await?;
// Allows writes at the same time as reads.
conn.execute("PRAGMA journal_mode = WAL;").await?;
// Do not sync to disk after *every* write.
conn.execute("PRAGMA synchronous = NORMAL;").await?;
// Keep 10k database pages in memory (~40MiB).
conn.execute("PRAGMA cache_size = 10000;").await?;
// Stores temporary tables, indexes, and sorting operations in memory.
conn.execute("PRAGMA temp_store = MEMORY;").await?;
// Use mmap to access database.
conn.execute("PRAGMA mmap_size = 268435456;").await?;
// Clear space of deleted rows at transaction end.
conn.execute("PRAGMA auto_vacuum = FULL;").await?;
Ok(())
})
})
.after_release(|conn, _meta| {
Box::pin(async move {
// Attempt to optimize the database. (Currently just runs ANALYZE)
conn.execute("PRAGMA optimize;").await?;
// Rebuild the DB file which defragments and clears out deleted pages.
conn.execute("VACUUM;").await?;
Ok(true)
})
});
let conn = match db_path {
Some(path) => {
info!("Connecting to sqlite database at {path}");
if !Sqlite::database_exists(path).await.unwrap_or(false) {
info!("Creating a new sqlite database at {path}");
Sqlite::create_database(path).await.unwrap();
} else {
info!("Connecting to existing sqlite database at {path}");
}
let full_url = format!("sqlite:{path}");
options.connect(&full_url).await?
}
None => {
warn!("No sqlite_path set in config. Using an in-memory database.");
// We force it to a single connection that never dies or else the data and schema in the in-memory DB is lost.
options
.min_connections(1)
.max_connections(1)
.idle_timeout(None)
.max_lifetime(None)
.connect("sqlite::memory:")
.await?
}
};
run_migrations(&conn).await?;
Ok(DbHandle { conn })
}
}

15
src/database/migration.rs Normal file
View File

@@ -0,0 +1,15 @@
use std::ops::Deref;
use sqlx::Acquire;
use sqlx::migrate::Migrate;
use crate::Result;
pub(crate) async fn run_migrations<'a, A>(db: A) -> Result<()>
where
A: Acquire<'a>,
<A::Connection as Deref>::Target: Migrate,
{
sqlx::migrate!("./migrations").run(db).await?;
Ok(())
}

2
src/database/mod.rs Normal file
View File

@@ -0,0 +1,2 @@
pub(crate) mod db_handle;
pub(crate) mod migration;

View File

@@ -14,6 +14,8 @@ pub(crate) enum CustomError {
FromUtf8(#[allow(dead_code)] FromUtf8Error), FromUtf8(#[allow(dead_code)] FromUtf8Error),
PathStripPrefix(#[allow(dead_code)] std::path::StripPrefixError), PathStripPrefix(#[allow(dead_code)] std::path::StripPrefixError),
UrlParseError(#[allow(dead_code)] url::ParseError), UrlParseError(#[allow(dead_code)] url::ParseError),
Migrate(#[allow(dead_code)] sqlx::migrate::MigrateError),
Sql(#[allow(dead_code)] sqlx::Error),
} }
impl From<std::io::Error> for CustomError { impl From<std::io::Error> for CustomError {
@@ -81,3 +83,15 @@ impl From<url::ParseError> for CustomError {
CustomError::UrlParseError(value) CustomError::UrlParseError(value)
} }
} }
impl From<sqlx::migrate::MigrateError> for CustomError {
fn from(value: sqlx::migrate::MigrateError) -> Self {
CustomError::Migrate(value)
}
}
impl From<sqlx::Error> for CustomError {
fn from(value: sqlx::Error) -> Self {
CustomError::Sql(value)
}
}

View File

@@ -23,10 +23,7 @@ use crate::error::CustomError;
pub(crate) async fn is_directory<D: AsRef<Path>>(dir: D) -> Result<bool, CustomError> { pub(crate) async fn is_directory<D: AsRef<Path>>(dir: D) -> Result<bool, CustomError> {
let metadata = tokio::fs::metadata(dir).await; let metadata = tokio::fs::metadata(dir).await;
let result = match metadata { let result = matches!(metadata, Ok(metadata) if metadata.is_dir());
Ok(metadata) if metadata.is_dir() => true,
_ => false,
};
Ok(result) Ok(result)
} }

View File

@@ -15,7 +15,7 @@ where
let dest = AsRef::<OsStr>::as_ref(dest.as_ref()); let dest = AsRef::<OsStr>::as_ref(dest.as_ref());
command.arg("-C"); command.arg("-C");
command.arg(dest); command.arg(dest);
command.args(&["init"]); command.args(["init"]);
command.arg(format!("--initial-branch={}", branch.as_ref())); command.arg(format!("--initial-branch={}", branch.as_ref()));
command.kill_on_drop(true); command.kill_on_drop(true);
let output = command.output().await?; let output = command.output().await?;
@@ -43,7 +43,7 @@ where
let mut command = Command::new("git"); let mut command = Command::new("git");
command.arg("-C"); command.arg("-C");
command.arg(dest); command.arg(dest);
command.args(&["remote", "add"]); command.args(["remote", "add"]);
command.arg(name); command.arg(name);
command.arg(url); command.arg(url);
command.kill_on_drop(true); command.kill_on_drop(true);
@@ -78,7 +78,7 @@ where
let mut command = Command::new("git"); let mut command = Command::new("git");
command.arg("-C"); command.arg("-C");
command.arg(dest); command.arg(dest);
command.args(&["fetch"]); command.args(["fetch"]);
if let Some(d) = depth { if let Some(d) = depth {
command.arg(format!("--depth={}", d)); command.arg(format!("--depth={}", d));
} }
@@ -118,7 +118,7 @@ where
let mut command = Command::new("git"); let mut command = Command::new("git");
command.arg("-C"); command.arg("-C");
command.arg(dest); command.arg(dest);
command.args(&["checkout"]); command.args(["checkout"]);
if force { if force {
command.arg("--force"); command.arg("--force");
} }
@@ -149,7 +149,7 @@ where
let mut command = Command::new("git"); let mut command = Command::new("git");
command.arg("-C"); command.arg("-C");
command.arg(dest); command.arg(dest);
command.args(&["remote", "get-url"]); command.args(["remote", "get-url"]);
command.arg(remote.as_ref()); command.arg(remote.as_ref());
command.kill_on_drop(true); command.kill_on_drop(true);
let output = command.output().await?; let output = command.output().await?;
@@ -181,7 +181,7 @@ where
let mut command = Command::new("git"); let mut command = Command::new("git");
command.arg("-C"); command.arg("-C");
command.arg(dest); command.arg(dest);
command.args(&["reset"]); command.args(["reset"]);
if hard { if hard {
command.arg("--hard"); command.arg("--hard");
} }
@@ -213,7 +213,7 @@ where
let mut command = Command::new("git"); let mut command = Command::new("git");
command.arg("-C"); command.arg("-C");
command.arg(dest); command.arg(dest);
command.args(&["clean"]); command.args(["clean"]);
if recurse_into_untracked_directories { if recurse_into_untracked_directories {
command.arg("-d"); command.arg("-d");
} }

View File

@@ -12,18 +12,21 @@ use self::init_tracing::shutdown_telemetry;
mod cli; mod cli;
mod command; mod command;
mod config; mod config;
mod database;
mod error; mod error;
mod fs_util; mod fs_util;
mod git_util; mod git_util;
mod init_tracing; mod init_tracing;
mod nix_util; mod nix_util;
fn main() -> Result<ExitCode, CustomError> { pub(crate) type Result<T> = std::result::Result<T, CustomError>;
fn main() -> Result<ExitCode> {
let rt = tokio::runtime::Runtime::new()?; let rt = tokio::runtime::Runtime::new()?;
rt.block_on(async { main_body().await }) rt.block_on(async { main_body().await })
} }
async fn main_body() -> Result<ExitCode, CustomError> { async fn main_body() -> Result<ExitCode> {
init_telemetry().expect("Telemetry should initialize successfully."); init_telemetry().expect("Telemetry should initialize successfully.");
let args = Cli::parse(); let args = Cli::parse();
match args.command { match args.command {

View File

@@ -29,7 +29,7 @@ where
// nixos-rebuild build --show-trace --sudo --max-jobs "$JOBS" --flake "$DIR/../../#odo" --log-format internal-json -v "${@}" // nixos-rebuild build --show-trace --sudo --max-jobs "$JOBS" --flake "$DIR/../../#odo" --log-format internal-json -v "${@}"
let mut command = Command::new("nixos-rebuild"); let mut command = Command::new("nixos-rebuild");
command.current_dir(build_path); command.current_dir(build_path);
command.args(&[ command.args([
"build", "build",
"--show-trace", "--show-trace",
"--sudo", "--sudo",