From 26e359dddef053a48c0fde37fa1782c02166bd99 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Wed, 25 Jan 2023 20:27:00 -0800 Subject: [PATCH] Initial global config implementation for watch command. Signed-off-by: David Calavera --- Cargo.lock | 51 +++++++++++++++- Cargo.toml | 2 +- crates/cargo-lambda-cli/Cargo.toml | 1 + crates/cargo-lambda-cli/src/main.rs | 20 ++++-- crates/cargo-lambda-metadata/Cargo.toml | 1 + crates/cargo-lambda-metadata/src/cargo.rs | 57 ++++++++++++++++- crates/cargo-lambda-watch/src/lib.rs | 74 ++++++++++++----------- crates/cargo-lambda-watch/src/watcher.rs | 5 +- 8 files changed, 162 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0bb5718..a4ba2adc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -755,6 +755,7 @@ dependencies = [ "cargo-lambda-build", "cargo-lambda-deploy", "cargo-lambda-invoke", + "cargo-lambda-metadata", "cargo-lambda-new", "cargo-lambda-watch", "cargo-test-macro", @@ -856,6 +857,7 @@ dependencies = [ "strum", "strum_macros", "thiserror", + "toml", "tracing", ] @@ -967,7 +969,7 @@ dependencies = [ "snapbox", "tar", "termcolor", - "toml_edit", + "toml_edit 0.15.0", "url", "winapi", ] @@ -2772,6 +2774,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -3750,6 +3761,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c68e921cef53841b8925c2abadd27c9b891d9613bdc43d6b823062866df38e8" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4251,11 +4271,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.18.0", +] + [[package]] name = "toml_datetime" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808b51e57d0ef8f71115d8f3a01e7d3750d01c79cac4b3eda910f4389fdf92fd" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" dependencies = [ "serde", ] @@ -4274,6 +4306,19 @@ dependencies = [ "toml_datetime", ] +[[package]] +name = "toml_edit" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581" +dependencies = [ + "indexmap", + "nom8", + "serde", + "serde_spanned", + "toml_datetime", +] + [[package]] name = "tower" version = "0.4.13" diff --git a/Cargo.toml b/Cargo.toml index 424b98b1..ce37bddd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ strum = "0.24.0" strum_macros = "0.24.0" thiserror = "1.0.31" tokio = { version = "1.18.2" } -tracing = { version = "0.1.35", features = ["log"] } +tracing = { version = "0.1.37", features = ["log"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } uuid = { version = "1.1.2", features = ["v4"] } which = "4.2.5" diff --git a/crates/cargo-lambda-cli/Cargo.toml b/crates/cargo-lambda-cli/Cargo.toml index c10a18cc..39f65457 100644 --- a/crates/cargo-lambda-cli/Cargo.toml +++ b/crates/cargo-lambda-cli/Cargo.toml @@ -20,6 +20,7 @@ description.workspace = true cargo-lambda-build.workspace = true cargo-lambda-deploy.workspace = true cargo-lambda-invoke.workspace = true +cargo-lambda-metadata.workspace = true cargo-lambda-new.workspace = true cargo-lambda-watch.workspace = true clap = { workspace = true, features = ["suggestions"] } diff --git a/crates/cargo-lambda-cli/src/main.rs b/crates/cargo-lambda-cli/src/main.rs index 462dcd36..22af1be8 100644 --- a/crates/cargo-lambda-cli/src/main.rs +++ b/crates/cargo-lambda-cli/src/main.rs @@ -1,12 +1,13 @@ #![warn(rust_2018_idioms, unused_lifetimes, clippy::multiple_crate_versions)] -use std::boxed::Box; +use std::{boxed::Box, path::PathBuf}; use cargo_lambda_build::{Build, Zig}; use cargo_lambda_deploy::Deploy; use cargo_lambda_invoke::Invoke; +use cargo_lambda_metadata::cargo::{load_global_metadata, PackageMetadata}; use cargo_lambda_new::{Init, New}; use cargo_lambda_watch::Watch; -use clap::{CommandFactory, Parser, Subcommand}; +use clap::{CommandFactory, Parser, Subcommand, ValueHint}; use miette::{miette, IntoDiagnostic, Result}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -29,6 +30,9 @@ struct Lambda { /// Print version information #[arg(short = 'V', long)] version: bool, + /// Location for the lambda metadata file + #[arg(long, value_hint = ValueHint::FilePath, global = true)] + metadata: Option, } #[derive(Clone, Debug, Subcommand)] @@ -54,14 +58,14 @@ enum LambdaSubcommand { } impl LambdaSubcommand { - async fn run(self) -> Result<()> { + async fn run(self, metadata: PackageMetadata) -> Result<()> { match self { Self::Build(mut b) => b.run().await, Self::Deploy(d) => d.run().await, Self::Init(mut i) => i.run().await, Self::Invoke(i) => i.run().await, Self::New(mut n) => n.run().await, - Self::Watch(w) => w.run().await, + Self::Watch(w) => cargo_lambda_watch::run(metadata.watch(), w).await, } } } @@ -131,5 +135,11 @@ async fn main() -> Result<()> { subscriber.init(); } - subcommand.run().await + let global_data = if let Some(path) = lambda.metadata { + load_global_metadata(path)? + } else { + PackageMetadata::default() + }; + + subcommand.run(global_data).await } diff --git a/crates/cargo-lambda-metadata/Cargo.toml b/crates/cargo-lambda-metadata/Cargo.toml index 4cbeab3f..125afe55 100644 --- a/crates/cargo-lambda-metadata/Cargo.toml +++ b/crates/cargo-lambda-metadata/Cargo.toml @@ -25,4 +25,5 @@ serde_json.workspace = true strum.workspace = true strum_macros.workspace = true thiserror.workspace = true +toml = "0.6.0" tracing.workspace = true diff --git a/crates/cargo-lambda-metadata/src/cargo.rs b/crates/cargo-lambda-metadata/src/cargo.rs index df8d1959..a3a4e520 100644 --- a/crates/cargo-lambda-metadata/src/cargo.rs +++ b/crates/cargo-lambda-metadata/src/cargo.rs @@ -1,9 +1,10 @@ use cargo_metadata::{Metadata as CargoMetadata, Package}; -use miette::{IntoDiagnostic, Result}; +use miette::{Context, IntoDiagnostic, Result}; use serde::Deserialize; use std::{ collections::{HashMap, HashSet}, fmt::Debug, + fs::read_to_string, path::{Path, PathBuf}, }; use tracing::{debug, trace}; @@ -38,6 +39,21 @@ pub struct PackageMetadata { pub deploy: Option, #[serde(default)] pub build: BuildConfig, + #[serde(default)] + watch: WatchConfig, +} + +impl PackageMetadata { + pub fn watch(&self) -> WatchConfig { + if !self.env.is_empty() && self.watch.env.is_empty() { + tracing::warn!("the option `env` is deprecated, use `watch.env` instead"); + let mut w = self.watch.clone(); + w.env = self.env.clone(); + w + } else { + self.watch.clone() + } + } } #[derive(Clone, Debug, Default, Deserialize)] @@ -126,6 +142,26 @@ impl DeployConfig { } } +#[derive(Clone, Debug, Default, Deserialize)] +pub struct WatchConfig { + #[serde(default)] + pub env: HashMap, +} + +/// Load Cargo Lambda metadata from a global file, not Cargo.toml. +/// The sections in this file are not namespaced with *.lambda +/// because this file is specific to Cargo Lambda and no other tool. +pub fn load_global_metadata + Debug>(metadata_path: P) -> Result { + let data = read_to_string(&metadata_path) + .into_diagnostic() + .wrap_err(format!("failed to read metadata file: {:?}", metadata_path))?; + + toml::from_str(&data).into_diagnostic().wrap_err(format!( + "failed to parse metadata file: {:?}", + metadata_path + )) +} + /// Extract all the binary target names from a Cargo.toml file pub fn binary_targets + Debug>(manifest_path: P) -> Result> { let metadata = load_metadata(manifest_path)?; @@ -202,7 +238,7 @@ pub fn load_metadata + Debug>(manifest_path: P) -> Result PathBuf { format!("../../tests/fixtures/{name}/Cargo.toml").into() @@ -572,4 +608,19 @@ mod tests { let subcommand = opts.subcommand.unwrap(); assert_eq!(vec!["brazil".to_string(), "build".to_string()], subcommand); } + + #[test] + fn test_load_global_metadata() { + let data = r#"[watch] +env = { "foo" = "bar" } +[deploy] +memory = 512 +"#; + let temp = temp_dir().join("cargo-metadata.toml"); + std::fs::write(&temp, data.as_bytes()).unwrap(); + + let meta = load_global_metadata(temp).unwrap(); + assert_eq!(Memory::Mb512, meta.deploy.unwrap().memory.unwrap()); + assert_eq!("bar", meta.watch.env["foo"]); + } } diff --git a/crates/cargo-lambda-watch/src/lib.rs b/crates/cargo-lambda-watch/src/lib.rs index c87be94d..b28a2075 100644 --- a/crates/cargo-lambda-watch/src/lib.rs +++ b/crates/cargo-lambda-watch/src/lib.rs @@ -1,4 +1,5 @@ use axum::{extract::Extension, http::header::HeaderName, Router}; +use cargo_lambda_metadata::cargo::WatchConfig; use clap::{Args, ValueHint}; use miette::{IntoDiagnostic, Result, WrapErr}; use opentelemetry::{ @@ -20,7 +21,7 @@ use tower_http::{ trace::TraceLayer, }; -use tracing::{info, Subscriber}; +use tracing::{info, trace, Subscriber}; use tracing_opentelemetry::OpenTelemetryLayer; use tracing_subscriber::registry::LookupSpan; @@ -85,42 +86,43 @@ struct CargoOptions { release: bool, } -impl Watch { - #[tracing::instrument(skip(self), target = "cargo_lambda")] - pub async fn run(&self) -> Result<()> { - tracing::trace!(options = ?self, "watching project"); - - let ip = IpAddr::from_str(&self.invoke_address) - .into_diagnostic() - .wrap_err("invalid invoke address")?; - let addr = SocketAddr::from((ip, self.invoke_port)); - let no_reload = self.no_reload; - let cargo_options = self.cargo_options.clone(); - - let base = dunce::canonicalize(".").into_diagnostic()?; - - let (mut global_ignore, _) = ignore_files::from_environment(Some("CARGO_LAMBDA")).await; - let (mut ignore_files, _) = ignore_files::from_origin(&base).await; - ignore_files.append(&mut global_ignore); - - let watcher_config = WatcherConfig { - base, - ignore_files, - no_reload, - manifest_path: cargo_options.manifest_path.clone(), - ..Default::default() - }; - - Toplevel::new() - .start("Lambda server", move |s| { - start_server(s, addr, cargo_options, watcher_config) - }) - .catch_signals() - .handle_shutdown_requests(Duration::from_millis(1000)) - .await - .map_err(|e| miette::miette!("{}", e)) - } +#[tracing::instrument(skip(global, local), target = "cargo_lambda")] +pub async fn run(global: WatchConfig, local: Watch) -> Result<()> { + trace!(options = ?local, "watching project"); + + let ip = IpAddr::from_str(&local.invoke_address) + .into_diagnostic() + .wrap_err("invalid invoke address")?; + let addr = SocketAddr::from((ip, local.invoke_port)); + let no_reload = local.no_reload; + let cargo_options = local.cargo_options.clone(); + + let base = dunce::canonicalize(".").into_diagnostic()?; + + let (mut global_ignore, _) = ignore_files::from_environment(Some("CARGO_LAMBDA")).await; + let (mut ignore_files, _) = ignore_files::from_origin(&base).await; + ignore_files.append(&mut global_ignore); + + let watcher_config = WatcherConfig { + base, + ignore_files, + no_reload, + manifest_path: cargo_options.manifest_path.clone(), + env_vars: global.env.clone(), + ..Default::default() + }; + + Toplevel::new() + .start("Lambda server", move |s| { + start_server(s, addr, cargo_options, watcher_config) + }) + .catch_signals() + .handle_shutdown_requests(Duration::from_millis(1000)) + .await + .map_err(|e| miette::miette!("{}", e)) +} +impl Watch { pub fn xray_layer(&self) -> OpenTelemetryLayer where S: Subscriber + for<'span> LookupSpan<'span>, diff --git a/crates/cargo-lambda-watch/src/watcher.rs b/crates/cargo-lambda-watch/src/watcher.rs index 5360026b..59883b78 100644 --- a/crates/cargo-lambda-watch/src/watcher.rs +++ b/crates/cargo-lambda-watch/src/watcher.rs @@ -1,7 +1,7 @@ use crate::requests::ServerError; use cargo_lambda_metadata::cargo::function_environment_metadata; use ignore_files::{IgnoreFile, IgnoreFilter}; -use std::{convert::Infallible, path::PathBuf, sync::Arc, time::Duration}; +use std::{collections::HashMap, convert::Infallible, path::PathBuf, sync::Arc, time::Duration}; use tracing::{debug, error, trace, warn}; use watchexec::{ action::{Action, Outcome, PreSpawn}, @@ -23,6 +23,7 @@ pub(crate) struct WatcherConfig { pub manifest_path: PathBuf, pub ignore_files: Vec, pub no_reload: bool, + pub env_vars: HashMap, } pub(crate) async fn new(cmd: Command, wc: WatcherConfig) -> Result, ServerError> { @@ -146,6 +147,7 @@ async fn runtime(cmd: Command, wc: WatcherConfig) -> Result Result