diff --git a/genmeta-identity/src/checkout.rs b/genmeta-identity/src/checkout.rs index aa846ee..ceaf7bc 100644 --- a/genmeta-identity/src/checkout.rs +++ b/genmeta-identity/src/checkout.rs @@ -66,7 +66,11 @@ pub(crate) fn payment_instruction_block(url: &str) -> Result { "[!] Please complete your payment within 15 minutes.\n Open the link, or scan the QR code below\n\n", ); - for line in qr.trim_end().lines() { + for line in qr + .trim_end() + .lines() + .skip_while(|line| line.trim().is_empty()) + { block.push_str(" "); block.push_str(line); block.push('\n'); @@ -230,6 +234,20 @@ mod tests { ); } + #[test] + fn payment_block_drops_the_blank_quiet_zone_rows_above_the_qr() { + let rendered = payment_instruction_block("https://pay.example.test/checkout").unwrap(); + let first_qr_row = rendered + .split_once("Open the link, or scan the QR code below\n\n") + .unwrap() + .1 + .lines() + .next() + .unwrap(); + + assert!(first_qr_row.contains(['▀', '▄', '█']), "{rendered}"); + } + #[test] fn payment_block_falls_back_to_the_link_only_when_qr_encoding_is_impossible() { let url = format!("https://pay.example.test/{}", "x".repeat(10_000)); diff --git a/genmeta-identity/src/cli/flow/device.rs b/genmeta-identity/src/cli/flow/device.rs index 7f0cd79..473ba06 100644 --- a/genmeta-identity/src/cli/flow/device.rs +++ b/genmeta-identity/src/cli/flow/device.rs @@ -92,6 +92,7 @@ pub(crate) fn format_generated_device_name( truncate_to_device_name_limit(generated) } +#[cfg(target_os = "linux")] pub(crate) fn linux_os_release_name(content: &str) -> Option { content.lines().find_map(|line| { let value = line.trim().strip_prefix("NAME=")?.trim(); @@ -148,10 +149,9 @@ pub(crate) fn resolve_device_name(explicit: Option<&str>, home_scope: HomeScope) mod tests { use dhttp::home::HomeScope; - use super::{ - format_generated_device_name, linux_os_release_name, normalize_explicit_device_name, - select_host_label, - }; + #[cfg(target_os = "linux")] + use super::linux_os_release_name; + use super::{format_generated_device_name, normalize_explicit_device_name, select_host_label}; #[test] fn explicit_device_name_is_only_trimmed() { @@ -232,6 +232,7 @@ mod tests { ); } + #[cfg(target_os = "linux")] #[test] fn linux_os_release_name_reads_name_not_pretty_name() { let content = r#"PRETTY_NAME="Ubuntu 24.04.2 LTS" @@ -241,6 +242,7 @@ VERSION_ID="24.04" assert_eq!(linux_os_release_name(content).as_deref(), Some("Ubuntu")); } + #[cfg(target_os = "linux")] #[test] fn linux_os_release_name_accepts_unquoted_name() { let content = "ID=arch\nNAME=Arch Linux\n"; @@ -250,6 +252,7 @@ VERSION_ID="24.04" ); } + #[cfg(target_os = "linux")] #[test] fn linux_os_release_name_ignores_empty_name() { assert_eq!(linux_os_release_name("NAME=\"\"\n"), None); diff --git a/genmeta-identity/src/cli/flow/progress.rs b/genmeta-identity/src/cli/flow/progress.rs index 5b0d509..5a74830 100644 --- a/genmeta-identity/src/cli/flow/progress.rs +++ b/genmeta-identity/src/cli/flow/progress.rs @@ -1,7 +1,4 @@ -use std::{ - future::Future, - io::{self, IsTerminal}, -}; +use std::future::Future; use tracing::info_span; use tracing_indicatif::span_ext::IndicatifSpanExt; @@ -36,7 +33,7 @@ impl ProgressCopy { } } - fn completion_message(self, _is_terminal: bool) -> Option<&'static str> { + fn completion_message(self) -> Option<&'static str> { match self.completion { CompletionPolicy::Clear => None, CompletionPolicy::Retain => Some(self.success), @@ -65,17 +62,16 @@ pub(crate) const RENEW_IDENTITY: ProgressCopy = ProgressCopy::retain("Renewing identity...", CERTIFICATE_REQUESTED); pub(crate) const SAVE_DEFAULT: ProgressCopy = ProgressCopy::clear("Saving default identity..."); -fn finish_success(span: &tracing::Span, copy: ProgressCopy) { - let is_terminal = io::stderr().is_terminal(); - let Some(success) = copy.completion_message(is_terminal) else { +fn finish_success(copy: ProgressCopy) { + let Some(success) = copy.completion_message() else { return; }; - if is_terminal { - span.pb_set_finish_message(success); - } else { - super::transcript::print_line(success); - } + // Do not rely on `pb_set_finish_message` to keep the completed line: a finished + // progress bar can still be erased by later terminal redraws. Print the + // completion as a permanent transcript line instead; the progress bar itself + // is cleared when the span is dropped. + super::transcript::print_line(success); } pub(crate) async fn run( @@ -87,7 +83,7 @@ pub(crate) async fn run( span.pb_start(); let result = future.await; if result.is_ok() { - finish_success(&span, copy); + finish_success(copy); } drop(span); result @@ -102,7 +98,7 @@ pub(crate) fn run_sync( span.pb_start(); let result = operation(); if result.is_ok() { - finish_success(&span, copy); + finish_success(copy); } drop(span); result @@ -125,21 +121,19 @@ mod tests { }; #[test] - fn key_and_certificate_completions_are_stable_in_every_output_mode() { - for is_terminal in [true, false] { - assert_eq!( - GENERATE_KEY.completion_message(is_terminal), - Some("✔ Generate secp384r1 ECC key pair locally.") - ); - assert_eq!( - REQUEST_CERT.completion_message(is_terminal), - Some("✔ Generate CSR locally and request certificate.") - ); - assert_eq!( - RENEW_IDENTITY.completion_message(is_terminal), - Some("✔ Generate CSR locally and request certificate.") - ); - } + fn key_and_certificate_completions_are_stable() { + assert_eq!( + GENERATE_KEY.completion_message(), + Some("✔ Generate secp384r1 ECC key pair locally.") + ); + assert_eq!( + REQUEST_CERT.completion_message(), + Some("✔ Generate CSR locally and request certificate.") + ); + assert_eq!( + RENEW_IDENTITY.completion_message(), + Some("✔ Generate CSR locally and request certificate.") + ); assert_eq!( REQUEST_CERT.running, @@ -150,20 +144,14 @@ mod tests { #[test] fn transient_progress_has_no_completion_copy() { - for is_terminal in [true, false] { - for copy in [ - CHECK_NAME, - SEND_CODE, - VERIFY_EMAIL, - WAIT_FOR_PAYMENT, - SAVE_DEFAULT, - ] { - assert_eq!( - copy.completion_message(is_terminal), - None, - "copy: {copy:?}, terminal: {is_terminal}" - ); - } + for copy in [ + CHECK_NAME, + SEND_CODE, + VERIFY_EMAIL, + WAIT_FOR_PAYMENT, + SAVE_DEFAULT, + ] { + assert_eq!(copy.completion_message(), None, "copy: {copy:?}"); } } diff --git a/genmeta-identity/src/cli/flow/welcome.rs b/genmeta-identity/src/cli/flow/welcome.rs index 828c07f..dba5959 100644 --- a/genmeta-identity/src/cli/flow/welcome.rs +++ b/genmeta-identity/src/cli/flow/welcome.rs @@ -63,10 +63,10 @@ const SERVER_CONF_TEMPLATE: &str = "server { const WELCOME_PAGE_PATH: &str = "templates/welcome/index.html"; #[cfg(target_os = "macos")] -const PISHOO_RELOAD_COMMAND: &str = "sudo brew service reload pishoo"; +const PISHOO_RELOAD_COMMAND: &str = "Don't forget to add $USER to the _www group!\n `sudo dseditgroup -o edit -a $USER -t user _www`\n `sudo brew service reload pishoo`"; #[cfg(not(target_os = "macos"))] -const PISHOO_RELOAD_COMMAND: &str = "sudo systemctl reload pishoo"; +const PISHOO_RELOAD_COMMAND: &str = "Don't forget to add $USER to the dhttp group!\n `sudo usermod -aG dhttp $USER`\n `sudo systemctl reload pishoo`"; pub(crate) async fn maybe_create_welcome_service( dhttp_home: &DhttpHome, @@ -122,7 +122,7 @@ pub(crate) async fn maybe_create_welcome_service( pub(crate) fn format_welcome_service_created(name: &str) -> String { format!( - "Now, you can reload pishoo and visit the welcome page!\n `{PISHOO_RELOAD_COMMAND}`\n `genmeta curl https://{name}~/welcome`" + "Now, you can reload pishoo and visit the welcome page!\n{PISHOO_RELOAD_COMMAND}\n `genmeta curl https://{name}~/welcome`" ) } @@ -464,6 +464,8 @@ mod tests { format_welcome_service_created("alice.smith"), concat!( "Now, you can reload pishoo and visit the welcome page!\n", + "Don't forget to add $USER to the dhttp group!\n", + " `sudo usermod -aG dhttp $USER`\n", " `sudo systemctl reload pishoo`\n", " `genmeta curl https://alice.smith~/welcome`", ) @@ -477,6 +479,8 @@ mod tests { format_welcome_service_created("alice.smith"), concat!( "Now, you can reload pishoo and visit the welcome page!\n", + "Don't forget to add $USER to the _www group!\n", + " `sudo dseditgroup -o edit -a $USER -t user _www`\n", " `sudo brew service reload pishoo`\n", " `genmeta curl https://alice.smith~/welcome`", )