mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -05:00
feat: "deno task" subcommand (#13725)
Co-authored-by: David Sherret <dsherret@gmail.com>
This commit is contained in:
parent
808f797633
commit
47f22777be
20 changed files with 468 additions and 5 deletions
31
Cargo.lock
generated
31
Cargo.lock
generated
|
@ -750,6 +750,7 @@ dependencies = [
|
|||
"deno_lint",
|
||||
"deno_net",
|
||||
"deno_runtime",
|
||||
"deno_task_shell",
|
||||
"deno_url",
|
||||
"deno_web",
|
||||
"deno_webgpu",
|
||||
|
@ -778,7 +779,7 @@ dependencies = [
|
|||
"os_pipe",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"pretty_assertions",
|
||||
"pretty_assertions 0.7.2",
|
||||
"rand 0.8.4",
|
||||
"regex",
|
||||
"ring",
|
||||
|
@ -1065,6 +1066,18 @@ dependencies = [
|
|||
"winres",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_task_shell"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad23aacc3db4f37eb88fb9c874a85a4abe5eab14a98fc070ee8df9e204f243e6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
"pretty_assertions 1.1.0",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_tls"
|
||||
version = "0.27.0"
|
||||
|
@ -2252,9 +2265,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
|||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
@ -2896,6 +2909,18 @@ dependencies = [
|
|||
"output_vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76d5b548b725018ab5496482b45cb8bef21e9fed1858a6d674e3a8a0f0bb5d50"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"ctor",
|
||||
"diff",
|
||||
"output_vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
|
|
|
@ -51,6 +51,7 @@ deno_doc = "0.32.0"
|
|||
deno_graph = "0.24.0"
|
||||
deno_lint = { version = "0.26.0", features = ["docs"] }
|
||||
deno_runtime = { version = "0.48.0", path = "../runtime" }
|
||||
deno_task_shell = "0.1.6"
|
||||
|
||||
atty = "=0.2.14"
|
||||
base64 = "=0.13.0"
|
||||
|
|
|
@ -530,6 +530,7 @@ pub struct ConfigFileJson {
|
|||
pub import_map: Option<String>,
|
||||
pub lint: Option<Value>,
|
||||
pub fmt: Option<Value>,
|
||||
pub tasks: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -648,6 +649,19 @@ impl ConfigFile {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_tasks_config(
|
||||
&self,
|
||||
) -> Result<Option<BTreeMap<String, String>>, AnyError> {
|
||||
if let Some(config) = self.json.tasks.clone() {
|
||||
let tasks_config: BTreeMap<String, String> =
|
||||
serde_json::from_value(config)
|
||||
.context("Failed to parse \"tasks\" configuration")?;
|
||||
Ok(Some(tasks_config))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// If the configuration file contains "extra" modules (like TypeScript
|
||||
/// `"types"`) options, return them as imports to be added to a module graph.
|
||||
pub fn to_maybe_imports(&self) -> MaybeImportsResult {
|
||||
|
@ -784,6 +798,10 @@ mod tests {
|
|||
"singleQuote": true,
|
||||
"proseWrap": "preserve"
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"build": "deno run --allow-read --allow-write build.ts",
|
||||
"server": "deno run --allow-net --allow-read server.ts"
|
||||
}
|
||||
}"#;
|
||||
let config_dir = ModuleSpecifier::parse("file:///deno/").unwrap();
|
||||
|
@ -841,6 +859,16 @@ mod tests {
|
|||
assert_eq!(fmt_config.options.line_width, Some(80));
|
||||
assert_eq!(fmt_config.options.indent_width, Some(4));
|
||||
assert_eq!(fmt_config.options.single_quote, Some(true));
|
||||
|
||||
let tasks_config = config_file.to_tasks_config().unwrap().unwrap();
|
||||
assert_eq!(
|
||||
tasks_config["build"],
|
||||
"deno run --allow-read --allow-write build.ts",
|
||||
);
|
||||
assert_eq!(
|
||||
tasks_config["server"],
|
||||
"deno run --allow-net --allow-read server.ts"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
103
cli/flags.rs
103
cli/flags.rs
|
@ -139,6 +139,11 @@ pub struct RunFlags {
|
|||
pub script: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct TaskFlags {
|
||||
pub task: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct TestFlags {
|
||||
pub ignore: Vec<PathBuf>,
|
||||
|
@ -187,6 +192,7 @@ pub enum DenoSubcommand {
|
|||
Lint(LintFlags),
|
||||
Repl(ReplFlags),
|
||||
Run(RunFlags),
|
||||
Task(TaskFlags),
|
||||
Test(TestFlags),
|
||||
Types,
|
||||
Upgrade(UpgradeFlags),
|
||||
|
@ -500,6 +506,7 @@ pub fn flags_from_vec(args: Vec<String>) -> clap::Result<Flags> {
|
|||
Some(("compile", m)) => compile_parse(&mut flags, m),
|
||||
Some(("lsp", m)) => lsp_parse(&mut flags, m),
|
||||
Some(("vendor", m)) => vendor_parse(&mut flags, m),
|
||||
Some(("task", m)) => task_parse(&mut flags, m),
|
||||
_ => handle_repl_flags(&mut flags, ReplFlags { eval: None }),
|
||||
}
|
||||
|
||||
|
@ -568,6 +575,7 @@ If the flag is set, restrict these messages to errors.",
|
|||
.subcommand(lint_subcommand())
|
||||
.subcommand(repl_subcommand())
|
||||
.subcommand(run_subcommand())
|
||||
.subcommand(task_subcommand())
|
||||
.subcommand(test_subcommand())
|
||||
.subcommand(types_subcommand())
|
||||
.subcommand(upgrade_subcommand())
|
||||
|
@ -1256,6 +1264,25 @@ Deno allows specifying the filename '-' to read the file from stdin.
|
|||
)
|
||||
}
|
||||
|
||||
fn task_subcommand<'a>() -> App<'a> {
|
||||
App::new("task")
|
||||
.setting(AppSettings::TrailingVarArg)
|
||||
.arg(config_arg())
|
||||
.arg(Arg::new("task").help("Task to be executed"))
|
||||
.arg(
|
||||
Arg::new("task_args")
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.help("Additional arguments passed to the task"),
|
||||
)
|
||||
.about("Run a task defined in the configuration file")
|
||||
.long_about(
|
||||
"Run a task defined in the configuration file
|
||||
|
||||
deno task build",
|
||||
)
|
||||
}
|
||||
|
||||
fn test_subcommand<'a>() -> App<'a> {
|
||||
runtime_args(App::new("test"), true, true)
|
||||
.setting(AppSettings::TrailingVarArg)
|
||||
|
@ -2197,6 +2224,26 @@ fn run_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
|||
flags.subcommand = DenoSubcommand::Run(RunFlags { script });
|
||||
}
|
||||
|
||||
fn task_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
||||
config_arg_parse(flags, matches);
|
||||
|
||||
let mut task_name = "".to_string();
|
||||
if let Some(task) = matches.value_of("task") {
|
||||
task_name = task.to_string();
|
||||
|
||||
let task_args: Vec<String> = matches
|
||||
.values_of("task_args")
|
||||
.unwrap_or_default()
|
||||
.map(String::from)
|
||||
.collect();
|
||||
for v in task_args {
|
||||
flags.argv.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
flags.subcommand = DenoSubcommand::Task(TaskFlags { task: task_name });
|
||||
}
|
||||
|
||||
fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
||||
runtime_args_parse(flags, matches, true, true);
|
||||
// NOTE: `deno test` always uses `--no-prompt`, tests shouldn't ever do
|
||||
|
@ -5063,4 +5110,60 @@ mod tests {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_subcommand() {
|
||||
let r =
|
||||
flags_from_vec(svec!["deno", "task", "build", "--", "hello", "world",]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Task(TaskFlags {
|
||||
task: "build".to_string(),
|
||||
}),
|
||||
argv: svec!["hello", "world"],
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
||||
let r = flags_from_vec(svec!["deno", "task", "build"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Task(TaskFlags {
|
||||
task: "build".to_string(),
|
||||
}),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_subcommand_empty() {
|
||||
let r = flags_from_vec(svec!["deno", "task",]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Task(TaskFlags {
|
||||
task: "".to_string(),
|
||||
}),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_subcommand_config() {
|
||||
let r = flags_from_vec(svec!["deno", "task", "--config", "deno.jsonc"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Task(TaskFlags {
|
||||
task: "".to_string(),
|
||||
}),
|
||||
config_path: Some("deno.jsonc".to_string()),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
11
cli/main.rs
11
cli/main.rs
|
@ -56,6 +56,7 @@ use crate::flags::InstallFlags;
|
|||
use crate::flags::LintFlags;
|
||||
use crate::flags::ReplFlags;
|
||||
use crate::flags::RunFlags;
|
||||
use crate::flags::TaskFlags;
|
||||
use crate::flags::TestFlags;
|
||||
use crate::flags::UninstallFlags;
|
||||
use crate::flags::UpgradeFlags;
|
||||
|
@ -1228,6 +1229,13 @@ async fn run_command(
|
|||
Ok(worker.get_exit_code())
|
||||
}
|
||||
|
||||
async fn task_command(
|
||||
flags: Flags,
|
||||
task_flags: TaskFlags,
|
||||
) -> Result<i32, AnyError> {
|
||||
tools::task::execute_script(flags, task_flags).await
|
||||
}
|
||||
|
||||
async fn coverage_command(
|
||||
flags: Flags,
|
||||
coverage_flags: CoverageFlags,
|
||||
|
@ -1360,6 +1368,9 @@ fn get_subcommand(
|
|||
DenoSubcommand::Run(run_flags) => {
|
||||
run_command(flags, run_flags).boxed_local()
|
||||
}
|
||||
DenoSubcommand::Task(task_flags) => {
|
||||
task_command(flags, task_flags).boxed_local()
|
||||
}
|
||||
DenoSubcommand::Test(test_flags) => {
|
||||
test_command(flags, test_flags).boxed_local()
|
||||
}
|
||||
|
|
|
@ -310,6 +310,17 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"description": "Configuration for deno task",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[A-Za-z][A-Za-z0-9_\\-]*$": {
|
||||
"type": "string",
|
||||
"description": "Command to execute for this task name."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,8 @@ mod lsp;
|
|||
mod repl;
|
||||
#[path = "run_tests.rs"]
|
||||
mod run;
|
||||
#[path = "task_tests.rs"]
|
||||
mod task;
|
||||
#[path = "test_tests.rs"]
|
||||
mod test;
|
||||
#[path = "upgrade_tests.rs"]
|
||||
|
|
71
cli/tests/integration/task_tests.rs
Normal file
71
cli/tests/integration/task_tests.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::itest;
|
||||
|
||||
// Most of the tests for this are in deno_task_shell.
|
||||
// These tests are intended to only test integration.
|
||||
|
||||
itest!(task_no_args {
|
||||
args: "task --config task/deno.json",
|
||||
output: "task/task_no_args.out",
|
||||
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(task_non_existent {
|
||||
args: "task --config task/deno.json non_existent",
|
||||
output: "task/task_non_existent.out",
|
||||
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(task_boolean_logic {
|
||||
args: "task --config task/deno.json boolean_logic",
|
||||
output: "task/task_boolean_logic.out",
|
||||
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
|
||||
});
|
||||
|
||||
itest!(task_exit_code_1 {
|
||||
args: "task --config task/deno.json exit_code_5",
|
||||
output: "task/task_exit_code_5.out",
|
||||
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
|
||||
exit_code: 5,
|
||||
});
|
||||
|
||||
itest!(task_additional_args {
|
||||
args: "task --config task/deno.json echo 2",
|
||||
output: "task/task_additional_args.out",
|
||||
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
|
||||
});
|
||||
|
||||
itest!(task_additional_args_no_shell_expansion {
|
||||
args_vec: vec!["task", "--config", "task/deno.json", "echo", "$(echo 5)"],
|
||||
output: "task/task_additional_args_no_shell_expansion.out",
|
||||
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
|
||||
});
|
||||
|
||||
itest!(task_additional_args_nested_strings {
|
||||
args_vec: vec![
|
||||
"task",
|
||||
"--config",
|
||||
"task/deno.json",
|
||||
"echo",
|
||||
"string \"quoted string\""
|
||||
],
|
||||
output: "task/task_additional_args_nested_strings.out",
|
||||
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
|
||||
});
|
||||
|
||||
itest!(task_additional_args_no_logic {
|
||||
args_vec: vec![
|
||||
"task",
|
||||
"--config",
|
||||
"task/deno.json",
|
||||
"echo",
|
||||
"||",
|
||||
"echo",
|
||||
"5"
|
||||
],
|
||||
output: "task/task_additional_args_no_logic.out",
|
||||
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
|
||||
});
|
8
cli/tests/testdata/task/deno.json
vendored
Normal file
8
cli/tests/testdata/task/deno.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"tasks": {
|
||||
"boolean_logic": "sleep 0.1 && echo 3 && echo 4 & echo 1 && echo 2 || echo NOPE",
|
||||
"echo": "echo 1",
|
||||
"strings": "deno run main.ts && deno eval \"console.log(\\\"test\\\")\"",
|
||||
"exit_code_5": "echo $(echo 10 ; exit 2) && exit 5"
|
||||
}
|
||||
}
|
1
cli/tests/testdata/task/task_additional_args.out
vendored
Normal file
1
cli/tests/testdata/task/task_additional_args.out
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
1 2
|
1
cli/tests/testdata/task/task_additional_args_nested_strings.out
vendored
Normal file
1
cli/tests/testdata/task/task_additional_args_nested_strings.out
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
1 string "quoted string"
|
1
cli/tests/testdata/task/task_additional_args_no_logic.out
vendored
Normal file
1
cli/tests/testdata/task/task_additional_args_no_logic.out
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
1 || echo 5
|
1
cli/tests/testdata/task/task_additional_args_no_shell_expansion.out
vendored
Normal file
1
cli/tests/testdata/task/task_additional_args_no_shell_expansion.out
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
1 $(echo 5)
|
4
cli/tests/testdata/task/task_boolean_logic.out
vendored
Normal file
4
cli/tests/testdata/task/task_boolean_logic.out
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
1
|
||||
2
|
||||
3
|
||||
4
|
1
cli/tests/testdata/task/task_exit_code_5.out
vendored
Normal file
1
cli/tests/testdata/task/task_exit_code_5.out
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
10
|
9
cli/tests/testdata/task/task_no_args.out
vendored
Normal file
9
cli/tests/testdata/task/task_no_args.out
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
Available tasks:
|
||||
- boolean_logic
|
||||
sleep 0.1 && echo 3 && echo 4 & echo 1 && echo 2 || echo NOPE
|
||||
- echo
|
||||
echo 1
|
||||
- exit_code_5
|
||||
echo $(echo 10 ; exit 2) && exit 5
|
||||
- strings
|
||||
deno run main.ts && deno eval "console.log(\"test\")"
|
10
cli/tests/testdata/task/task_non_existent.out
vendored
Normal file
10
cli/tests/testdata/task/task_non_existent.out
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
Task not found: non_existent
|
||||
Available tasks:
|
||||
- boolean_logic
|
||||
sleep 0.1 && echo 3 && echo 4 & echo 1 && echo 2 || echo NOPE
|
||||
- echo
|
||||
echo 1
|
||||
- exit_code_5
|
||||
echo $(echo 10 ; exit 2) && exit 5
|
||||
- strings
|
||||
deno run main.ts && deno eval "console.log(\"test\")"
|
|
@ -7,6 +7,7 @@ pub mod installer;
|
|||
pub mod lint;
|
||||
pub mod repl;
|
||||
pub mod standalone;
|
||||
pub mod task;
|
||||
pub mod test;
|
||||
pub mod upgrade;
|
||||
pub mod vendor;
|
||||
|
|
165
cli/tools/task.rs
Normal file
165
cli/tools/task.rs
Normal file
|
@ -0,0 +1,165 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::colors;
|
||||
use crate::config_file::ConfigFile;
|
||||
use crate::flags::Flags;
|
||||
use crate::flags::TaskFlags;
|
||||
use crate::proc_state::ProcState;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn get_tasks_config(
|
||||
maybe_config_file: Option<&ConfigFile>,
|
||||
) -> Result<BTreeMap<String, String>, AnyError> {
|
||||
if let Some(config_file) = maybe_config_file {
|
||||
let maybe_tasks_config = config_file.to_tasks_config()?;
|
||||
if let Some(tasks_config) = maybe_tasks_config {
|
||||
for key in tasks_config.keys() {
|
||||
if key.is_empty() {
|
||||
bail!("Configuration file task names cannot be empty");
|
||||
} else if !key
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-'))
|
||||
{
|
||||
bail!("Configuration file task names must only contain alpha-numeric characters, underscores (_), or dashes (-). Task: {}", key);
|
||||
} else if !key.chars().next().unwrap().is_ascii_alphabetic() {
|
||||
bail!("Configuration file task names must start with an alphabetic character. Task: {}", key);
|
||||
}
|
||||
}
|
||||
Ok(tasks_config)
|
||||
} else {
|
||||
bail!("No tasks found in configuration file")
|
||||
}
|
||||
} else {
|
||||
bail!("No config file found")
|
||||
}
|
||||
}
|
||||
|
||||
fn print_available_tasks(tasks_config: BTreeMap<String, String>) {
|
||||
eprintln!("{}", colors::green("Available tasks:"));
|
||||
|
||||
for name in tasks_config.keys() {
|
||||
eprintln!("- {}", colors::cyan(name));
|
||||
eprintln!(" {}", tasks_config[name])
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute_script(
|
||||
flags: Flags,
|
||||
task_flags: TaskFlags,
|
||||
) -> Result<i32, AnyError> {
|
||||
let flags = Arc::new(flags);
|
||||
let ps = ProcState::build(flags.clone()).await?;
|
||||
let tasks_config = get_tasks_config(ps.maybe_config_file.as_ref())?;
|
||||
let config_file_url = &ps.maybe_config_file.as_ref().unwrap().specifier;
|
||||
let config_file_path = if config_file_url.scheme() == "file" {
|
||||
config_file_url.to_file_path().unwrap()
|
||||
} else {
|
||||
bail!("Only local configuration files are supported")
|
||||
};
|
||||
|
||||
if task_flags.task.is_empty() {
|
||||
print_available_tasks(tasks_config);
|
||||
return Ok(1);
|
||||
}
|
||||
|
||||
let cwd = config_file_path.parent().unwrap();
|
||||
let task_name = task_flags.task;
|
||||
let maybe_script = tasks_config.get(&task_name);
|
||||
|
||||
if let Some(script) = maybe_script {
|
||||
let additional_args = flags
|
||||
.argv
|
||||
.iter()
|
||||
// surround all the additional arguments in double quotes
|
||||
// and santize any command substition
|
||||
.map(|a| format!("\"{}\"", a.replace('"', "\\\"").replace('$', "\\$")))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
let script = format!("{} {}", script, additional_args);
|
||||
let seq_list = deno_task_shell::parser::parse(&script)
|
||||
.with_context(|| format!("Error parsing script '{}'.", task_name))?;
|
||||
let env_vars = std::env::vars().collect::<HashMap<String, String>>();
|
||||
let exit_code = deno_task_shell::execute(seq_list, env_vars, cwd).await;
|
||||
Ok(exit_code)
|
||||
} else {
|
||||
eprintln!("Task not found: {}", task_name);
|
||||
print_available_tasks(tasks_config);
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn tasks_no_tasks() {
|
||||
run_task_error_test(r#"{}"#, "No tasks found in configuration file");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_name_invalid_chars() {
|
||||
run_task_error_test(
|
||||
r#"{
|
||||
"tasks": {
|
||||
"build": "deno test",
|
||||
"some%test": "deno bundle mod.ts"
|
||||
}
|
||||
}"#,
|
||||
concat!(
|
||||
"Configuration file task names must only contain alpha-numeric ",
|
||||
"characters, underscores (_), or dashes (-). Task: some%test",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_name_non_alpha_starting_char() {
|
||||
run_task_error_test(
|
||||
r#"{
|
||||
"tasks": {
|
||||
"build": "deno test",
|
||||
"1test": "deno bundle mod.ts"
|
||||
}
|
||||
}"#,
|
||||
concat!(
|
||||
"Configuration file task names must start with an ",
|
||||
"alphabetic character. Task: 1test",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_name_empty() {
|
||||
run_task_error_test(
|
||||
r#"{
|
||||
"tasks": {
|
||||
"build": "deno test",
|
||||
"": "deno bundle mod.ts"
|
||||
}
|
||||
}"#,
|
||||
"Configuration file task names cannot be empty",
|
||||
);
|
||||
}
|
||||
|
||||
fn run_task_error_test(config_text: &str, expected_error: &str) {
|
||||
let config_dir = ModuleSpecifier::parse("file:///deno/").unwrap();
|
||||
let config_specifier = config_dir.join("tsconfig.json").unwrap();
|
||||
let config_file = ConfigFile::new(config_text, &config_specifier).unwrap();
|
||||
assert_eq!(
|
||||
get_tasks_config(Some(&config_file))
|
||||
.err()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
expected_error,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1705,6 +1705,7 @@ pub fn run_powershell_script_file(
|
|||
#[derive(Debug, Default)]
|
||||
pub struct CheckOutputIntegrationTest {
|
||||
pub args: &'static str,
|
||||
pub args_vec: Vec<&'static str>,
|
||||
pub output: &'static str,
|
||||
pub input: Option<&'static str>,
|
||||
pub output_str: Option<&'static str>,
|
||||
|
@ -1715,7 +1716,15 @@ pub struct CheckOutputIntegrationTest {
|
|||
|
||||
impl CheckOutputIntegrationTest {
|
||||
pub fn run(&self) {
|
||||
let args = self.args.split_whitespace();
|
||||
let args = if self.args_vec.is_empty() {
|
||||
std::borrow::Cow::Owned(self.args.split_whitespace().collect::<Vec<_>>())
|
||||
} else {
|
||||
assert!(
|
||||
self.args.is_empty(),
|
||||
"Do not provide args when providing args_vec."
|
||||
);
|
||||
std::borrow::Cow::Borrowed(&self.args_vec)
|
||||
};
|
||||
let deno_exe = deno_exe_path();
|
||||
println!("deno_exe path {}", deno_exe.display());
|
||||
|
||||
|
@ -1730,7 +1739,7 @@ impl CheckOutputIntegrationTest {
|
|||
let mut command = deno_cmd();
|
||||
println!("deno_exe args {}", self.args);
|
||||
println!("deno_exe testdata path {:?}", &testdata_dir);
|
||||
command.args(args);
|
||||
command.args(args.iter());
|
||||
command.envs(self.envs.clone());
|
||||
command.current_dir(&testdata_dir);
|
||||
command.stdin(Stdio::piped());
|
||||
|
|
Loading…
Reference in a new issue