From 19669a7f0f62e10b32e8d2c610bfcdf2a68e56e1 Mon Sep 17 00:00:00 2001 From: ReeseHatfield Date: Fri, 18 Sep 2026 21:40:19 -0400 Subject: [PATCH 1/5] dev command --- src/bin/oseda.rs | 14 +++---- src/cmd/dev.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/mod.rs | 1 + src/lib.rs | 1 + 4 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 src/cmd/dev.rs diff --git a/src/bin/oseda.rs b/src/bin/oseda.rs index e91dd15..2880b45 100644 --- a/src/bin/oseda.rs +++ b/src/bin/oseda.rs @@ -2,14 +2,9 @@ use std::{error::Error, process}; use clap::Parser; use oseda_cli::{ - cmd::{ - check, - deploy::{self}, - export::{self}, - fork::{self}, - init, run, update, - }, - Cli, Commands, + Cli, Commands, cmd::{ + check, deploy::{self}, dev, export::{self}, fork::{self}, init, run, update + } }; /// CLI entry point @@ -38,6 +33,9 @@ fn main() { println!("Successfully updated oseda"); println!("You may need to restart the shell for updates to take effect") }), + Commands::Dev(options) => dev::dev(options) + .map(|_| println!("Oseda dev server stopped")) + .map_err(|e| e.into()), }; // little annoying, but makes the exit code match what users would expect diff --git a/src/cmd/dev.rs b/src/cmd/dev.rs new file mode 100644 index 0000000..2ba5c4f --- /dev/null +++ b/src/cmd/dev.rs @@ -0,0 +1,99 @@ +use std::{ + process::Command, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; + +use clap::Args; + +use crate::cmd::run::is_cwd_oseda_project; + +/// Options for the `oseda dev` command +#[derive(Args, Debug, Clone)] +pub struct DevOptions { + /// Port to run the Vite dev server on + #[arg(long, default_value_t = 3000)] + pub port: u16, +} + +#[derive(Debug)] +pub enum OsedaDevError { + NotOsedaProjectError(String), + ServeError(String), +} + +impl std::error::Error for OsedaDevError {} +impl std::fmt::Display for OsedaDevError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NotOsedaProjectError(msg) => write!( + f, + "Current working directory is not an Oseda project: {}", + msg + ), + Self::ServeError(msg) => write!(f, "Oseda Dev Server Error: {}", msg), + } + } +} + +pub fn dev(opts: DevOptions) -> Result<(), OsedaDevError> { + dev_with_shutdown(opts, Arc::new(AtomicBool::new(false))) +} + +pub fn dev_with_shutdown( + opts: DevOptions, + shutdown_flag: Arc, +) -> Result<(), OsedaDevError> { + if !is_cwd_oseda_project() { + return Err(OsedaDevError::NotOsedaProjectError( + "oseda-config.json not found".to_string(), + )); + } + + let mut cmd = Command::new("npx"); + cmd.arg("vite") + .arg("--port") + .arg(opts.port.to_string()) + // fail if port not allowed + .arg("--strictPort"); + + let mut child = cmd.spawn().map_err(|e| { + println!("Error starting `npx vite`: {e}"); + println!("Please ensure that `npx` and `vite` are installed and in your PATH."); + OsedaDevError::ServeError("failed to start vite dev server".into()) + })?; + + let ctrlc_flag = shutdown_flag.clone(); + ctrlc::set_handler(move || { + println!("\nSIGINT received. Shutting down dev server..."); + ctrlc_flag.store(true, Ordering::SeqCst); + }) + .map_err(|e| { + println!("Error setting ctrl+c handler: {e}"); + OsedaDevError::ServeError("failed to set handler".into()) + })?; + + // block until ctrl+c/shutdown flag OR the vite process dies on its own + while !shutdown_flag.load(Ordering::SeqCst) { + if let Ok(Some(status)) = child.try_wait() { + println!("`vite` exited on its own with status: {status}"); + return Err(OsedaDevError::ServeError( + "vite dev server exited unexpectedly".into(), + )); + } + std::thread::sleep(Duration::from_millis(100)); + } + + if let Err(e) = child.kill() { + println!("Failed to kill `vite`: {e}"); + } else { + println!("`vite` dev server terminated."); + } + + let _ = child.wait(); + + Ok(()) +} \ No newline at end of file diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 344a04d..64f22e4 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -5,3 +5,4 @@ pub mod fork; pub mod init; pub mod run; pub mod update; +pub mod dev; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b2b6a34..5450c26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,4 +41,5 @@ pub enum Commands { Export(cmd::export::ExportOptions), /// Update the oseda binary from crates.io Update, + Dev(cmd::dev::DevOptions), } From 8056dc272da92d9e111416d53241d07aceeeefaa Mon Sep 17 00:00:00 2001 From: ReeseHatfield Date: Fri, 18 Sep 2026 21:43:46 -0400 Subject: [PATCH 2/5] semver --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58caacc..8f30d8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1109,7 +1109,7 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "oseda-cli" -version = "3.1.1" +version = "3.2.0" dependencies = [ "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index 463fe51..366a8e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ homepage = "https://oseda.net" repository = "https://github.com/oseda-dev/oseda-cli" readme = "README.md" name = "oseda-cli" -version = "3.1.1" +version = "3.2.0" edition = "2021" [[bin]] From 4341ca16a3948eeebcbf85906345dd4cc18d43f3 Mon Sep 17 00:00:00 2001 From: ReeseHatfield Date: Fri, 18 Sep 2026 21:48:23 -0400 Subject: [PATCH 3/5] docs --- src/cmd/dev.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/cmd/dev.rs b/src/cmd/dev.rs index 2ba5c4f..a25dc4e 100644 --- a/src/cmd/dev.rs +++ b/src/cmd/dev.rs @@ -14,7 +14,7 @@ use crate::cmd::run::is_cwd_oseda_project; /// Options for the `oseda dev` command #[derive(Args, Debug, Clone)] pub struct DevOptions { - /// Port to run the Vite dev server on + /// Port to run the vite dev server on #[arg(long, default_value_t = 3000)] pub port: u16, } @@ -39,10 +39,29 @@ impl std::fmt::Display for OsedaDevError { } } + +/// Run in dev mode, with auto-reload on save +/// +/// # Arguments: +/// * `opts` - options for subcommand +/// * `shutdown_flag` - Arc to kill process +/// +/// # Returns +/// * `Ok()` on success +/// * `Err` on any issue related to running oseda in dev mode pub fn dev(opts: DevOptions) -> Result<(), OsedaDevError> { dev_with_shutdown(opts, Arc::new(AtomicBool::new(false))) } +/// Run in dev mode, with auto-reload on save, with a shutdown flag +/// +/// # Arguments: +/// * `opts` - options for subcommand +/// * `shutdown_flag` - Arc to kill process +/// +/// # Returns +/// * `Ok()` on success +/// * `Err` on any issue related to running oseda in dev mode pub fn dev_with_shutdown( opts: DevOptions, shutdown_flag: Arc, From a45d41ceab55be6800057eda2e490337e4cf721e Mon Sep 17 00:00:00 2001 From: ReeseHatfield Date: Fri, 18 Sep 2026 21:49:12 -0400 Subject: [PATCH 4/5] update usage --- Usage.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Usage.md b/Usage.md index 74f65f3..456d414 100644 --- a/Usage.md +++ b/Usage.md @@ -12,6 +12,7 @@ This document contains the help content for the `oseda` command-line program. * [`oseda fork`↴](#oseda-fork) * [`oseda export`↴](#oseda-export) * [`oseda update`↴](#oseda-update) +* [`oseda dev`↴](#oseda-dev) ## `oseda` @@ -28,6 +29,7 @@ oseda project scafolding CLI * `fork` — Fork the library repository to submit your course * `export` — Export the Oseda project to a PDF file This will install the npm package `decktape` This relies on a chromium backend, as a result, it may take a while to run * `update` — Update the oseda binary from crates.io +* `dev` — Options for the `oseda dev` command @@ -120,6 +122,20 @@ Update the oseda binary from crates.io +## `oseda dev` + +Options for the `oseda dev` command + +**Usage:** `oseda dev [OPTIONS]` + +###### **Options:** + +* `--port ` — Port to run the vite dev server on + + Default value: `3000` + + +
From caaed7afa00bf2a8a00b1dbdf568dc578785630b Mon Sep 17 00:00:00 2001 From: ReeseHatfield Date: Fri, 18 Sep 2026 21:51:17 -0400 Subject: [PATCH 5/5] fix usage string I forgot --- Usage.md | 4 ++-- src/lib.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Usage.md b/Usage.md index 456d414..9193c66 100644 --- a/Usage.md +++ b/Usage.md @@ -29,7 +29,7 @@ oseda project scafolding CLI * `fork` — Fork the library repository to submit your course * `export` — Export the Oseda project to a PDF file This will install the npm package `decktape` This relies on a chromium backend, as a result, it may take a while to run * `update` — Update the oseda binary from crates.io -* `dev` — Options for the `oseda dev` command +* `dev` — Run an Oseda project in dev mode, with hot-reloading @@ -124,7 +124,7 @@ Update the oseda binary from crates.io ## `oseda dev` -Options for the `oseda dev` command +Run an Oseda project in dev mode, with hot-reloading **Usage:** `oseda dev [OPTIONS]` diff --git a/src/lib.rs b/src/lib.rs index 5450c26..b81db09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,5 +41,6 @@ pub enum Commands { Export(cmd::export::ExportOptions), /// Update the oseda binary from crates.io Update, + /// Run an Oseda project in dev mode, with hot-reloading Dev(cmd::dev::DevOptions), }