Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -825,17 +825,22 @@ async fn compile_rust_wasm_process(
// Adapt the module using wasm-tools

// For use inside of process_dir
let wasm_file_name = process_dir
// Run `wasm-tools component new`, putting output in pkg/
// and rewriting all `_`s to `-`s
// cargo hates `-`s and so outputs with `_`s; Kimap hates
// `_`s and so we convert to and enforce all `-`s
let wasm_file_name_cab = process_dir
.file_name()
.and_then(|s| s.to_str())
.unwrap()
.replace("-", "_");
let wasm_file_name_hep = wasm_file_name_cab.replace("_", "-");

let wasm_file_prefix = Path::new("target/wasm32-wasip1/release");
let wasm_file = wasm_file_prefix.join(&format!("{}.wasm", wasm_file_name));
let wasm_file_cab = wasm_file_prefix.join(&format!("{wasm_file_name_cab}.wasm"));

let wasm_path = format!("../pkg/{}.wasm", wasm_file_name);
let wasm_path = Path::new(&wasm_path);
let wasm_file_pkg = format!("../pkg/{wasm_file_name_hep}.wasm");
let wasm_file_pkg = Path::new(&wasm_file_pkg);

let wasi_snapshot_file = Path::new("target/wasi_snapshot_preview1.wasm");

Expand All @@ -844,9 +849,9 @@ async fn compile_rust_wasm_process(
.args(&[
"component",
"new",
wasm_file.to_str().unwrap(),
wasm_file_cab.to_str().unwrap(),
"-o",
wasm_path.to_str().unwrap(),
wasm_file_pkg.to_str().unwrap(),
"--adapt",
wasi_snapshot_file.to_str().unwrap(),
])
Expand Down
68 changes: 50 additions & 18 deletions src/new/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ fn replace_vars(

let (publisher_dotted_snake, publisher_dotted_kebab) = replace_dots(publisher);
let publisher_dotted_upper_camel = snake_to_upper_camel_case(&publisher_dotted_snake);

let js: HashSet<String> = ["js", "jsx", "ts", "tsx"]
.iter()
.map(|e| e.to_string())
.collect();

let input = input
// wit
.replace(
Expand All @@ -140,27 +146,42 @@ fn replace_vars(
// manifest.json
.replace(
&format!("{template_package_name_kebab}.wasm"),
&format!("{package_name_snake}.wasm"),
&format!("{package_name_kebab}.wasm"),
)
// tests manifest.json
.replace(
&format!("{template_package_name_kebab}_test.wasm"),
&format!("{package_name_snake}_test.wasm"),
&format!("{template_package_name_kebab}-test.wasm"),
&format!("{package_name_kebab}-test.wasm"),
)
// part of a var name
.replace(
&format!("{template_package_name}_"),
&format!("{package_name_snake}_"),
)
// part of a var name
.replace(
&format!("_{template_package_name}"),
&format!("_{package_name_snake}"),
)
// field in a struct
.replace(
&format!("{template_package_name}:"),
&format!("{package_name_snake}:"),
)
.replace(
&format!("{template_package_name}-"),
&format!("{package_name_kebab}-"),
);
let input = if extension == "wit" {
input
.replace(
&format!("{template_package_name}-"),
&format!("{package_name_kebab}-"),
)
.replace(&template_package_name_kebab, &package_name_kebab)
.replace(template_package_name, package_name)
} else if js.contains(extension) {
input
.replace(template_package_name, &package_name_snake)
.replace(&template_package_name_kebab, &package_name_kebab)
} else {
input
.replace(
&format!("{template_package_name}-"),
&format!("{package_name_kebab}-"),
)
.replace(template_package_name, package_name)
.replace(&template_package_name_kebab, &package_name_kebab)
};
Expand All @@ -176,8 +197,13 @@ fn replace_vars(
.to_string()
}

fn is_url_safe(input: &str) -> bool {
let re = regex::Regex::new(r"^[a-zA-Z0-9\-_.~]+$").unwrap();
pub fn is_kimap_safe(input: &str, is_publisher: bool) -> bool {
let expression = if is_publisher {
r"^[a-zA-Z0-9\-.]+$"
} else {
r"^[a-zA-Z0-9\-]+$"
};
let re = regex::Regex::new(expression).unwrap();
re.is_match(input)
}

Expand Down Expand Up @@ -216,20 +242,26 @@ pub fn execute(
));
}

if !is_url_safe(&package_name) {
if !is_kimap_safe(&package_name, false) {
let error = if !is_from_dir {
eyre!("`package_name` '{}' must be URL safe.", package_name)
eyre!(
"`package_name` '{}' must be Kimap safe (a-z, A-Z, 0-9, - allowed).",
package_name
)
} else {
eyre!(
"`package_name` (derived from given directory {:?}) '{}' must be URL safe.",
"`package_name` (derived from given directory {:?}) '{}' must be Kimap safe (a-z, A-Z, 0-9, - allowed).",
new_dir,
package_name,
)
};
return Err(error);
}
if !is_url_safe(&publisher) {
return Err(eyre!("`publisher` '{}' must be URL safe.", publisher));
if !is_kimap_safe(&publisher, true) {
return Err(eyre!(
"`publisher` '{}' must be Kimap safe (a-z, A-Z, 0-9, -, . allowed).",
publisher
));
}

let ui_infix = if ui {
Expand Down
13 changes: 7 additions & 6 deletions src/publish/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use tracing::{info, instrument};
use kinode_process_lib::kernel_types::Erc721Metadata;

use crate::build::{download_file, make_pkg_publisher, read_and_update_metadata, zip_pkg};
use crate::new::is_kimap_safe;

sol! {
function mint (
Expand Down Expand Up @@ -96,11 +97,6 @@ pub fn make_remote_link(url: &str, text: &str) -> String {
format!("\x1B]8;;{}\x1B\\{}\x1B]8;;\x1B\\", url, text)
}

fn is_valid_kimap_package_name(s: &str) -> bool {
s.chars()
.all(|c| c.is_ascii_lowercase() || c == '-' || c.is_ascii_digit())
}

#[instrument(level = "trace", skip_all)]
fn calculate_metadata_hash(package_dir: &Path) -> Result<String> {
let metadata_text = fs::read_to_string(package_dir.join("metadata.json"))?;
Expand Down Expand Up @@ -346,11 +342,16 @@ pub async fn execute(
let name = metadata.name.clone().unwrap();
let publisher = metadata.properties.publisher.clone();

if !is_valid_kimap_package_name(&name) {
if !is_kimap_safe(&name, false) {
return Err(eyre!(
"The App Store requires package names have only lowercase letters, digits, and `-`s"
));
}
if !is_kimap_safe(&publisher, true) {
return Err(eyre!(
"The App Store requires publisher names have only lowercase letters, digits, `-`s, and `.`s"
));
}

let metadata_hash = check_remote_metadata(&metadata, metadata_uri, package_dir).await?;
check_pkg_hash(&metadata, package_dir, metadata_uri)?;
Expand Down
39 changes: 34 additions & 5 deletions src/start_package/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use tracing::{info, instrument};
use kinode_process_lib::kernel_types::{Erc721Metadata, PackageManifestEntry};

use crate::build::{hash_zip_pkg, make_pkg_publisher, make_zip_filename, read_and_update_metadata};
use crate::publish::make_local_file_link_path;
use crate::new::is_kimap_safe;
use crate::publish::{make_local_file_link_path, make_remote_link};
use crate::{inject_message, KIT_LOG_PATH_DEFAULT};

#[instrument(level = "trace", skip_all)]
Expand Down Expand Up @@ -82,10 +83,38 @@ fn install(

#[instrument(level = "trace", skip_all)]
fn check_manifest(pkg_dir: &Path, manifest_file_name: &str) -> Result<()> {
let manifest = fs::File::open(pkg_dir.join(manifest_file_name))
.with_suggestion(|| format!("Missing required {} file. See discussion at https://book.kinode.org/my_first_app/chapter_1.html?highlight=manifest.json#pkgmanifestjson", manifest_file_name))?;
let manifest: Vec<PackageManifestEntry> = serde_json::from_reader(manifest)
.with_suggestion(|| format!("Failed to parse required {} file. See discussion at https://book.kinode.org/my_first_app/chapter_1.html?highlight=manifest.json#pkgmanifestjson", manifest_file_name))?;
let manifest_path = pkg_dir.join(manifest_file_name);
let book_link = make_remote_link("https://book.kinode.org/my_first_app/chapter_1.html?highlight=manifest.json#pkgmanifestjson", "Kinode book");
let manifest = fs::File::open(&manifest_path).with_suggestion(|| {
format!("Missing required manifest.json file. See discussion {book_link}")
})?;
let manifest: Vec<PackageManifestEntry> =
serde_json::from_reader(manifest).with_suggestion(|| {
format!("Failed to parse required manifest.json file. See discussion {book_link}")
})?;
let manifest_json = make_local_file_link_path(&manifest_path, "manifest.json")?;
for entry in &manifest {
let file_name = &entry.process_name;
let file_path = entry.process_wasm_path
.strip_prefix("/")
.and_then(|s| s.strip_suffix(".wasm"))
.ok_or_else(|| {

eyre!(
"{manifest_json} has unexpected Wasm path: {:?} (expected beginning `/` and ending `.wasm`)",
entry.process_wasm_path,
)
})?;
if !is_kimap_safe(file_name, false) {
return Err(eyre!("{manifest_json} file name '{file_name}' must be Kimap safe (a-z, A-Z, 0-9, - allowed)"));
}
if !is_kimap_safe(file_path, false) {
return Err(eyre!(
"{manifest_json} file path {:?} must be Kimap safe (a-z, A-Z, 0-9, - allowed)",
entry.process_wasm_path,
));
}
}
let has_all_entries = manifest.iter().fold(true, |has_all_entries, entry| {
let file_path = entry
.process_wasm_path
Expand Down