Commit

Author:

Hash:

Timestamp:

+123 -114 +/-5 browse

Kevin Schoon [me@kevinschoon.com]

706b2f060f79240c4ee7b6e5b9f202cb6737d382

Sun, 28 Jan 2024 17:17:19 +0000 (1.4 years ago)

update ayllu-build to use derive based clap
1diff --git a/Cargo.lock b/Cargo.lock
2index 2f132f5..03c4cdf 100644
3--- a/Cargo.lock
4+++ b/Cargo.lock
5 @@ -574,6 +574,7 @@ dependencies = [
6 "capnp",
7 "capnp-rpc",
8 "clap 4.4.8",
9+ "clap_complete",
10 "nickel-lang-core",
11 "petgraph",
12 "rand",
13 @@ -986,9 +987,9 @@ dependencies = [
14
15 [[package]]
16 name = "clap_complete"
17- version = "4.4.5"
18+ version = "4.4.9"
19 source = "registry+https://github.com/rust-lang/crates.io-index"
20- checksum = "a51919c5608a32e34ea1d6be321ad070065e17613e168c5b6977024290f2630b"
21+ checksum = "df631ae429f6613fcd3a7c1adbdb65f637271e561b03680adaa6573015dfb106"
22 dependencies = [
23 "clap 4.4.8",
24 ]
25 diff --git a/ayllu-build/Cargo.toml b/ayllu-build/Cargo.toml
26index e91dbbe..e832950 100644
27--- a/ayllu-build/Cargo.toml
28+++ b/ayllu-build/Cargo.toml
29 @@ -25,3 +25,4 @@ sqlx = { version = "0.7.3", features = ["sqlite", "macros"] }
30 tokio = { version = "1.34.0", features = ["full"] }
31 sysinfo = "0.29.11"
32 serde_json = "1.0.108"
33+ clap_complete = "4.4.9"
34 diff --git a/ayllu-build/src/config.rs b/ayllu-build/src/config.rs
35index c6c8b85..7f34e57 100644
36--- a/ayllu-build/src/config.rs
37+++ b/ayllu-build/src/config.rs
38 @@ -35,6 +35,7 @@ pub struct Builder {
39
40 #[derive(Deserialize, Serialize, Clone, Debug)]
41 pub struct Config {
42+ pub log_level: String,
43 pub builder: Builder,
44 }
45
46 diff --git a/ayllu-build/src/main.rs b/ayllu-build/src/main.rs
47index 1bfc4e9..8d00605 100644
48--- a/ayllu-build/src/main.rs
49+++ b/ayllu-build/src/main.rs
50 @@ -1,13 +1,14 @@
51- use std::io::stderr;
52+ use std::io::{stderr, stdout};
53 use std::path::{Path, PathBuf};
54+ use std::str::FromStr;
55
56- use anyhow::{Result};
57- use clap::{arg, value_parser, Command};
58- use tokio::{runtime::Builder as TokioBuilder, task::LocalSet};
59+ use anyhow::Result;
60+ use clap::{arg, Command, CommandFactory, Parser, Subcommand};
61+ use clap_complete::{generate, Generator, Shell};
62+ use tokio::task::LocalSet;
63 use tracing::{log::info, Level};
64
65-
66- use ayllu_database::{migrate, Builder};
67+ use ayllu_database::Builder;
68 mod config;
69 mod database_ext;
70 mod evaluate;
71 @@ -15,111 +16,113 @@ mod executor;
72 mod models;
73 mod rpc_server;
74
75- // TODO: migrate to derive based system
76+ #[derive(Parser)]
77+ #[command(author, version, about, long_about = None)]
78+ #[command(name = "ayllu-build")]
79+ #[command(about = "Ayllu Build System")]
80+ struct Cli {
81+ /// Path to your configuration file
82+ #[arg(short, long, value_name = "FILE")]
83+ config: Option<PathBuf>,
84+
85+ /// Sets the logging level
86+ #[arg(short, long, value_name = "LEVEL")]
87+ level: Option<Level>,
88+
89+ #[command(subcommand)]
90+ command: Commands,
91+ }
92
93- pub fn main() -> Result<()> {
94- let command = Command::new("ayllu-build")
95- .about("ayllu build system")
96- .arg(
97- arg!(-c --config <FILE> "optional path to a configuration file")
98- .id("config")
99- .required(false)
100- .value_parser(value_parser!(String)),
101- )
102- .arg(
103- arg!(-l --level <LEVEL> "logging level [ERROR,WARN,INFO,DEBUG,TRACE]")
104- .id("level")
105- .required(false)
106- .value_parser(value_parser!(Level)),
107- )
108- .subcommand_required(true)
109- .subcommand(Command::new("serve").about("run the build server and listen for commands"))
110- .subcommand(Command::new("migrate").about("initialize the database and apply migrations"))
111- .subcommand(
112- Command::new("evaluate")
113- .arg(
114- arg!([PATH] "alternate path for the build directory")
115- .default_value(".ayllu/build")
116- .required(false),
117- )
118- .about("evaluate a local build script"),
119- )
120- .subcommand(
121- Command::new("plan")
122- .arg(
123- arg!([PATH] "alternate path for the build directory")
124- .default_value(".ayllu/build")
125- .required(false),
126- )
127- .about("generate an execution plan and generate DOT output"),
128- );
129+ #[derive(Subcommand, Debug, PartialEq)]
130+ enum Commands {
131+ /// generate autocomplete commands for common shells
132+ Complete {
133+ #[arg(long)]
134+ shell: Shell,
135+ },
136+ /// run migrations against the database
137+ Migrate {},
138+ /// run the build server and listen for commands
139+ Serve {},
140+ /// evaluate a local build script
141+ Evaluate {
142+ /// Path to your configuration file
143+ #[arg(short, long, value_name = "FILE")]
144+ alternate_path: Option<PathBuf>,
145+ },
146+ /// generate an execution plan and output a DOT script
147+ Plan {
148+ /// Path to your configuration file
149+ #[arg(short, long, value_name = "FILE")]
150+ alternate_path: Option<PathBuf>,
151+ },
152+ }
153+
154+ fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
155+ generate(gen, cmd, cmd.get_name().to_string(), &mut stdout());
156+ }
157
158- let matches = command.get_matches();
159- let config_path = matches
160- .get_one::<String>("config")
161- .map(Path::new);
162- let config = config::load(config_path)?;
163- let config = config.builder;
164- let log_level = matches.get_one::<Level>("level").unwrap_or(&Level::INFO);
165+ #[tokio::main(flavor = "current_thread")]
166+ async fn main() -> Result<()> {
167+ let cli = Cli::parse();
168+ let cfg = config::load(cli.config.as_ref().map(|cfg| cfg.as_path()))?;
169+ let log_level = Level::from_str(&cfg.log_level)?;
170 tracing_subscriber::fmt()
171 .compact()
172- .with_max_level(*log_level)
173 .with_line_number(true)
174 .with_level(true)
175- .with_writer(stderr)
176+ .with_max_level(cli.level.unwrap_or(log_level))
177 .init();
178- info!("logger initialized");
179- if let Some(_matches) = matches.subcommand_matches("serve") {
180- TokioBuilder::new_current_thread()
181- .enable_all()
182- .build()
183- .unwrap()
184- .block_on(async move {
185- migrate(&config.database.path, "./migrations").await?;
186- LocalSet::new().run_until(rpc_server::serve(config)).await
187- })
188- .unwrap();
189- } else if let Some(_matches) = matches.subcommand_matches("migrate") {
190- TokioBuilder::new_current_thread()
191- .enable_all()
192- .build()
193- .unwrap()
194- .block_on(async move {
195- migrate(&config.database.path, "./migrations")
196- .await
197- .unwrap()
198- })
199- } else if let Some(matches) = matches.subcommand_matches("evaluate") {
200- TokioBuilder::new_current_thread()
201- .enable_all()
202- .build()
203- .unwrap()
204- .block_on(async move {
205- // TODO fix me
206- // migrate(&config.database.path, "./migrations").await?;
207- let db = Builder::default()
208- .url(&config.database.path)
209- .log_queries(*log_level == Level::DEBUG)
210- .build()
211- .await?;
212- let build_dir = matches.get_one::<String>("PATH").unwrap();
213- let rt = evaluate::RuntimeBuilder::default()
214- .source(evaluate::Source::Path(PathBuf::from(build_dir)))
215- .database(db)
216- .log_dir(Path::new(&config.log_path).to_path_buf())
217- .build();
218- rt.eval().await?;
219- let result: Result<()> = Ok(());
220- result
221- })?
222- } else if let Some(matches) = matches.subcommand_matches("plan") {
223- let build_dir = matches.get_one::<String>("PATH").unwrap();
224- let rt = evaluate::RuntimeBuilder::default()
225- .source(evaluate::Source::Path(PathBuf::from(build_dir)))
226- .build();
227- let graph = rt.plan()?;
228- let dot = rt.to_dot(&graph)?;
229- print!("{}", dot)
230- };
231- Ok(())
232+ tracing::info!("logger initialized");
233+ let default_build_dir = PathBuf::from_str(".ayllu/build").unwrap();
234+ match cli.command {
235+ Commands::Complete { shell } => {
236+ let mut cmd = Cli::command();
237+ print_completions(shell, &mut cmd);
238+ Ok(())
239+ }
240+ Commands::Migrate {} => {
241+ let db = Builder::default()
242+ .url(&cfg.builder.database.path)
243+ .migrations("./migrations")
244+ .build()
245+ .await?;
246+ db.close().await?;
247+ Ok(())
248+ }
249+ Commands::Serve {} => {
250+ LocalSet::new()
251+ .run_until(rpc_server::serve(&cfg.builder))
252+ .await?;
253+ Ok(())
254+ }
255+ Commands::Evaluate { alternate_path } => {
256+ // TODO fix me
257+ let db = Builder::default()
258+ .url(&cfg.builder.database.path)
259+ .log_queries(log_level == Level::DEBUG)
260+ .build()
261+ .await?;
262+ let rt = evaluate::RuntimeBuilder::default()
263+ .source(evaluate::Source::Path(
264+ alternate_path.unwrap_or(default_build_dir),
265+ ))
266+ .database(db)
267+ .log_dir(Path::new(&cfg.builder.log_path).to_path_buf())
268+ .build();
269+ rt.eval().await?;
270+ Ok(())
271+ }
272+ Commands::Plan { alternate_path } => {
273+ let rt = evaluate::RuntimeBuilder::default()
274+ .source(evaluate::Source::Path(
275+ alternate_path.unwrap_or(default_build_dir),
276+ ))
277+ .build();
278+ let graph = rt.plan()?;
279+ let dot = rt.to_dot(&graph)?;
280+ print!("{}", dot);
281+ Ok(())
282+ }
283+ }
284 }
285 diff --git a/ayllu-build/src/rpc_server.rs b/ayllu-build/src/rpc_server.rs
286index da7d429..5871a98 100644
287--- a/ayllu-build/src/rpc_server.rs
288+++ b/ayllu-build/src/rpc_server.rs
289 @@ -1,9 +1,8 @@
290 use std::error::Error as StdError;
291
292+ use anyhow::Result;
293 use capnp::{capability::Promise, Error as CapnpError};
294-
295- use sysinfo::{SystemExt};
296-
297+ use sysinfo::SystemExt;
298
299 use crate::config::Builder as Config;
300 // use crate::database_ext::XmppExt;
301 @@ -12,7 +11,7 @@ use ayllu_api::build_capnp::worker::{
302 Server, SubmitParams, SubmitResults,
303 };
304
305- use ayllu_database::Wrapper as Database;
306+ use ayllu_database::{Builder, Wrapper as Database};
307 use ayllu_rpc::Server as RpcHelper;
308
309 struct ServerImpl {
310 @@ -51,8 +50,12 @@ impl Server for ServerImpl {
311 }
312 }
313
314- pub async fn serve(cfg: Config) -> Result<(), Box<dyn StdError>> {
315- let db = Database::new(&cfg.database.path, true, false).await?;
316+ pub async fn serve(cfg: &Config) -> Result<()> {
317+ let db = Builder::default()
318+ .url(&cfg.database.path)
319+ .migrations("./migrations")
320+ .build()
321+ .await?;
322 let server = ServerImpl { db };
323 let runtime = RpcHelper::<Client, ServerImpl>::new(&cfg.address, server);
324 runtime.serve().await?;