Refactor/project structure (#3085)

* refactor project structure

* environment-based default registry

* fix tests

* update build container

* use docker platform for iso build emulation

* simplify compat

* Fix docker platform spec in run-compat.sh

* handle riscv compat

* fix bug with dep error exists attr

* undo removal of sorting

* use qemu for iso stage

---------

Co-authored-by: Mariusz Kogen <k0gen@pm.me>
Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
Aiden McClelland
2025-12-22 13:39:38 -07:00
committed by GitHub
parent eda08d5b0f
commit 96ae532879
389 changed files with 744 additions and 4005 deletions

45
core/src/id/action.rs Normal file
View File

@@ -0,0 +1,45 @@
use std::path::Path;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use crate::{Id, InvalidId};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
#[ts(type = "string")]
pub struct ActionId(Id);
impl FromStr for ActionId {
type Err = InvalidId;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(ActionId(Id::try_from(s.to_owned())?))
}
}
impl AsRef<ActionId> for ActionId {
fn as_ref(&self) -> &ActionId {
self
}
}
impl std::fmt::Display for ActionId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl AsRef<str> for ActionId {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl AsRef<Path> for ActionId {
fn as_ref(&self) -> &Path {
self.0.as_ref().as_ref()
}
}
impl<'de> Deserialize<'de> for ActionId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
Ok(ActionId(serde::Deserialize::deserialize(deserializer)?))
}
}

60
core/src/id/gateway.rs Normal file
View File

@@ -0,0 +1,60 @@
use std::convert::Infallible;
use std::path::Path;
use std::str::FromStr;
use imbl_value::InternedString;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
#[ts(type = "string")]
pub struct GatewayId(InternedString);
impl GatewayId {
pub fn as_str(&self) -> &str {
&*self.0
}
}
impl From<InternedString> for GatewayId {
fn from(value: InternedString) -> Self {
Self(value)
}
}
impl From<GatewayId> for InternedString {
fn from(value: GatewayId) -> Self {
value.0
}
}
impl FromStr for GatewayId {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(GatewayId(InternedString::intern(s)))
}
}
impl AsRef<GatewayId> for GatewayId {
fn as_ref(&self) -> &GatewayId {
self
}
}
impl std::fmt::Display for GatewayId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl AsRef<str> for GatewayId {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl AsRef<Path> for GatewayId {
fn as_ref(&self) -> &Path {
self.0.as_ref()
}
}
impl<'de> Deserialize<'de> for GatewayId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
Ok(GatewayId(serde::Deserialize::deserialize(deserializer)?))
}
}

View File

@@ -0,0 +1,40 @@
use std::path::Path;
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize};
use ts_rs::TS;
use crate::{Id, InvalidId};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
#[ts(type = "string")]
pub struct HealthCheckId(Id);
impl std::fmt::Display for HealthCheckId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl FromStr for HealthCheckId {
type Err = InvalidId;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Id::from_str(s).map(HealthCheckId)
}
}
impl AsRef<str> for HealthCheckId {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl<'de> Deserialize<'de> for HealthCheckId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(HealthCheckId(Deserialize::deserialize(deserializer)?))
}
}
impl AsRef<Path> for HealthCheckId {
fn as_ref(&self) -> &Path {
self.0.as_ref().as_ref()
}
}

62
core/src/id/host.rs Normal file
View File

@@ -0,0 +1,62 @@
use std::path::Path;
use std::str::FromStr;
use imbl_value::InternedString;
use serde::{Deserialize, Deserializer, Serialize};
use ts_rs::TS;
use crate::{Id, InvalidId};
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
#[ts(type = "string")]
pub struct HostId(Id);
impl FromStr for HostId {
type Err = InvalidId;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(Id::try_from(s.to_owned())?))
}
}
impl From<Id> for HostId {
fn from(id: Id) -> Self {
Self(id)
}
}
impl From<HostId> for Id {
fn from(value: HostId) -> Self {
value.0
}
}
impl From<HostId> for InternedString {
fn from(value: HostId) -> Self {
value.0.into()
}
}
impl std::fmt::Display for HostId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl std::ops::Deref for HostId {
type Target = str;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl AsRef<str> for HostId {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl<'de> Deserialize<'de> for HostId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(HostId(Deserialize::deserialize(deserializer)?))
}
}
impl AsRef<Path> for HostId {
fn as_ref(&self) -> &Path {
self.0.as_ref().as_ref()
}
}

47
core/src/id/image.rs Normal file
View File

@@ -0,0 +1,47 @@
use std::fmt::Debug;
use std::path::Path;
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize};
use ts_rs::TS;
use crate::util::VersionString;
use crate::{Id, InvalidId, PackageId};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
#[ts(type = "string")]
pub struct ImageId(Id);
impl AsRef<Path> for ImageId {
fn as_ref(&self) -> &Path {
self.0.as_ref().as_ref()
}
}
impl std::fmt::Display for ImageId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl ImageId {
pub fn for_package(&self, pkg_id: &PackageId, pkg_version: Option<&VersionString>) -> String {
format!(
"start9/{}/{}:{}",
pkg_id,
self.0,
pkg_version.map(|v| { v.as_str() }).unwrap_or("latest")
)
}
}
impl FromStr for ImageId {
type Err = InvalidId;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(ImageId(Id::try_from(s.to_owned())?))
}
}
impl<'de> Deserialize<'de> for ImageId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(ImageId(Deserialize::deserialize(deserializer)?))
}
}

View File

@@ -0,0 +1,5 @@
use imbl_value::InternedString;
#[derive(Debug, thiserror::Error)]
#[error("Invalid ID: {0}")]
pub struct InvalidId(pub(super) InternedString);

120
core/src/id/mod.rs Normal file
View File

@@ -0,0 +1,120 @@
use std::borrow::Borrow;
use std::str::FromStr;
use imbl_value::InternedString;
use regex::Regex;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
mod action;
mod gateway;
mod health_check;
mod host;
mod image;
mod invalid_id;
mod package;
mod replay;
mod service_interface;
mod volume;
pub use action::ActionId;
pub use gateway::GatewayId;
pub use health_check::HealthCheckId;
pub use host::HostId;
pub use image::ImageId;
pub use invalid_id::InvalidId;
pub use package::{PackageId, SYSTEM_PACKAGE_ID};
pub use replay::ReplayId;
pub use service_interface::ServiceInterfaceId;
pub use volume::VolumeId;
lazy_static::lazy_static! {
static ref ID_REGEX: Regex = Regex::new("^[a-z0-9]+(-[a-z0-9]+)*$").unwrap();
pub static ref SYSTEM_ID: Id = Id(InternedString::intern("x_system"));
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Id(InternedString);
impl std::fmt::Debug for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl TryFrom<InternedString> for Id {
type Error = InvalidId;
fn try_from(value: InternedString) -> Result<Self, Self::Error> {
if ID_REGEX.is_match(&value) {
Ok(Id(value))
} else {
Err(InvalidId(value))
}
}
}
impl TryFrom<String> for Id {
type Error = InvalidId;
fn try_from(value: String) -> Result<Self, Self::Error> {
if ID_REGEX.is_match(&value) {
Ok(Id(InternedString::intern(value)))
} else {
Err(InvalidId(InternedString::intern(value)))
}
}
}
impl TryFrom<&str> for Id {
type Error = InvalidId;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if ID_REGEX.is_match(value) {
Ok(Id(InternedString::intern(value)))
} else {
Err(InvalidId(InternedString::intern(value)))
}
}
}
impl FromStr for Id {
type Err = InvalidId;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}
impl From<Id> for InternedString {
fn from(value: Id) -> Self {
value.0
}
}
impl std::ops::Deref for Id {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::fmt::Display for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &*self.0)
}
}
impl AsRef<str> for Id {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Borrow<str> for Id {
fn borrow(&self) -> &str {
self.0.as_ref()
}
}
impl<'de> Deserialize<'de> for Id {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let unchecked: InternedString = Deserialize::deserialize(deserializer)?;
Id::try_from(unchecked).map_err(serde::de::Error::custom)
}
}
impl Serialize for Id {
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
where
Ser: Serializer,
{
serializer.serialize_str(self)
}
}

89
core/src/id/package.rs Normal file
View File

@@ -0,0 +1,89 @@
use std::borrow::Borrow;
use std::path::Path;
use std::str::FromStr;
use imbl_value::InternedString;
use serde::{Deserialize, Serialize, Serializer};
use ts_rs::TS;
use crate::{Id, InvalidId, SYSTEM_ID};
lazy_static::lazy_static! {
pub static ref SYSTEM_PACKAGE_ID: PackageId = PackageId(SYSTEM_ID.clone());
}
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, TS)]
#[ts(type = "string")]
pub struct PackageId(Id);
impl FromStr for PackageId {
type Err = InvalidId;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(PackageId(Id::try_from(s.to_owned())?))
}
}
impl From<Id> for PackageId {
fn from(id: Id) -> Self {
PackageId(id)
}
}
impl From<PackageId> for Id {
fn from(value: PackageId) -> Self {
value.0
}
}
impl From<PackageId> for InternedString {
fn from(value: PackageId) -> Self {
value.0.into()
}
}
impl std::ops::Deref for PackageId {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<PackageId> for PackageId {
fn as_ref(&self) -> &PackageId {
self
}
}
impl std::fmt::Display for PackageId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl AsRef<str> for PackageId {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl Borrow<str> for PackageId {
fn borrow(&self) -> &str {
self.0.as_ref()
}
}
impl<'a> Borrow<str> for &'a PackageId {
fn borrow(&self) -> &str {
self.0.as_ref()
}
}
impl AsRef<Path> for PackageId {
fn as_ref(&self) -> &Path {
self.0.as_ref().as_ref()
}
}
impl<'de> Deserialize<'de> for PackageId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
Ok(PackageId(Deserialize::deserialize(deserializer)?))
}
}
impl Serialize for PackageId {
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
where
Ser: Serializer,
{
Serialize::serialize(&self.0, serializer)
}
}

53
core/src/id/replay.rs Normal file
View File

@@ -0,0 +1,53 @@
use std::convert::Infallible;
use std::path::Path;
use std::str::FromStr;
use imbl_value::InternedString;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
#[ts(type = "string")]
pub struct ReplayId(InternedString);
impl<T> From<T> for ReplayId
where
T: Into<InternedString>,
{
fn from(value: T) -> Self {
Self(value.into())
}
}
impl FromStr for ReplayId {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(ReplayId(InternedString::intern(s)))
}
}
impl AsRef<ReplayId> for ReplayId {
fn as_ref(&self) -> &ReplayId {
self
}
}
impl std::fmt::Display for ReplayId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl AsRef<str> for ReplayId {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl AsRef<Path> for ReplayId {
fn as_ref(&self) -> &Path {
self.0.as_ref()
}
}
impl<'de> Deserialize<'de> for ReplayId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
Ok(ReplayId(serde::Deserialize::deserialize(deserializer)?))
}
}

View File

@@ -0,0 +1,59 @@
use std::path::Path;
use std::str::FromStr;
use rpc_toolkit::clap::builder::ValueParserFactory;
use serde::{Deserialize, Deserializer, Serialize};
use ts_rs::TS;
use crate::Id;
use crate::util::FromStrParser;
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
#[ts(export, type = "string")]
pub struct ServiceInterfaceId(Id);
impl From<Id> for ServiceInterfaceId {
fn from(id: Id) -> Self {
Self(id)
}
}
impl std::fmt::Display for ServiceInterfaceId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl std::ops::Deref for ServiceInterfaceId {
type Target = str;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl AsRef<str> for ServiceInterfaceId {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl<'de> Deserialize<'de> for ServiceInterfaceId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(ServiceInterfaceId(Deserialize::deserialize(deserializer)?))
}
}
impl AsRef<Path> for ServiceInterfaceId {
fn as_ref(&self) -> &Path {
self.0.as_ref().as_ref()
}
}
impl FromStr for ServiceInterfaceId {
type Err = <Id as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Id::from_str(s).map(Self)
}
}
impl ValueParserFactory for ServiceInterfaceId {
type Parser = FromStrParser<Self>;
fn value_parser() -> Self::Parser {
FromStrParser::new()
}
}

70
core/src/id/volume.rs Normal file
View File

@@ -0,0 +1,70 @@
use std::borrow::Borrow;
use std::path::Path;
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize};
use ts_rs::TS;
use crate::{Id, InvalidId};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, TS)]
#[ts(type = "string")]
pub enum VolumeId {
Backup,
Custom(Id),
}
impl FromStr for VolumeId {
type Err = InvalidId;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"BACKUP" => VolumeId::Backup,
s => VolumeId::Custom(Id::try_from(s.to_owned())?),
})
}
}
impl std::fmt::Display for VolumeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
VolumeId::Backup => write!(f, "BACKUP"),
VolumeId::Custom(id) => write!(f, "{}", id),
}
}
}
impl AsRef<str> for VolumeId {
fn as_ref(&self) -> &str {
match self {
VolumeId::Backup => "BACKUP",
VolumeId::Custom(id) => id.as_ref(),
}
}
}
impl Borrow<str> for VolumeId {
fn borrow(&self) -> &str {
self.as_ref()
}
}
impl AsRef<Path> for VolumeId {
fn as_ref(&self) -> &Path {
AsRef::<str>::as_ref(self).as_ref()
}
}
impl<'de> Deserialize<'de> for VolumeId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let unchecked: String = Deserialize::deserialize(deserializer)?;
Ok(match unchecked.as_ref() {
"BACKUP" => VolumeId::Backup,
_ => VolumeId::Custom(Id::try_from(unchecked).map_err(serde::de::Error::custom)?),
})
}
}
impl Serialize for VolumeId {
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
where
Ser: serde::Serializer,
{
serializer.serialize_str(self.as_ref())
}
}