Skip to content

Commit

Permalink
Make manifests more extension friendly and update documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Reboot-Codes committed Nov 22, 2024
1 parent 169933a commit 16081ea
Show file tree
Hide file tree
Showing 14 changed files with 181 additions and 99 deletions.
99 changes: 66 additions & 33 deletions clover-hub/src/server/warehouse/repos/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,40 +694,14 @@ impl Manifest {
}
};

debug!("Resolving manifest.modules");
let modules = match OptionalStrTHashMap::compile(spec.modules.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await {
let directory = match Optional::compile(spec.directory.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await {
Ok(val) => {
debug!("Resolved manifest.modules");
debug!("Resolved manifest.directory");
val
},
Err(e) => {
err = Some(e);
OptionalStrTHashMap::None
}
};

debug!("Resolving manifest.applications");
let applications = match OptionalStrTHashMap::compile(spec.applications.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await {
Ok(val) => {
debug!("Resolved manifest.applications");
val
},
Err(e) => {
err = Some(e);
OptionalStrTHashMap::None
}
};

debug!("Resolving manifest.expression-packs");
#[cfg(feature = "core")]
let expression_packs = match OptionalStrTHashMap::compile(spec.expression_packs.clone(), ResolutionCtx { base: resolution_ctx.clone().base, builtin: builtin_rfqdn(true), here: resolution_ctx.clone().here }, repo_dir_path.clone()).await {
Ok(val) => {
debug!("Resolved manifest.expression-packs");
val
},
Err(e) => {
err = Some(e);
OptionalStrTHashMap::None
Default::default()
}
};

Expand All @@ -738,16 +712,75 @@ impl Manifest {
name,
version,
base,
modules,
applications,
#[cfg(feature = "core")]
expression_packs
directory
})
}
}
}
}

impl ManifestCompilationFrom<Option<RawDirectorySpec>> for Optional<DirectorySpec> {
async fn compile(spec: Option<RawDirectorySpec>, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result<Self, SimpleError> where Self: Sized, RawDirectorySpec: for<'a> Deserialize<'a> {
match spec {
Some(spec) => {
let mut err = None;

debug!("Resolving manifest.modules");
let modules = match OptionalStrTHashMap::compile(spec.modules.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await {
Ok(val) => {
debug!("Resolved manifest.modules");
val
},
Err(e) => {
err = Some(e);
OptionalStrTHashMap::None
}
};

debug!("Resolving manifest.applications");
let applications = match OptionalStrTHashMap::compile(spec.applications.clone(), resolution_ctx.clone(), repo_dir_path.clone()).await {
Ok(val) => {
debug!("Resolved manifest.applications");
val
},
Err(e) => {
err = Some(e);
OptionalStrTHashMap::None
}
};

debug!("Resolving manifest.expression-packs");
#[cfg(feature = "core")]
let expression_packs = match OptionalStrTHashMap::compile(spec.expression_packs.clone(), ResolutionCtx { base: resolution_ctx.clone().base, builtin: builtin_rfqdn(true), here: resolution_ctx.clone().here }, repo_dir_path.clone()).await {
Ok(val) => {
debug!("Resolved manifest.expression-packs");
val
},
Err(e) => {
err = Some(e);
OptionalStrTHashMap::None
}
};

match err {
Some(e) => { Err(e) },
None => {
Ok(Optional::Some(DirectorySpec {
modules,
applications,
#[cfg(feature = "core")]
expression_packs
}))
}
}
},
None => {
Ok(Optional::None)
}
}
}
}

impl ManifestCompilationFrom<RawApplicationSpec> for ApplicationSpec {
async fn compile(spec: RawApplicationSpec, resolution_ctx: ResolutionCtx, repo_dir_path: OsPath) -> Result<Self, SimpleError> where Self: Sized {
let mut err = None;
Expand Down
57 changes: 43 additions & 14 deletions clover-hub/src/server/warehouse/repos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ pub fn builtin_rfqdn(is_core: bool) -> String {
}

pub fn replace_simple_directives(value: String, resolution_ctx: ResolutionCtx) -> String {
debug!("replace_simple_directives: {} + {:#?}", value.clone(), resolution_ctx.clone());
debug!("replace_simple_directives (provided): {} + {:#?}", value.clone(), resolution_ctx.clone());

let base_re = Regex::new("(?<directive>\\@base)").unwrap();
let here_re = Regex::new("(?<directive>\\@here)").unwrap();
let builtin_re = Regex::new("(?<directive>\\@builtin)").unwrap();
let builtin_re = Regex::new("(?<directive>\\@builtin)(\\:(?<domain>core|clover))?").unwrap();

let mut val = value.clone();
match resolution_ctx.base {
Expand All @@ -46,11 +46,49 @@ pub fn replace_simple_directives(value: String, resolution_ctx: ResolutionCtx) -
}

let binding1 = val.clone();
let val = here_re.replace(&binding1, resolution_ctx.here.to_string());
let here_import_temp = here_re.replace_all(&binding1, "");
// Did the regex replace an `@here` directive? If so, append the stripped value to the "here" path.
let val = if here_import_temp != val {
let mut val_unsafe_path = resolution_ctx.here.join(&here_import_temp.to_string());
val_unsafe_path.resolve();

if val_unsafe_path.to_string().starts_with(&resolution_ctx.here.to_string()) {
val_unsafe_path.to_string()
} else {
warn!(
"Directory resolution for \"{}\" (resolved to: \"{}\") is not confined to repo directory, removing entry value entirely since this is a security issue.",
binding1.clone(),
val_unsafe_path.to_string()
);
"".to_string()
}
} else {
val
};

let binding2 = val.clone();
let val = builtin_re.replace(&binding2, resolution_ctx.builtin.to_string());
let builtin_import_temp = builtin_re.replace_all(&binding2, "");
let val = if builtin_import_temp != val {
match builtin_re.captures(&binding2).unwrap().name("domain") {
Some(domain) => {
if domain.as_str() == "core" {
builtin_re.replace_all(&binding2, builtin_rfqdn(true)).to_string()
} else if domain.as_str() == "clover" {
builtin_re.replace_all(&binding2, builtin_rfqdn(false)).to_string()
} else {
error!("No case for domain: \"{}\", yet it's in the directive regex, this is a bug, please report it; refusing to resolve built-in directive (value unchanged)!", domain.as_str());
binding2.clone()
}
},
None => {
builtin_re.replace_all(&binding2, resolution_ctx.builtin.to_string()).to_string()
},
}
} else {
val
};

debug!("replace simple directives: {}", val.clone());
debug!("replace simple directives (completed): {}", val.clone());

String::from(val)
}
Expand Down Expand Up @@ -206,7 +244,6 @@ pub async fn resolve_entry_value(value: String, resolution_ctx: ResolutionCtx, r
resolution_ctx.here.parent().unwrap_or(OsPath::new().join("/")).join(import_re.captures(&value.clone()).unwrap().name("src").unwrap().as_str()).to_string()
);
let mut import_path = OsPath::new();
let mut within_repo = false;
let mut segments = 0;

for import_path_seg in raw_import_path.to_path() {
Expand All @@ -219,19 +256,11 @@ pub async fn resolve_entry_value(value: String, resolution_ctx: ResolutionCtx, r
import_path.push(import_path_seg.to_str().unwrap_or(""));
}
}

if import_path_seg == "@repo" {
within_repo = true;
}
}

import_path.resolve();

if !import_path.to_string().starts_with(&repo_dir_path.to_string()) {
within_repo = false;
}

if !within_repo {
return Err(SimpleError::new(format!("Path: \"{}\", is not confined within the repository root (\"{}\"). Refusing to evaluate.", import_path.to_string(), repo_dir_path.to_string())));
}

Expand Down
12 changes: 12 additions & 0 deletions clover-hub/src/server/warehouse/repos/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ pub struct ManifestSpec {
pub name: Option<String>,
pub version: String,
pub base: Option<String>,
#[serde(default)]
pub directory: Option<RawDirectorySpec>
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RawDirectorySpec {
#[serde(default)]
pub modules: OptionalListManifestSpecEntry<RawModuleSpec>,
#[serde(default)]
Expand Down Expand Up @@ -211,6 +217,12 @@ pub struct Manifest {
pub version: RequiredString,
#[serde(default)]
pub base: OptionalString,
#[serde(default)]
pub directory: Optional<DirectorySpec>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DirectorySpec {
#[serde(default)]
pub modules: OptionalStrTHashMap<ModuleSpec>,
#[serde(default)]
Expand Down
3 changes: 0 additions & 3 deletions core/applications.clover.jsonc

This file was deleted.

3 changes: 3 additions & 0 deletions core/applications/applications.clover.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"@base.*": "@import('./*/manifest.clover.jsonc')"
}
2 changes: 1 addition & 1 deletion core/applications/test/manifest.clover.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Test",
"version": "1.0.0",
"intents": {
"@builtin.from-launcher": "ws-intent://@self/from-launcher"
"@builtin.from-launcher": "ws-intent://main/from-launcher"
},
"containers": {
"main": {
Expand Down
3 changes: 0 additions & 3 deletions core/expression-packs.clover.jsonc

This file was deleted.

3 changes: 3 additions & 0 deletions core/expression-packs/expression-packs.clover.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"@base.*": "@import('./*/manifest.clover.jsonc')"
}
5 changes: 5 additions & 0 deletions core/manifest.clover.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"modules": "@import('./modules/manifest.clover.jsonc')",
"applications": "@import('./applications/manifest.clover.jsonc')"
// "expression-packs": "@import('./modules/manifest.clover.jsonc')"
}
3 changes: 0 additions & 3 deletions core/modules.clover.jsonc

This file was deleted.

3 changes: 3 additions & 0 deletions core/modules/modules.clover.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"@base.*": "@import('./*/manifest.clover.jsonc')"
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ The top-level `base` key of a manifest can be set to define the RFQDN (reverse f

## @builtin

`@builtin` resolves to the applicable built-in RFQDN for this area of the manifest. For CloverHub, this will usually resolve to `com.reboot-codes.clover`. Or, for CORE (like expression packs), `com.reboot-codes.clover.CORE`.
`@builtin` resolves to the applicable built-in RFQDN for this area of the manifest. For CloverHub, this will usually resolve to `com.reboot-codes.clover`. Or, for CORE (like expression packs), `com.reboot-codes.clover.CORE`. You can also use built-in directives for specific domains:

- `@builtin:clover`: `com.reboot-codes.clover`
- `@builtin:core`: `com.reboot-codes.clover.CORE`

## @import

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,51 +15,53 @@ Here's a basic manifest example for an App, Module, and an Expression pack:
}
],
"base": "com.reboot-codes.clover-tutorial",
"applications": {
"@base.tutorial-app": {
"name": "Tutorial App",
"intents": {
"com.reboot-codes.clover.from-launcher": "ws-intent://@self/from-launcher"
},
"containers": {
"main": {
"interface": true,
"build": {
"url": "@here/Dockerfile"
"directory": {
"applications": {
"@base.tutorial-app": {
"name": "Tutorial App",
"intents": {
"com.reboot-codes.clover.from-launcher": "ws-intent://main/from-launcher"
},
"containers": {
"main": {
"interface": true,
"build": {
"url": "@here/Dockerfile"
}
}
}
}
}
},
"modules": {
"@base.tutorial-module": {
"name": "Tutorial Module",
"location": [
"wrist"
],
"components": {
"wrist-light": {
"type": "sensor",
"input": "RGBA",
"bus": {
"can-fd": {
"enabled": true
},
"modules": {
"@base.tutorial-module": {
"name": "Tutorial Module",
"location": [
"wrist"
],
"components": {
"wrist-light": {
"type": "sensor",
"input": "RGBA",
"bus": {
"can-fd": {
"enabled": true
}
},
"output": {
"timing": "on-request",
"format": "RGBA"
}
},
"output": {
"timing": "on-request",
"format": "RGBA"
}
}
}
}
},
"expression-packs": {
"@base.tutorial-expression-pack": {
"name": "Tutorial Expression Pack",
"expressions": {
"com.reboot-codes.clover.CORE.neutral": {
"static": "@here/neutral.png"
},
"expression-packs": {
"@base.tutorial-expression-pack": {
"name": "Tutorial Expression Pack",
"expressions": {
"com.reboot-codes.clover.CORE.neutral": {
"static": "@here/neutral.png"
}
}
}
}
Expand Down
Loading

0 comments on commit 16081ea

Please sign in to comment.