mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +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:
@@ -102,7 +102,7 @@ pub fn update_tasks(
|
||||
}
|
||||
}
|
||||
None => {
|
||||
tracing::error!("action request exists in an invalid state {:?}", v.task);
|
||||
tracing::error!("{}", t!("service.action.action-request-invalid-state", task = format!("{:?}", v.task)));
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -151,7 +151,7 @@ impl Handler<RunAction> for ServiceActor {
|
||||
.de()?;
|
||||
if matches!(&action.visibility, ActionVisibility::Disabled(_)) {
|
||||
return Err(Error::new(
|
||||
eyre!("action {action_id} is disabled"),
|
||||
eyre!("{}", t!("service.action.action-is-disabled", action_id = action_id)),
|
||||
ErrorKind::Action,
|
||||
));
|
||||
}
|
||||
@@ -162,7 +162,7 @@ impl Handler<RunAction> for ServiceActor {
|
||||
_ => false,
|
||||
} {
|
||||
return Err(Error::new(
|
||||
eyre!("service is not in allowed status for {action_id}"),
|
||||
eyre!("{}", t!("service.action.service-not-in-allowed-status", action_id = action_id)),
|
||||
ErrorKind::Action,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::service::effects::context::EffectContext;
|
||||
|
||||
#[derive(Debug, Default, Parser)]
|
||||
pub struct ContainerClientConfig {
|
||||
#[arg(long = "socket")]
|
||||
#[arg(long = "socket", help = "help.arg.socket-path")]
|
||||
pub socket: Option<PathBuf>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use rust_i18n::t;
|
||||
|
||||
use crate::action::{ActionInput, ActionResult, display_action_result};
|
||||
use crate::db::model::package::{
|
||||
@@ -80,7 +81,7 @@ pub async fn export_action(
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClearActionsParams {
|
||||
#[arg(long)]
|
||||
#[arg(long, help = "help.arg.except-actions")]
|
||||
pub except: Vec<ActionId>,
|
||||
}
|
||||
|
||||
@@ -117,7 +118,9 @@ pub struct GetActionInputParams {
|
||||
#[arg(skip)]
|
||||
procedure_id: Guid,
|
||||
#[ts(optional)]
|
||||
#[arg(help = "help.arg.package-id")]
|
||||
package_id: Option<PackageId>,
|
||||
#[arg(help = "help.arg.action-id")]
|
||||
action_id: ActionId,
|
||||
}
|
||||
async fn get_action_input(
|
||||
@@ -155,9 +158,12 @@ pub struct RunActionParams {
|
||||
#[arg(skip)]
|
||||
procedure_id: Guid,
|
||||
#[ts(optional)]
|
||||
#[arg(help = "help.arg.package-id")]
|
||||
package_id: Option<PackageId>,
|
||||
#[arg(help = "help.arg.action-id")]
|
||||
action_id: ActionId,
|
||||
#[ts(type = "any")]
|
||||
#[arg(help = "help.arg.action-input")]
|
||||
input: Value,
|
||||
}
|
||||
async fn run_action(
|
||||
@@ -175,7 +181,7 @@ async fn run_action(
|
||||
|
||||
if package_id != &context.seed.id {
|
||||
return Err(Error::new(
|
||||
eyre!("calling actions on other packages is unsupported at this time"),
|
||||
eyre!("{}", t!("service.effects.action.calling-actions-on-other-packages-unsupported")),
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
context
|
||||
@@ -220,7 +226,7 @@ async fn create_task(
|
||||
TaskCondition::InputNotMatches => {
|
||||
let Some(input) = task.input.as_ref() else {
|
||||
return Err(Error::new(
|
||||
eyre!("input-not-matches trigger requires input to be specified"),
|
||||
eyre!("{}", t!("service.effects.action.input-not-matches-requires-input")),
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
};
|
||||
@@ -238,9 +244,7 @@ async fn create_task(
|
||||
else {
|
||||
return Err(Error::new(
|
||||
eyre!(
|
||||
"action {} of {} has no input",
|
||||
task.action_id,
|
||||
task.package_id
|
||||
"{}", t!("service.effects.action.action-has-no-input", action_id = task.action_id, package_id = task.package_id)
|
||||
),
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
@@ -286,9 +290,9 @@ async fn create_task(
|
||||
#[ts(type = "{ only: string[] } | { except: string[] }")]
|
||||
#[ts(export)]
|
||||
pub struct ClearTasksParams {
|
||||
#[arg(long, conflicts_with = "except")]
|
||||
#[arg(long, conflicts_with = "except", help = "help.arg.only-tasks")]
|
||||
pub only: Option<Vec<ReplayId>>,
|
||||
#[arg(long, conflicts_with = "only")]
|
||||
#[arg(long, conflicts_with = "only", help = "help.arg.except-tasks")]
|
||||
pub except: Option<Vec<ReplayId>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -319,9 +319,9 @@ impl CallbackHandlers {
|
||||
#[ts(type = "{ only: number[] } | { except: number[] }")]
|
||||
#[ts(export)]
|
||||
pub struct ClearCallbacksParams {
|
||||
#[arg(long, conflicts_with = "except")]
|
||||
#[arg(long, conflicts_with = "except", help = "help.arg.only-callbacks")]
|
||||
pub only: Option<Vec<CallbackId>>,
|
||||
#[arg(long, conflicts_with = "only")]
|
||||
#[arg(long, conflicts_with = "only", help = "help.arg.except-callbacks")]
|
||||
pub except: Option<Vec<CallbackId>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::str::FromStr;
|
||||
|
||||
use clap::builder::ValueParserFactory;
|
||||
use exver::VersionRange;
|
||||
use imbl_value::InternedString;
|
||||
use rust_i18n::t;
|
||||
|
||||
use crate::db::model::package::{
|
||||
CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, ManifestPreference,
|
||||
@@ -148,13 +148,25 @@ impl FromStr for DependencyRequirement {
|
||||
.map(|id| id.parse().map_err(Error::from))
|
||||
.collect(),
|
||||
Some((kind, _)) => Err(Error::new(
|
||||
eyre!("unknown dependency kind {kind}"),
|
||||
eyre!(
|
||||
"{}",
|
||||
t!(
|
||||
"service.effects.dependency.unknown-dependency-kind",
|
||||
kind = kind
|
||||
)
|
||||
),
|
||||
ErrorKind::InvalidRequest,
|
||||
)),
|
||||
None => match rest {
|
||||
"r" | "running" => Ok(BTreeSet::new()),
|
||||
kind => Err(Error::new(
|
||||
eyre!("unknown dependency kind {kind}"),
|
||||
eyre!(
|
||||
"{}",
|
||||
t!(
|
||||
"service.effects.dependency.unknown-dependency-kind",
|
||||
kind = kind
|
||||
)
|
||||
),
|
||||
ErrorKind::InvalidRequest,
|
||||
)),
|
||||
},
|
||||
@@ -293,8 +305,7 @@ pub struct CheckDependenciesParam {
|
||||
#[ts(export)]
|
||||
pub struct CheckDependenciesResult {
|
||||
package_id: PackageId,
|
||||
#[ts(type = "string | null")]
|
||||
title: Option<InternedString>,
|
||||
title: Option<String>,
|
||||
installed_version: Option<VersionString>,
|
||||
satisfies: BTreeSet<VersionString>,
|
||||
is_running: bool,
|
||||
@@ -334,7 +345,7 @@ pub async fn check_dependencies(
|
||||
.collect();
|
||||
results.push(CheckDependenciesResult {
|
||||
package_id,
|
||||
title,
|
||||
title: title.map(|t| t.localized()),
|
||||
installed_version: None,
|
||||
satisfies: BTreeSet::new(),
|
||||
is_running: false,
|
||||
@@ -360,7 +371,7 @@ pub async fn check_dependencies(
|
||||
.collect();
|
||||
results.push(CheckDependenciesResult {
|
||||
package_id,
|
||||
title,
|
||||
title: title.map(|t| t.localized()),
|
||||
installed_version,
|
||||
satisfies,
|
||||
is_running,
|
||||
|
||||
@@ -11,6 +11,6 @@ pub(super) use crate::service::effects::context::EffectContext;
|
||||
#[ts(export)]
|
||||
pub struct EventId {
|
||||
#[serde(default)]
|
||||
#[arg(default_value_t, long)]
|
||||
#[arg(default_value_t, long, help = "help.arg.event-id")]
|
||||
pub event_id: Guid,
|
||||
}
|
||||
|
||||
@@ -107,22 +107,23 @@ fn open_file_read(path: impl AsRef<Path>) -> Result<File, Error> {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Parser)]
|
||||
pub struct ExecParams {
|
||||
#[arg(long)]
|
||||
#[arg(long, help = "help.arg.force-tty")]
|
||||
force_tty: bool,
|
||||
#[arg(long)]
|
||||
#[arg(long, help = "help.arg.force-stderr-tty")]
|
||||
force_stderr_tty: bool,
|
||||
#[arg(long)]
|
||||
#[arg(long, help = "help.arg.pty-size")]
|
||||
pty_size: Option<TermSize>,
|
||||
#[arg(short, long)]
|
||||
#[arg(short, long, help = "help.arg.env-variable")]
|
||||
env: Vec<String>,
|
||||
#[arg(long)]
|
||||
#[arg(long, help = "help.arg.env-file-path")]
|
||||
env_file: Option<PathBuf>,
|
||||
#[arg(short, long)]
|
||||
#[arg(short, long, help = "help.arg.workdir-path")]
|
||||
workdir: Option<PathBuf>,
|
||||
#[arg(short, long)]
|
||||
#[arg(short, long, help = "help.arg.user-name")]
|
||||
user: Option<String>,
|
||||
#[arg(help = "help.arg.chroot-path")]
|
||||
chroot: PathBuf,
|
||||
#[arg(trailing_var_arg = true)]
|
||||
#[arg(trailing_var_arg = true, help = "help.arg.command-to-execute")]
|
||||
command: Vec<OsString>,
|
||||
}
|
||||
impl ExecParams {
|
||||
|
||||
@@ -28,6 +28,7 @@ use tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode;
|
||||
use ts_rs::TS;
|
||||
use url::Url;
|
||||
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::package::{
|
||||
InstalledState, ManifestPreference, PackageState, PackageStateMatchModelRef, TaskSeverity,
|
||||
@@ -172,7 +173,7 @@ impl ServiceRef {
|
||||
}
|
||||
let service = Arc::try_unwrap(self.0).map_err(|_| {
|
||||
Error::new(
|
||||
eyre!("ServiceActor held somewhere after actor shutdown"),
|
||||
eyre!("{}", t!("service.mod.service-actor-held-after-shutdown")),
|
||||
ErrorKind::Unknown,
|
||||
)
|
||||
})?;
|
||||
@@ -183,7 +184,7 @@ impl ServiceRef {
|
||||
Arc::try_unwrap(service.seed)
|
||||
.map_err(|_| {
|
||||
Error::new(
|
||||
eyre!("ServiceActorSeed held somewhere after actor shutdown"),
|
||||
eyre!("{}", t!("service.mod.service-actor-seed-held-after-shutdown")),
|
||||
ErrorKind::Unknown,
|
||||
)
|
||||
})?
|
||||
@@ -375,7 +376,7 @@ impl Service {
|
||||
{
|
||||
Ok(PackageState::Installed(InstalledState { manifest }))
|
||||
} else {
|
||||
Err(Error::new(eyre!("Race condition detected - package state changed during load"), ErrorKind::Database))
|
||||
Err(Error::new(eyre!("{}", t!("service.mod.race-condition-detected")), ErrorKind::Database))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -446,7 +447,7 @@ impl Service {
|
||||
handle_installed(S9pk::open(s9pk_path, Some(id)).await?).await
|
||||
}
|
||||
PackageStateMatchModelRef::Error(e) => Err(Error::new(
|
||||
eyre!("Failed to parse PackageDataEntry, found {e:?}"),
|
||||
eyre!("{}", t!("service.mod.failed-to-parse-package-data-entry", error = format!("{e:?}"))),
|
||||
ErrorKind::Deserialization,
|
||||
)),
|
||||
}
|
||||
@@ -552,7 +553,7 @@ impl Service {
|
||||
true
|
||||
} else {
|
||||
tracing::warn!(
|
||||
"Deleting task {id} because action no longer exists"
|
||||
"{}", t!("service.mod.deleting-task-action-no-longer-exists", id = id)
|
||||
);
|
||||
false
|
||||
}
|
||||
@@ -684,6 +685,7 @@ struct ServiceActorSeed {
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
pub struct RebuildParams {
|
||||
#[arg(help = "help.arg.package-id")]
|
||||
pub id: PackageId,
|
||||
}
|
||||
pub async fn rebuild(ctx: RpcContext, RebuildParams { id }: RebuildParams) -> Result<(), Error> {
|
||||
@@ -789,7 +791,7 @@ pub async fn attach(
|
||||
.join("\n");
|
||||
return Err(Error::new(
|
||||
eyre!(
|
||||
"no matching subcontainers are running for {id}; some possible choices are:\n{subcontainers}"
|
||||
"{}", t!("service.mod.no-matching-subcontainers", id = id, subcontainers = subcontainers)
|
||||
),
|
||||
ErrorKind::NotFound,
|
||||
));
|
||||
@@ -828,7 +830,7 @@ pub async fn attach(
|
||||
.map(format_subcontainer_pair)
|
||||
.join("\n");
|
||||
return Err(Error::new(
|
||||
eyre!("multiple subcontainers found for {id}: \n{subcontainer_ids}"),
|
||||
eyre!("{}", t!("service.mod.multiple-subcontainers-found", id = id, subcontainer_ids = subcontainer_ids)),
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
}
|
||||
@@ -985,7 +987,7 @@ pub async fn attach(
|
||||
"signal" => {
|
||||
if data.len() != 4 {
|
||||
return Err(Error::new(
|
||||
eyre!("invalid byte length for signal: {}", data.len()),
|
||||
eyre!("{}", t!("service.mod.invalid-byte-length-for-signal", length = data.len())),
|
||||
ErrorKind::InvalidRequest
|
||||
));
|
||||
}
|
||||
@@ -1118,14 +1120,14 @@ async fn get_passwd_command(etc_passwd_path: PathBuf, user: &str) -> RootCommand
|
||||
}
|
||||
}
|
||||
Err(Error::new(
|
||||
eyre!("Could not parse /etc/passwd for shell: {}", contents),
|
||||
eyre!("{}", t!("service.mod.could-not-parse-etc-passwd", contents = contents)),
|
||||
ErrorKind::Filesystem,
|
||||
))
|
||||
}
|
||||
.await
|
||||
.map(RootCommand)
|
||||
.unwrap_or_else(|e| {
|
||||
tracing::error!("Could not get the /etc/passwd: {e}");
|
||||
tracing::error!("{}", t!("service.mod.could-not-get-etc-passwd", error = e));
|
||||
tracing::debug!("{e:?}");
|
||||
RootCommand("/bin/sh".to_string())
|
||||
})
|
||||
@@ -1133,18 +1135,19 @@ async fn get_passwd_command(etc_passwd_path: PathBuf, user: &str) -> RootCommand
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser)]
|
||||
pub struct CliAttachParams {
|
||||
#[arg(help = "help.arg.package-id")]
|
||||
pub id: PackageId,
|
||||
#[arg(long)]
|
||||
#[arg(long, help = "help.arg.force-tty")]
|
||||
pub force_tty: bool,
|
||||
#[arg(trailing_var_arg = true)]
|
||||
#[arg(trailing_var_arg = true, help = "help.arg.command-to-execute")]
|
||||
pub command: Vec<OsString>,
|
||||
#[arg(long, short)]
|
||||
#[arg(long, short, help = "help.arg.subcontainer-name")]
|
||||
subcontainer: Option<InternedString>,
|
||||
#[arg(long, short)]
|
||||
#[arg(long, short, help = "help.arg.container-name")]
|
||||
name: Option<InternedString>,
|
||||
#[arg(long, short)]
|
||||
#[arg(long, short, help = "help.arg.user-name")]
|
||||
user: Option<InternedString>,
|
||||
#[arg(long, short)]
|
||||
#[arg(long, short, help = "help.arg.image-id")]
|
||||
image_id: Option<ImageId>,
|
||||
}
|
||||
#[instrument[skip_all]]
|
||||
@@ -1287,7 +1290,7 @@ pub async fn cli_attach(
|
||||
"exit" => {
|
||||
if data.len() != 4 {
|
||||
return Err(Error::new(
|
||||
eyre!("invalid byte length for exit code: {}", data.len()),
|
||||
eyre!("{}", t!("service.mod.invalid-byte-length-for-exit-code", length = data.len())),
|
||||
ErrorKind::InvalidRequest
|
||||
));
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ impl PersistentContainer {
|
||||
.get()
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("PersistentContainer has been destroyed"),
|
||||
eyre!("{}", t!("service.persistent-container.container-destroyed")),
|
||||
ErrorKind::Incoherent,
|
||||
)
|
||||
})?
|
||||
@@ -354,7 +354,7 @@ impl PersistentContainer {
|
||||
.get()
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("PersistentContainer has been destroyed"),
|
||||
eyre!("{}", t!("service.persistent-container.container-destroyed")),
|
||||
ErrorKind::Incoherent,
|
||||
)
|
||||
})?
|
||||
@@ -364,7 +364,7 @@ impl PersistentContainer {
|
||||
let handle = NonDetachingJoinHandle::from(tokio::spawn(async move {
|
||||
let chown_status = async {
|
||||
let res = server.run_unix(&path, |err| {
|
||||
tracing::error!("error on unix socket {}: {err}", path.display())
|
||||
tracing::error!("{}", t!("service.persistent-container.error-on-unix-socket", path = path.display(), error = err))
|
||||
})?;
|
||||
Command::new("chown")
|
||||
.arg("100000:100000")
|
||||
@@ -386,7 +386,7 @@ impl PersistentContainer {
|
||||
}));
|
||||
let shutdown = recv.await.map_err(|_| {
|
||||
Error::new(
|
||||
eyre!("unix socket server thread panicked"),
|
||||
eyre!("{}", t!("service.persistent-container.unix-socket-server-panicked")),
|
||||
ErrorKind::Unknown,
|
||||
)
|
||||
})??;
|
||||
@@ -396,7 +396,7 @@ impl PersistentContainer {
|
||||
.is_some()
|
||||
{
|
||||
return Err(Error::new(
|
||||
eyre!("PersistentContainer already initialized"),
|
||||
eyre!("{}", t!("service.persistent-container.already-initialized")),
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
}
|
||||
@@ -473,7 +473,7 @@ impl PersistentContainer {
|
||||
if let Some(destroy) = self.destroy(uninit) {
|
||||
destroy.await?;
|
||||
}
|
||||
tracing::info!("Service for {} exited", self.s9pk.as_manifest().id);
|
||||
tracing::info!("{}", t!("service.persistent-container.service-exited", id = self.s9pk.as_manifest().id));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ impl Actor for ServiceActor {
|
||||
}
|
||||
.await
|
||||
{
|
||||
tracing::error!("error synchronizing state of service: {e}");
|
||||
tracing::error!("{}", t!("service.service-actor.error-synchronizing-state", error = e));
|
||||
tracing::debug!("{e:?}");
|
||||
tracing::error!("Retrying in {}s...", SYNC_RETRY_COOLDOWN_SECONDS);
|
||||
tracing::error!("{}", t!("service.service-actor.retrying-in-seconds", seconds = SYNC_RETRY_COOLDOWN_SECONDS));
|
||||
tokio::time::timeout(
|
||||
Duration::from_secs(SYNC_RETRY_COOLDOWN_SECONDS),
|
||||
async {
|
||||
|
||||
@@ -25,7 +25,7 @@ impl ServiceActorSeed {
|
||||
fut.await.map_err(Error::from)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("No backup to resume"),
|
||||
eyre!("{}", t!("service.transition.backup.no-backup-to-resume")),
|
||||
ErrorKind::Cancelled,
|
||||
))
|
||||
};
|
||||
|
||||
@@ -62,7 +62,7 @@ pub async fn cleanup(ctx: &RpcContext, id: &PackageId, soft: bool) -> Result<(),
|
||||
| PackageState::Removing(InstalledState { manifest }) => manifest,
|
||||
s => {
|
||||
return Err(Error::new(
|
||||
eyre!("Invalid package state for cleanup: {s:?}"),
|
||||
eyre!("{}", t!("service.uninstall.invalid-package-state-for-cleanup", state = format!("{s:?}"))),
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user