From b79c029f21862afea0649b097fa567db180f4667 Mon Sep 17 00:00:00 2001 From: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:38:52 -0700 Subject: [PATCH] Feature/registry improvements (#2772) * add build cli script for cross-building cli * sdk alpha.13 * registry improvements --- Makefile | 2 +- core/Cargo.lock | 88 +++++------ core/build-cli.sh | 50 ++++++ core/startos/registry.service | 13 ++ core/startos/src/bins/mod.rs | 10 ++ core/startos/src/lib.rs | 3 +- core/startos/src/logs.rs | 12 +- core/startos/src/registry/admin.rs | 65 ++++++++ core/startos/src/registry/info.rs | 126 +++++++++++++++ core/startos/src/registry/mod.rs | 29 +--- core/startos/src/registry/os/asset/get.rs | 13 +- core/startos/src/registry/package/category.rs | 147 ++++++++++++++++++ core/startos/src/registry/package/mod.rs | 11 ++ core/startos/src/registry/package/signer.rs | 133 ++++++++++++++++ core/startos/src/registry/signer/mod.rs | 2 +- core/startos/startd.service | 2 +- sdk/package/lib/manifest/setupManifest.ts | 13 +- sdk/package/package-lock.json | 4 +- sdk/package/package.json | 2 +- 19 files changed, 636 insertions(+), 89 deletions(-) create mode 100755 core/build-cli.sh create mode 100644 core/startos/registry.service create mode 100644 core/startos/src/registry/info.rs create mode 100644 core/startos/src/registry/package/category.rs create mode 100644 core/startos/src/registry/package/signer.rs diff --git a/Makefile b/Makefile index c13beef94..132270636 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ touch: metadata: $(VERSION_FILE) $(PLATFORM_FILE) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) sudo: - sudo -v + sudo true clean: rm -f system-images/**/*.tar diff --git a/core/Cargo.lock b/core/Cargo.lock index acd2ceb32..32f0939d8 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -128,9 +128,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" [[package]] name = "arrayref" @@ -237,7 +237,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -248,7 +248,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -538,7 +538,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.85", + "syn 2.0.86", "which", ] @@ -847,7 +847,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1255,7 +1255,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1279,7 +1279,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1290,7 +1290,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1321,7 +1321,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1344,7 +1344,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1367,7 +1367,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1589,7 +1589,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1868,7 +1868,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -2849,9 +2849,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00419de735aac21d53b0de5ce2c03bd3627277cf471300f27ebc89f7d828047" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -3302,7 +3302,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3368,7 +3368,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3575,7 +3575,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3631,7 +3631,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3707,7 +3707,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3805,7 +3805,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3828,7 +3828,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4169,7 +4169,7 @@ dependencies = [ [[package]] name = "rpc-toolkit" version = "0.2.3" -source = "git+https://github.com/Start9Labs/rpc-toolkit.git?branch=refactor%2Fno-dyn-ctx#39a872a1294c7d864faca63f072092ce300ffbe5" +source = "git+https://github.com/Start9Labs/rpc-toolkit.git?branch=refactor%2Fno-dyn-ctx#021379f21c4d11c5a62c07460f4531ce9b555155" dependencies = [ "async-stream", "async-trait", @@ -4496,7 +4496,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4570,7 +4570,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -5003,7 +5003,7 @@ dependencies = [ "quote", "regex-syntax 0.6.29", "strsim 0.10.0", - "syn 2.0.85", + "syn 2.0.86", "unicode-width", ] @@ -5245,9 +5245,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" dependencies = [ "proc-macro2", "quote", @@ -5363,22 +5363,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -5494,7 +5494,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -5768,7 +5768,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -5920,7 +5920,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "termcolor", ] @@ -5990,7 +5990,7 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -6215,7 +6215,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "wasm-bindgen-shared", ] @@ -6249,7 +6249,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6633,7 +6633,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -6653,7 +6653,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] diff --git a/core/build-cli.sh b/core/build-cli.sh new file mode 100755 index 000000000..8e069a690 --- /dev/null +++ b/core/build-cli.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +cd "$(dirname "${BASH_SOURCE[0]}")" + +set -ea +shopt -s expand_aliases + +if [ -z "$ARCH" ]; then + ARCH=$(uname -m) +fi +if [ "$ARCH" = "arm64" ]; then + ARCH="aarch64" +fi + +if [ -z "$KERNEL_NAME" ]; then + KERNEL_NAME=$(uname -s) +fi + +if [ -z "$TARGET" ]; then + if [ "$KERNEL_NAME" = "Linux" ]; then + TARGET="$ARCH-unknown-linux-musl" + elif [ "$KERNEL_NAME" = "Darwin" ]; then + TARGET="$ARCH-apple-darwin" + else + >&2 echo "unknown kernel $KERNEL_NAME" + exit 1 + fi +fi + +USE_TTY= +if tty -s; then + USE_TTY="-it" +fi + +cd .. +FEATURES="$(echo $ENVIRONMENT | sed 's/-/,/g')" +RUSTFLAGS="" + +if [[ "${ENVIRONMENT}" =~ (^|-)unstable($|-) ]]; then + RUSTFLAGS="--cfg tokio_unstable" +fi + +alias 'rust-zig-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$(pwd)":/home/rust/src -w /home/rust/src -P messense/cargo-zigbuild' + +echo "FEATURES=\"$FEATURES\"" +echo "RUSTFLAGS=\"$RUSTFLAGS\"" +rust-zig-builder sh -c "cd core && cargo zigbuild --release --no-default-features --features cli,daemon,$FEATURES --locked --bin start-cli --target=$TARGET" +if [ "$(ls -nd core/target/$TARGET/release/start-cli | awk '{ print $3 }')" != "$UID" ]; then + rust-zig-builder sh -c "cd core && chown -R $UID:$UID target && chown -R $UID:$UID /root/.cargo" +fi \ No newline at end of file diff --git a/core/startos/registry.service b/core/startos/registry.service new file mode 100644 index 000000000..63941a25e --- /dev/null +++ b/core/startos/registry.service @@ -0,0 +1,13 @@ +[Unit] +Description=StartOS Registry + +[Service] +Type=simple +Environment=RUST_LOG=startos=debug,patch_db=warn +ExecStart=/usr/local/bin/registry +Restart=always +RestartSec=3 +ManagedOOMPreference=avoid + +[Install] +WantedBy=multi-user.target diff --git a/core/startos/src/bins/mod.rs b/core/startos/src/bins/mod.rs index 4a4670a5b..6ffecfce9 100644 --- a/core/startos/src/bins/mod.rs +++ b/core/startos/src/bins/mod.rs @@ -28,6 +28,16 @@ fn select_executable(name: &str) -> Option)> { "embassy-sdk" => Some(|_| deprecated::renamed("embassy-sdk", "start-sdk")), "embassyd" => Some(|_| deprecated::renamed("embassyd", "startd")), "embassy-init" => Some(|_| deprecated::removed("embassy-init")), + "contents" => Some(|_| { + #[cfg(feature = "cli")] + println!("start-cli"); + #[cfg(feature = "container-runtime")] + println!("start-cli (container)"); + #[cfg(feature = "daemon")] + println!("startd"); + #[cfg(feature = "registry")] + println!("registry"); + }), _ => None, } } diff --git a/core/startos/src/lib.rs b/core/startos/src/lib.rs index d9d626754..83c4d462d 100644 --- a/core/startos/src/lib.rs +++ b/core/startos/src/lib.rs @@ -115,8 +115,7 @@ pub fn main_api() -> ParentHandler { let api = ParentHandler::new() .subcommand( "git-info", - from_fn(|_: RpcContext| version::git_info()) - .with_about("Display the githash of StartOS CLI"), + from_fn(|_: C| version::git_info()).with_about("Display the githash of StartOS CLI"), ) .subcommand( "echo", diff --git a/core/startos/src/logs.rs b/core/startos/src/logs.rs index 2db7e9952..1cb3327b6 100644 --- a/core/startos/src/logs.rs +++ b/core/startos/src/logs.rs @@ -360,11 +360,7 @@ pub fn logs< source: impl for<'a> LogSourceFn<'a, C, Extra>, ) -> ParentHandler> { ParentHandler::new() - .root_handler( - logs_nofollow::(source.clone()) - .with_inherited(|params, _| params) - .no_cli(), - ) + .root_handler(logs_nofollow::(source.clone()).no_cli()) .subcommand( "follow", logs_follow::(source) @@ -436,7 +432,7 @@ where fn logs_nofollow( f: impl for<'a> LogSourceFn<'a, C, Extra>, -) -> impl HandlerFor, Ok = LogResponse, Err = Error> +) -> impl HandlerFor, InheritedParams = Empty, Ok = LogResponse, Err = Error> where C: Context, Extra: FromArgMatches + Args + Send + Sync + 'static, @@ -444,7 +440,7 @@ where from_fn_async( move |HandlerArgs { context, - inherited_params: + params: LogsParams { extra, limit, @@ -453,7 +449,7 @@ where before, }, .. - }: HandlerArgs>| { + }: HandlerArgs>| { let f = f.clone(); async move { fetch_logs( diff --git a/core/startos/src/registry/admin.rs b/core/startos/src/registry/admin.rs index a327a9587..f3cac9f7e 100644 --- a/core/startos/src/registry/admin.rs +++ b/core/startos/src/registry/admin.rs @@ -60,6 +60,13 @@ fn signers_api() -> ParentHandler { "add", from_fn_async(cli_add_signer).with_about("Add signer"), ) + .subcommand( + "edit", + from_fn_async(edit_signer) + .with_metadata("admin", Value::Bool(true)) + .no_display() + .with_call_remote::(), + ) } impl Model> { @@ -143,6 +150,64 @@ pub async fn add_signer(ctx: RegistryContext, signer: SignerInfo) -> Result, + #[arg(short = 'c', long)] + pub add_contact: Vec, + #[arg(short = 'k', long)] + pub add_key: Vec, + #[arg(short = 'C', long)] + pub remove_contact: Vec, + #[arg(short = 'K', long)] + pub remove_key: Vec, +} + +pub async fn edit_signer( + ctx: RegistryContext, + EditSignerParams { + id, + set_name, + add_contact, + add_key, + remove_contact, + remove_key, + }: EditSignerParams, +) -> Result<(), Error> { + ctx.db + .mutate(|db| { + db.as_index_mut() + .as_signers_mut() + .as_idx_mut(&id) + .or_not_found(&id)? + .mutate(|s| { + if let Some(name) = set_name { + s.name = name; + } + s.contact.extend(add_contact); + for rm in remove_contact { + let Some((idx, _)) = s.contact.iter().enumerate().find(|(_, c)| *c == &rm) + else { + continue; + }; + s.contact.remove(idx); + } + + s.keys.extend(add_key); + for rm in remove_key { + s.keys.remove(&rm); + } + Ok(()) + }) + }) + .await +} + #[derive(Debug, Deserialize, Serialize, Parser)] #[command(rename_all = "kebab-case")] #[serde(rename_all = "camelCase")] diff --git a/core/startos/src/registry/info.rs b/core/startos/src/registry/info.rs new file mode 100644 index 000000000..402f0891a --- /dev/null +++ b/core/startos/src/registry/info.rs @@ -0,0 +1,126 @@ +use std::collections::BTreeMap; +use std::path::PathBuf; + +use clap::Parser; +use imbl_value::InternedString; +use itertools::Itertools; +use models::DataUrl; +use rpc_toolkit::{from_fn_async, Context, Empty, HandlerArgs, HandlerExt, ParentHandler}; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +use crate::context::CliContext; +use crate::prelude::*; +use crate::registry::context::RegistryContext; +use crate::registry::package::index::Category; +use crate::util::serde::{HandlerExtSerde, WithIoFormat}; + +pub fn info_api() -> ParentHandler> { + ParentHandler::>::new() + .root_handler( + from_fn_async(get_info) + .with_display_serializable() + .with_about("Display registry name, icon, and package categories") + .with_call_remote::(), + ) + .subcommand( + "set-name", + from_fn_async(set_name) + .with_metadata("admin", Value::Bool(true)) + .no_display() + .with_about("Set the name for the registry") + .with_call_remote::(), + ) + .subcommand( + "set-icon", + from_fn_async(set_icon) + .with_metadata("admin", Value::Bool(true)) + .no_cli(), + ) + .subcommand( + "set-icon", + from_fn_async(cli_set_icon) + .no_display() + .with_about("Set the icon for the registry"), + ) +} + +#[derive(Debug, Default, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct RegistryInfo { + pub name: Option, + pub icon: Option>, + #[ts(as = "BTreeMap::")] + pub categories: BTreeMap, +} + +pub async fn get_info(ctx: RegistryContext) -> Result { + let peek = ctx.db.peek().await.into_index(); + Ok(RegistryInfo { + name: peek.as_name().de()?, + icon: peek.as_icon().de()?, + categories: peek.as_package().as_categories().de()?, + }) +} + +#[derive(Debug, Deserialize, Serialize, Parser, TS)] +#[command(rename_all = "kebab-case")] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct SetNameParams { + pub name: String, +} + +pub async fn set_name( + ctx: RegistryContext, + SetNameParams { name }: SetNameParams, +) -> Result<(), Error> { + ctx.db + .mutate(|db| db.as_index_mut().as_name_mut().ser(&Some(name))) + .await +} + +#[derive(Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct SetIconParams { + pub icon: DataUrl<'static>, +} + +pub async fn set_icon( + ctx: RegistryContext, + SetIconParams { icon }: SetIconParams, +) -> Result<(), Error> { + ctx.db + .mutate(|db| db.as_index_mut().as_icon_mut().ser(&Some(icon))) + .await +} + +#[derive(Debug, Deserialize, Serialize, Parser, TS)] +#[command(rename_all = "kebab-case")] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct CliSetIconParams { + pub icon: PathBuf, +} + +pub async fn cli_set_icon( + HandlerArgs { + context: ctx, + parent_method, + method, + params: CliSetIconParams { icon }, + .. + }: HandlerArgs, +) -> Result<(), Error> { + let data_url = DataUrl::from_path(icon).await?; + ctx.call_remote::( + &parent_method.into_iter().chain(method).join("."), + imbl_value::json!({ + "icon": data_url, + }), + ) + .await?; + Ok(()) +} diff --git a/core/startos/src/registry/mod.rs b/core/startos/src/registry/mod.rs index 6a90ab640..0cbbce4e0 100644 --- a/core/startos/src/registry/mod.rs +++ b/core/startos/src/registry/mod.rs @@ -28,6 +28,7 @@ pub mod auth; pub mod context; pub mod db; pub mod device_info; +pub mod info; pub mod os; pub mod package; pub mod signer; @@ -57,25 +58,6 @@ pub async fn get_full_index(ctx: RegistryContext) -> Result { ctx.db.peek().await.into_index().de() } -#[derive(Debug, Default, Deserialize, Serialize, TS)] -#[serde(rename_all = "camelCase")] -#[ts(export)] -pub struct RegistryInfo { - pub name: Option, - pub icon: Option>, - #[ts(as = "BTreeMap::")] - pub categories: BTreeMap, -} - -pub async fn get_info(ctx: RegistryContext) -> Result { - let peek = ctx.db.peek().await.into_index(); - Ok(RegistryInfo { - name: peek.as_name().de()?, - icon: peek.as_icon().de()?, - categories: peek.as_package().as_categories().de()?, - }) -} - pub fn registry_api() -> ParentHandler { ParentHandler::new() .subcommand( @@ -85,13 +67,8 @@ pub fn registry_api() -> ParentHandler { .with_about("List info including registry name and packages") .with_call_remote::(), ) - .subcommand( - "info", - from_fn_async(get_info) - .with_display_serializable() - .with_about("Display registry name, icon, and package categories") - .with_call_remote::(), - ) + .subcommand("info", info::info_api::()) + // set info and categories .subcommand( "os", os::os_api::().with_about("Commands related to OS assets and versions"), diff --git a/core/startos/src/registry/os/asset/get.rs b/core/startos/src/registry/os/asset/get.rs index 6c0479964..0d63435f2 100644 --- a/core/startos/src/registry/os/asset/get.rs +++ b/core/startos/src/registry/os/asset/get.rs @@ -40,7 +40,12 @@ pub fn get_api() -> ParentHandler { .with_about("Download img"), ) .subcommand("squashfs", from_fn_async(get_squashfs).no_cli()) - .subcommand("squashfs", from_fn_async(cli_get_os_asset).no_display().with_about("Download squashfs")) + .subcommand( + "squashfs", + from_fn_async(cli_get_os_asset) + .no_display() + .with_about("Download squashfs"), + ) } #[derive(Debug, Deserialize, Serialize, TS)] @@ -104,7 +109,11 @@ pub async fn get_squashfs( pub struct CliGetOsAssetParams { pub version: Version, pub platform: InternedString, - #[arg(long = "download", short = 'd')] + #[arg( + long = "download", + short = 'd', + help = "The path of the directory to download to" + )] pub download: Option, #[arg( long = "reverify", diff --git a/core/startos/src/registry/package/category.rs b/core/startos/src/registry/package/category.rs new file mode 100644 index 000000000..97b0fb227 --- /dev/null +++ b/core/startos/src/registry/package/category.rs @@ -0,0 +1,147 @@ +use std::collections::BTreeMap; + +use clap::Parser; +use imbl_value::InternedString; +use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +use crate::context::CliContext; +use crate::prelude::*; +use crate::registry::context::RegistryContext; +use crate::registry::package::index::Category; +use crate::s9pk::manifest::Description; +use crate::util::serde::{display_serializable, HandlerExtSerde, WithIoFormat}; + +pub fn category_api() -> ParentHandler { + ParentHandler::new() + .subcommand( + "add", + from_fn_async(add_category) + .with_metadata("admin", Value::Bool(true)) + .no_display() + .with_about("Add a category to the registry") + .with_call_remote::(), + ) + .subcommand( + "remove", + from_fn_async(remove_category) + .with_metadata("admin", Value::Bool(true)) + .no_display() + .with_about("Remove a category from the registry") + .with_call_remote::(), + ) + .subcommand( + "list", + from_fn_async(list_categories) + .with_display_serializable() + .with_custom_display_fn(|params, categories| { + Ok(display_categories(params.params, categories)) + }) + .with_call_remote::(), + ) +} + +#[derive(Debug, Deserialize, Serialize, Parser, TS)] +#[command(rename_all = "kebab-case")] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct AddCategoryParams { + #[ts(type = "string")] + pub id: InternedString, + pub name: String, + #[arg(short, long, help = "Short description for the category")] + pub short: String, + #[arg(short, long, help = "Long description for the category")] + pub long: String, +} + +pub async fn add_category( + ctx: RegistryContext, + AddCategoryParams { + id, + name, + short, + long, + }: AddCategoryParams, +) -> Result<(), Error> { + ctx.db + .mutate(|db| { + db.as_index_mut() + .as_package_mut() + .as_categories_mut() + .insert( + &id, + &Category { + name, + description: Description { short, long }, + }, + ) + }) + .await?; + Ok(()) +} + +#[derive(Debug, Deserialize, Serialize, Parser, TS)] +#[command(rename_all = "kebab-case")] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct RemoveCategoryParams { + #[ts(type = "string")] + pub id: InternedString, +} + +pub async fn remove_category( + ctx: RegistryContext, + RemoveCategoryParams { id }: RemoveCategoryParams, +) -> Result<(), Error> { + ctx.db + .mutate(|db| { + db.as_index_mut() + .as_package_mut() + .as_categories_mut() + .remove(&id) + }) + .await?; + Ok(()) +} + +pub async fn list_categories( + ctx: RegistryContext, +) -> Result, Error> { + ctx.db + .peek() + .await + .into_index() + .into_package() + .into_categories() + .de() +} + +pub fn display_categories( + params: WithIoFormat, + categories: BTreeMap, +) { + use prettytable::*; + + if let Some(format) = params.format { + return display_serializable(format, categories); + } + + let mut table = Table::new(); + table.add_row(row![bc => + "ID", + "NAME", + "SHORT DESCRIPTION", + "LONG DESCRIPTION", + ]); + for (id, info) in categories { + table.add_row(row![ + &*id, + &info.name, + &info.description.short, + &info.description.long, + ]); + } + table.print_tty(false).unwrap(); +} diff --git a/core/startos/src/registry/package/mod.rs b/core/startos/src/registry/package/mod.rs index ef312dc28..74d244deb 100644 --- a/core/startos/src/registry/package/mod.rs +++ b/core/startos/src/registry/package/mod.rs @@ -5,8 +5,10 @@ use crate::prelude::*; use crate::util::serde::HandlerExtSerde; pub mod add; +pub mod category; pub mod get; pub mod index; +pub mod signer; pub fn package_api() -> ParentHandler { ParentHandler::new() @@ -29,6 +31,10 @@ pub fn package_api() -> ParentHandler { .no_display() .with_about("Add package to registry index"), ) + .subcommand( + "signer", + signer::signer_api::().with_about("Add, remove, and list package signers"), + ) .subcommand( "get", from_fn_async(get::get_package) @@ -40,4 +46,9 @@ pub fn package_api() -> ParentHandler { .with_about("List installation candidate package(s)") .with_call_remote::(), ) + .subcommand( + "category", + category::category_api::() + .with_about("Update the categories for packages on the registry"), + ) } diff --git a/core/startos/src/registry/package/signer.rs b/core/startos/src/registry/package/signer.rs new file mode 100644 index 000000000..56bfc9b1c --- /dev/null +++ b/core/startos/src/registry/package/signer.rs @@ -0,0 +1,133 @@ +use std::collections::BTreeMap; + +use clap::Parser; +use models::PackageId; +use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +use crate::context::CliContext; +use crate::prelude::*; +use crate::registry::admin::display_signers; +use crate::registry::context::RegistryContext; +use crate::registry::signer::SignerInfo; +use crate::rpc_continuations::Guid; +use crate::util::serde::HandlerExtSerde; + +pub fn signer_api() -> ParentHandler { + ParentHandler::new() + .subcommand( + "add", + from_fn_async(add_package_signer) + .with_metadata("admin", Value::Bool(true)) + .no_display() + .with_about("Add package signer") + .with_call_remote::(), + ) + .subcommand( + "remove", + from_fn_async(remove_package_signer) + .with_metadata("admin", Value::Bool(true)) + .no_display() + .with_about("Remove package signer") + .with_call_remote::(), + ) + .subcommand( + "list", + from_fn_async(list_package_signers) + .with_display_serializable() + .with_custom_display_fn(|handle, result| Ok(display_signers(handle.params, result))) + .with_about("List package signers and related signer info") + .with_call_remote::(), + ) +} + +#[derive(Debug, Deserialize, Serialize, Parser, TS)] +#[command(rename_all = "kebab-case")] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct PackageSignerParams { + pub id: PackageId, + pub signer: Guid, +} + +pub async fn add_package_signer( + ctx: RegistryContext, + PackageSignerParams { id, signer }: PackageSignerParams, +) -> Result<(), Error> { + ctx.db + .mutate(|db| { + ensure_code!( + db.as_index().as_signers().contains_key(&signer)?, + ErrorKind::InvalidRequest, + "unknown signer {signer}" + ); + + db.as_index_mut() + .as_package_mut() + .as_packages_mut() + .as_idx_mut(&id) + .or_not_found(&id)? + .as_authorized_mut() + .mutate(|s| Ok(s.insert(signer)))?; + + Ok(()) + }) + .await +} + +pub async fn remove_package_signer( + ctx: RegistryContext, + PackageSignerParams { id, signer }: PackageSignerParams, +) -> Result<(), Error> { + ctx.db + .mutate(|db| { + if !db + .as_index_mut() + .as_package_mut() + .as_packages_mut() + .as_idx_mut(&id) + .or_not_found(&id)? + .as_authorized_mut() + .mutate(|s| Ok(s.remove(&signer)))? + { + return Err(Error::new( + eyre!("signer {signer} is not authorized to sign for {id}"), + ErrorKind::NotFound, + )); + } + + Ok(()) + }) + .await +} + +#[derive(Debug, Deserialize, Serialize, Parser, TS)] +#[command(rename_all = "kebab-case")] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct ListPackageSignersParams { + pub id: PackageId, +} + +pub async fn list_package_signers( + ctx: RegistryContext, + ListPackageSignersParams { id }: ListPackageSignersParams, +) -> Result, Error> { + let db = ctx.db.peek().await; + db.as_index() + .as_package() + .as_packages() + .as_idx(&id) + .or_not_found(&id)? + .as_authorized() + .de()? + .into_iter() + .filter_map(|guid| { + db.as_index() + .as_signers() + .as_idx(&guid) + .map(|s| s.de().map(|s| (guid, s))) + }) + .collect() +} diff --git a/core/startos/src/registry/signer/mod.rs b/core/startos/src/registry/signer/mod.rs index c203f0dad..137c40f0f 100644 --- a/core/startos/src/registry/signer/mod.rs +++ b/core/startos/src/registry/signer/mod.rs @@ -25,7 +25,7 @@ pub struct SignerInfo { pub keys: HashSet, } -#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[derive(Clone, Debug, Deserialize, Serialize, TS, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[ts(export)] // TODO: better types diff --git a/core/startos/startd.service b/core/startos/startd.service index 56cf92e22..6ce17697e 100644 --- a/core/startos/startd.service +++ b/core/startos/startd.service @@ -3,7 +3,7 @@ Description=StartOS Daemon [Service] Type=simple -Environment=RUST_LOG=startos=debug,js_engine=debug,patch_db=warn +Environment=RUST_LOG=startos=debug,patch_db=warn ExecStart=/usr/bin/startd Restart=always RestartSec=3 diff --git a/sdk/package/lib/manifest/setupManifest.ts b/sdk/package/lib/manifest/setupManifest.ts index 2ae999e62..c529f1ab7 100644 --- a/sdk/package/lib/manifest/setupManifest.ts +++ b/sdk/package/lib/manifest/setupManifest.ts @@ -6,6 +6,7 @@ import { } from "../../../base/lib/types/ManifestTypes" import { SDKVersion } from "../StartSdk" import { VersionGraph } from "../version/VersionGraph" +import { execSync } from "child_process" /** * @description Use this function to define critical information about your package @@ -26,6 +27,16 @@ export function setupManifest< return manifest } +function gitHash(): string { + const hash = execSync("git rev-parse HEAD").toString().trim() + try { + execSync("git diff-index --quiet HEAD --") + return hash + } catch (e) { + return hash + "-modified" + } +} + export function buildManifest< Id extends string, Version extends string, @@ -56,7 +67,7 @@ export function buildManifest< ) return { ...manifest, - gitHash: null, + gitHash: gitHash(), osVersion: SDKVersion, version: versions.current.options.version, releaseNotes: versions.current.options.releaseNotes, diff --git a/sdk/package/package-lock.json b/sdk/package/package-lock.json index 2294d800b..2cc87d374 100644 --- a/sdk/package/package-lock.json +++ b/sdk/package/package-lock.json @@ -1,12 +1,12 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha9", + "version": "0.3.6-alpha.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha9", + "version": "0.3.6-alpha.13", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/sdk/package/package.json b/sdk/package/package.json index acc523594..661263f70 100644 --- a/sdk/package/package.json +++ b/sdk/package/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha.12", + "version": "0.3.6-alpha.13", "description": "Software development kit to facilitate packaging services for StartOS", "main": "./package/lib/index.js", "types": "./package/lib/index.d.ts",