Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Allow local tasks to extend global tasks. #1085

Merged
merged 3 commits into from
Sep 29, 2023
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
4 changes: 2 additions & 2 deletions nextgen/config/src/inherited_tasks_config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::language_platform::{LanguageType, PlatformType};
use crate::project::{validate_deps, TaskConfig};
use crate::project_config::{validate_tasks, ProjectType};
use crate::project_config::ProjectType;
use crate::shapes::InputPath;
use crate::validate::check_yml_extension;
use moon_common::path::standardize_separators;
Expand Down Expand Up @@ -51,7 +51,7 @@ cacheable!(
#[setting(merge = merge::append_vec)]
pub implicit_inputs: Vec<InputPath>,

#[setting(nested, merge = merge::merge_btreemap, validate = validate_tasks)]
#[setting(nested, merge = merge::merge_btreemap)]
pub tasks: BTreeMap<Id, TaskConfig>,
}
);
Expand Down
21 changes: 1 addition & 20 deletions nextgen/config/src/project_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,6 @@ fn validate_channel<D, C>(value: &str, _data: &D, _ctx: &C) -> Result<(), Valida
Ok(())
}

pub fn validate_tasks<D, C>(
tasks: &BTreeMap<Id, PartialTaskConfig>,
_data: &D,
_ctx: &C,
) -> Result<(), ValidateError> {
for (id, config) in tasks {
if let Some(extends_from) = &config.extends {
if !tasks.contains_key(extends_from) {
return Err(ValidateError::new(format!(
"task {} is extending an unknown task {}",
id, extends_from
)));
}
}
}

Ok(())
}

derive_enum!(
#[derive(ConfigEnum, Copy, Default)]
pub enum ProjectType {
Expand Down Expand Up @@ -108,7 +89,7 @@ cacheable!(

pub tags: Vec<Id>,

#[setting(nested, validate = validate_tasks)]
#[setting(nested)]
pub tasks: BTreeMap<Id, TaskConfig>,

#[setting(nested)]
Expand Down
17 changes: 0 additions & 17 deletions nextgen/config/tests/project_config_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,23 +578,6 @@ tasks:
assert!(config.tasks.contains_key("base"));
assert!(config.tasks.contains_key("extender"));
}

#[test]
#[should_panic(expected = "task extender is extending an unknown task unknown")]
fn errors_if_extending_unknown_task() {
test_load_config(
CONFIG_PROJECT_FILENAME,
r"
tasks:
base:
command: 'base'
extender:
extends: 'unknown'
args: '--more'
",
|path| ProjectConfig::load_from(path, "."),
);
}
}

mod toolchain {
Expand Down
2 changes: 2 additions & 0 deletions nextgen/task-builder/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod tasks_builder;
mod tasks_builder_error;

pub use tasks_builder::*;
pub use tasks_builder_error::*;
78 changes: 61 additions & 17 deletions nextgen/task-builder/src/tasks_builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(dead_code)]

use crate::tasks_builder_error::TasksBuilderError;
use moon_args::split_args;
use moon_common::{color, Id};
use moon_config::{
Expand All @@ -23,21 +24,43 @@ struct ConfigChain<'proj> {

// This is a standalone function as recursive closures are not possible!
fn extract_config<'builder, 'proj>(
task_id: &'builder Id,
tasks_map: &'builder FxHashMap<&'proj Id, &'proj TaskConfig>,
configs: &'builder mut Vec<ConfigChain<'proj>>,
global: bool,
) {
if let Some(config) = tasks_map.get(task_id) {
task_id: &'builder Id,
tasks: &'builder FxHashMap<&'proj Id, &'proj TaskConfig>,
extendable_tasks: &'builder FxHashMap<&'proj Id, &'proj TaskConfig>,
inherited: bool,
extended: bool,
) -> miette::Result<()> {
let config = if extended {
extendable_tasks.get(task_id)
} else {
tasks.get(task_id)
};

if let Some(config) = config {
if let Some(extend_task_id) = &config.extends {
extract_config(extend_task_id, tasks_map, configs, global);
if !extendable_tasks.contains_key(extend_task_id) {
return Err(TasksBuilderError::UnknownExtendsSource {
source_id: task_id.to_owned(),
target_id: extend_task_id.to_owned(),
}
.into());
}

extract_config(
configs,
extend_task_id,
tasks,
extendable_tasks,
inherited,
true,
)?;
}

configs.push(ConfigChain {
config,
inherited: global,
});
configs.push(ConfigChain { config, inherited });
}

Ok(())
}

#[derive(Debug)]
Expand Down Expand Up @@ -230,7 +253,7 @@ impl<'proj> TasksBuilder<'proj> {
);

let mut task = Task::default();
let chain = self.get_config_inherit_chain(id);
let chain = self.get_config_inherit_chain(id)?;

// Determine command and args before building options and the task,
// as we need to figure out if we're running in local mode or not.
Expand Down Expand Up @@ -413,7 +436,7 @@ impl<'proj> TasksBuilder<'proj> {
};

let configs = self
.get_config_inherit_chain(id)
.get_config_inherit_chain(id)?
.iter()
.map(|link| &link.config.options)
.collect::<Vec<_>>();
Expand Down Expand Up @@ -589,13 +612,34 @@ impl<'proj> TasksBuilder<'proj> {
Ok((command, args))
}

fn get_config_inherit_chain(&self, id: &Id) -> Vec<ConfigChain> {
fn get_config_inherit_chain(&self, id: &Id) -> miette::Result<Vec<ConfigChain>> {
let mut configs = vec![];

extract_config(id, &self.global_tasks, &mut configs, true);
extract_config(id, &self.local_tasks, &mut configs, false);

configs
let mut extendable_tasks = FxHashMap::default();
extendable_tasks.extend(&self.global_tasks);
extendable_tasks.extend(&self.local_tasks);

// Inherit all global first
extract_config(
&mut configs,
id,
&self.global_tasks,
&extendable_tasks,
true,
false,
)?;

// Then all local
extract_config(
&mut configs,
id,
&self.local_tasks,
&extendable_tasks,
false,
false,
)?;

Ok(configs)
}

fn apply_filters_to_deps(&self, deps: Vec<Target>) -> Vec<Target> {
Expand Down
14 changes: 14 additions & 0 deletions nextgen/task-builder/src/tasks_builder_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use miette::Diagnostic;
use moon_common::{Id, Style, Stylize};
use thiserror::Error;

#[derive(Error, Debug, Diagnostic)]
pub enum TasksBuilderError {
#[diagnostic(code(task_builder::unknown_extends))]
#[error(
"Task {} is extending an unknown task {}.",
.source_id.style(Style::Id),
.target_id.style(Style::Id),
)]
UnknownExtendsSource { source_id: Id, target_id: Id },
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
tags: [extends]

tasks:
base:
extends: unknown
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ tasks:
extends: base
local: false

# Tests global inheritance

local-base:
inputs:
- 'local-base'
Expand All @@ -52,3 +50,9 @@ tasks:
- 'local-extender'
options:
mergeArgs: 'prepend'

local-extends-global:
extends: 'global-base'
inputs:
- 'local-extender'

27 changes: 27 additions & 0 deletions nextgen/task-builder/tests/tasks_builder_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,15 @@ mod tasks_builder {
mod extending {
use super::*;

#[tokio::test]
#[should_panic(expected = "Task base is extending an unknown task unknown.")]
async fn errors_for_unknown_extend_task() {
let sandbox = create_sandbox("builder");
let tasks = build_tasks(sandbox.path(), "extends-unknown/moon.yml").await;

tasks.get("base").unwrap();
}

#[tokio::test]
async fn handles_args() {
let sandbox = create_sandbox("builder");
Expand Down Expand Up @@ -1286,6 +1295,24 @@ mod tasks_builder {
assert_eq!(task.options.retry_count, 3);
}

#[tokio::test]
async fn can_extend_a_global_from_local() {
let sandbox = create_sandbox("builder");
let tasks = build_tasks(sandbox.path(), "extends/moon.yml").await;
let task = tasks.get("local-extends-global").unwrap();

assert_eq!(task.command, "global-base");
assert_eq!(
task.inputs,
vec![
InputPath::ProjectFile("global-base".into()),
InputPath::ProjectFile("local-extender".into()),
InputPath::WorkspaceGlob(".moon/*.yml".into()),
InputPath::WorkspaceFile("global/tasks/tag-extends.yml".into()),
]
);
}

#[tokio::test]
async fn can_create_extends_chains() {
let sandbox = create_sandbox("builder");
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

- Updated `moon dep-graph` to support a task in closest project, similar to `moon run`.

#### 🐞 Fixes

- Fixed an issue where local tasks could not extend global tasks using the `extends` setting.

## 1.14.2

#### 🐞 Fixes
Expand Down