Skip to content

Commit d6f7d3c

Browse files
authored
Add cwd option to before commands, add wait option to dev #4740 #3551 (#4834)
1 parent 90d5929 commit d6f7d3c

File tree

6 files changed

+205
-50
lines changed

6 files changed

+205
-50
lines changed

.changes/before-command-cwd.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri-utils": minor
3+
"cli.rs": minor
4+
"cli.js": minor
5+
---
6+
7+
Change `before_dev_command` and `before_build_command` config value to allow configuring the current working directory.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri-utils": minor
3+
"cli.rs": minor
4+
"cli.js": minor
5+
---
6+
7+
Allow configuring the `before_dev_command` to force the CLI to wait for the command to finish before proceeding.

core/tauri-utils/src/config.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,6 +2377,41 @@ impl std::fmt::Display for AppUrl {
23772377
}
23782378
}
23792379

2380+
/// Describes the shell command to run before `tauri dev`.
2381+
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2382+
#[cfg_attr(feature = "schema", derive(JsonSchema))]
2383+
#[serde(rename_all = "camelCase", untagged)]
2384+
pub enum BeforeDevCommand {
2385+
/// Run the given script with the default options.
2386+
Script(String),
2387+
/// Run the given script with custom options.
2388+
ScriptWithOptions {
2389+
/// The script to execute.
2390+
script: String,
2391+
/// The current working directory.
2392+
cwd: Option<String>,
2393+
/// Whether `tauri dev` should wait for the command to finish or not. Defaults to `false`.
2394+
#[serde(default)]
2395+
wait: bool,
2396+
},
2397+
}
2398+
2399+
/// Describes the shell command to run before `tauri build`.
2400+
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2401+
#[cfg_attr(feature = "schema", derive(JsonSchema))]
2402+
#[serde(rename_all = "camelCase", untagged)]
2403+
pub enum BeforeBuildCommand {
2404+
/// Run the given script with the default options.
2405+
Script(String),
2406+
/// Run the given script with custom options.
2407+
ScriptWithOptions {
2408+
/// The script to execute.
2409+
script: String,
2410+
/// The current working directory.
2411+
cwd: Option<String>,
2412+
},
2413+
}
2414+
23802415
/// The Build configuration object.
23812416
#[skip_serializing_none]
23822417
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
@@ -2411,12 +2446,12 @@ pub struct BuildConfig {
24112446
///
24122447
/// The TAURI_PLATFORM, TAURI_ARCH, TAURI_FAMILY, TAURI_PLATFORM_VERSION, TAURI_PLATFORM_TYPE and TAURI_DEBUG environment variables are set if you perform conditional compilation.
24132448
#[serde(alias = "before-dev-command")]
2414-
pub before_dev_command: Option<String>,
2449+
pub before_dev_command: Option<BeforeDevCommand>,
24152450
/// A shell command to run before `tauri build` kicks in.
24162451
///
24172452
/// The TAURI_PLATFORM, TAURI_ARCH, TAURI_FAMILY, TAURI_PLATFORM_VERSION, TAURI_PLATFORM_TYPE and TAURI_DEBUG environment variables are set if you perform conditional compilation.
24182453
#[serde(alias = "before-build-command")]
2419-
pub before_build_command: Option<String>,
2454+
pub before_build_command: Option<BeforeBuildCommand>,
24202455
/// Features passed to `cargo` commands.
24212456
pub features: Option<Vec<String>>,
24222457
/// Whether we should inject the Tauri API on `window.__TAURI__` or not.

tooling/cli/schema.json

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2449,16 +2449,24 @@
24492449
},
24502450
"beforeDevCommand": {
24512451
"description": "A shell command to run before `tauri dev` kicks in.\n\nThe TAURI_PLATFORM, TAURI_ARCH, TAURI_FAMILY, TAURI_PLATFORM_VERSION, TAURI_PLATFORM_TYPE and TAURI_DEBUG environment variables are set if you perform conditional compilation.",
2452-
"type": [
2453-
"string",
2454-
"null"
2452+
"anyOf": [
2453+
{
2454+
"$ref": "#/definitions/BeforeDevCommand"
2455+
},
2456+
{
2457+
"type": "null"
2458+
}
24552459
]
24562460
},
24572461
"beforeBuildCommand": {
24582462
"description": "A shell command to run before `tauri build` kicks in.\n\nThe TAURI_PLATFORM, TAURI_ARCH, TAURI_FAMILY, TAURI_PLATFORM_VERSION, TAURI_PLATFORM_TYPE and TAURI_DEBUG environment variables are set if you perform conditional compilation.",
2459-
"type": [
2460-
"string",
2461-
"null"
2463+
"anyOf": [
2464+
{
2465+
"$ref": "#/definitions/BeforeBuildCommand"
2466+
},
2467+
{
2468+
"type": "null"
2469+
}
24622470
]
24632471
},
24642472
"features": {
@@ -2499,6 +2507,69 @@
24992507
}
25002508
]
25012509
},
2510+
"BeforeDevCommand": {
2511+
"description": "Describes the shell command to run before `tauri dev`.",
2512+
"anyOf": [
2513+
{
2514+
"description": "Run the given script with the default options.",
2515+
"type": "string"
2516+
},
2517+
{
2518+
"description": "Run the given script with custom options.",
2519+
"type": "object",
2520+
"required": [
2521+
"script"
2522+
],
2523+
"properties": {
2524+
"script": {
2525+
"description": "The script to execute.",
2526+
"type": "string"
2527+
},
2528+
"cwd": {
2529+
"description": "The current working directory.",
2530+
"type": [
2531+
"string",
2532+
"null"
2533+
]
2534+
},
2535+
"wait": {
2536+
"description": "Whether `tauri dev` should wait for the command to finish or not. Defaults to `false`.",
2537+
"default": false,
2538+
"type": "boolean"
2539+
}
2540+
}
2541+
}
2542+
]
2543+
},
2544+
"BeforeBuildCommand": {
2545+
"description": "Describes the shell command to run before `tauri build`.",
2546+
"anyOf": [
2547+
{
2548+
"description": "Run the given script with the default options.",
2549+
"type": "string"
2550+
},
2551+
{
2552+
"description": "Run the given script with custom options.",
2553+
"type": "object",
2554+
"required": [
2555+
"script"
2556+
],
2557+
"properties": {
2558+
"script": {
2559+
"description": "The script to execute.",
2560+
"type": "string"
2561+
},
2562+
"cwd": {
2563+
"description": "The current working directory.",
2564+
"type": [
2565+
"string",
2566+
"null"
2567+
]
2568+
}
2569+
}
2570+
}
2571+
]
2572+
},
25022573
"PluginConfig": {
25032574
"description": "The plugin configs holds a HashMap mapping a plugin name to its configuration object.",
25042575
"type": "object",

tooling/cli/src/build.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::{
66
helpers::{
77
app_paths::{app_dir, tauri_dir},
88
command_env,
9-
config::{get as get_config, AppUrl, WindowUrl, MERGE_CONFIG_EXTENSION_NAME},
9+
config::{
10+
get as get_config, AppUrl, BeforeBuildCommand, WindowUrl, MERGE_CONFIG_EXTENSION_NAME,
11+
},
1012
updater_signature::{read_key_from_file, secret_key as updater_secret_key, sign_file},
1113
},
1214
interface::{AppInterface, AppSettings, Interface},
@@ -112,23 +114,29 @@ pub fn command(mut options: Options) -> Result<()> {
112114
std::process::exit(1);
113115
}
114116

115-
if let Some(before_build) = &config_.build.before_build_command {
116-
if !before_build.is_empty() {
117+
if let Some(before_build) = config_.build.before_build_command.clone() {
118+
let (script, script_cwd) = match before_build {
119+
BeforeBuildCommand::Script(s) if s.is_empty() => (None, None),
120+
BeforeBuildCommand::Script(s) => (Some(s), None),
121+
BeforeBuildCommand::ScriptWithOptions { script, cwd } => (Some(script), cwd.map(Into::into)),
122+
};
123+
let cwd = script_cwd.unwrap_or_else(|| app_dir().clone());
124+
if let Some(before_build) = script {
117125
info!(action = "Running"; "beforeBuildCommand `{}`", before_build);
118126
#[cfg(target_os = "windows")]
119127
let status = Command::new("cmd")
120128
.arg("/S")
121129
.arg("/C")
122-
.arg(before_build)
123-
.current_dir(app_dir())
130+
.arg(&before_build)
131+
.current_dir(cwd)
124132
.envs(command_env(options.debug))
125133
.piped()
126134
.with_context(|| format!("failed to run `{}` with `cmd /C`", before_build))?;
127135
#[cfg(not(target_os = "windows"))]
128136
let status = Command::new("sh")
129137
.arg("-c")
130-
.arg(before_build)
131-
.current_dir(app_dir())
138+
.arg(&before_build)
139+
.current_dir(cwd)
132140
.envs(command_env(options.debug))
133141
.piped()
134142
.with_context(|| format!("failed to run `{}` with `sh -c`", before_build))?;

tooling/cli/src/dev.rs

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ use crate::{
66
helpers::{
77
app_paths::{app_dir, tauri_dir},
88
command_env,
9-
config::{get as get_config, AppUrl, WindowUrl},
9+
config::{get as get_config, AppUrl, BeforeDevCommand, WindowUrl},
1010
},
1111
interface::{AppInterface, ExitReason, Interface},
12-
Result,
12+
CommandExt, Result,
1313
};
1414
use clap::Parser;
1515

16-
use anyhow::Context;
16+
use anyhow::{bail, Context};
1717
use log::{error, info, warn};
1818
use once_cell::sync::OnceCell;
1919
use shared_child::SharedChild;
@@ -89,65 +89,92 @@ fn command_internal(mut options: Options) -> Result<()> {
8989

9090
let config = get_config(options.config.as_deref())?;
9191

92-
if let Some(before_dev) = &config
92+
if let Some(before_dev) = config
9393
.lock()
9494
.unwrap()
9595
.as_ref()
9696
.unwrap()
9797
.build
9898
.before_dev_command
99+
.clone()
99100
{
100-
if !before_dev.is_empty() {
101+
let (script, script_cwd, wait) = match before_dev {
102+
BeforeDevCommand::Script(s) if s.is_empty() => (None, None, false),
103+
BeforeDevCommand::Script(s) => (Some(s), None, false),
104+
BeforeDevCommand::ScriptWithOptions { script, cwd, wait } => {
105+
(Some(script), cwd.map(Into::into), wait)
106+
}
107+
};
108+
let cwd = script_cwd.unwrap_or_else(|| app_dir().clone());
109+
if let Some(before_dev) = script {
101110
info!(action = "Running"; "BeforeDevCommand (`{}`)", before_dev);
102-
#[cfg(target_os = "windows")]
111+
#[cfg(windows)]
103112
let mut command = {
104113
let mut command = Command::new("cmd");
105114
command
106115
.arg("/S")
107116
.arg("/C")
108-
.arg(before_dev)
109-
.current_dir(app_dir())
117+
.arg(&before_dev)
118+
.current_dir(cwd)
110119
.envs(command_env(true));
111120
command
112121
};
113-
#[cfg(not(target_os = "windows"))]
122+
#[cfg(not(windows))]
114123
let mut command = {
115124
let mut command = Command::new("sh");
116125
command
117126
.arg("-c")
118-
.arg(before_dev)
119-
.current_dir(app_dir())
127+
.arg(&before_dev)
128+
.current_dir(cwd)
120129
.envs(command_env(true));
121130
command
122131
};
123-
command.stdin(Stdio::piped());
124-
command.stdout(os_pipe::dup_stdout()?);
125-
command.stderr(os_pipe::dup_stderr()?);
126-
127-
let child = SharedChild::spawn(&mut command)
128-
.unwrap_or_else(|_| panic!("failed to run `{}`", before_dev));
129-
let child = Arc::new(child);
130-
let child_ = child.clone();
131132

132-
std::thread::spawn(move || {
133-
let status = child_
134-
.wait()
135-
.expect("failed to wait on \"beforeDevCommand\"");
136-
if !(status.success() || KILL_BEFORE_DEV_FLAG.get().unwrap().load(Ordering::Relaxed)) {
137-
error!("The \"beforeDevCommand\" terminated with a non-zero status code.");
138-
exit(status.code().unwrap_or(1));
133+
if wait {
134+
let status = command.piped().with_context(|| {
135+
format!(
136+
"failed to run `{}` with `{}`",
137+
before_dev,
138+
if cfg!(windows) { "cmd /S /C" } else { "sh -c" }
139+
)
140+
})?;
141+
if !status.success() {
142+
bail!(
143+
"beforeDevCommand `{}` failed with exit code {}",
144+
before_dev,
145+
status.code().unwrap_or_default()
146+
);
139147
}
140-
});
148+
} else {
149+
command.stdin(Stdio::piped());
150+
command.stdout(os_pipe::dup_stdout()?);
151+
command.stderr(os_pipe::dup_stderr()?);
152+
153+
let child = SharedChild::spawn(&mut command)
154+
.unwrap_or_else(|_| panic!("failed to run `{}`", before_dev));
155+
let child = Arc::new(child);
156+
let child_ = child.clone();
141157

142-
BEFORE_DEV.set(Mutex::new(child)).unwrap();
143-
KILL_BEFORE_DEV_FLAG.set(AtomicBool::default()).unwrap();
158+
std::thread::spawn(move || {
159+
let status = child_
160+
.wait()
161+
.expect("failed to wait on \"beforeDevCommand\"");
162+
if !(status.success() || KILL_BEFORE_DEV_FLAG.get().unwrap().load(Ordering::Relaxed)) {
163+
error!("The \"beforeDevCommand\" terminated with a non-zero status code.");
164+
exit(status.code().unwrap_or(1));
165+
}
166+
});
144167

145-
let _ = ctrlc::set_handler(move || {
146-
kill_before_dev_process();
147-
#[cfg(not(debug_assertions))]
148-
let _ = check_for_updates();
149-
exit(130);
150-
});
168+
BEFORE_DEV.set(Mutex::new(child)).unwrap();
169+
KILL_BEFORE_DEV_FLAG.set(AtomicBool::default()).unwrap();
170+
171+
let _ = ctrlc::set_handler(move || {
172+
kill_before_dev_process();
173+
#[cfg(not(debug_assertions))]
174+
let _ = check_for_updates();
175+
exit(130);
176+
});
177+
}
151178
}
152179
}
153180

0 commit comments

Comments
 (0)