mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 04:01:58 +00:00
Feature/consolidate setup (#3092)
* start consolidating * add start-cli flash-os * combine install and setup and refactor all * use http * undo mock * fix translation * translations * use dialogservice wrapper * better ST messaging on setup * only warn on update if breakages (#3097) * finish setup wizard and ui language-keyboard feature * fix typo * wip: localization * remove start-tunnel readme * switch to posix strings for language internal * revert mock * translate backend strings * fix missing about text * help text for args * feat: add "Add new gateway" option (#3098) * feat: add "Add new gateway" option * Update web/projects/ui/src/app/routes/portal/components/form/controls/select.component.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * add translation --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Matt Hill <mattnine@protonmail.com> * fix dns selection * keyboard keymap also * ability to shutdown after install * revert mock * working setup flow + manifest localization * (mostly) redundant localization on frontend * version bump * omit live medium from disk list and better space management * ignore missing package archive on 035 migration * fix device migration * add i18n helper to sdk * fix install over 0.3.5.1 * fix grub config --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com> Co-authored-by: Alex Inkin <alexander@inkin.ru> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -67,7 +67,7 @@ pub async fn get_available_governors() -> Result<BTreeSet<Governor>, Error> {
|
||||
for_cpu
|
||||
.entry(current_cpu.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("governors listed before cpu"),
|
||||
eyre!("{}", t!("util.cpupower.governors-listed-before-cpu")),
|
||||
ErrorKind::ParseSysInfo,
|
||||
)
|
||||
})?)
|
||||
@@ -95,6 +95,7 @@ pub async fn current_governor() -> Result<Option<Governor>, Error> {
|
||||
let Some(raw) = Command::new("cpupower")
|
||||
.arg("frequency-info")
|
||||
.arg("-p")
|
||||
.env("LANG", "C.UTF-8")
|
||||
.invoke(ErrorKind::CpuSettings)
|
||||
.await
|
||||
.and_then(|s| Ok(Some(String::from_utf8(s)?)))
|
||||
@@ -122,7 +123,10 @@ pub async fn current_governor() -> Result<Option<Governor>, Error> {
|
||||
}
|
||||
}
|
||||
Err(Error::new(
|
||||
eyre!("Failed to parse cpupower output:\n{raw}"),
|
||||
eyre!(
|
||||
"{}",
|
||||
t!("util.cpupower.failed-to-parse-output", output = raw)
|
||||
),
|
||||
ErrorKind::ParseSysInfo,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -775,6 +775,23 @@ pub fn dir_copy<'a, P0: AsRef<Path> + 'a + Send + Sync, P1: AsRef<Path> + 'a + S
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub async fn copy_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<(), Error> {
|
||||
let src = src.as_ref();
|
||||
tokio::fs::metadata(src)
|
||||
.await
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, src.display()))?;
|
||||
let dst = dst.as_ref();
|
||||
if let Some(parent) = dst.parent() {
|
||||
tokio::fs::create_dir_all(parent)
|
||||
.await
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("mkdir -p {parent:?}")))?;
|
||||
}
|
||||
tokio::fs::copy(src, dst)
|
||||
.await
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("cp {src:?} -> {dst:?}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct TimeoutStream<S: AsyncRead + AsyncWrite = TcpStream> {
|
||||
timeout: Duration,
|
||||
|
||||
@@ -25,6 +25,7 @@ impl LshwDevice {
|
||||
Self::Display(_) => "display",
|
||||
}
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
pub fn from_value(value: &Value) -> Option<Self> {
|
||||
match value["class"].as_str() {
|
||||
Some("processor") => Some(LshwDevice::Processor(LshwProcessor::from_value(value))),
|
||||
@@ -41,6 +42,7 @@ pub struct LshwProcessor {
|
||||
pub capabilities: BTreeSet<InternedString>,
|
||||
}
|
||||
impl LshwProcessor {
|
||||
#[instrument(skip_all)]
|
||||
fn from_value(value: &Value) -> Self {
|
||||
Self {
|
||||
product: value["product"].as_str().map(From::from),
|
||||
@@ -63,6 +65,7 @@ pub struct LshwDisplay {
|
||||
pub driver: Option<InternedString>,
|
||||
}
|
||||
impl LshwDisplay {
|
||||
#[instrument(skip_all)]
|
||||
fn from_value(value: &Value) -> Self {
|
||||
Self {
|
||||
product: value["product"].as_str().map(From::from),
|
||||
|
||||
@@ -666,6 +666,27 @@ pub fn new_guid() -> InternedString {
|
||||
))
|
||||
}
|
||||
|
||||
/// A utility for lazily computing a Display value. This is useful for i18n
|
||||
/// where you want to defer the translation until the value is actually displayed,
|
||||
/// avoiding allocations in the common case where the message is not rendered.
|
||||
pub struct LazyDisplay<F>(F);
|
||||
impl<F: Fn() -> D, D: fmt::Display> LazyDisplay<F> {
|
||||
pub fn new(f: F) -> Self {
|
||||
LazyDisplay(f)
|
||||
}
|
||||
}
|
||||
impl<F: Fn() -> D, D: fmt::Display> fmt::Display for LazyDisplay<F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", (self.0)())
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a lazily-evaluated Display implementation.
|
||||
/// The closure is only called when the value is actually displayed.
|
||||
pub fn lazy_display<F: Fn() -> D, D: fmt::Display>(f: F) -> LazyDisplay<F> {
|
||||
LazyDisplay::new(f)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, TS)]
|
||||
#[ts(type = "string")]
|
||||
pub enum PathOrUrl {
|
||||
|
||||
@@ -97,7 +97,7 @@ impl WebSocket {
|
||||
if self.ping_state.is_some() {
|
||||
self.fused = true;
|
||||
break Poll::Ready(Some(Err(axum::Error::new(eyre!(
|
||||
"Timeout: WebSocket did not respond to ping within {PING_TIMEOUT:?}"
|
||||
"{}", t!("util.net.websocket-ping-timeout", timeout = format!("{PING_TIMEOUT:?}"))
|
||||
)))));
|
||||
}
|
||||
self.ping_state = Some((false, rand::random()));
|
||||
|
||||
@@ -17,13 +17,13 @@ use crate::util::{Apply, PathOrUrl};
|
||||
pub fn util<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new().subcommand(
|
||||
"b3sum",
|
||||
from_fn_async(b3sum).with_about("Calculate blake3 hash for a file"),
|
||||
from_fn_async(b3sum).with_about("about.calculate-blake3-hash-for-file"),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||
pub struct B3sumParams {
|
||||
#[arg(long = "no-mmap", action = clap::ArgAction::SetFalse)]
|
||||
#[arg(long = "no-mmap", action = clap::ArgAction::SetFalse, help = "help.arg.no-mmap")]
|
||||
allow_mmap: bool,
|
||||
file: String,
|
||||
}
|
||||
@@ -57,7 +57,7 @@ pub async fn b3sum(
|
||||
.await
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("unknown scheme: {}", url.scheme()),
|
||||
eyre!("{}", t!("util.rpc.unknown-scheme", scheme = url.scheme())),
|
||||
ErrorKind::InvalidRequest,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -652,7 +652,7 @@ impl std::str::FromStr for Duration {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let units_idx = s.find(|c: char| c.is_alphabetic()).ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("Must specify units for duration"),
|
||||
eyre!("{}", t!("util.serde.must-specify-units-for-duration")),
|
||||
crate::ErrorKind::Deserialization,
|
||||
)
|
||||
})?;
|
||||
@@ -691,7 +691,7 @@ impl std::str::FromStr for Duration {
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
eyre!("Invalid units for duration"),
|
||||
eyre!("{}", t!("util.serde.invalid-units-for-duration")),
|
||||
crate::ErrorKind::Deserialization,
|
||||
));
|
||||
}
|
||||
@@ -1050,7 +1050,7 @@ impl<T: TryFrom<Vec<u8>>> FromStr for Base64<T> {
|
||||
.map(Self)
|
||||
.map_err(|_| {
|
||||
Error::new(
|
||||
eyre!("failed to create from buffer"),
|
||||
eyre!("{}", t!("util.serde.failed-to-create-from-buffer")),
|
||||
ErrorKind::Deserialization,
|
||||
)
|
||||
})
|
||||
@@ -1151,7 +1151,7 @@ pub fn apply_expr(input: jaq_core::Val, expr: &str) -> Result<jaq_core::Val, Err
|
||||
|
||||
let Some(expr) = expr else {
|
||||
return Err(Error::new(
|
||||
eyre!("Failed to parse expression: {:?}", errs),
|
||||
eyre!("{}", t!("util.serde.failed-to-parse-expression", errors = format!("{:?}", errs))),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
));
|
||||
};
|
||||
@@ -1167,7 +1167,7 @@ pub fn apply_expr(input: jaq_core::Val, expr: &str) -> Result<jaq_core::Val, Err
|
||||
|
||||
if !errs.is_empty() {
|
||||
return Err(Error::new(
|
||||
eyre!("Failed to compile expression: {:?}", errs),
|
||||
eyre!("{}", t!("util.serde.failed-to-compile-expression", errors = format!("{:?}", errs))),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
));
|
||||
};
|
||||
@@ -1182,14 +1182,14 @@ pub fn apply_expr(input: jaq_core::Val, expr: &str) -> Result<jaq_core::Val, Err
|
||||
.with_kind(crate::ErrorKind::Deserialization)?
|
||||
else {
|
||||
return Err(Error::new(
|
||||
eyre!("expr returned no results"),
|
||||
eyre!("{}", t!("util.serde.expr-returned-no-results")),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
));
|
||||
};
|
||||
|
||||
if res_iter.next().is_some() {
|
||||
return Err(Error::new(
|
||||
eyre!("expr returned too many results"),
|
||||
eyre!("{}", t!("util.serde.expr-returned-too-many-results")),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ fn map_miette(m: miette::Error) -> Error {
|
||||
}
|
||||
fn noninteractive_err() -> Error {
|
||||
Error::new(
|
||||
eyre!("Terminal must be in interactive mode for this wizard"),
|
||||
eyre!("{}", t!("util.tui.terminal-must-be-interactive")),
|
||||
ErrorKind::Filesystem,
|
||||
)
|
||||
}
|
||||
@@ -21,7 +21,7 @@ where
|
||||
{
|
||||
move |s| {
|
||||
s.parse::<T>()
|
||||
.map_err(|_| format!("Please enter a valid {what}."))
|
||||
.map_err(|_| t!("util.tui.enter-valid-value", what = what).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ pub async fn prompt<T, E: std::fmt::Display, Parse: FnMut(&str) -> Result<T, E>>
|
||||
}
|
||||
}
|
||||
ReadlineEvent::Eof | ReadlineEvent::Interrupted => {
|
||||
return Err(Error::new(eyre!("Aborted"), ErrorKind::Cancelled));
|
||||
return Err(Error::new(eyre!("{}", t!("util.tui.aborted")), ErrorKind::Cancelled));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@@ -83,7 +83,7 @@ pub async fn prompt_multiline<
|
||||
Err(e) => writeln!(&mut rl_ctx.shared_writer, "{e}")?,
|
||||
},
|
||||
ReadlineEvent::Eof | ReadlineEvent::Interrupted => {
|
||||
return Err(Error::new(eyre!("Aborted"), ErrorKind::Cancelled));
|
||||
return Err(Error::new(eyre!("{}", t!("util.tui.aborted")), ErrorKind::Cancelled));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@@ -119,7 +119,7 @@ pub async fn choose_custom_display<'t, T>(
|
||||
.await
|
||||
.map_err(map_miette)?;
|
||||
if choice.len() < 1 {
|
||||
return Err(Error::new(eyre!("Aborted"), ErrorKind::Cancelled));
|
||||
return Err(Error::new(eyre!("{}", t!("util.tui.aborted")), ErrorKind::Cancelled));
|
||||
}
|
||||
let (idx, choice_str) = string_choices
|
||||
.iter()
|
||||
@@ -127,7 +127,7 @@ pub async fn choose_custom_display<'t, T>(
|
||||
.find(|(_, s)| s.as_str() == choice[0].as_str())
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("selected choice does not appear in input"),
|
||||
eyre!("{}", t!("util.tui.selected-choice-not-in-input")),
|
||||
ErrorKind::Incoherent,
|
||||
)
|
||||
})?;
|
||||
|
||||
Reference in New Issue
Block a user