git.delta.rocks / jrsonnet / refs/commits / ad7852dc047c

difftreelog

Merge pull request #4 from CertainLach/commmand-completions

Yaroslav Bolyukin2024-06-02parents: #d8bac4e #d940537.patch.diff
in: trunk
feat(fleet-cmd): add automatic completions

16 files changed

modifiedCargo.lockdiffbeforeafterboth
before · Cargo.lock
317 packageslockfile v3
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,8 @@
 nixlike = { path = "./crates/nixlike" }
 better-command = { path = "./crates/better-command" }
 fleet-shared = { path = "./crates/fleet-shared" }
+nix-eval = { path = "./crates/nix-eval" }
+
 tokio = { version = "1.36.0", features = [
 	"fs",
 	"rt",
@@ -15,13 +17,13 @@
 	"time",
 	"rt-multi-thread",
 ] }
-# Using fixed version for rust on stable nixos branches.
-clap = { version = ">=4.4, <4.5", features = [
+clap = { version = "4.5", features = [
 	"derive",
 	"env",
 	"wrap_help",
 	"unicode",
 ] }
+clap_complete = "4.5"
 age = { version = "0.10", features = ["ssh"] }
 anyhow = "1.0"
 tracing = "0.1"
@@ -29,4 +31,4 @@
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 tempfile = "3.10"
-nix = {version = "0.27.1", features = ["user", "fs"]}
+nix = { version = "0.29.0", features = ["user", "fs"] }
modifiedcmds/fleet/Cargo.tomldiffbeforeafterboth
--- a/cmds/fleet/Cargo.toml
+++ b/cmds/fleet/Cargo.toml
@@ -10,6 +10,7 @@
 better-command.workspace = true
 tokio.workspace = true
 clap.workspace = true
+clap_complete.workspace = true
 age = { workspace = true, features = ["armor"] }
 anyhow.workspace = true
 tracing.workspace = true
@@ -19,7 +20,7 @@
 tempfile.workspace = true
 time = { version = "0.3", features = ["serde"] }
 once_cell = "1.19"
-hostname = "0.3"
+hostname = "0.4.0"
 age-core = "0.10"
 peg = "0.8"
 base64 = "0.22.1"
@@ -27,7 +28,7 @@
 tokio-util = { version = "0.7", features = ["codec"] }
 async-trait = "0.1"
 futures = "0.3"
-itertools = "0.12"
+itertools = "0.13"
 shlex = "1.3"
 tabled = { version = "0.15" }
 owo-colors = { version = "4.0", features = [
@@ -43,7 +44,7 @@
 tracing-indicatif = { version = "0.3", optional = true }
 human-repr = { version = "1.1", optional = true }
 indicatif = { version = "0.17", optional = true }
-nix-eval = { version = "0.1.0", path = "../../crates/nix-eval" }
+nix-eval.workspace = true
 
 [features]
 # Not quite stable
addedcmds/fleet/src/cmds/complete.rsdiffbeforeafterboth
--- /dev/null
+++ b/cmds/fleet/src/cmds/complete.rs
@@ -0,0 +1,23 @@
+use std::io::stdout;
+
+use clap::{Command, Parser};
+use clap_complete::Shell;
+
+#[derive(Parser)]
+pub struct Complete {
+	/// For which shell to generate the completions
+	#[arg(long, short)]
+	shell: Shell,
+}
+
+impl Complete {
+	pub fn run(&self, mut command: Command) {
+		let Self { shell } = self;
+
+		let bin_name = command
+			.get_bin_name()
+			.unwrap_or_else(|| command.get_name())
+			.to_owned();
+		clap_complete::generate(*shell, &mut command, &bin_name, &mut stdout());
+	}
+}
modifiedcmds/fleet/src/cmds/mod.rsdiffbeforeafterboth
--- a/cmds/fleet/src/cmds/mod.rs
+++ b/cmds/fleet/src/cmds/mod.rs
@@ -1,3 +1,4 @@
 pub mod build_systems;
 pub mod info;
 pub mod secrets;
+pub mod complete;
modifiedcmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth
--- a/cmds/fleet/src/cmds/secrets/mod.rs
+++ b/cmds/fleet/src/cmds/secrets/mod.rs
@@ -32,6 +32,7 @@
 		/// Secret name
 		name: String,
 		/// Secret owners
+		#[clap(long, short)]
 		machines: Vec<String>,
 		/// Override secret if already present
 		#[clap(long)]
@@ -363,7 +364,7 @@
 
 async fn parse_secret() -> Result<Option<Vec<u8>>> {
 	let mut input = vec![];
-	io::stdin().read_to_end(&mut input)?;
+	stdin().read_to_end(&mut input)?;
 	if input.is_empty() {
 		Ok(None)
 	} else {
modifiedcmds/fleet/src/main.rsdiffbeforeafterboth
--- a/cmds/fleet/src/main.rs
+++ b/cmds/fleet/src/main.rs
@@ -14,23 +14,20 @@
 use std::{ffi::OsString, process::ExitCode};
 
 use anyhow::{bail, Result};
-use clap::Parser;
-
+use clap::{CommandFactory, Parser};
 use cmds::{
 	build_systems::{BuildSystems, Deploy},
+	complete::Complete,
 	info::Info,
 	secrets::Secret,
 };
-use futures::future::LocalBoxFuture;
-use futures::stream::FuturesUnordered;
-use futures::TryStreamExt;
+use futures::{future::LocalBoxFuture, stream::FuturesUnordered, TryStreamExt};
 use host::{Config, FleetOpts};
 #[cfg(feature = "indicatif")]
 use human_repr::HumanCount;
 #[cfg(feature = "indicatif")]
 use indicatif::{ProgressState, ProgressStyle};
-use tracing::{error, info};
-use tracing::{info_span, Instrument};
+use tracing::{error, info, info_span, Instrument};
 #[cfg(feature = "indicatif")]
 use tracing_indicatif::IndicatifLayer;
 use tracing_subscriber::{prelude::*, EnvFilter};
@@ -87,6 +84,9 @@
 	Prefetch(Prefetch),
 	/// Config parsing
 	Info(Info),
+	/// Command completions
+	#[clap(hide(true))]
+	Complete(Complete),
 }
 
 #[derive(Parser)]
@@ -105,52 +105,59 @@
 		Opts::Secret(s) => s.run(config).await?,
 		Opts::Info(i) => i.run(config).await?,
 		Opts::Prefetch(p) => p.run(config).await?,
+		// TODO: actually parse commands before starting the async runtime
+		Opts::Complete(c) => {
+			tokio::task::spawn_blocking(move || c.run(RootOpts::command())).await?
+		}
 	};
 	Ok(())
 }
 
 fn setup_logging() {
 	#[cfg(feature = "indicatif")]
-	let indicatif_layer = IndicatifLayer::new().with_progress_style(
-		ProgressStyle::with_template(
-			"{color_start}{span_child_prefix} {span_name}{{{span_fields}}}{color_end} {wide_msg} {color_start}{download_progress} {elapsed}{color_end}",
-		)
-		.unwrap()
-		.with_key("download_progress", |state: &ProgressState, writer: &mut dyn std::fmt::Write| {
-			let Some(len) = state.len() else {
-				return;
-			};
-			let pos = state.pos();
-			if pos > len {
-				let _ = write!(writer, "{}", pos.human_count_bare());
-			} else {
-				let _ = write!(writer, "{} / {}", pos.human_count_bare(), len.human_count_bare());
-			}
-		})
-		.with_key(
-			"color_start",
-			|state: &ProgressState, writer: &mut dyn std::fmt::Write| {
-				use std::time::Duration;
-				let elapsed = state.elapsed();
+	let indicatif_layer = {
+		use std::time::Duration;
+
+		IndicatifLayer::new().with_progress_style(
+			ProgressStyle::with_template(
+				"{color_start}{span_child_prefix} {span_name}{{{span_fields}}}{color_end} {wide_msg} {color_start}{download_progress} {elapsed}{color_end}",
+			)
+				.unwrap()
+				.with_key("download_progress", |state: &ProgressState, writer: &mut dyn std::fmt::Write| {
+					let Some(len) = state.len() else {
+						return;
+					};
+					let pos = state.pos();
+					if pos > len {
+						let _ = write!(writer, "{}", pos.human_count_bare());
+					} else {
+						let _ = write!(writer, "{} / {}", pos.human_count_bare(), len.human_count_bare());
+					}
+				})
+				.with_key(
+					"color_start",
+					|state: &ProgressState, writer: &mut dyn std::fmt::Write| {
+						let elapsed = state.elapsed();
 
-				if elapsed > Duration::from_secs(60) {
-					// Red
-					let _ = write!(writer, "\x1b[{}m", 1 + 30);
-				} else if elapsed > Duration::from_secs(30) {
-					// Yellow
-					let _ = write!(writer, "\x1b[{}m", 3 + 30);
-				}
-			},
+						if elapsed > Duration::from_secs(60) {
+							// Red
+							let _ = write!(writer, "\x1b[{}m", 1 + 30);
+						} else if elapsed > Duration::from_secs(30) {
+							// Yellow
+							let _ = write!(writer, "\x1b[{}m", 3 + 30);
+						}
+					},
+				)
+				.with_key(
+					"color_end",
+					|state: &ProgressState, writer: &mut dyn std::fmt::Write| {
+						if state.elapsed() > Duration::from_secs(30) {
+							let _ = write!(writer, "\x1b[0m");
+						}
+					},
+				),
 		)
-		.with_key(
-			"color_end",
-			|state: &ProgressState, writer: &mut dyn std::fmt::Write| {
-				if state.elapsed() > Duration::from_secs(30) {
-					let _ = write!(writer, "\x1b[0m");
-				}
-			},
-		),
-	);
+	};
 
 	let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
 
@@ -160,7 +167,7 @@
 			.with_target(false);
 		#[cfg(feature = "indicatif")]
 		let sub = sub.with_writer(indicatif_layer.get_stdout_writer());
-		sub.with_filter(filter) // .withou,
+		sub.with_filter(filter) // .without,
 	});
 	// #[cfg(feature = "indicatif")]
 	#[cfg(feature = "indicatif")]
@@ -203,3 +210,14 @@
 		}
 	}
 }
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+
+	#[test]
+	fn verify_command() {
+		use clap::CommandFactory;
+		RootOpts::command().debug_assert();
+	}
+}
modifiedcmds/generator-helper/Cargo.tomldiffbeforeafterboth
--- a/cmds/generator-helper/Cargo.toml
+++ b/cmds/generator-helper/Cargo.toml
@@ -7,6 +7,6 @@
 age.workspace = true
 anyhow.workspace = true
 clap.workspace = true
-ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
+ed25519-dalek = { version = "2.1", features = ["rand_core"] }
 fleet-shared.workspace = true
 rand = "0.8.5"
modifiedcmds/install-secrets/Cargo.tomldiffbeforeafterboth
--- a/cmds/install-secrets/Cargo.toml
+++ b/cmds/install-secrets/Cargo.toml
@@ -2,8 +2,7 @@
 name = "fleet-install-secrets"
 version = "0.1.0"
 edition = "2021"
-rust-version = "1.73"
-
+rust-version = "1.77.2"
 
 [dependencies]
 clap.workspace = true
modifiedcrates/better-command/Cargo.tomldiffbeforeafterboth
--- a/crates/better-command/Cargo.toml
+++ b/crates/better-command/Cargo.toml
@@ -4,7 +4,6 @@
 edition = "2021"
 
 [dependencies]
-once_cell = "1.19"
 regex = "1.10"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
modifiedcrates/better-command/src/handler.rsdiffbeforeafterboth
--- a/crates/better-command/src/handler.rs
+++ b/crates/better-command/src/handler.rs
@@ -2,10 +2,9 @@
 
 use std::{
 	collections::HashMap,
-	sync::{Arc, Mutex},
+	sync::{Arc, LazyLock, Mutex},
 };
 
-use once_cell::sync::Lazy;
 use regex::Regex;
 use serde::Deserialize;
 use tracing::{info, info_span, warn, Span};
@@ -92,9 +91,9 @@
 }
 fn process_message(m: &str) -> String {
 	// Supposed to remove formatting characters except colors, as some programs try to reset cursor position etc.
-	static OSC_CLEANER: Lazy<Regex> =
-		Lazy::new(|| Regex::new(r"\x1B\]([^\x07\x1C]*[\x07\x1C])?|\r").unwrap());
-	static DETABBER: Lazy<Regex> = Lazy::new(|| Regex::new(r"\t").unwrap());
+	static OSC_CLEANER: LazyLock<Regex> =
+		LazyLock::new(|| Regex::new(r"\x1B\]([^\x07\x1C]*[\x07\x1C])?|\r").unwrap());
+	static DETABBER: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\t").unwrap());
 	let m = OSC_CLEANER.replace_all(m, "");
 	// Indicatif can't format tabs. This is not the correct tab formatting, as correct one should be aligned,
 	// and not just be replaced with the constant number of spaces, but it's ok for now, as statuses are single-line.
modifiedcrates/better-command/src/lib.rsdiffbeforeafterboth
--- a/crates/better-command/src/lib.rs
+++ b/crates/better-command/src/lib.rs
@@ -1,17 +1,2 @@
 mod handler;
 pub use handler::{ClonableHandler, Handler, NixHandler, NoopHandler, PlainHandler};
-
-pub fn add(left: usize, right: usize) -> usize {
-	left + right
-}
-
-#[cfg(test)]
-mod tests {
-	use super::*;
-
-	#[test]
-	fn it_works() {
-		let result = add(2, 2);
-		assert_eq!(result, 4);
-	}
-}
modifiedcrates/nixlike/Cargo.tomldiffbeforeafterboth
--- a/crates/nixlike/Cargo.toml
+++ b/crates/nixlike/Cargo.toml
@@ -4,7 +4,7 @@
 edition = "2021"
 
 [dependencies]
-alejandra = {git = "https://github.com/kamadorueda/alejandra"}
+alejandra = { git = "https://github.com/kamadorueda/alejandra" }
 linked-hash-map = "0.5.6"
 peg = "0.8.2"
 serde = "1.0.196"
modifiedflake.lockdiffbeforeafterboth
--- a/flake.lock
+++ b/flake.lock
@@ -7,11 +7,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1716569590,
-        "narHash": "sha256-5eDbq8TuXFGGO3mqJFzhUbt5zHVTf5zilQoyW5jnJwo=",
+        "lastModified": 1717025063,
+        "narHash": "sha256-dIubLa56W9sNNz0e8jGxrX3CAkPXsq7snuFA/Ie6dn8=",
         "owner": "ipetkov",
         "repo": "crane",
-        "rev": "109987da061a1bf452f435f1653c47511587d919",
+        "rev": "480dff0be03dac0e51a8dfc26e882b0d123a450e",
         "type": "github"
       },
       "original": {
@@ -40,11 +40,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1716658583,
-        "narHash": "sha256-A93mYmlLvCz0YjQiQ5Tc3DpLrP6Brs+gAlK9nlnSOVg=",
+        "lastModified": 1717282945,
+        "narHash": "sha256-Jrn+/CdB/d2hUqduYQdTwGJYDAdaR5cAdlxnq+yEtXI=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "3e280884c0b0e8222ec6b05a99db01505964e1c3",
+        "rev": "ab5efd0f3c62dd3b75d21d0de1dd63efc76be5d8",
         "type": "github"
       },
       "original": {
@@ -56,11 +56,11 @@
     },
     "nixpkgs-stable-for-tests": {
       "locked": {
-        "lastModified": 1716361217,
-        "narHash": "sha256-mzZDr00WUiUXVm1ujBVv6A0qRd8okaITyUp4ezYRgc4=",
+        "lastModified": 1716991068,
+        "narHash": "sha256-Av0UWCCiIGJxsZ6TFc+OiKCJNqwoxMNVYDBChmhjNpo=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "46397778ef1f73414b03ed553a3368f0e7e33c2f",
+        "rev": "25cf937a30bf0801447f6bf544fc7486c6309234",
         "type": "github"
       },
       "original": {
@@ -89,11 +89,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1716603336,
-        "narHash": "sha256-81u/zd7V+XRTq88zwRLxw5GnwZyEiAvGA2BvAXUe864=",
+        "lastModified": 1717208326,
+        "narHash": "sha256-4gVhbC+NjSQ4c6cJvJGNCI1oTcD+8jRRNAnOF9faGCE=",
         "owner": "oxalica",
         "repo": "rust-overlay",
-        "rev": "4d0f1e4d5d65c23cdbb77e4b0d91940be7309bd4",
+        "rev": "ab69b67fac9a96709fbef0b899db308ca714a120",
         "type": "github"
       },
       "original": {
modifiedpkgs/fleet.nixdiffbeforeafterboth
--- a/pkgs/fleet.nix
+++ b/pkgs/fleet.nix
@@ -6,4 +6,11 @@
   strictDeps = true;
 
   cargoExtraArgs = "--locked -p ${pname}";
+
+  postInstall = ''
+    for shell in bash fish zsh; do
+      installShellCompletion --cmd fleet \
+        --$shell <($out/bin/fleet complete --shell $shell --print)
+    done
+  '';
 }
modifiedrust-toolchain.tomldiffbeforeafterboth
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2024-05-01"
+channel = "nightly-2024-06-01"
 components = ["rustfmt", "clippy", "rust-analyzer", "rust-src"]