Skip to content

Commit

Permalink
new: Support OS specific tasks. (#1626)
Browse files Browse the repository at this point in the history
* Add new option.

* Support arrays.

* Add tests.

* Update docs.

* Add blog post.

* Add more tests.

* Update changelog.
  • Loading branch information
milesj authored Aug 22, 2024
1 parent 6f38ef0 commit 102ca1b
Show file tree
Hide file tree
Showing 25 changed files with 499 additions and 33 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
#### 🚀 Updates

- Resolved the `actionPipelineV2` experiment and the new pipeline is now always enabled. The old
pipeline code has been deleted. If you run into any issues, please report an issue!
pipeline code has been deleted. If you run into any problems, please report an issue!
- Resolved the `strictProjectAliases` experiment and enabled its functionality going forward.
- Added an `os` task option, which allows a specific operating system to be targeted for the task.

#### ⚙️ Internal

Expand Down
71 changes: 69 additions & 2 deletions crates/cli/tests/run_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,11 +709,11 @@ mod hashing {
// Hashes change because `.moon/workspace.yml` is different from `walk_strategy`
assert_eq!(
hash_vcs,
"6327629ca97be90906a73654ab686bcc1ac032ae9d1f6baba2945a9ee9847a7a"
"500718ab88fb35afcdc851a737e6b816f663b9ce8fe8ff702de1c1270fb6bd55"
);
assert_eq!(
hash_glob,
"ef9068f77d3a2f21c38b389eafeab2fce98d50f2dc647cbb865633089139588a"
"5acf190dfc1d52c0e049d8fa29a218e955dc71f96abc0aea2d71318777590540"
);
}
}
Expand Down Expand Up @@ -2150,3 +2150,70 @@ mod task_scripts {
assert.success();
}
}

mod task_os {
use super::*;

#[test]
fn runs_linux() {
let sandbox = cases_sandbox();

let assert = sandbox.run_moon(|cmd| {
cmd.arg("run").arg("taskOs:linux");
});

let output = assert.output();

if cfg!(target_os = "linux") {
assert!(output.contains("runs-linux"));
assert!(!output.contains("no op"));
} else {
assert!(!output.contains("runs-linux"));
assert!(output.contains("no op"));
}

assert.success();
}

#[test]
fn runs_macos() {
let sandbox = cases_sandbox();

let assert = sandbox.run_moon(|cmd| {
cmd.arg("run").arg("taskOs:macos");
});

let output = assert.output();

if cfg!(target_os = "macos") {
assert!(output.contains("runs-macos"));
assert!(!output.contains("no op"));
} else {
assert!(!output.contains("runs-macos"));
assert!(output.contains("no op"));
}

assert.success();
}

#[test]
fn runs_windows() {
let sandbox = cases_sandbox();

let assert = sandbox.run_moon(|cmd| {
cmd.arg("run").arg("taskOs:windows");
});

let output = assert.output();

if cfg!(target_os = "windows") {
assert!(output.contains("runs-windows"));
assert!(!output.contains("no op"));
} else {
assert!(!output.contains("runs-windows"));
assert!(output.contains("no op"));
}

assert.success();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ source: crates/cli/tests/run_test.rs
expression: "[h1, h2, extract_hash_from_run(sandbox.path(), \"outputs:asDep\"),\n extract_hash_from_run(sandbox.path(), \"outputs:withDeps\")]"
---
[
"1422397d8d98982522d641bde303d85ea5aaa38a62d11aedc12874a0c412ce75",
"80a89bc94a93ad2432768508033325eea5175710f2b04040be6baa5ba8d399ae",
"151b7cde0d43b4a0e97d20c987dcc5eae9a7930a098c2f6c1f86fdebe4fcda2f",
"24150f8e62da23153e8cc9eda2b2023b9bfd8a951d58dd7a3d4aff8cb794162f",
"5a4c192dee8dd54ccdc34f1d9c88bb8e0a532fe906b6598f34162c37ce38cd6b",
"4c5dff75f53bb1803d29db8058bc3143b11b459ce19aca798c64f893de968f22",
"1ab53b2b88ead148ffee30993e8ac7a1ee0300eccef64aff4575af44c2ced2b5",
"ca63359e2f62b94917d71ce730bc68b9286cd03de0cad3ed6ddc27158587e935",
]
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ source: crates/cli/tests/run_test.rs
expression: "[extract_hash_from_run(sandbox.path(), \"outputs:asDep\"),\n extract_hash_from_run(sandbox.path(), \"outputs:withDeps\")]"
---
[
"1422397d8d98982522d641bde303d85ea5aaa38a62d11aedc12874a0c412ce75",
"80a89bc94a93ad2432768508033325eea5175710f2b04040be6baa5ba8d399ae",
"5a4c192dee8dd54ccdc34f1d9c88bb8e0a532fe906b6598f34162c37ce38cd6b",
"4c5dff75f53bb1803d29db8058bc3143b11b459ce19aca798c64f893de968f22",
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ expression: assert.output()
Tasks: 2 completed
Time: 100ms

a194402f2adf4f84c706b4f18d7442f994b724c5dba0cd68064892e56cf33f8b
4464729b6919152a9d8d8a513f52e91bc540c31fdb809c4910f02e26bbba0ea2
1 change: 1 addition & 0 deletions crates/config-schema/src/typescript_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ fn generate_project(out_dir: &Path) -> miette::Result<()> {
"TaskDependency".into(),
"TaskDependencyConfig".into(),
"TaskMergeStrategy".into(),
"TaskOperatingSystem".into(),
"TaskOptionAffectedFiles".into(),
"TaskOptionEnvFile".into(),
"TaskOptionsConfig".into(),
Expand Down
3 changes: 3 additions & 0 deletions crates/config/src/project/dep_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ cacheable!(
pub struct DependencyConfig {
/// ID of the depended on project.
pub id: Id,

/// Scope of the dependency relationship.
pub scope: DependencyScope,

/// Source of where the dependeny came from.
pub source: DependencySource,

/// Metadata about the source.
pub via: Option<String>,
}
Expand Down
5 changes: 5 additions & 0 deletions crates/config/src/project/overrides_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ cacheable!(
pub struct ProjectToolchainTypeScriptConfig {
/// Disables all TypeScript functionality for this project.
pub disabled: bool,

/// Appends sources of project reference to `include` in `tsconfig.json`.
pub include_project_reference_sources: Option<bool>,

/// Appends shared types to `include` in `tsconfig.json`.
pub include_shared_types: Option<bool>,

/// Updates and routes `outDir` in `tsconfig.json` to moon's cache.
pub route_out_dir_to_cache: Option<bool>,

/// Syncs all project dependencies as `references` in `tsconfig.json`.
pub sync_project_references: Option<bool>,

/// Syncs all project dependencies as `paths` in `tsconfig.json`.
pub sync_project_references_to_paths: Option<bool>,
}
Expand Down
30 changes: 29 additions & 1 deletion crates/config/src/project/task_options_config.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::portable_path::FilePath;
use crate::shapes::InputPath;
use crate::shapes::{InputPath, OneOrMany};
use moon_common::cacheable;
use schematic::schema::{StringType, UnionType};
use schematic::{derive_enum, Config, ConfigEnum, Schema, SchemaBuilder, Schematic, ValidateError};
use serde::{de, Deserialize, Deserializer, Serialize};
use serde_yaml::Value;
use std::env::consts;
use std::str::FromStr;

fn validate_interactive<C>(
Expand Down Expand Up @@ -139,6 +140,30 @@ derive_enum!(
}
);

derive_enum!(
/// The operating system in which to only run this task on.
#[derive(ConfigEnum, Copy)]
pub enum TaskOperatingSystem {
Linux,
#[serde(alias = "mac")]
Macos,
#[serde(alias = "win")]
Windows,
}
);

impl TaskOperatingSystem {
pub fn is_current_system(&self) -> bool {
let os = consts::OS;

match self {
Self::Linux => os == "linux" || os.ends_with("bsd"),
Self::Macos => os == "macos",
Self::Windows => os == "windows",
}
}
}

derive_enum!(
/// A list of available shells on Unix.
#[derive(ConfigEnum, Copy)]
Expand Down Expand Up @@ -222,6 +247,9 @@ cacheable!(
/// tasks using the same resource from running concurrently.
pub mutex: Option<String>,

/// The operating system in which to only run this task on.
pub os: Option<OneOrMany<TaskOperatingSystem>>,

/// The style in which task output will be printed to the console.
#[setting(env = "MOON_OUTPUT_STYLE")]
pub output_style: Option<TaskOutputStyle>,
Expand Down
2 changes: 2 additions & 0 deletions crates/config/src/shapes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod input_path;
mod output_path;
mod poly;

pub use input_path::*;
pub use output_path::*;
pub use poly::*;
34 changes: 34 additions & 0 deletions crates/config/src/shapes/poly.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use moon_common::cacheable;
use schematic::schema::UnionType;
use schematic::{Schema, SchemaBuilder, Schematic};

cacheable!(
#[derive(Clone, Debug, Eq, PartialEq)]
#[serde(untagged, expecting = "expected a single value, or a list of values")]
pub enum OneOrMany<T: Schematic> {
One(T),
Many(Vec<T>),
}
);

impl<T: Schematic + Clone> OneOrMany<T> {
pub fn to_list(&self) -> Vec<T> {
match self {
Self::One(item) => vec![item.to_owned()],
Self::Many(list) => list.to_owned(),
}
}
}

impl<T: Schematic> Schematic for OneOrMany<T> {
fn schema_name() -> Option<String> {
None
}

fn build_schema(mut schema: SchemaBuilder) -> Schema {
schema.union(UnionType::new_any([
schema.infer::<T>(),
schema.infer::<Vec<T>>(),
]))
}
}
54 changes: 53 additions & 1 deletion crates/config/tests/task_config_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod utils;

use moon_common::Id;
use moon_config::{
FilePath, InputPath, OutputPath, PlatformType, TaskArgs, TaskConfig, TaskDependency,
FilePath, InputPath, OneOrMany, OutputPath, PlatformType, TaskArgs, TaskConfig, TaskDependency,
TaskDependencyConfig, TaskMergeStrategy, TaskOutputStyle, TaskType,
};
use moon_target::Target;
Expand Down Expand Up @@ -597,6 +597,58 @@ options:
options:
interactive: true
persistent: true
",
|code| TaskConfig::parse(code),
);
}
}

mod os {
use super::*;
use moon_config::TaskOperatingSystem;

#[test]
fn can_set_one() {
let config = test_parse_config(
r"
options:
os: windows
",
|code| TaskConfig::parse(code),
);

assert_eq!(
config.options.os,
Some(OneOrMany::One(TaskOperatingSystem::Windows))
);
}

#[test]
fn can_set_many() {
let config = test_parse_config(
r"
options:
os: [linux, macos]
",
|code| TaskConfig::parse(code),
);

assert_eq!(
config.options.os,
Some(OneOrMany::Many(vec![
TaskOperatingSystem::Linux,
TaskOperatingSystem::Macos
]))
);
}

#[test]
#[should_panic(expected = "expected a single value, or a list of values")]
fn errors_for_unknown() {
test_parse_config(
r"
options:
os: unknown
",
|code| TaskConfig::parse(code),
);
Expand Down
24 changes: 22 additions & 2 deletions crates/task-builder/src/tasks_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ impl<'proj> TasksBuilder<'proj> {

// And lastly, before we return the task and options, we should finalize
// all necessary fields and populate/calculate with values.

if task.command.is_empty() {
task.command = "noop".into();
}
Expand All @@ -410,8 +411,6 @@ impl<'proj> TasksBuilder<'proj> {
task.deps = self.merge_vec(task.deps, global_deps, TaskMergeStrategy::Append, true);
}

task.id = id.to_owned();

if !global_inputs.is_empty() {
task.inputs =
self.merge_vec(task.inputs, global_inputs, TaskMergeStrategy::Append, true);
Expand Down Expand Up @@ -459,6 +458,23 @@ impl<'proj> TasksBuilder<'proj> {
}
}

if let Some(os_list) = &task.options.os {
let for_current_system = os_list.iter().any(|os| os.is_current_system());

if !for_current_system {
trace!(
target = target.as_str(),
os_list = ?os_list.iter().map(|os| os.to_string()).collect::<Vec<_>>(),
"Task has been marked for another operating system, disabling command/script",
);

task.command = "noop".into();
task.args.clear();
task.script = None;
}
}

task.id = id.to_owned();
task.target = target;

Ok(task)
Expand Down Expand Up @@ -541,6 +557,10 @@ impl<'proj> TasksBuilder<'proj> {
options.mutex = Some(mutex.clone());
}

if let Some(os) = &config.os {
options.os = Some(os.to_list());
}

if let Some(output_style) = &config.output_style {
options.output_style = Some(*output_style);
}
Expand Down
13 changes: 13 additions & 0 deletions crates/task-builder/tests/__fixtures__/builder/options-os/moon.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
tasks:
os-windows:
command: 'execute --win'
options:
os: windows
os-linux:
script: 'execute --nix'
options:
os: linux
os-macos:
command: 'execute --mac'
options:
os: macos
Loading

0 comments on commit 102ca1b

Please sign in to comment.