mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
feat(task): dependencies (#26467)
This commit adds support for "dependencies" in `deno task` subcommand: ```jsonc { "tasks": { "build": "deno run -RW build.ts", "generate": "deno run -RW generate.ts", "serve": { "command": "deno run -RN server.ts", "dependencies": ["build", "generate"] } } } ``` Executing `deno task serve` will first execute `build` and `generate` tasks (in parallel) and once both complete the `serve` task will be executed. Number of tasks run in parallel is equal to the no of cores on the machine, and respects `DENO_JOBS` env var if one is specified. Part of https://github.com/denoland/deno/issues/26462 --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com> Co-authored-by: Marvin Hagemeister <marvin@deno.com>
This commit is contained in:
parent
069bc15030
commit
661aa22c03
36 changed files with 509 additions and 17 deletions
|
@ -448,6 +448,13 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"required": true,
|
"required": true,
|
||||||
"description": "The task to execute"
|
"description": "The task to execute"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "Tasks that should be executed before this task"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -15,6 +16,10 @@ use deno_core::anyhow::anyhow;
|
||||||
use deno_core::anyhow::bail;
|
use deno_core::anyhow::bail;
|
||||||
use deno_core::anyhow::Context;
|
use deno_core::anyhow::Context;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::futures::future::LocalBoxFuture;
|
||||||
|
use deno_core::futures::stream::futures_unordered;
|
||||||
|
use deno_core::futures::FutureExt;
|
||||||
|
use deno_core::futures::StreamExt;
|
||||||
use deno_core::url::Url;
|
use deno_core::url::Url;
|
||||||
use deno_path_util::normalize_path;
|
use deno_path_util::normalize_path;
|
||||||
use deno_runtime::deno_node::NodeResolver;
|
use deno_runtime::deno_node::NodeResolver;
|
||||||
|
@ -68,6 +73,13 @@ pub async fn execute_script(
|
||||||
let node_resolver = factory.node_resolver().await?;
|
let node_resolver = factory.node_resolver().await?;
|
||||||
let env_vars = task_runner::real_env_vars();
|
let env_vars = task_runner::real_env_vars();
|
||||||
|
|
||||||
|
let no_of_concurrent_tasks = if let Ok(value) = std::env::var("DENO_JOBS") {
|
||||||
|
value.parse::<NonZeroUsize>().ok()
|
||||||
|
} else {
|
||||||
|
std::thread::available_parallelism().ok()
|
||||||
|
}
|
||||||
|
.unwrap_or_else(|| NonZeroUsize::new(2).unwrap());
|
||||||
|
|
||||||
let task_runner = TaskRunner {
|
let task_runner = TaskRunner {
|
||||||
tasks_config,
|
tasks_config,
|
||||||
task_flags: &task_flags,
|
task_flags: &task_flags,
|
||||||
|
@ -75,7 +87,9 @@ pub async fn execute_script(
|
||||||
node_resolver: node_resolver.as_ref(),
|
node_resolver: node_resolver.as_ref(),
|
||||||
env_vars,
|
env_vars,
|
||||||
cli_options,
|
cli_options,
|
||||||
|
concurrency: no_of_concurrent_tasks.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
task_runner.run_task(task_name).await
|
task_runner.run_task(task_name).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,30 +107,156 @@ struct TaskRunner<'a> {
|
||||||
node_resolver: &'a NodeResolver,
|
node_resolver: &'a NodeResolver,
|
||||||
env_vars: HashMap<String, String>,
|
env_vars: HashMap<String, String>,
|
||||||
cli_options: &'a CliOptions,
|
cli_options: &'a CliOptions,
|
||||||
|
concurrency: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TaskRunner<'a> {
|
impl<'a> TaskRunner<'a> {
|
||||||
async fn run_task(
|
pub async fn run_task(
|
||||||
&self,
|
&self,
|
||||||
task_name: &String,
|
task_name: &str,
|
||||||
) -> Result<i32, deno_core::anyhow::Error> {
|
) -> Result<i32, deno_core::anyhow::Error> {
|
||||||
let Some((dir_url, task_or_script)) = self.tasks_config.task(task_name)
|
match sort_tasks_topo(task_name, &self.tasks_config) {
|
||||||
else {
|
Ok(sorted) => self.run_tasks_in_parallel(sorted).await,
|
||||||
|
Err(err) => match err {
|
||||||
|
TaskError::NotFound(name) => {
|
||||||
if self.task_flags.is_run {
|
if self.task_flags.is_run {
|
||||||
return Err(anyhow!("Task not found: {}", task_name));
|
return Err(anyhow!("Task not found: {}", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
log::error!("Task not found: {}", task_name);
|
log::error!("Task not found: {}", name);
|
||||||
if log::log_enabled!(log::Level::Error) {
|
if log::log_enabled!(log::Level::Error) {
|
||||||
|
self.print_available_tasks()?;
|
||||||
|
}
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
TaskError::TaskDepCycle { path } => {
|
||||||
|
log::error!("Task cycle detected: {}", path.join(" -> "));
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_available_tasks(&self) -> Result<(), std::io::Error> {
|
||||||
print_available_tasks(
|
print_available_tasks(
|
||||||
&mut std::io::stderr(),
|
&mut std::io::stderr(),
|
||||||
&self.cli_options.start_dir,
|
&self.cli_options.start_dir,
|
||||||
&self.tasks_config,
|
&self.tasks_config,
|
||||||
)?;
|
)
|
||||||
}
|
}
|
||||||
return Ok(1);
|
|
||||||
|
async fn run_tasks_in_parallel(
|
||||||
|
&self,
|
||||||
|
task_names: Vec<String>,
|
||||||
|
) -> Result<i32, deno_core::anyhow::Error> {
|
||||||
|
struct PendingTasksContext {
|
||||||
|
completed: HashSet<String>,
|
||||||
|
running: HashSet<String>,
|
||||||
|
task_names: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PendingTasksContext {
|
||||||
|
fn has_remaining_tasks(&self) -> bool {
|
||||||
|
self.completed.len() < self.task_names.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_complete(&mut self, task_name: String) {
|
||||||
|
self.running.remove(&task_name);
|
||||||
|
self.completed.insert(task_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_next_task<'a>(
|
||||||
|
&mut self,
|
||||||
|
runner: &'a TaskRunner<'a>,
|
||||||
|
) -> Option<LocalBoxFuture<'a, Result<(i32, String), AnyError>>> {
|
||||||
|
for name in &self.task_names {
|
||||||
|
if self.completed.contains(name) || self.running.contains(name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let should_run = if let Ok((_, def)) = runner.get_task(name) {
|
||||||
|
match def {
|
||||||
|
TaskOrScript::Task(_, def) => def
|
||||||
|
.dependencies
|
||||||
|
.iter()
|
||||||
|
.all(|dep| self.completed.contains(dep)),
|
||||||
|
TaskOrScript::Script(_, _) => true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !should_run {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.running.insert(name.clone());
|
||||||
|
let name = name.clone();
|
||||||
|
return Some(
|
||||||
|
async move {
|
||||||
|
runner
|
||||||
|
.run_task_no_dependencies(&name)
|
||||||
|
.await
|
||||||
|
.map(|exit_code| (exit_code, name))
|
||||||
|
}
|
||||||
|
.boxed_local(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut context = PendingTasksContext {
|
||||||
|
completed: HashSet::with_capacity(task_names.len()),
|
||||||
|
running: HashSet::with_capacity(self.concurrency),
|
||||||
|
task_names,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut queue = futures_unordered::FuturesUnordered::new();
|
||||||
|
|
||||||
|
while context.has_remaining_tasks() {
|
||||||
|
while queue.len() < self.concurrency {
|
||||||
|
if let Some(task) = context.get_next_task(self) {
|
||||||
|
queue.push(task);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If queue is empty at this point, then there are no more tasks in the queue.
|
||||||
|
let Some(result) = queue.next().await else {
|
||||||
|
debug_assert_eq!(context.task_names.len(), 0);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (exit_code, name) = result?;
|
||||||
|
if exit_code > 0 {
|
||||||
|
return Ok(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.mark_complete(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_task(
|
||||||
|
&self,
|
||||||
|
task_name: &str,
|
||||||
|
) -> Result<(&Url, TaskOrScript), TaskError> {
|
||||||
|
let Some(result) = self.tasks_config.task(task_name) else {
|
||||||
|
return Err(TaskError::NotFound(task_name.to_string()));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_task_no_dependencies(
|
||||||
|
&self,
|
||||||
|
task_name: &String,
|
||||||
|
) -> Result<i32, deno_core::anyhow::Error> {
|
||||||
|
let (dir_url, task_or_script) = self.get_task(task_name.as_str()).unwrap();
|
||||||
|
|
||||||
match task_or_script {
|
match task_or_script {
|
||||||
TaskOrScript::Task(_tasks, definition) => {
|
TaskOrScript::Task(_tasks, definition) => {
|
||||||
self.run_deno_task(dir_url, task_name, definition).await
|
self.run_deno_task(dir_url, task_name, definition).await
|
||||||
|
@ -234,6 +374,59 @@ impl<'a> TaskRunner<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum TaskError {
|
||||||
|
NotFound(String),
|
||||||
|
TaskDepCycle { path: Vec<String> },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort_tasks_topo(
|
||||||
|
name: &str,
|
||||||
|
task_config: &WorkspaceTasksConfig,
|
||||||
|
) -> Result<Vec<String>, TaskError> {
|
||||||
|
fn sort_visit<'a>(
|
||||||
|
name: &'a str,
|
||||||
|
sorted: &mut Vec<String>,
|
||||||
|
mut path: Vec<&'a str>,
|
||||||
|
tasks_config: &'a WorkspaceTasksConfig,
|
||||||
|
) -> Result<(), TaskError> {
|
||||||
|
// Already sorted
|
||||||
|
if sorted.iter().any(|sorted_name| sorted_name == name) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graph has a cycle
|
||||||
|
if path.contains(&name) {
|
||||||
|
path.push(name);
|
||||||
|
return Err(TaskError::TaskDepCycle {
|
||||||
|
path: path.iter().map(|s| s.to_string()).collect(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(def) = tasks_config.task(name) else {
|
||||||
|
return Err(TaskError::NotFound(name.to_string()));
|
||||||
|
};
|
||||||
|
|
||||||
|
if let TaskOrScript::Task(_, actual_def) = def.1 {
|
||||||
|
for dep in &actual_def.dependencies {
|
||||||
|
let mut path = path.clone();
|
||||||
|
path.push(name);
|
||||||
|
sort_visit(dep, sorted, path, tasks_config)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sorted.push(name.to_string());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sorted: Vec<String> = vec![];
|
||||||
|
|
||||||
|
sort_visit(name, &mut sorted, Vec::new(), task_config)?;
|
||||||
|
|
||||||
|
Ok(sorted)
|
||||||
|
}
|
||||||
|
|
||||||
fn output_task(task_name: &str, script: &str) {
|
fn output_task(task_name: &str, script: &str) {
|
||||||
log::info!(
|
log::info!(
|
||||||
"{} {} {}",
|
"{} {} {}",
|
||||||
|
@ -339,6 +532,14 @@ fn print_available_tasks(
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
writeln!(writer, " {}", desc.task.command)?;
|
writeln!(writer, " {}", desc.task.command)?;
|
||||||
|
if !desc.task.dependencies.is_empty() {
|
||||||
|
writeln!(
|
||||||
|
writer,
|
||||||
|
" {} {}",
|
||||||
|
colors::gray("depends on:"),
|
||||||
|
colors::cyan(desc.task.dependencies.join(", "))
|
||||||
|
)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
61
tests/specs/task/dependencies/__test__.jsonc
Normal file
61
tests/specs/task/dependencies/__test__.jsonc
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"tests": {
|
||||||
|
"basic1": {
|
||||||
|
"cwd": "basic1",
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "task run",
|
||||||
|
"output": "./basic1.out"
|
||||||
|
},
|
||||||
|
"basic2": {
|
||||||
|
"cwd": "basic2",
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "task run",
|
||||||
|
"output": "./basic2.out"
|
||||||
|
},
|
||||||
|
"cross_package": {
|
||||||
|
"cwd": "cross_package/package1",
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "task run",
|
||||||
|
"output": "./cross_package.out",
|
||||||
|
"exitCode": 1
|
||||||
|
},
|
||||||
|
"diamond": {
|
||||||
|
"cwd": "diamond",
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "task a",
|
||||||
|
"output": "./diamond.out"
|
||||||
|
},
|
||||||
|
"diamond_list": {
|
||||||
|
"cwd": "diamond",
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "task",
|
||||||
|
"output": "./diamond_list.out"
|
||||||
|
},
|
||||||
|
"diamond_big": {
|
||||||
|
"cwd": "diamond_big",
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "task a",
|
||||||
|
"output": "./diamond_big.out"
|
||||||
|
},
|
||||||
|
"diamond_big_list": {
|
||||||
|
"cwd": "diamond_big",
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "task",
|
||||||
|
"output": "./diamond_big_list.out"
|
||||||
|
},
|
||||||
|
"cycle": {
|
||||||
|
"cwd": "cycle",
|
||||||
|
"tempDir": true,
|
||||||
|
"output": "./cycle.out",
|
||||||
|
"args": "task a",
|
||||||
|
"exitCode": 1
|
||||||
|
},
|
||||||
|
"cycle_2": {
|
||||||
|
"cwd": "cycle_2",
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "task a",
|
||||||
|
"output": "./cycle_2.out",
|
||||||
|
"exitCode": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
tests/specs/task/dependencies/basic1.out
Normal file
12
tests/specs/task/dependencies/basic1.out
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Task build1 deno run ../build1.js
|
||||||
|
Task build2 deno run ../build2.js
|
||||||
|
[UNORDERED_START]
|
||||||
|
Starting build1
|
||||||
|
build1 performing more work...
|
||||||
|
build1 finished
|
||||||
|
Starting build2
|
||||||
|
build2 performing more work...
|
||||||
|
build2 finished
|
||||||
|
[UNORDERED_END]
|
||||||
|
Task run deno run ../run.js
|
||||||
|
run finished
|
10
tests/specs/task/dependencies/basic1/deno.json
Normal file
10
tests/specs/task/dependencies/basic1/deno.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"build1": "deno run ../build1.js",
|
||||||
|
"build2": "deno run ../build2.js",
|
||||||
|
"run": {
|
||||||
|
"command": "deno run ../run.js",
|
||||||
|
"dependencies": ["build1", "build2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
tests/specs/task/dependencies/basic2.out
Normal file
10
tests/specs/task/dependencies/basic2.out
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Task build1 deno run ../build1.js
|
||||||
|
Starting build1
|
||||||
|
build1 performing more work...
|
||||||
|
build1 finished
|
||||||
|
Task build2 deno run ../build2.js
|
||||||
|
Starting build2
|
||||||
|
build2 performing more work...
|
||||||
|
build2 finished
|
||||||
|
Task run deno run ../run.js
|
||||||
|
run finished
|
13
tests/specs/task/dependencies/basic2/deno.json
Normal file
13
tests/specs/task/dependencies/basic2/deno.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"build1": "deno run ../build1.js",
|
||||||
|
"build2": {
|
||||||
|
"command": "deno run ../build2.js",
|
||||||
|
"dependencies": ["build1"]
|
||||||
|
},
|
||||||
|
"run": {
|
||||||
|
"command": "deno run ../run.js",
|
||||||
|
"dependencies": ["build2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
tests/specs/task/dependencies/build1.js
Normal file
9
tests/specs/task/dependencies/build1.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { randomTimeout } from "./util.js";
|
||||||
|
|
||||||
|
console.log("Starting build1");
|
||||||
|
|
||||||
|
await randomTimeout(500, 750);
|
||||||
|
console.log("build1 performing more work...");
|
||||||
|
await randomTimeout(500, 750);
|
||||||
|
|
||||||
|
console.log("build1 finished");
|
9
tests/specs/task/dependencies/build2.js
Normal file
9
tests/specs/task/dependencies/build2.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { randomTimeout } from "./util.js";
|
||||||
|
|
||||||
|
console.log("Starting build2");
|
||||||
|
|
||||||
|
await randomTimeout(250, 750);
|
||||||
|
console.log("build2 performing more work...");
|
||||||
|
await randomTimeout(250, 750);
|
||||||
|
|
||||||
|
console.log("build2 finished");
|
5
tests/specs/task/dependencies/cross_package.out
Normal file
5
tests/specs/task/dependencies/cross_package.out
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Task not found: ../package2:run
|
||||||
|
Available tasks:
|
||||||
|
- run
|
||||||
|
deno run.js
|
||||||
|
depends on: ../package2:run
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"run": {
|
||||||
|
"command": "deno run.js",
|
||||||
|
"dependencies": ["../package2:run"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"run": "deno run.js"
|
||||||
|
}
|
||||||
|
}
|
1
tests/specs/task/dependencies/cycle.out
Normal file
1
tests/specs/task/dependencies/cycle.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Task cycle detected: a -> a
|
1
tests/specs/task/dependencies/cycle/a.js
Normal file
1
tests/specs/task/dependencies/cycle/a.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running a");
|
8
tests/specs/task/dependencies/cycle/deno.jsonc
Normal file
8
tests/specs/task/dependencies/cycle/deno.jsonc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"a": {
|
||||||
|
"command": "deno run a.js",
|
||||||
|
"dependencies": ["a"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
tests/specs/task/dependencies/cycle_2.out
Normal file
1
tests/specs/task/dependencies/cycle_2.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Task cycle detected: a -> b -> a
|
1
tests/specs/task/dependencies/cycle_2/a.js
Normal file
1
tests/specs/task/dependencies/cycle_2/a.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running a");
|
1
tests/specs/task/dependencies/cycle_2/b.js
Normal file
1
tests/specs/task/dependencies/cycle_2/b.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running b");
|
12
tests/specs/task/dependencies/cycle_2/deno.jsonc
Normal file
12
tests/specs/task/dependencies/cycle_2/deno.jsonc
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"a": {
|
||||||
|
"command": "deno run a.js",
|
||||||
|
"dependencies": ["b"]
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"command": "deno run b.js",
|
||||||
|
"dependencies": ["a"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
tests/specs/task/dependencies/diamond.out
Normal file
10
tests/specs/task/dependencies/diamond.out
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Task d deno run d.js
|
||||||
|
Running d
|
||||||
|
[UNORDERED_START]
|
||||||
|
Task b deno run b.js
|
||||||
|
Running b
|
||||||
|
Task c deno run c.js
|
||||||
|
Running c
|
||||||
|
[UNORDERED_END]
|
||||||
|
Task a deno run a.js
|
||||||
|
Running a
|
1
tests/specs/task/dependencies/diamond/a.js
Normal file
1
tests/specs/task/dependencies/diamond/a.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running a");
|
1
tests/specs/task/dependencies/diamond/b.js
Normal file
1
tests/specs/task/dependencies/diamond/b.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running b");
|
1
tests/specs/task/dependencies/diamond/c.js
Normal file
1
tests/specs/task/dependencies/diamond/c.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running c");
|
1
tests/specs/task/dependencies/diamond/d.js
Normal file
1
tests/specs/task/dependencies/diamond/d.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running d");
|
22
tests/specs/task/dependencies/diamond/deno.jsonc
Normal file
22
tests/specs/task/dependencies/diamond/deno.jsonc
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
// a
|
||||||
|
// / \
|
||||||
|
// b c
|
||||||
|
// \ /
|
||||||
|
// d
|
||||||
|
"tasks": {
|
||||||
|
"a": {
|
||||||
|
"command": "deno run a.js",
|
||||||
|
"dependencies": ["b", "c"]
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"command": "deno run b.js",
|
||||||
|
"dependencies": ["d"]
|
||||||
|
},
|
||||||
|
"c": {
|
||||||
|
"command": "deno run c.js",
|
||||||
|
"dependencies": ["d"]
|
||||||
|
},
|
||||||
|
"d": "deno run d.js"
|
||||||
|
}
|
||||||
|
}
|
13
tests/specs/task/dependencies/diamond_big.out
Normal file
13
tests/specs/task/dependencies/diamond_big.out
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
Task e deno run e.js
|
||||||
|
Running e
|
||||||
|
[UNORDERED_START]
|
||||||
|
Task b deno run b.js
|
||||||
|
Running b
|
||||||
|
Task d deno run d.js
|
||||||
|
Running d
|
||||||
|
Task c deno run c.js
|
||||||
|
Running c
|
||||||
|
Finished b
|
||||||
|
[UNORDERED_END]
|
||||||
|
Task a deno run a.js
|
||||||
|
Running a
|
1
tests/specs/task/dependencies/diamond_big/a.js
Normal file
1
tests/specs/task/dependencies/diamond_big/a.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running a");
|
4
tests/specs/task/dependencies/diamond_big/b.js
Normal file
4
tests/specs/task/dependencies/diamond_big/b.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
console.log("Running b");
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log("Finished b");
|
||||||
|
}, 10);
|
1
tests/specs/task/dependencies/diamond_big/c.js
Normal file
1
tests/specs/task/dependencies/diamond_big/c.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running c");
|
1
tests/specs/task/dependencies/diamond_big/d.js
Normal file
1
tests/specs/task/dependencies/diamond_big/d.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running d");
|
28
tests/specs/task/dependencies/diamond_big/deno.jsonc
Normal file
28
tests/specs/task/dependencies/diamond_big/deno.jsonc
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
// a
|
||||||
|
// / \
|
||||||
|
// b c
|
||||||
|
// | |
|
||||||
|
// | d
|
||||||
|
// \ /
|
||||||
|
// e
|
||||||
|
"tasks": {
|
||||||
|
"a": {
|
||||||
|
"command": "deno run a.js",
|
||||||
|
"dependencies": ["b", "c"]
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"command": "deno run b.js",
|
||||||
|
"dependencies": ["e"]
|
||||||
|
},
|
||||||
|
"c": {
|
||||||
|
"command": "deno run c.js",
|
||||||
|
"dependencies": ["d"]
|
||||||
|
},
|
||||||
|
"d": {
|
||||||
|
"command": "deno run d.js",
|
||||||
|
"dependencies": ["e"]
|
||||||
|
},
|
||||||
|
"e": "deno run e.js"
|
||||||
|
}
|
||||||
|
}
|
1
tests/specs/task/dependencies/diamond_big/e.js
Normal file
1
tests/specs/task/dependencies/diamond_big/e.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Running e");
|
15
tests/specs/task/dependencies/diamond_big_list.out
Normal file
15
tests/specs/task/dependencies/diamond_big_list.out
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Available tasks:
|
||||||
|
- a
|
||||||
|
deno run a.js
|
||||||
|
depends on: b, c
|
||||||
|
- b
|
||||||
|
deno run b.js
|
||||||
|
depends on: e
|
||||||
|
- c
|
||||||
|
deno run c.js
|
||||||
|
depends on: d
|
||||||
|
- d
|
||||||
|
deno run d.js
|
||||||
|
depends on: e
|
||||||
|
- e
|
||||||
|
deno run e.js
|
12
tests/specs/task/dependencies/diamond_list.out
Normal file
12
tests/specs/task/dependencies/diamond_list.out
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Available tasks:
|
||||||
|
- a
|
||||||
|
deno run a.js
|
||||||
|
depends on: b, c
|
||||||
|
- b
|
||||||
|
deno run b.js
|
||||||
|
depends on: d
|
||||||
|
- c
|
||||||
|
deno run c.js
|
||||||
|
depends on: d
|
||||||
|
- d
|
||||||
|
deno run d.js
|
1
tests/specs/task/dependencies/run.js
Normal file
1
tests/specs/task/dependencies/run.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("run finished");
|
4
tests/specs/task/dependencies/util.js
Normal file
4
tests/specs/task/dependencies/util.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export async function randomTimeout(min, max) {
|
||||||
|
const timeout = Math.floor(Math.random() * (max - min + 1) + min);
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, timeout));
|
||||||
|
}
|
Loading…
Reference in a new issue