Repository navigation
crates/minimal{2,d}: scaffold binary crates for Minimal One #227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| [package] | ||
| name = "minimal2" | ||
| version = "0.0.1" | ||
| edition.workspace = true | ||
| publish.workspace = true | ||
|
|
||
| [dependencies] | ||
| clap.workspace = true | ||
| clap_complete.workspace = true | ||
| dirs.workspace = true | ||
| futures.workspace = true | ||
|
|
||
| tokio.workspace = true | ||
| tokio-util.workspace = true | ||
| tracing.workspace = true | ||
| tracing-subscriber.workspace = true | ||
|
|
||
| ot.workspace = true |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||||||||||||||||||
| use std::process::Command; | ||||||||||||||||||||||
| fn main() { | ||||||||||||||||||||||
| let output = Command::new("git") | ||||||||||||||||||||||
| .args(["rev-parse", "--short", "HEAD"]) | ||||||||||||||||||||||
| .output() | ||||||||||||||||||||||
| .unwrap(); | ||||||||||||||||||||||
| let git_hash = String::from_utf8(output.stdout).unwrap(); | ||||||||||||||||||||||
| println!("cargo:rustc-env=GIT_HASH={}", git_hash); | ||||||||||||||||||||||
|
twitchyliquid64 marked this conversation as resolved.
Resolved
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let is_clean = std::process::Command::new("git") | ||||||||||||||||||||||
| .args(["status", "--porcelain=v1"]) | ||||||||||||||||||||||
| .output() | ||||||||||||||||||||||
| .map(|o| o.stdout.is_empty()) | ||||||||||||||||||||||
| .unwrap_or(false); | ||||||||||||||||||||||
|
Comment on lines
+10
to
+14
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check git command success before reading stdout. Line 13 checks 🛡️ Proposed fix let is_clean = std::process::Command::new("git")
.args(["status", "--porcelain=v1"])
.output()
- .map(|o| o.stdout.is_empty())
+ .map(|o| o.status.success() && o.stdout.is_empty())
.unwrap_or(false);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| if !is_clean && std::env::var("PROFILE").unwrap() == "release" { | ||||||||||||||||||||||
| println!("cargo::warning=Building a release binary from an unclean working tree!!"); | ||||||||||||||||||||||
| println!("cargo::warning=Always build production binaries from a clean checkout."); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| println!( | ||||||||||||||||||||||
| "cargo:rustc-env=LONG_VERSION={} ({}{})", | ||||||||||||||||||||||
| std::env::var("CARGO_PKG_VERSION").unwrap().as_str(), | ||||||||||||||||||||||
| if is_clean { "" } else { "dirty " }, | ||||||||||||||||||||||
| git_hash.strip_suffix("\n").unwrap(), | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,66 @@ | ||||||||||
| //! The minimal CLI which pairs/talks-with minimald. | ||||||||||
|
|
||||||||||
| use clap::{Args, CommandFactory, Parser, Subcommand}; | ||||||||||
| use clap_complete::Shell; | ||||||||||
| use std::path::PathBuf; | ||||||||||
| use tracing_subscriber::{EnvFilter, fmt, prelude::*}; | ||||||||||
|
|
||||||||||
| #[derive(Parser)] | ||||||||||
| #[command(name = "minimal", version = env!("CARGO_PKG_VERSION"), long_version = env!("LONG_VERSION"))] | ||||||||||
|
twitchyliquid64 marked this conversation as resolved.
Resolved
|
||||||||||
| #[command(about = "The Minimal CLI")] | ||||||||||
| struct Cli { 8000 | ||||||||||
| #[command(subcommand)] | ||||||||||
| command: Command, | ||||||||||
|
|
||||||||||
| #[command(flatten)] | ||||||||||
| global_args: GlobalArgs, | ||||||||||
| } | ||||||||||
|
|
||||||||||
| #[derive(Subcommand)] | ||||||||||
| enum Command { | ||||||||||
| /// Generate shell completion script | ||||||||||
| #[command( | ||||||||||
| long_about = "Generate a shell tab-completion script for the minimal CLI.\nSupported shells include bash, zsh, elvish and fish.\n\n source <(minimal completions bash)" | ||||||||||
| )] | ||||||||||
| Completions(CompletionsArgs), | ||||||||||
| } | ||||||||||
|
|
||||||||||
| #[derive(Debug, clap::Args)] | ||||||||||
| struct CompletionsArgs { | ||||||||||
| /// The shell type for a CLI completion script should be printed | ||||||||||
| #[arg(value_parser)] | ||||||||||
| shell: Shell, | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /// Shared arguments all subcommands | ||||||||||
| #[derive(Debug, Args)] | ||||||||||
| pub struct GlobalArgs { | ||||||||||
| /// Override the base directory used for operations (default: ~/.cache/minimal) | ||||||||||
| #[arg(long)] | ||||||||||
| minimal_dir: Option<PathBuf>, | ||||||||||
| } | ||||||||||
|
|
||||||||||
| #[tokio::main] | ||||||||||
| async fn main() -> Result<(), ()> { | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win Use application-grade error type for
Suggested fix+use anyhow::Result;
@@
-async fn main() -> Result<(), ()> {
+async fn main() -> Result<()> {As per coding guidelines, "In application crates, use 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| { | ||||||||||
| EnvFilter::new("info") | ||||||||||
| .add_directive("topiary=off".parse().unwrap()) | ||||||||||
| .add_directive("libcgroups=off".parse().unwrap()) | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| tracing_subscriber::registry() | ||||||||||
| .with(fmt::layer().with_writer(ot::StdoutWriter::new)) | ||||||||||
| .with(filter) | ||||||||||
| .init(); | ||||||||||
|
|
||||||||||
| let cli = Cli::parse(); | ||||||||||
|
|
||||||||||
| match cli.command { | ||||||||||
| Command::Completions(CompletionsArgs { shell }) => { | ||||||||||
| let mut cmd = Cli::command(); | ||||||||||
| let name = cmd.get_name().to_string(); | ||||||||||
| clap_complete::generate(shell, &mut cmd, name, &mut std::io::stdout()); | ||||||||||
| Ok(()) | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| [package] | ||
| name = "minimald" | ||
| version = "0.0.1" | ||
| edition.workspace = true | ||
| publish.workspace = true | ||
|
|
||
| [dependencies] | ||
| clap.workspace = true | ||
| clap_complete.workspace = true | ||
| dirs.workspace = true | ||
| futures.workspace = true | ||
|
|
||
| russh.workspace = true | ||
| serde.workspace = true | ||
| tokio.workspace = true | ||
| tokio-util.workspace = true | ||
| tracing.workspace = true | ||
| tracing-subscriber.workspace = true | ||
|
|
||
| ot.workspace = true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| use std::process::Command; | ||
| fn main() { | ||
| let output = Command::new("git") | ||
| .args(["rev-parse", "--short", "HEAD"]) | ||
| .output() | ||
| .unwrap(); | ||
| let git_hash = String::from_utf8(output.stdout).unwrap(); | ||
| println!("cargo:rustc-env=GIT_HASH={}", git_hash); | ||
|
twitchyliquid64 marked this conversation as resolved.
Resolved
|
||
|
|
||
| let is_clean = std::process::Command::new("git") | ||
| .args(["status", "--porcelain=v1"]) | ||
| .output() | ||
| .map(|o| o.stdout.is_empty()) | ||
| .unwrap_or(false); | ||
| if !is_clean && std::env::var("PROFILE").unwrap() == "release" { | ||
| println!("cargo::warning=Building a release binary from an unclean working tree!!"); | ||
| println!("cargo::warning=Always build production binaries from a clean checkout."); | ||
| } | ||
|
|
||
| println!( | ||
| "cargo:rustc-env=LONG_VERSION={} ({}{})", | ||
| std::env::var("CARGO_PKG_VERSION").unwrap().as_str(), | ||
| if is_clean { "" } else { "dirty " }, | ||
| git_hash.strip_suffix("\n").unwrap(), | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| use russh::server::{Config as RuConfig, RunningSession}; | ||
| pub use std::sync::{Arc, Mutex}; | ||
| use tokio::net::UnixStream; | ||
|
|
||
| /// The auth state of the SSH connection. | ||
| #[derive(Debug, Default, PartialEq, Eq)] | ||
| pub enum Auth { | ||
| #[default] | ||
| Pending, | ||
| Local, | ||
| } | ||
|
|
||
| /// Represents the SSH connection. Lives for the | ||
| /// lifetime of the connection. | ||
| #[derive(Debug, Default)] | ||
| #[allow(dead_code)] | ||
| pub struct Connection { | ||
| pub auth: Auth, | ||
|
|
||
| /// The username given via SSH. Not populated till the client | ||
| /// authenticates the connection. | ||
| pub ssh_username: Option<String>, | ||
| } | ||
|
|
||
| impl Connection { | ||
| pub(crate) async fn from_socket( | ||
| s: UnixStream, | ||
| c: Arc<RuConfig>, | ||
| was_local_uds: bool, | ||
| ) -> (ConnectionHandle, RunningSession<ConnectionHandler>) { | ||
| let h = ConnectionHandle(Arc::new(Mutex::new(Self { | ||
| auth: if was_local_uds { | ||
| Auth::Local | ||
| } else { | ||
| Auth::Pending | ||
| }, | ||
| ..Default::default() | ||
| }))); | ||
|
|
||
| ( | ||
| h.clone(), | ||
| russh::server::run_stream(c, s, ConnectionHandler(h)) | ||
| .await | ||
| .unwrap(), | ||
| ) | ||
|
Comment on lines
+42
to
+45
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Protocol setup error is unwrapped and can panic per connection. Return/map this error into As per coding guidelines, “Only use 🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
|
|
||
| /// A thread-safe handle to the connection. | ||
| #[derive(Clone)] | ||
| pub struct ConnectionHandle(Arc<Mutex<Connection>>); | ||
|
|
||
| /// An error when handling the SSH connection. | ||
| #[derive(Debug)] | ||
| #[allow(dead_code)] | ||
| pub enum ConnectionError { | ||
| Protocol(russh::Error), | ||
| } | ||
|
|
||
| impl From<russh::Error> for ConnectionError { | ||
| fn from(value: russh::Error) -> Self { | ||
| ConnectionError::Protocol(value) | ||
| } | ||
| } | ||
|
|
||
| /// A [`russh::server::Handler`] for a [`Connection`]. | ||
| pub struct ConnectionHandler(ConnectionHandle); | ||
|
|
||
| impl russh::server::Handler for ConnectionHandler { | ||
| type Error = ConnectionError; | ||
|
|
||
| async fn auth_none(&mut self, user: &str) -> Result<russh::server::Auth, Self::Error> { | ||
| let mut s = self.0.0.lock().unwrap(); | ||
| if s.auth == Auth::Local { | ||
| s.ssh_username = Some(user.to_string()); | ||
| Ok(russh::server::Auth::Accept) | ||
| } else { | ||
| Ok(russh::server::Auth::reject()) | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.