From 0554e2f3172d44f508de097367325409c4ceac57 Mon Sep 17 00:00:00 2001 From: Tei Roberts Date: Fri, 4 Aug 2023 14:03:42 +0200 Subject: [PATCH 01/11] fix(campfire): open-browser on linux --- Cargo.lock | 24 ++++++++++ campfire/Cargo.toml | 3 ++ campfire/src/web/browser.rs | 95 ++++++++++++++++++++++--------------- 3 files changed, 84 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c8ef5ec2b..8f323aefdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2270,6 +2270,7 @@ dependencies = [ "indicatif", "itertools", "log", + "nix 0.26.2", "num_cpus", "openssl", "regex", @@ -5009,6 +5010,15 @@ dependencies = [ "autocfg 1.1.0", ] +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg 1.1.0", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -5303,6 +5313,20 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", + "pin-utils", + "static_assertions", +] + [[package]] name = "noise" version = "0.7.0" diff --git a/campfire/Cargo.toml b/campfire/Cargo.toml index 6ddbfa2e81..c4e0950147 100644 --- a/campfire/Cargo.toml +++ b/campfire/Cargo.toml @@ -32,6 +32,9 @@ cfg-if = "1.0" [target.'cfg(target_os="windows")'.dependencies] openssl = { version = "0.10", features = ["vendored"], optional = true } +[target.'cfg(target_os="linux")'.dependencies] +nix = "0.26" + [target.'cfg(not(target_os="windows"))'.dependencies] openssl = { version = "0.10", optional = true } diff --git a/campfire/src/web/browser.rs b/campfire/src/web/browser.rs index f2bfab6b07..62a9364145 100644 --- a/campfire/src/web/browser.rs +++ b/campfire/src/web/browser.rs @@ -1,6 +1,6 @@ use anyhow::Context; use openssl::hash::MessageDigest; -use tokio::process::Command; +use std::process::Command; pub async fn open() -> anyhow::Result<()> { let cert_file = tokio::fs::read("./localhost.crt") @@ -20,50 +20,44 @@ pub async fn open() -> anyhow::Result<()> { eprintln!("Got SPKI: {:?}", &spki); - open_browser(&spki, "http://localhost:5173").await?; + spawn(&spki, "http://localhost:5173")?; Ok(()) } -#[allow(unused_variables)] -async fn open_browser(spki: &str, url: &str) -> anyhow::Result<()> { - cfg_if::cfg_if! { - if #[cfg(target_os = "macos")] { - let mut command = Command::new("open"); - command - // Feeding a url to chrome here makes `--args spki` not be fed to chrome - .args(["-a", "Google Chrome", "--args"]) - .arg(format!("--ignore-certificate-errors-spki-list={spki}")); - - } - else if #[cfg(target_os = "linux")] { - let _spki = spki; - let _url = url; - let mut command = Command::new("google-chrome"); - command - .args(["-a", "Google Chrome", url, "--args"]) - .arg(format!("--ignore-certificate-errors-spki-list={spki}")) - .spawn() - .context("Failed to spawn browser")?; - - anyhow::bail!("Launching the browser for linux is not supported. This is because cargo will cleanup the browser background process when campfire terminates") - } - else { - let mut command = Command::new("google-chrome"); - command - .arg(format!("--ignore-certificate-errors-spki-list={spki}")) - .spawn() - .context("Failed to spawn browser")?; - - anyhow::bail!("Launching the browser for windows is not yet supported.") - } - } +#[cfg(unix)] +fn detach_process(child: &mut Command) -> &mut Command { + use std::os::unix::process::CommandExt; + // Safety + // Does not access any memory from *this* process. + unsafe { + child.pre_exec(|| { + // Detach the child by moving it to a new process group + if let Err(e) = nix::unistd::setsid() { + // Safety: e is repr(i32) and it thus safe to format + eprintln!("Failed to detach child {e}") + }; + + Ok(()) + }) + }; + + // Prevent output from leaking into the parent terminal + child + .stdin(std::process::Stdio::null()) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) +} - let status = command +#[cfg(target_os = "macos")] +fn spawn(spki: &str, _url: &str) -> anyhow::Result<()> { + let status = std::process::Command::new("open") + // Feeding a url to chrome here makes `--args spki` not be fed to chrome + .args(["-a", "Google Chrome", "--args"]) + .arg(format!("--ignore-certificate-errors-spki-list={spki}")) .spawn() - .context("Failed to spawn browser")? + .context("Failed to open Google Chrome")? .wait() - .await .context("Failed to wait for launch command to exit")?; if !status.success() { @@ -72,3 +66,28 @@ async fn open_browser(spki: &str, url: &str) -> anyhow::Result<()> { Ok(()) } + +#[cfg(target_os = "linux")] +fn spawn(spki: &str, url: &str) -> anyhow::Result<()> { + let status = detach_process( + std::process::Command::new("google-chrome") + .arg(url) + // Feeding a url to chrome here makes `--args spki` not be fed to chrome + .arg(format!("--ignore-certificate-errors-spki-list={spki}")), + ) + .spawn() + .context("Failed to open Google Chrome")? + .wait() + .context("Failed to wait for launch command to exit")?; + + if !status.success() { + anyhow::bail!("Failed to launch browser. Process exited with {status:?}"); + } + + Ok(()) +} + +#[cfg(target_os = "windows")] +fn spawn(_spki: &str, _url: &str) -> anyhow::Result<()> { + anyhow::bail!("Launching the browser for windows is not yet supported.") +} From 2a5fb5cda956b8b8541f611e36f1b09d7480a294 Mon Sep 17 00:00:00 2001 From: Tei Roberts Date: Fri, 4 Aug 2023 19:01:16 +0200 Subject: [PATCH 02/11] wip: file watcher --- Cargo.lock | 92 +++++++++++++++++++++++ campfire/Cargo.toml | 7 +- campfire/src/web/mod.rs | 5 +- campfire/src/web/serve.rs | 153 ++++++++++++++++++++++++++++++++++++++ web/www/package-lock.json | 6 +- 5 files changed, 259 insertions(+), 4 deletions(-) create mode 100644 campfire/src/web/serve.rs diff --git a/Cargo.lock b/Cargo.lock index 8f323aefdf..b8d8ed1a98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2264,6 +2264,7 @@ dependencies = [ "cargo_toml", "cfg-if", "clap 4.3.3", + "flume", "futures", "guppy", "home", @@ -2271,6 +2272,8 @@ dependencies = [ "itertools", "log", "nix 0.26.2", + "notify", + "notify-debouncer-full", "num_cpus", "openssl", "regex", @@ -2283,6 +2286,7 @@ dependencies = [ "toml 0.7.4", "toml_edit", "tracing", + "walkdir", "which", ] @@ -3390,6 +3394,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "file-id" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13be71e6ca82e91bc0cb862bebaac0b2d1924a5a1d970c822b2f98b63fda8c3" +dependencies = [ + "winapi-util", +] + [[package]] name = "file-per-thread-logger" version = "0.2.0" @@ -3513,6 +3526,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "fsio" version = "0.1.3" @@ -4554,6 +4576,26 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -4697,6 +4739,26 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "kqueue" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -5347,6 +5409,36 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "notify" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5738a2795d57ea20abec2d6d76c6081186709c0024187cd5977265eda6598b51" +dependencies = [ + "bitflags 1.3.2", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "notify-debouncer-full" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416969970ec751a5d702a88c6cd19ac1332abe997fce43f96db0418550426241" +dependencies = [ + "file-id", + "notify", + "parking_lot", + "walkdir", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" diff --git a/campfire/Cargo.toml b/campfire/Cargo.toml index c4e0950147..7b53cc24b7 100644 --- a/campfire/Cargo.toml +++ b/campfire/Cargo.toml @@ -28,6 +28,10 @@ num_cpus = "1.15.0" home = "0.5" tracing = "0.1" cfg-if = "1.0" +notify = { version = "6.0", optional = true } +flume = { version = "0.10", optional = true } +walkdir = { version = "2.3.2", optional = true } +notify-debouncer-full = { version = "0.2", optional = true, default-features = false } [target.'cfg(target_os="windows")'.dependencies] openssl = { version = "0.10", features = ["vendored"], optional = true } @@ -39,4 +43,5 @@ nix = "0.26" openssl = { version = "0.10", optional = true } [features] -default = ["openssl"] +default = ["openssl", "serve"] +serve = ["dep:notify", "dep:flume", "walkdir", "notify-debouncer-full"] diff --git a/campfire/src/web/mod.rs b/campfire/src/web/mod.rs index a065e8b9c9..19155a9e0e 100644 --- a/campfire/src/web/mod.rs +++ b/campfire/src/web/mod.rs @@ -1,10 +1,11 @@ use clap::Subcommand; -use self::build::BuildOptions; +use self::{build::BuildOptions, serve::Serve}; #[cfg(feature = "openssl")] mod browser; mod build; +mod serve; #[derive(Debug, Subcommand, Clone)] pub enum Web { @@ -13,6 +14,7 @@ pub enum Web { /// Launches chrome with the correct flags to explicitly trust /// the self-signed certificate OpenBrowser, + Serve(Serve), } pub async fn run(command: Web) -> anyhow::Result<()> { @@ -28,5 +30,6 @@ pub async fn run(command: Web) -> anyhow::Result<()> { anyhow::bail!("The `openssl` feature must be enabled to use this command") } } + Web::Serve(args) => args.run().await, } } diff --git a/campfire/src/web/serve.rs b/campfire/src/web/serve.rs new file mode 100644 index 0000000000..16c7073cbb --- /dev/null +++ b/campfire/src/web/serve.rs @@ -0,0 +1,153 @@ +use std::{ + collections::{BTreeSet, HashSet}, + path::{Path, PathBuf}, + time::Duration, +}; + +use clap::Args; +use futures::StreamExt; +use itertools::process_results; +use notify::{EventKind, RecursiveMode, Watcher}; +use notify_debouncer_full::{DebounceEventResult, Debouncer, FileIdMap}; +use walkdir::DirEntry; + +use super::build::{self, BuildOptions}; + +pub struct WatcherState { + watcher: Debouncer, + watching: HashSet, +} + +impl WatcherState { + pub fn add(&mut self, path: impl Into) -> anyhow::Result<()> { + let path = path.as_ref(); + + if self.watching.insert(path.to_path_buf()) { + log::info!("Watching new entry: {path:?}"); + self.watcher + .watcher() + .watch(&path, RecursiveMode::NonRecursive)?; + } + + Ok(()) + } + + pub fn update_subdir(&mut self, dir: impl AsRef) -> anyhow::Result<()> { + let dir = dir.as_ref(); + for entry in find_watched_dirs(dir) { + let entry = entry?; + + self.add(entry.path())?; + } + + Ok(()) + } +} + +impl Extend for WatcherState where W: Watcher, I: Into { + fn extend>(&mut self, iter: T) { + for item in iter { + self.add(item); + } + } +} + + +impl Extend for WatcherState where W: Watcher { + fn extend>(&mut self, iter: T) { + for item in iter { + self.add(item.path()); + } + } +} +#[derive(Debug, Args, Clone)] +pub struct Serve { + #[clap(flatten)] + build: BuildOptions, +} + +impl Serve { + pub async fn run(&self) -> anyhow::Result<()> { + let (tx, rx) = flume::unbounded(); + + let mut watcher = notify_debouncer_full::new_debouncer( + Duration::from_millis(2000), + None, + move |event: DebounceEventResult| { + tx.send(event); + }, + )?; + + let watcher = WatcherState { + watcher, + watching:BTreeSet::new(), + }; + + + watcher.extend(find_watched_dirs(".")); + + let rx = rx.into_stream(); + while let Some(events) = rx.next().await { + let events = events.map_err(|v| anyhow::anyhow!("File watch error: {v:?}"))?; + for event in events { + match event.event.kind { + EventKind::Any => todo!(), + EventKind::Access(_) => todo!(), + EventKind::Create(notify::event::CreateKind::File) => { + log::info!("File created: {path:?}"); + } + EventKind::Create(notify::event::CreateKind::Folder) => { + log::info!("Folder created: {path:?}"); + update_watch_subdir(watching, watcher, dir) + } + EventKind::Modify(_) => todo!(), + EventKind::Remove(_) => todo!(), + EventKind::Other => todo!(), + } + } + } + + Ok(()) + } +} + +pub fn update_watch_subdir( + watching: &mut BTreeSet, + watcher: impl Watcher, + dir: impl AsRef, +) -> anyhow::Result<()> { + for entry in find_watched_dirs() { + let entry = entry?; + + let path = entry.path(); + if watching.insert(path.to_path_buf()) { + log::info!("Watching new entry: {path:?}"); + watcher.watch(entry.path(), RecursiveMode::NonRecursive)?; + } + } + + Ok(()) +} + +pub fn find_watched_dirs(dir) -> impl Iterator> { + walkdir::WalkDir::new(dir).into_iter().filter_entry(|v| { + let path = v.path(); + + if path.starts_with(".") { + log::debug!("Ignoring hidden path: {path:?}"); + return false; + } + + match path.to_str() { + None => { + log::error!("Path is not UTF-8: {path:?}"); + false + } + Some("." | "node_modules" | "target") => false, + Some(v) => { + log::debug!("Watching path: {path:?}"); + true + } + } + }) +} diff --git a/web/www/package-lock.json b/web/www/package-lock.json index 447cc50067..3a20adecaa 100644 --- a/web/www/package-lock.json +++ b/web/www/package-lock.json @@ -8,7 +8,7 @@ "name": "client-vite", "version": "0.0.0", "dependencies": { - "ambient_web": "file:../client/pkg" + "ambient_web": "file:../pkg" }, "devDependencies": { "@types/audioworklet": "^0.0.47", @@ -19,8 +19,10 @@ }, "../client/pkg": { "version": "0.3.0-dev", + "extraneous": true, "license": "MIT OR Apache-2.0" }, + "../pkg": {}, "node_modules/@esbuild/android-arm": { "version": "0.18.11", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.11.tgz", @@ -380,7 +382,7 @@ "dev": true }, "node_modules/ambient_web": { - "resolved": "../client/pkg", + "resolved": "../pkg", "link": true }, "node_modules/esbuild": { From c6483597efd361e59358b13ed856c03373d5a050 Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Tue, 8 Aug 2023 17:35:14 +0200 Subject: [PATCH 03/11] feat: file watcher --- campfire/src/web/browser.rs | 2 +- campfire/src/web/serve.rs | 162 +++++++++++++++++++++--------------- web/Cargo.lock | 127 +++++++++++++++------------- 3 files changed, 165 insertions(+), 126 deletions(-) diff --git a/campfire/src/web/browser.rs b/campfire/src/web/browser.rs index 62a9364145..cc661328bb 100644 --- a/campfire/src/web/browser.rs +++ b/campfire/src/web/browser.rs @@ -25,7 +25,7 @@ pub async fn open() -> anyhow::Result<()> { Ok(()) } -#[cfg(unix)] +#[cfg(target_os = "linux")] fn detach_process(child: &mut Command) -> &mut Command { use std::os::unix::process::CommandExt; // Safety diff --git a/campfire/src/web/serve.rs b/campfire/src/web/serve.rs index 16c7073cbb..c69dd1478b 100644 --- a/campfire/src/web/serve.rs +++ b/campfire/src/web/serve.rs @@ -4,10 +4,14 @@ use std::{ time::Duration, }; +use anyhow::Context; use clap::Args; use futures::StreamExt; use itertools::process_results; -use notify::{EventKind, RecursiveMode, Watcher}; +use notify::{ + event::{CreateKind, RemoveKind}, + EventKind, RecursiveMode, Watcher, +}; use notify_debouncer_full::{DebounceEventResult, Debouncer, FileIdMap}; use walkdir::DirEntry; @@ -15,12 +19,22 @@ use super::build::{self, BuildOptions}; pub struct WatcherState { watcher: Debouncer, - watching: HashSet, + watching: BTreeSet, } impl WatcherState { - pub fn add(&mut self, path: impl Into) -> anyhow::Result<()> { - let path = path.as_ref(); + pub fn new(watcher: Debouncer) -> Self { + Self { + watcher, + watching: BTreeSet::new(), + } + } + + pub fn add(&mut self, path: impl AsRef) -> anyhow::Result<()> { + let path = path + .as_ref() + .canonicalize() + .context("Failed to canonicalize path")?; if self.watching.insert(path.to_path_buf()) { log::info!("Watching new entry: {path:?}"); @@ -32,6 +46,17 @@ impl WatcherState { Ok(()) } + pub fn remove(&mut self, path: impl AsRef) -> anyhow::Result<()> { + let path = path.as_ref().canonicalize()?; + + if self.watching.remove(&path) { + log::info!("Watching new entry: {path:?}"); + self.watcher.watcher().unwatch(&path)?; + } + + Ok(()) + } + pub fn update_subdir(&mut self, dir: impl AsRef) -> anyhow::Result<()> { let dir = dir.as_ref(); for entry in find_watched_dirs(dir) { @@ -44,22 +69,6 @@ impl WatcherState { } } -impl Extend for WatcherState where W: Watcher, I: Into { - fn extend>(&mut self, iter: T) { - for item in iter { - self.add(item); - } - } -} - - -impl Extend for WatcherState where W: Watcher { - fn extend>(&mut self, iter: T) { - for item in iter { - self.add(item.path()); - } - } -} #[derive(Debug, Args, Clone)] pub struct Serve { #[clap(flatten)] @@ -70,39 +79,57 @@ impl Serve { pub async fn run(&self) -> anyhow::Result<()> { let (tx, rx) = flume::unbounded(); - let mut watcher = notify_debouncer_full::new_debouncer( - Duration::from_millis(2000), + let watcher = notify_debouncer_full::new_debouncer( + Duration::from_millis(500), None, move |event: DebounceEventResult| { - tx.send(event); + tx.send(event).unwrap(); }, )?; - let watcher = WatcherState { - watcher, - watching:BTreeSet::new(), - }; + let mut watcher = WatcherState::new(watcher); + + log::info!("Created watcher"); + process_results(find_watched_dirs("campfire"), |mut v| { + v.try_for_each(|v| watcher.add(v.path())) + }) + .context("Failed to watch initial root")??; - watcher.extend(find_watched_dirs(".")); + let mut rx = rx.into_stream(); - let rx = rx.into_stream(); while let Some(events) = rx.next().await { let events = events.map_err(|v| anyhow::anyhow!("File watch error: {v:?}"))?; for event in events { match event.event.kind { - EventKind::Any => todo!(), - EventKind::Access(_) => todo!(), - EventKind::Create(notify::event::CreateKind::File) => { - log::info!("File created: {path:?}"); + EventKind::Create(CreateKind::File) => { + for path in &event.paths { + log::info!("File created: {path:?}"); + watcher.add(path)?; + } + } + EventKind::Create(CreateKind::Folder) => { + for path in &event.paths { + log::info!("Folder created: {path:?}"); + + process_results(find_watched_dirs(path), |mut v| { + v.try_for_each(|v| watcher.add(v.path())) + }) + .context("Failed to watch new folder")??; + } + } + + EventKind::Modify(v) => { + log::info!("Modified {v:?}"); + } + EventKind::Remove(RemoveKind::Folder) => { + for path in &event.paths { + watcher.remove(path)?; + } } - EventKind::Create(notify::event::CreateKind::Folder) => { - log::info!("Folder created: {path:?}"); - update_watch_subdir(watching, watcher, dir) + v => { + log::info!("Other event: {v:?}"); } - EventKind::Modify(_) => todo!(), - EventKind::Remove(_) => todo!(), - EventKind::Other => todo!(), } } } @@ -111,43 +138,46 @@ impl Serve { } } -pub fn update_watch_subdir( - watching: &mut BTreeSet, - watcher: impl Watcher, +// pub fn update_watch_subdir( +// watching: &mut BTreeSet, +// watcher: impl Watcher, +// dir: impl AsRef, +// ) -> anyhow::Result<()> { +// for entry in find_watched_dirs(dir.as_ref()) { +// let entry = entry?; + +// let path = entry.path(); +// if watching.insert(path.to_path_buf()) { +// log::info!("Watching new entry: {path:?}"); +// watcher.watch(entry.path(), RecursiveMode::NonRecursive)?; +// } +// } + +// Ok(()) +// } + +pub fn find_watched_dirs( dir: impl AsRef, -) -> anyhow::Result<()> { - for entry in find_watched_dirs() { - let entry = entry?; +) -> impl Iterator> { + let dir = dir.as_ref(); + log::info!("Walking directory {dir:?}"); - let path = entry.path(); - if watching.insert(path.to_path_buf()) { - log::info!("Watching new entry: {path:?}"); - watcher.watch(entry.path(), RecursiveMode::NonRecursive)?; - } - } - - Ok(()) -} - -pub fn find_watched_dirs(dir) -> impl Iterator> { walkdir::WalkDir::new(dir).into_iter().filter_entry(|v| { let path = v.path(); + let fname = v.file_name(); - if path.starts_with(".") { - log::debug!("Ignoring hidden path: {path:?}"); - return false; - } + // if path.starts_with(".") { + // log::debug!("Ignoring hidden path: {path:?}"); + // return false; + // } - match path.to_str() { + match fname.to_str() { None => { log::error!("Path is not UTF-8: {path:?}"); false } - Some("." | "node_modules" | "target") => false, - Some(v) => { - log::debug!("Watching path: {path:?}"); - true - } + Some("node_modules" | "target" | ".git" | "build" | "tmp") => false, + Some(_) => true, } }) } diff --git a/web/Cargo.lock b/web/Cargo.lock index 06b4552881..d9bcf740fc 100644 --- a/web/Cargo.lock +++ b/web/Cargo.lock @@ -116,10 +116,10 @@ dependencies = [ "ambient_gpu", "ambient_input", "ambient_model", + "ambient_native_std", "ambient_procedurals", "ambient_profiling", "ambient_renderer", - "ambient_native_std", "ambient_sys", "ambient_ui_native", "anyhow", @@ -239,8 +239,8 @@ version = "0.3.0-dev" dependencies = [ "ambient_ecs", "ambient_gpu", - "ambient_profiling", "ambient_native_std", + "ambient_profiling", "ambient_sys", "bytemuck", "chrono", @@ -270,11 +270,12 @@ dependencies = [ "ambient_element_component", "ambient_gizmos", "ambient_gpu", + "ambient_native_std", "ambient_network", "ambient_renderer", "ambient_rpc", "ambient_shared_types", - "ambient_native_std", + "ambient_std", "ambient_sys", "ambient_ui_native", "glam", @@ -287,11 +288,11 @@ dependencies = [ name = "ambient_ecs" version = "0.3.0-dev" dependencies = [ + "ambient_native_std", "ambient_profiling", "ambient_project_macro", "ambient_project_rt", "ambient_shared_types", - "ambient_native_std", "anyhow", "atomic_refcell", "bit-set", @@ -328,8 +329,8 @@ dependencies = [ "ambient_ecs", "ambient_element", "ambient_layout", - "ambient_renderer", "ambient_native_std", + "ambient_renderer", "ambient_ui_native", "env_logger", "glam", @@ -398,9 +399,9 @@ dependencies = [ "ambient_ecs", "ambient_gpu", "ambient_meshes", + "ambient_native_std", "ambient_profiling", "ambient_renderer", - "ambient_native_std", "bytemuck", "dashmap", "glam", @@ -464,8 +465,8 @@ version = "0.3.0-dev" dependencies = [ "ambient_core", "ambient_ecs", - "ambient_shared_types", "ambient_native_std", + "ambient_shared_types", "glam", "serde", "serde_json", @@ -515,8 +516,8 @@ dependencies = [ "ambient_editor_derive", "ambient_gpu", "ambient_meshes", - "ambient_renderer", "ambient_native_std", + "ambient_renderer", "ambient_sys", "ambient_ui_native", "anyhow", @@ -532,6 +533,53 @@ dependencies = [ "wgpu", ] +[[package]] +name = "ambient_native_std" +version = "0.3.0-dev" +dependencies = [ + "ambient_asset_cache", + "ambient_cb", + "ambient_color", + "ambient_friendly_id", + "ambient_math", + "ambient_profiling", + "ambient_sys", + "anyhow", + "as-any", + "async-trait", + "bincode", + "bytemuck", + "chrono", + "convert_case 0.6.0", + "data-encoding", + "futures", + "git-version", + "glam", + "itertools", + "log", + "mikktspace", + "once_cell", + "ordered-float 3.7.0", + "parking_lot", + "percent-encoding", + "pin-project", + "rand", + "relative-path", + "reqwest", + "ring", + "sentry-anyhow", + "serde", + "serde_json", + "serde_path_to_error", + "thiserror", + "tokio", + "toml", + "tracing", + "ulid", + "url", + "wgpu", +] + [[package]] name = "ambient_network" version = "0.3.0-dev" @@ -542,11 +590,11 @@ dependencies = [ "ambient_element", "ambient_gizmos", "ambient_gpu", + "ambient_native_std", "ambient_profiling", "ambient_proxy", "ambient_renderer", "ambient_rpc", - "ambient_native_std", "ambient_sys", "ambient_ui_native", "ambient_world_audio", @@ -599,8 +647,8 @@ dependencies = [ "ambient_element", "ambient_gpu", "ambient_meshes", - "ambient_renderer", "ambient_native_std", + "ambient_renderer", "glam", ] @@ -611,9 +659,9 @@ dependencies = [ "ambient_core", "ambient_ecs", "ambient_gpu", + "ambient_native_std", "ambient_renderer", "ambient_shared_types", - "ambient_native_std", "parking_lot", "paste", "tracing", @@ -722,8 +770,8 @@ dependencies = [ "ambient_gpu", "ambient_layout", "ambient_meshes", - "ambient_renderer", "ambient_native_std", + "ambient_renderer", "async-trait", "bytemuck", "glam", @@ -740,8 +788,8 @@ dependencies = [ "ambient_ecs", "ambient_gpu", "ambient_meshes", - "ambient_profiling", "ambient_native_std", + "ambient_profiling", "ambient_sys", "anyhow", "async-trait", @@ -789,50 +837,10 @@ dependencies = [ ] [[package]] -name = "ambient_native_std" +name = "ambient_std" version = "0.3.0-dev" dependencies = [ - "ambient_asset_cache", - "ambient_cb", - "ambient_color", - "ambient_friendly_id", - "ambient_math", - "ambient_profiling", - "ambient_sys", - "anyhow", - "as-any", - "async-trait", - "bincode", - "bytemuck", - "chrono", - "convert_case 0.6.0", - "data-encoding", - "futures", - "git-version", - "glam", "itertools", - "log", - "mikktspace", - "once_cell", - "ordered-float 3.7.0", - "parking_lot", - "percent-encoding", - "pin-project", - "rand", - "relative-path", - "reqwest", - "ring", - "sentry-anyhow", - "serde", - "serde_json", - "serde_path_to_error", - "thiserror", - "tokio", - "toml", - "tracing", - "ulid", - "url", - "wgpu", ] [[package]] @@ -871,8 +879,8 @@ dependencies = [ "ambient_gpu", "ambient_input", "ambient_layout", - "ambient_renderer", "ambient_native_std", + "ambient_renderer", "anyhow", "async-trait", "glam", @@ -928,10 +936,10 @@ dependencies = [ "ambient_input", "ambient_layout", "ambient_meshes", + "ambient_native_std", "ambient_rect", "ambient_renderer", "ambient_shared_types", - "ambient_native_std", "ambient_sys", "ambient_text", "ambient_ui", @@ -968,13 +976,14 @@ dependencies = [ "ambient_gpu", "ambient_input", "ambient_model", + "ambient_native_std", "ambient_network", "ambient_procedurals", "ambient_profiling", "ambient_project", "ambient_renderer", "ambient_shared_types", - "ambient_native_std", + "ambient_std", "ambient_sys", "anyhow", "async-trait", @@ -1023,11 +1032,11 @@ dependencies = [ "ambient_ecs_editor", "ambient_element", "ambient_layout", + "ambient_native_std", "ambient_network", "ambient_primitives", "ambient_renderer", "ambient_rpc", - "ambient_native_std", "ambient_sys", "ambient_ui_native", "ambient_wasm", @@ -1057,8 +1066,8 @@ dependencies = [ "ambient_core", "ambient_ecs", "ambient_element", - "ambient_profiling", "ambient_native_std", + "ambient_profiling", "ambient_sys", "ambient_ui_native", "anyhow", From 5ed3497cd78789f7d3f8dfb026b1d40980ce3a09 Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Tue, 8 Aug 2023 17:50:22 +0200 Subject: [PATCH 04/11] fix: gltf model loading #644 --- crates/model_import/src/lib.rs | 2 +- crates/model_import/src/model_crate.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/model_import/src/lib.rs b/crates/model_import/src/lib.rs index a2fb604e9e..d1e90b8266 100644 --- a/crates/model_import/src/lib.rs +++ b/crates/model_import/src/lib.rs @@ -323,7 +323,7 @@ fn material_filter_matches(filter: &MaterialFilter, mat: &PbrMaterialDesc) -> bo // } // } -pub const MODEL_EXTENSIONS: &[&str] = &["glb", "fbx", "obj", "x"]; +pub const MODEL_EXTENSIONS: &[&str] = &["glb", "gltf", "fbx", "obj", "x"]; /// `../[path]` pub fn dotdot_path(path: impl Into) -> RelativePathBuf { diff --git a/crates/model_import/src/model_crate.rs b/crates/model_import/src/model_crate.rs index b37cd21043..83e0e8ff99 100644 --- a/crates/model_import/src/model_crate.rs +++ b/crates/model_import/src/model_crate.rs @@ -254,8 +254,12 @@ impl ModelCrate { force_assimp: bool, resolve_texture: TextureResolver, ) -> anyhow::Result<()> { - let is_fbx = url.extension().unwrap_or_default() == "fbx"; - let is_glb = url.extension().unwrap_or_default() == "glb"; + let ext = url.extension(); + let ext = ext.as_deref(); + + let is_fbx = ext == Some("fbx"); + let is_glb = ext == Some("glb") || ext == Some("gltf"); + if force_assimp { crate::assimp::import_url(assets, url, self, resolve_texture).await?; } else if is_fbx { From 197f6d1e9b808fd7b9476766f54450ff8e0fad59 Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Tue, 8 Aug 2023 17:53:25 +0200 Subject: [PATCH 05/11] feat: build web client on change --- campfire/src/web/build.rs | 22 ++++++++++++---------- campfire/src/web/mod.rs | 2 +- campfire/src/web/serve.rs | 30 ++++++++++++++++++++++-------- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/campfire/src/web/build.rs b/campfire/src/web/build.rs index db11284bc4..3529c46e16 100644 --- a/campfire/src/web/build.rs +++ b/campfire/src/web/build.rs @@ -1,6 +1,7 @@ use anyhow::Context; use clap::{Args, ValueEnum}; -use std::{path::PathBuf, process::Command}; +use std::path::PathBuf; +use tokio::process::Command; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] pub(crate) enum Target { @@ -20,10 +21,10 @@ pub struct BuildOptions { target: Target, } -pub fn run(opts: BuildOptions) -> anyhow::Result<()> { - ensure_wasm_pack()?; +pub async fn run(opts: &BuildOptions) -> anyhow::Result<()> { + ensure_wasm_pack().await?; - let output_path = run_cargo_build(&opts)?; + let output_path = run_cargo_build(&opts).await?; eprintln!("Built package: {:?}", output_path); @@ -31,12 +32,13 @@ pub fn run(opts: BuildOptions) -> anyhow::Result<()> { } #[cfg(not(target_os = "linux"))] -pub(crate) fn install_wasm_pack() -> anyhow::Result<()> { +pub(crate) async fn install_wasm_pack() -> anyhow::Result<()> { eprintln!("Installing wasm-pack from source"); let status = Command::new("cargo") .args(["install", "wasm-pack"]) .spawn()? - .wait()?; + .wait() + .await?; if !status.success() { anyhow::bail!("Failed to install wasm-pack"); @@ -77,10 +79,10 @@ pub(crate) fn install_wasm_pack() -> anyhow::Result<()> { Ok(()) } -pub fn ensure_wasm_pack() -> anyhow::Result<()> { +pub async fn ensure_wasm_pack() -> anyhow::Result<()> { match which::which("wasm-pack") { Err(_) => { - install_wasm_pack()?; + install_wasm_pack().await?; assert!(which::which("wasm-pack").is_ok(), "wasm-pack is in PATH"); @@ -93,7 +95,7 @@ pub fn ensure_wasm_pack() -> anyhow::Result<()> { } } -pub fn run_cargo_build(opts: &BuildOptions) -> anyhow::Result { +pub async fn run_cargo_build(opts: &BuildOptions) -> anyhow::Result { let mut command = Command::new("wasm-pack"); command.args(["build", "client"]).current_dir("web"); @@ -121,7 +123,7 @@ pub fn run_cargo_build(opts: &BuildOptions) -> anyhow::Result { eprintln!("Building web client"); - let res = command.spawn()?.wait()?; + let res = command.spawn()?.wait().await?; if !res.success() { anyhow::bail!("Building package failed with status code: {res}"); diff --git a/campfire/src/web/mod.rs b/campfire/src/web/mod.rs index 19155a9e0e..85ed6c6c08 100644 --- a/campfire/src/web/mod.rs +++ b/campfire/src/web/mod.rs @@ -19,7 +19,7 @@ pub enum Web { pub async fn run(command: Web) -> anyhow::Result<()> { match command { - Web::Build(args) => build::run(args), + Web::Build(args) => build::run(&args).await, Web::OpenBrowser => { #[cfg(feature = "openssl")] { diff --git a/campfire/src/web/serve.rs b/campfire/src/web/serve.rs index c69dd1478b..d7f4d863d9 100644 --- a/campfire/src/web/serve.rs +++ b/campfire/src/web/serve.rs @@ -37,7 +37,7 @@ impl WatcherState { .context("Failed to canonicalize path")?; if self.watching.insert(path.to_path_buf()) { - log::info!("Watching new entry: {path:?}"); + log::debug!("Watching new entry: {path:?}"); self.watcher .watcher() .watch(&path, RecursiveMode::NonRecursive)?; @@ -50,7 +50,7 @@ impl WatcherState { let path = path.as_ref().canonicalize()?; if self.watching.remove(&path) { - log::info!("Watching new entry: {path:?}"); + log::debug!("Watching new entry: {path:?}"); self.watcher.watcher().unwatch(&path)?; } @@ -89,9 +89,11 @@ impl Serve { let mut watcher = WatcherState::new(watcher); - log::info!("Created watcher"); + build::run(&self.build).await?; - process_results(find_watched_dirs("campfire"), |mut v| { + log::debug!("Created watcher"); + + process_results(find_watched_dirs("."), |mut v| { v.try_for_each(|v| watcher.add(v.path())) }) .context("Failed to watch initial root")??; @@ -100,6 +102,8 @@ impl Serve { while let Some(events) = rx.next().await { let events = events.map_err(|v| anyhow::anyhow!("File watch error: {v:?}"))?; + + let mut needs_rebuild = false; for event in events { match event.event.kind { EventKind::Create(CreateKind::File) => { @@ -107,31 +111,41 @@ impl Serve { log::info!("File created: {path:?}"); watcher.add(path)?; } + needs_rebuild = true; } EventKind::Create(CreateKind::Folder) => { for path in &event.paths { - log::info!("Folder created: {path:?}"); + log::debug!("Folder created: {path:?}"); process_results(find_watched_dirs(path), |mut v| { v.try_for_each(|v| watcher.add(v.path())) }) .context("Failed to watch new folder")??; } + needs_rebuild = true; } EventKind::Modify(v) => { - log::info!("Modified {v:?}"); + log::debug!("Modified {v:?}"); + needs_rebuild = true; } EventKind::Remove(RemoveKind::Folder) => { for path in &event.paths { watcher.remove(path)?; } + needs_rebuild = true; } v => { - log::info!("Other event: {v:?}"); + log::debug!("Other event: {v:?}"); } } } + + if needs_rebuild { + log::debug!("Rebuilding..."); + build::run(&self.build).await?; + log::debug!("Finished building the web client"); + } } Ok(()) @@ -176,7 +190,7 @@ pub fn find_watched_dirs( log::error!("Path is not UTF-8: {path:?}"); false } - Some("node_modules" | "target" | ".git" | "build" | "tmp") => false, + Some("node_modules" | "target" | ".git" | "build" | "tmp" | "pkg") => false, Some(_) => true, } }) From e4d8ee3eaf8ab963ba13eacd9c4c3f895d899be7 Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Wed, 9 Aug 2023 10:42:58 +0200 Subject: [PATCH 06/11] feat: auto-reloading serve --- campfire/src/web/serve.rs | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/campfire/src/web/serve.rs b/campfire/src/web/serve.rs index d7f4d863d9..71a6c0d5d8 100644 --- a/campfire/src/web/serve.rs +++ b/campfire/src/web/serve.rs @@ -13,6 +13,7 @@ use notify::{ EventKind, RecursiveMode, Watcher, }; use notify_debouncer_full::{DebounceEventResult, Debouncer, FileIdMap}; +use tokio::select; use walkdir::DirEntry; use super::build::{self, BuildOptions}; @@ -77,6 +78,40 @@ pub struct Serve { impl Serve { pub async fn run(&self) -> anyhow::Result<()> { + build::run(&self.build).await?; + + let watch = self.watch_and_build(); + let serve = self.serve(); + + select! { + v = serve => v?, + v = watch => v?, + } + + Ok(()) + } + + pub async fn serve(&self) -> anyhow::Result<()> { + let dir = Path::new("web/www") + .canonicalize() + .context("Web server directory does not exist")?; + + let status = tokio::process::Command::new("npm") + .args(["run", "dev"]) + .current_dir(dir) + .spawn() + .context("Failed to spawn npm")? + .wait() + .await + .context("Failed to run dev web server")?; + + if !status.success() { + anyhow::bail!("Web server exited with non-zero status: {status:?}") + } + + Ok(()) + } + pub async fn watch_and_build(&self) -> anyhow::Result<()> { let (tx, rx) = flume::unbounded(); let watcher = notify_debouncer_full::new_debouncer( @@ -89,8 +124,6 @@ impl Serve { let mut watcher = WatcherState::new(watcher); - build::run(&self.build).await?; - log::debug!("Created watcher"); process_results(find_watched_dirs("."), |mut v| { @@ -99,7 +132,6 @@ impl Serve { .context("Failed to watch initial root")??; let mut rx = rx.into_stream(); - while let Some(events) = rx.next().await { let events = events.map_err(|v| anyhow::anyhow!("File watch error: {v:?}"))?; From 632e127c006075bd0cae5e0d61073e6dd771d518 Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Wed, 9 Aug 2023 11:04:20 +0200 Subject: [PATCH 07/11] fix: feature gates --- campfire/src/web/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/campfire/src/web/mod.rs b/campfire/src/web/mod.rs index 85ed6c6c08..359d4d91e0 100644 --- a/campfire/src/web/mod.rs +++ b/campfire/src/web/mod.rs @@ -1,10 +1,11 @@ use clap::Subcommand; -use self::{build::BuildOptions, serve::Serve}; +use self::build::BuildOptions; #[cfg(feature = "openssl")] mod browser; mod build; +#[cfg(feature = "serve")] mod serve; #[derive(Debug, Subcommand, Clone)] @@ -14,7 +15,8 @@ pub enum Web { /// Launches chrome with the correct flags to explicitly trust /// the self-signed certificate OpenBrowser, - Serve(Serve), + #[cfg(feature = "serve")] + Serve(serve::Serve), } pub async fn run(command: Web) -> anyhow::Result<()> { @@ -30,6 +32,7 @@ pub async fn run(command: Web) -> anyhow::Result<()> { anyhow::bail!("The `openssl` feature must be enabled to use this command") } } + #[cfg(feature = "serve")] Web::Serve(args) => args.run().await, } } From d1b005b1f6465575993067f276d6d90cc7ab3614 Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Wed, 9 Aug 2023 11:06:11 +0200 Subject: [PATCH 08/11] chore: campfire-ndf => campfire-slim --- .cargo/config.toml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index be2560801d..f43269c9b3 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -18,5 +18,5 @@ rustflags = ["--cfg=web_sys_unstable_apis"] [alias] campfire = "run --package campfire --" -campfire-ndf = "run --package campfire --no-default-features --" +campfire-slim = "run --package campfire --no-default-features --" cf = "run --package campfire --" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1619f2927..db861cc7b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -214,7 +214,7 @@ jobs: sudo apt install -y libxcb-xfixes0-dev mesa-vulkan-drivers - name: Run golden image tests - run: cargo campfire-ndf golden-images --ambient-path=./ambient check + run: cargo campfire-slim golden-images --ambient-path=./ambient check - uses: actions/upload-artifact@v3 if: always() with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bd5a4ef3f8..4629758ca2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,7 +103,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: rustup target add --toolchain stable wasm32-wasi - name: Release all packages required for API - run: cargo run -p campfire-ndf release publish --execute + run: cargo run -p campfire-slim release publish --execute publish-release: needs: [create-release, build-app, publish-api] From 6ab81a2ef487f8bacc146627091986b9ebac8e90 Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Wed, 9 Aug 2023 11:08:04 +0200 Subject: [PATCH 09/11] fix: linux build --- campfire/src/web/build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/campfire/src/web/build.rs b/campfire/src/web/build.rs index 3529c46e16..03c4149348 100644 --- a/campfire/src/web/build.rs +++ b/campfire/src/web/build.rs @@ -48,9 +48,9 @@ pub(crate) async fn install_wasm_pack() -> anyhow::Result<()> { } #[cfg(target_os = "linux")] -pub(crate) fn install_wasm_pack() -> anyhow::Result<()> { +pub(crate) async fn install_wasm_pack() -> anyhow::Result<()> { eprintln!("Installing wasm-pack"); - let mut curl = Command::new("curl") + let mut curl = std::process::Command::new("curl") .args([ "https://rustwasm.github.io/wasm-pack/installer/init.sh", "-sSf", @@ -59,7 +59,7 @@ pub(crate) fn install_wasm_pack() -> anyhow::Result<()> { .spawn() .context("Failed to spawn curl")?; - let mut sh = Command::new("sh") + let mut sh = std::process::Command::new("sh") .stdin(std::process::Stdio::from(curl.stdout.take().unwrap())) .spawn() .context("Failed to spawn sh")?; From 5a633cdd0897ce27881b4c20a4547495645fe2fb Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Wed, 9 Aug 2023 11:52:04 +0200 Subject: [PATCH 10/11] feat: auto install node modules --- campfire/src/web/build.rs | 6 +- campfire/src/web/serve.rs | 22 ++++- web/Cargo.lock | 177 ++++++++++++++++++++++++++++---------- web/www/package-lock.json | 6 +- 4 files changed, 161 insertions(+), 50 deletions(-) diff --git a/campfire/src/web/build.rs b/campfire/src/web/build.rs index 03c4149348..4a2891c055 100644 --- a/campfire/src/web/build.rs +++ b/campfire/src/web/build.rs @@ -36,6 +36,7 @@ pub(crate) async fn install_wasm_pack() -> anyhow::Result<()> { eprintln!("Installing wasm-pack from source"); let status = Command::new("cargo") .args(["install", "wasm-pack"]) + .kill_on_drop(true) .spawn()? .wait() .await?; @@ -98,7 +99,10 @@ pub async fn ensure_wasm_pack() -> anyhow::Result<()> { pub async fn run_cargo_build(opts: &BuildOptions) -> anyhow::Result { let mut command = Command::new("wasm-pack"); - command.args(["build", "client"]).current_dir("web"); + command + .args(["build", "client"]) + .current_dir("web") + .kill_on_drop(true); match &opts.profile[..] { "dev" | "debug" => command.arg("--dev"), diff --git a/campfire/src/web/serve.rs b/campfire/src/web/serve.rs index 71a6c0d5d8..9270caaf40 100644 --- a/campfire/src/web/serve.rs +++ b/campfire/src/web/serve.rs @@ -78,6 +78,22 @@ pub struct Serve { impl Serve { pub async fn run(&self) -> anyhow::Result<()> { + if !tokio::fs::try_exists("web/www/node_modules") + .await + .context("Failed to query node_modules directory")? + { + log::info!("Installing node modules"); + tokio::process::Command::new("npm") + .args(["install", "-d"]) + .current_dir("web/www") + .kill_on_drop(true) + .spawn() + .context("Failed to spawn npm")? + .wait() + .await + .context("Failed to run npm install")?; + } + build::run(&self.build).await?; let watch = self.watch_and_build(); @@ -99,6 +115,7 @@ impl Serve { let status = tokio::process::Command::new("npm") .args(["run", "dev"]) .current_dir(dir) + .kill_on_drop(true) .spawn() .context("Failed to spawn npm")? .wait() @@ -175,7 +192,10 @@ impl Serve { if needs_rebuild { log::debug!("Rebuilding..."); - build::run(&self.build).await?; + if let Err(err) = build::run(&self.build).await { + log::error!("Failed to build: {err}") + } + log::debug!("Finished building the web client"); } } diff --git a/web/Cargo.lock b/web/Cargo.lock index 55cc568c51..8c10e88e56 100644 --- a/web/Cargo.lock +++ b/web/Cargo.lock @@ -240,7 +240,6 @@ dependencies = [ "ambient_ecs", "ambient_gpu", "ambient_native_std", - "ambient_profiling", "ambient_sys", "bytemuck", "chrono", @@ -770,7 +769,6 @@ dependencies = [ "ambient_gpu", "ambient_meshes", "ambient_native_std", - "ambient_profiling", "ambient_sys", "anyhow", "async-trait", @@ -997,7 +995,7 @@ dependencies = [ "winit", "wit-bindgen-core", "wit-bindgen-rust", - "wit-component", + "wit-component 0.11.0", ] [[package]] @@ -1049,7 +1047,6 @@ dependencies = [ "ambient_ecs", "ambient_element", "ambient_native_std", - "ambient_profiling", "ambient_sys", "ambient_ui_native", "anyhow", @@ -1224,7 +1221,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -1422,7 +1419,7 @@ checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -1802,7 +1799,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -1813,7 +1810,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -2175,7 +2172,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -2281,7 +2278,7 @@ checksum = "55a9a55d1dab3b07854648d48e366f684aefe2ac78ae28cec3bf65e3cd53d9a3" dependencies = [ "execute-command-tokens", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -2475,7 +2472,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -4211,7 +4208,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -4296,9 +4293,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -4320,7 +4317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "097bf8b99121dfb8c75eed54dfbdbdb1d53e372c53d2353e8a15aad2a479249d" dependencies = [ "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -4347,6 +4344,17 @@ dependencies = [ "unicase", ] +[[package]] +name = "pulldown-cmark" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + [[package]] name = "qoi" version = "0.4.1" @@ -4406,9 +4414,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.27" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -4885,9 +4893,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.163" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] @@ -4924,13 +4932,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -4961,7 +4969,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -5156,6 +5164,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "spdx" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71" +dependencies = [ + "smallvec", +] + [[package]] name = "spin" version = "0.5.2" @@ -5298,9 +5315,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -5333,7 +5350,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -5482,7 +5499,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -5570,7 +5587,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -5836,7 +5853,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", "wasm-bindgen-shared", ] @@ -5870,7 +5887,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5890,6 +5907,15 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f8e9778e04cbf44f58acc301372577375a666b966c50b03ef46144f80436a8" +dependencies = [ + "leb128", +] + [[package]] name = "wasm-metadata" version = "0.8.0" @@ -5899,8 +5925,23 @@ dependencies = [ "anyhow", "indexmap 1.9.3", "serde", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.29.0", + "wasmparser 0.107.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51db59397fc650b5f2fc778e4a5c4456cd856bed7fc1ec15f8d3e28229dc463" +dependencies = [ + "anyhow", + "indexmap 2.0.0", + "serde", + "serde_json", + "spdx", + "wasm-encoder 0.30.0", + "wasmparser 0.108.0", ] [[package]] @@ -5913,6 +5954,16 @@ dependencies = [ "semver 1.0.17", ] +[[package]] +name = "wasmparser" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c956109dcb41436a39391139d9b6e2d0a5e0b158e1293ef352ec977e5e36c5" +dependencies = [ + "indexmap 2.0.0", + "semver 1.0.17", +] + [[package]] name = "wayland-client" version = "0.29.5" @@ -6445,33 +6496,33 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34a19aa69c4f33cb5ac10e55880a899f4d52ec85d4cde4d593b575e7a97e2b08" +checksum = "c9658ec54d4a3c9e2f079bc65a131093337595b595fbf82f805008469838cdea" dependencies = [ "anyhow", - "wit-component", - "wit-parser", + "wit-component 0.12.0", + "wit-parser 0.9.2", ] [[package]] name = "wit-bindgen-rust" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a50274c0cf2f8e33fc967825cef0114cdfe222d474c1d78aa77a6a801abaadf" +checksum = "21ae6a6198ba9765771b977e2af985a0d5ac71b59f999da5c4ee1c7bbd8ca8dc" dependencies = [ "heck", - "wasm-metadata", + "wasm-metadata 0.9.0", "wit-bindgen-core", "wit-bindgen-rust-lib", - "wit-component", + "wit-component 0.12.0", ] [[package]] name = "wit-bindgen-rust-lib" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d3d58b5ced269f1a1cdcecfe0317c059fe158da9b670fff9907903b244bb89a" +checksum = "5c31de8c6c77cac1fd4927c7584d1314cd5e838cfb40b53333d6dffc7a132dda" dependencies = [ "heck", "wit-bindgen-core", @@ -6487,10 +6538,26 @@ dependencies = [ "bitflags 1.3.2", "indexmap 1.9.3", "log", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", + "wasm-encoder 0.29.0", + "wasm-metadata 0.8.0", + "wasmparser 0.107.0", + "wit-parser 0.8.0", +] + +[[package]] +name = "wit-component" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "253bd426c532f1cae8c633c517c63719920535f3a7fada3589de40c5b734e393" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "indexmap 2.0.0", + "log", + "wasm-encoder 0.30.0", + "wasm-metadata 0.9.0", + "wasmparser 0.108.0", + "wit-parser 0.9.2", ] [[package]] @@ -6503,7 +6570,23 @@ dependencies = [ "id-arena", "indexmap 1.9.3", "log", - "pulldown-cmark", + "pulldown-cmark 0.8.0", + "semver 1.0.17", + "unicode-xid", + "url", +] + +[[package]] +name = "wit-parser" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "541efa2046e544de53a9da1e2f6299e63079840360c9e106f1f8275a97771318" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.0.0", + "log", + "pulldown-cmark 0.9.3", "semver 1.0.17", "unicode-xid", "url", diff --git a/web/www/package-lock.json b/web/www/package-lock.json index 3a20adecaa..0cc27b99ca 100644 --- a/web/www/package-lock.json +++ b/web/www/package-lock.json @@ -22,7 +22,11 @@ "extraneous": true, "license": "MIT OR Apache-2.0" }, - "../pkg": {}, + "../pkg": { + "name": "ambient_web", + "version": "0.3.0-dev", + "license": "MIT OR Apache-2.0" + }, "node_modules/@esbuild/android-arm": { "version": "0.18.11", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.11.tgz", From be1cefa34e5eab2967323e99f67ea21a54bc71a0 Mon Sep 17 00:00:00 2001 From: Tei Leelo Roberts Date: Wed, 9 Aug 2023 12:06:39 +0200 Subject: [PATCH 11/11] chore: update web/README.md --- web/README.md | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/web/README.md b/web/README.md index 39af82e691..9b17acd8d8 100644 --- a/web/README.md +++ b/web/README.md @@ -8,41 +8,18 @@ Note: you need to serve a project using `ambient` for the web client to connect ## Build Prerequisites -- [wasm-pack](https://rustwasm.github.io/wasm-pack/) -- [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) - Node `>= v.19` - WebGPU supported web browser (recent enough) -## Setup - -From `./web/www` +## Building and Serving ```sh -# Installs the dependencies and the webpack dev server -npm install -d - -cd .. -rustup target add wasm32-unknown-unknown -``` - -## Building - -From `./web/` - -``` -wasm-pack build client --dev +cargo campfire web serve ``` -## Running +This build the client and launch `vite dev server` binding to `:5173`. See the command output for exact url. -From `./web/www` - -``` - -npm run dev -``` - -This will launch `vite dev server` and bind to `:5173`. See the command output for exact url +Whenever a file changes the client will automatically rebuild and the changes will be reflected in the browser. **Note**: the self-signed certificate is only valid for `127.0.0.1` @@ -50,13 +27,14 @@ This will launch `vite dev server` and bind to `:5173`. See the command output f If using self-signed certificates, you need to toll Chrome to trust it -From `./` ```sh -./scripts/launch_chrome.sh +cargo campfire open-browser ``` -After opening the client it will attempt connect to a locally running `ambient server` on `127.0.0.1:9000` (the default) +**Note**: If you are on mac **make sure you close any existing Chrome instances using `Quit`** + + ## Known Issues