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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down
16 changes: 16 additions & 0 deletions Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand All @@ -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` — Run an Oseda project in dev mode, with hot-reloading



Expand Down Expand Up @@ -120,6 +122,20 @@ Update the oseda binary from crates.io



## `oseda dev`

Run an Oseda project in dev mode, with hot-reloading

**Usage:** `oseda dev [OPTIONS]`

###### **Options:**

* `--port <PORT>` — Port to run the vite dev server on

Default value: `3000`



<hr/>

<small><i>
Expand Down
14 changes: 6 additions & 8 deletions src/bin/oseda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
118 changes: 118 additions & 0 deletions src/cmd/dev.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
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),
}
}
}


/// 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<AtomicBool>,
) -> 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(())
}
1 change: 1 addition & 0 deletions src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod fork;
pub mod init;
pub mod run;
pub mod update;
pub mod dev;
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +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),
}
Loading