diff --git a/Cargo.lock b/Cargo.lock index a679715..5d96d34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,6 +174,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomicwrites" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef1bb8d1b645fe38d51dfc331d720fb5fc2c94b440c76cc79c80ff265ca33e3" +dependencies = [ + "rustix 0.38.44", + "tempfile", + "windows-sys 0.52.0", +] + [[package]] name = "atty" version = "0.2.14" @@ -299,6 +310,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +dependencies = [ + "memchr", +] + [[package]] name = "bumpalo" version = "3.19.0" @@ -328,11 +348,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.12" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0b03af37dad7a14518b7691d81acb0f8222604ad3d1b02f6b4bed5188c0cd5" +checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -362,7 +382,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.27", "serde", "serde_json", "thiserror 1.0.69", @@ -436,7 +456,7 @@ dependencies = [ "atty", "bitflags 1.3.2", "strsim 0.8.0", - "textwrap", + "textwrap 0.11.0", "unicode-width 0.1.14", "vec_map", ] @@ -769,11 +789,25 @@ dependencies = [ "uuid", ] +[[package]] +name = "drift" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43eb40edecda6106744f5e4f3d4dc78b3adf19d3cfb2d81cc4faa007da91e527" +dependencies = [ + "anyhow", + "indexmap", + "openapiv3", + "regex", + "serde", + "serde_json", +] + [[package]] name = "dropshot" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eedf902e40c1024b8ed9ca16378a54e9655cdf0e698245ba82d81a3778dcbc54" +checksum = "7cd9bdeafc752f117ed20e659b9763695ae5900adf3a32e93f9f6f4052fd5d66" dependencies = [ "async-stream", "async-trait", @@ -799,7 +833,7 @@ dependencies = [ "rustls-pemfile", "schemars", "scopeguard", - "semver", + "semver 1.0.27", "serde", "serde_json", "serde_path_to_error", @@ -820,16 +854,59 @@ dependencies = [ "waitgroup", ] +[[package]] +name = "dropshot-api-manager" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f448e29400392b55ed2c0133c79841e1bc1bc771e6e20841cb1a5c70a77ef65" +dependencies = [ + "anyhow", + "atomicwrites", + "camino", + "clap 4.5.45", + "debug-ignore", + "drift", + "dropshot", + "dropshot-api-manager-types", + "fs-err", + "hex", + "indent_write", + "newtype_derive", + "openapiv3", + "owo-colors", + "paste", + "semver 1.0.27", + "serde_json", + "sha2", + "similar", + "supports-color", + "textwrap 0.16.2", + "thiserror 2.0.16", +] + +[[package]] +name = "dropshot-api-manager-types" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b913840b90fcccce6afbdb146acf39aa3243b8510e255a70572e53a964fc96a" +dependencies = [ + "anyhow", + "camino", + "paste", + "semver 1.0.27", + "serde_json", +] + [[package]] name = "dropshot_endpoint" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "085c02a4c441ce91e77db3f9a8d1a2d557fc13f5d594dac98a5b389781a8642e" +checksum = "89d09440e73a9dcf8a0f7fbd6ab889a7751d59f0fe76e5082a0a6d5623ec6da3" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "semver", + "semver 1.0.27", "serde", "serde_tokenstream", "syn 2.0.106", @@ -953,6 +1030,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-err" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f150ffc8782f35521cec2b23727707cb4045706ba3c854e86bef66b3a8cdbd" +dependencies = [ + "autocfg", +] + [[package]] name = "futures" version = "0.3.31" @@ -1542,15 +1628,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indent_write" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" + [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", "hashbrown", "serde", + "serde_core", ] [[package]] @@ -1610,6 +1703,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1754,6 +1853,19 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +[[package]] +name = "lldp-dropshot-apis" +version = "0.1.0" +dependencies = [ + "anyhow", + "camino", + "clap 4.5.45", + "dropshot-api-manager", + "dropshot-api-manager-types", + "lldpd-api", + "semver 1.0.27", +] + [[package]] name = "lldpadm" version = "0.1.0" @@ -2015,6 +2127,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "newtype_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac8cd24d9f185bb7223958d8c1ff7a961b74b1953fd05dba7cc568a63b3861ec" +dependencies = [ + "rustc_version 0.1.7", +] + [[package]] name = "nix" version = "0.30.1" @@ -2188,7 +2309,7 @@ dependencies = [ "regress", "reqwest", "schemars", - "semver", + "semver 1.0.27", "serde", "serde_human_bytes", "serde_json", @@ -2238,7 +2359,7 @@ dependencies = [ "hex", "reqwest", "ring 0.16.20", - "semver", + "semver 1.0.27", "serde", "serde_derive", "serde_json", @@ -2318,6 +2439,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "owo-colors" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" + [[package]] name = "oximeter" version = "0.1.0" @@ -2970,9 +3097,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ "aho-corasick", "memchr", @@ -2982,9 +3109,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ "aho-corasick", "memchr", @@ -3101,13 +3228,22 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +dependencies = [ + "semver 0.1.20", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.27", ] [[package]] @@ -3257,7 +3393,7 @@ dependencies = [ "chrono", "dyn-clone", "schemars_derive", - "semver", + "semver 1.0.27", "serde", "serde_json", "uuid", @@ -3326,27 +3462,44 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" + +[[package]] +name = "semver" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.219" +version = "1.0.227" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "80ece43fc6fbed4eb5392ab50c07334d3e577cbf40997ee896fe7af40bba4245" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.227" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a576275b607a2c86ea29e410193df32bc680303c82f31e275bbfcafe8b33be5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.227" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "51e694923b8824cf0e9b382adf0f60d4e05f348f357b38833a3fa5ed7c2ede04" dependencies = [ "proc-macro2", "quote", @@ -3375,14 +3528,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -3525,6 +3679,15 @@ dependencies = [ "libc", ] +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +dependencies = [ + "bstr", +] + [[package]] name = "slab" version = "0.4.11" @@ -3612,6 +3775,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "smf" version = "0.10.0" @@ -3786,6 +3955,15 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "supports-color" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" +dependencies = [ + "is_ci", +] + [[package]] name = "syn" version = "1.0.109" @@ -3953,6 +4131,18 @@ dependencies = [ "unicode-width 0.1.14", ] +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "terminal_size", + "unicode-linebreak", + "unicode-width 0.2.1", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -4384,7 +4574,7 @@ dependencies = [ "hex", "proptest", "schemars", - "semver", + "semver 1.0.27", "serde", "serde_human_bytes", "strum 0.26.3", @@ -4420,7 +4610,7 @@ dependencies = [ "quote", "regress", "schemars", - "semver", + "semver 1.0.27", "serde", "serde_json", "syn 2.0.106", @@ -4437,7 +4627,7 @@ dependencies = [ "proc-macro2", "quote", "schemars", - "semver", + "semver 1.0.27", "serde", "serde_json", "serde_tokenstream", @@ -4463,6 +4653,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -4621,7 +4817,7 @@ dependencies = [ "cfg-if", "git2", "regex", - "rustc_version", + "rustc_version 0.4.1", "rustversion", "time", ] diff --git a/Cargo.toml b/Cargo.toml index 1fe9862..d2e9f13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "adm", + "dropshot-apis", "lldpd-api", "lldpd-client", "lldpd-common", @@ -30,7 +31,9 @@ anyhow = "1.0" camino = { version = "1.1", features = ["serde1"] } chrono = "0.4" clap = { version = "4.5.45", features = ["derive"] } -dropshot = "0.16.3" +dropshot = "0.16.4" +dropshot-api-manager = "0.2.2" +dropshot-api-manager-types = "0.2.2" futures = "0.3" http = "0.2.9" omicron-zone-package = "0.11.1" diff --git a/dropshot-apis/Cargo.toml b/dropshot-apis/Cargo.toml new file mode 100644 index 0000000..9e8e0c6 --- /dev/null +++ b/dropshot-apis/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "lldp-dropshot-apis" +version = "0.1.0" +edition = "2024" +license = "MPL-2.0" + +[dependencies] +anyhow.workspace = true +camino.workspace = true +clap.workspace = true +lldpd-api.workspace = true +dropshot-api-manager-types.workspace = true +dropshot-api-manager.workspace = true +semver.workspace = true diff --git a/dropshot-apis/src/main.rs b/dropshot-apis/src/main.rs new file mode 100644 index 0000000..8d49631 --- /dev/null +++ b/dropshot-apis/src/main.rs @@ -0,0 +1,78 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::process::ExitCode; + +use anyhow::Context; +use camino::Utf8PathBuf; +use clap::Parser; +use dropshot_api_manager::{Environment, ManagedApiConfig, ManagedApis}; +use dropshot_api_manager_types::{ManagedApiMetadata, Versions}; +use lldpd_api::*; + +pub fn environment() -> anyhow::Result { + // The workspace root is one level up from this crate's directory. + let workspace_root = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .to_path_buf(); + let env = Environment::new( + // This is the command used to run the OpenAPI manager. + "cargo xtask openapi".to_owned(), + workspace_root, + // This is the location within the workspace root where the OpenAPI + // documents are stored. + "openapi", + )?; + Ok(env) +} + +/// The list of APIs managed by the OpenAPI manager. +pub fn all_apis() -> anyhow::Result { + let apis = vec![ManagedApiConfig { + ident: "lldpd", + versions: Versions::Lockstep { + version: semver::Version::new(0, 0, 1), + }, + title: "Oxide LLDP Daemon", + metadata: ManagedApiMetadata { + description: Some("API for managing the LLDP daemon"), + contact_url: Some("https://oxide.computer"), + contact_email: Some("api@oxide.computer"), + ..Default::default() + }, + api_description: lldpd_api_mod::stub_api_description, + extra_validation: None, + }]; + + let apis = ManagedApis::new(apis).context("error creating ManagedApis")?; + Ok(apis) +} + +fn main() -> anyhow::Result { + let app = dropshot_api_manager::App::parse(); + let env = environment()?; + let apis = all_apis()?; + + Ok(app.exec(&env, &apis)) +} + +#[cfg(test)] +mod test { + use dropshot_api_manager::test_util::check_apis_up_to_date; + + use super::*; + + // Also recommended: a test which ensures documents are up-to-date. The + // OpenAPI manager comes with a helper function for this, called + // `check_apis_up_to_date`. + #[test] + fn test_apis_up_to_date() -> anyhow::Result { + let env = environment()?; + let apis = all_apis()?; + + let result = check_apis_up_to_date(&env, &apis)?; + Ok(result.to_exit_code()) + } +} diff --git a/lldpd-types/Cargo.toml b/lldpd-types/Cargo.toml index 3f4727d..6e2ca82 100644 --- a/lldpd-types/Cargo.toml +++ b/lldpd-types/Cargo.toml @@ -7,6 +7,6 @@ license = "MPL-2.0" [dependencies] chrono.workspace = true protocol.workspace = true -schemars.workspace = true +schemars = { workspace = true, features = ["chrono"] } serde.workspace = true uuid.workspace = true diff --git a/lldpd/src/api_server.rs b/lldpd/src/api_server.rs index 450b745..cdc4518 100644 --- a/lldpd/src/api_server.rs +++ b/lldpd/src/api_server.rs @@ -652,6 +652,10 @@ pub async fn api_server_manager( } } +pub fn http_api() -> dropshot::ApiDescription> { + lldpd_api_mod::api_description::().unwrap() +} + #[cfg(test)] mod tests { use crate::api_server::build_info; @@ -672,7 +676,3 @@ mod tests { assert_eq!(info.git_sha, ours); } } - -pub fn http_api() -> dropshot::ApiDescription> { - lldpd_api_mod::api_description::().unwrap() -} diff --git a/lldpd/src/main.rs b/lldpd/src/main.rs index 4eab6c6..664af18 100644 --- a/lldpd/src/main.rs +++ b/lldpd/src/main.rs @@ -92,8 +92,6 @@ pub struct SwitchInfo { enum Args { /// Run the LLDPD API server. Run(Opt), - /// Generate an OpenAPI specification for the LLDPD server. - Openapi, } #[derive(Debug, StructOpt)] @@ -246,23 +244,11 @@ async fn run_lldpd(opts: Opt) -> LldpdResult<()> { Ok(()) } -fn print_openapi() -> LldpdResult<()> { - lldpd_api::lldpd_api_mod::stub_api_description() - .unwrap() - .openapi("Oxide LLDP Daemon", "0.0.1".parse().unwrap()) - .description("API for managing the LLDP daemon") - .contact_url("https://oxide.computer") - .contact_email("api@oxide.computer") - .write(&mut std::io::stdout()) - .map_err(|e| LldpdError::Io(e.into())) -} - #[tokio::main(flavor = "multi_thread")] async fn main() -> LldpdResult<()> { let args = Args::from_args(); match args { - Args::Openapi => print_openapi(), Args::Run(opt) => run_lldpd(opt).await, } } diff --git a/xtask/src/external.rs b/xtask/src/external.rs new file mode 100644 index 0000000..f8a5c9d --- /dev/null +++ b/xtask/src/external.rs @@ -0,0 +1,131 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! External xtasks. (extasks?) + +use std::ffi::OsString; +use std::os::unix::process::CommandExt; +use std::process::Command; + +use anyhow::{Context, Result}; +use clap::Parser; + +/// Argument parser for external xtasks. +/// +/// In general we want all developer tasks to be discoverable simply by running +/// `cargo xtask`, but some development tools end up with a particularly +/// large dependency tree. It's not ideal to have to pay the cost of building +/// our release engineering tooling if all the user wants to do is check for +/// workspace dependency issues. +/// +/// `External` provides a pattern for creating xtasks that live in other crates. +/// An external xtask is defined on `crate::Cmds` as a tuple variant containing +/// `External`, which captures all arguments and options (even `--help`) as +/// a `Vec`. The main function then calls `External::exec` with the +/// appropriate bin target name and any additional Cargo arguments. +#[derive(Debug, Parser)] +#[clap( + disable_help_flag(true), + disable_help_subcommand(true), + disable_version_flag(true) +)] +pub struct External { + #[clap(trailing_var_arg(true), allow_hyphen_values(true))] + args: Vec, + + // This stores an in-progress Command builder. `cargo_args` appends args + // to it, and `exec` consumes it. Clap does not treat this as a command + // (`skip`), but fills in this field by calling `new_command`. + #[clap(skip = new_command())] + command: Command, +} + +impl External { + pub fn exec_bin( + self, + package: impl AsRef, + bin_target: impl AsRef, + ) -> Result<()> { + self.exec_common(&[ + "--package", + package.as_ref(), + "--bin", + bin_target.as_ref(), + ]) + } + + fn exec_common(mut self, args: &[&str]) -> Result<()> { + let error = self.command.args(args).arg("--").args(self.args).exec(); + Err(error).context("failed to exec `cargo run`") + } +} + +fn new_command() -> Command { + let mut command = cargo_command(CargoLocation::FromEnv); + command.arg("run"); + command +} + +/// Creates and prepares a `std::process::Command` for the `cargo` executable. +pub fn cargo_command(location: CargoLocation) -> Command { + let mut command = location.resolve(); + + for (key, _) in std::env::vars_os() { + let Some(key) = key.to_str() else { continue }; + if SANITIZED_ENV_VARS.matches(key) { + command.env_remove(key); + } + } + + command +} + +/// How to determine the location of the `cargo` executable. +#[derive(Clone, Copy, Debug)] +pub enum CargoLocation { + /// Use the `CARGO` environment variable, and fall back to `"cargo"` if it + /// is not set. + FromEnv, +} + +impl CargoLocation { + fn resolve(self) -> Command { + match self { + CargoLocation::FromEnv => { + let cargo = std::env::var_os("CARGO") + .unwrap_or_else(|| OsString::from("cargo")); + Command::new(&cargo) + } + } + } +} + +#[derive(Debug)] +struct SanitizedEnvVars { + // At the moment we only ban some prefixes, but we may also want to ban env + // vars by exact name in the future. + prefixes: &'static [&'static str], +} + +impl SanitizedEnvVars { + const fn new() -> Self { + // Remove many of the environment variables set in + // https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts. + // This is done to avoid recompilation with crates like ring between + // `cargo clippy` and `cargo xtask clippy`. (This is really a bug in + // both ring's build script and in Cargo.) + // + // The current list is informed by looking at ring's build script, so + // it's not guaranteed to be exhaustive and it may need to grow over + // time. + let prefixes = &["CARGO_PKG_", "CARGO_MANIFEST_", "CARGO_CFG_"]; + Self { prefixes } + } + + fn matches(&self, key: &str) -> bool { + self.prefixes.iter().any(|prefix| key.starts_with(prefix)) + } +} + +static SANITIZED_ENV_VARS: SanitizedEnvVars = SanitizedEnvVars::new(); diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 6ef275d..1205052 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -12,6 +12,8 @@ use std::path::Path; use anyhow::{anyhow, Context, Result}; use clap::{Parser, ValueEnum}; +mod external; + #[cfg(target_os = "illumos")] mod illumos; #[cfg(target_os = "illumos")] @@ -41,6 +43,8 @@ pub enum DistFormat { /// lldp xtask support #[clap(name = "xtask")] enum Xtasks { + /// manage OpenAPI documents + Openapi(Box), /// build an installable dataplane controller package Dist { /// package release bits @@ -95,6 +99,9 @@ fn collect_binaries(release: bool, dst: &str) -> Result<()> { async fn main() { let task = Xtasks::parse(); if let Err(e) = match task { + Xtasks::Openapi(external) => { + external.exec_bin("lldp-dropshot-apis", "lldp-dropshot-apis") + } Xtasks::Dist { release, format } => plat::dist(release, format).await, } { eprintln!("failed: {e}");