diff --git a/.github/workflows/moon.yml b/.github/workflows/moon.yml index 2f00e4fabda..267ae6793e6 100644 --- a/.github/workflows/moon.yml +++ b/.github/workflows/moon.yml @@ -25,15 +25,6 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: actions/cache@v3 - name: Cache cargo - with: - path: | - ~/.cargo/bin - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-cargo- - uses: actions/cache@v3 name: Cache node modules with: @@ -54,6 +45,7 @@ jobs: with: toolchain: 1.64.0 profile: minimal + - uses: Swatinem/rust-cache@v2 - uses: moonrepo/tool-version-action@v1 with: node: ${{ matrix.node-version }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee40ab4aae1..4203943c783 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,16 +72,6 @@ jobs: runs-on: ${{ matrix.host }} steps: - uses: actions/checkout@v3 - - uses: actions/cache@v3 - name: Cache cargo - if: ${{ !matrix.docker-target }} - with: - path: | - ~/.cargo/bin - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-cargo- - uses: actions-rs/toolchain@v1 if: ${{ !matrix.docker-target }} with: @@ -89,6 +79,7 @@ jobs: profile: minimal target: ${{ matrix.target }} toolchain: stable + - uses: Swatinem/rust-cache@v2 - name: Generate lockfile uses: actions-rs/cargo@v1 if: ${{ !matrix.docker-target }} @@ -167,15 +158,7 @@ jobs: cache: yarn check-latest: true node-version: 16 - - uses: actions/cache@v3 - name: Cache cargo - with: - path: | - ~/.cargo/bin - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-cargo- + - uses: Swatinem/rust-cache@v2 - name: Setup toolchain if: ${{ matrix.setup }} run: ${{ matrix.setup }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 58e95b5c70d..cbe09dc85df 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,21 +22,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/cache@v3 - name: Cache cargo - with: - path: | - ~/.cargo/bin - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-cargo- - uses: actions-rs/toolchain@v1 name: Setup toolchain with: toolchain: 1.64.0 profile: minimal components: rustfmt + - uses: Swatinem/rust-cache@v2 - uses: actions-rs/cargo@v1 name: Check formatting with: @@ -51,21 +43,13 @@ jobs: fail-fast: false steps: - uses: actions/checkout@v3 - - uses: actions/cache@v3 - name: Cache cargo - with: - path: | - ~/.cargo/bin - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-cargo- - uses: actions-rs/toolchain@v1 name: Setup toolchain with: toolchain: 1.64.0 profile: minimal components: clippy + - uses: Swatinem/rust-cache@v2 - uses: davidB/rust-cargo-make@v1 - name: Run linter run: cargo make lint @@ -78,21 +62,13 @@ jobs: fail-fast: false steps: - uses: actions/checkout@v3 - - uses: actions/cache@v3 - name: Cache cargo - with: - path: | - ~/.cargo/bin - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-cargo- - uses: actions-rs/toolchain@v1 name: Setup toolchain with: toolchain: 1.64.0 profile: minimal components: llvm-tools-preview + - uses: Swatinem/rust-cache@v2 - uses: davidB/rust-cargo-make@v1 - uses: actions-rs/cargo@v1 if: ${{ runner.os != 'Windows' && env.WITH_COVERAGE == 'true' }} diff --git a/Cargo.lock b/Cargo.lock index 3b9b60b56b0..73bac859820 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "async-channel" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + [[package]] name = "async-recursion" version = "1.0.0" @@ -294,6 +305,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + [[package]] name = "cached" version = "0.39.0" @@ -463,6 +480,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" +[[package]] +name = "concurrent-queue" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +dependencies = [ + "cache-padded", +] + [[package]] name = "console" version = "0.15.2" @@ -678,6 +704,25 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "deadpool" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" +dependencies = [ + "async-trait", + "deadpool-runtime", + "num_cpus", + "retain_mut", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaa37046cc0f6c3cc6090fbdbf73ef0b8ef4cfcc37f6befc0020f63e8cf121e1" + [[package]] name = "deunicode" version = "0.4.3" @@ -804,6 +849,12 @@ dependencies = [ "libc", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fastrand" version = "1.7.0" @@ -953,6 +1004,21 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.24" @@ -976,6 +1042,12 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + [[package]] name = "futures-util" version = "0.3.24" @@ -1004,6 +1076,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.7" @@ -1129,6 +1212,27 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +[[package]] +name = "http-types" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" +dependencies = [ + "anyhow", + "async-channel", + "base64", + "futures-lite", + "http", + "infer", + "pin-project-lite", + "rand 0.7.3", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "url", +] + [[package]] name = "httparse" version = "1.7.1" @@ -1284,6 +1388,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "infer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" + [[package]] name = "insta" version = "1.21.0" @@ -1501,7 +1611,7 @@ dependencies = [ "httparse", "lazy_static", "log", - "rand", + "rand 0.8.5", "regex", "serde_json", "serde_urlencoded", @@ -1575,6 +1685,7 @@ dependencies = [ "moon_lang", "moon_lang_node", "moon_logger", + "moon_notifier", "moon_platform_node", "moon_platform_system", "moon_project", @@ -1594,6 +1705,8 @@ dependencies = [ "strum", "tera", "tokio", + "wiremock", + "yaml-rust-davvid", ] [[package]] @@ -2139,6 +2252,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + [[package]] name = "parking_lot" version = "0.12.1" @@ -2178,7 +2297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.3", "subtle", ] @@ -2286,7 +2405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -2469,6 +2588,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -2476,8 +2608,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -2487,7 +2629,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -2496,7 +2647,16 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom", + "getrandom 0.2.7", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -2514,7 +2674,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom", + "getrandom 0.2.7", "redox_syscall", "thiserror", ] @@ -2591,6 +2751,12 @@ dependencies = [ "winreg", ] +[[package]] +name = "retain_mut" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" + [[package]] name = "rustix" version = "0.35.10" @@ -2738,6 +2904,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_qs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2968,7 +3145,7 @@ dependencies = [ "percent-encoding", "pest", "pest_derive", - "rand", + "rand 0.8.5", "regex", "serde", "serde_json", @@ -3205,7 +3382,7 @@ dependencies = [ "indexmap", "pin-project", "pin-project-lite", - "rand", + "rand 0.8.5", "slab", "tokio", "tokio-util", @@ -3436,6 +3613,7 @@ dependencies = [ "idna", "matches", "percent-encoding", + "serde", ] [[package]] @@ -3444,7 +3622,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" dependencies = [ - "getrandom", + "getrandom 0.2.7", ] [[package]] @@ -3516,6 +3694,12 @@ dependencies = [ "libc", ] +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "walkdir" version = "2.3.2" @@ -3537,6 +3721,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -3726,6 +3916,28 @@ dependencies = [ "winapi", ] +[[package]] +name = "wiremock" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "249dc68542861d17eae4b4e5e8fb381c2f9e8f255a84f6771d5fdf8b6c03ce3c" +dependencies = [ + "assert-json-diff", + "async-trait", + "base64", + "deadpool", + "futures", + "futures-timer", + "http-types", + "hyper", + "log", + "once_cell", + "regex", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "xattr" version = "0.2.3" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index e41ddf846d1..88cfe93e56c 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -48,11 +48,14 @@ serde_yaml = "0.9.13" strum = { version = "0.24.1", features = ["derive"] } tera = { version = "1.17.1", features = ["preserve_order"] } tokio = { workspace = true } +yaml-rust = { version = "0.4.5", package = "yaml-rust-davvid" } [dev-dependencies] moon_cache = { path = "../cache" } +moon_notifier = { path = "../notifier" } assert_cmd = "2.0.4" insta = "1.21.0" predicates = "2.1.1" pretty_assertions = "1.3.0" serial_test = "0.9.0" +wiremock = "0.5.15" diff --git a/crates/cli/src/commands/check.rs b/crates/cli/src/commands/check.rs index 297396cfa0c..8978a6d77a0 100644 --- a/crates/cli/src/commands/check.rs +++ b/crates/cli/src/commands/check.rs @@ -3,7 +3,6 @@ use crate::helpers::load_workspace; use moon_project::Project; use std::env; -#[derive(Default)] pub struct CheckOptions { pub report: bool, } diff --git a/crates/cli/src/commands/docker/prune.rs b/crates/cli/src/commands/docker/prune.rs index 976728990a1..c523980c2ca 100644 --- a/crates/cli/src/commands/docker/prune.rs +++ b/crates/cli/src/commands/docker/prune.rs @@ -74,7 +74,7 @@ pub async fn prune() -> Result<(), Box> { let project = workspace.projects.load(project_id)?; // We use a match here to exhaustively check all languages - match project.config.language { + match project.language { ProjectLanguage::JavaScript | ProjectLanguage::TypeScript => { is_using_node = true; } diff --git a/crates/cli/src/commands/migrate/from_package_json.rs b/crates/cli/src/commands/migrate/from_package_json.rs index a8b7ee3ebd7..87452461931 100644 --- a/crates/cli/src/commands/migrate/from_package_json.rs +++ b/crates/cli/src/commands/migrate/from_package_json.rs @@ -1,5 +1,8 @@ use crate::helpers::load_workspace; -use moon_config::{DependencyConfig, DependencyScope, ProjectDependsOn}; +use moon_config::{ + DependencyConfig, DependencyScope, PlatformType, ProjectConfig, ProjectDependsOn, + TaskCommandArgs, +}; use moon_constants::CONFIG_PROJECT_FILENAME; use moon_error::MoonError; use moon_lang_node::package::{DepsSet, PackageJson}; @@ -7,6 +10,120 @@ use moon_platform_node::create_tasks_from_scripts; use moon_utils::fs; use serde_yaml::to_string; use std::collections::HashMap; +use yaml_rust::yaml::{Hash, Yaml}; +use yaml_rust::YamlEmitter; + +// Dont use serde since it writes *everything*, which is a ton of nulled fields! +pub fn convert_to_yaml(config: &ProjectConfig) -> Result> { + let mut root = Hash::new(); + + root.insert( + Yaml::String("language".to_owned()), + Yaml::String(to_string(&config.language)?.trim().to_owned()), + ); + + if !config.depends_on.is_empty() { + let mut depends_on = vec![]; + + for dep in &config.depends_on { + match dep { + ProjectDependsOn::String(value) => { + depends_on.push(Yaml::String(value.to_owned())); + } + ProjectDependsOn::Object(value) => { + let mut dep_value = Hash::new(); + + dep_value.insert( + Yaml::String("id".to_owned()), + Yaml::String(value.id.to_owned()), + ); + + dep_value.insert( + Yaml::String("scope".to_owned()), + Yaml::String(to_string(&value.scope)?), + ); + + if let Some(via) = &value.via { + dep_value + .insert(Yaml::String("via".to_owned()), Yaml::String(via.to_owned())); + } + } + } + } + + root.insert( + Yaml::String("dependsOn".to_owned()), + Yaml::Array(depends_on), + ); + } + + // We're only declaring fields used in `create_tasks_from_scripts`, not everything + if !config.tasks.is_empty() { + let mut tasks = Hash::new(); + + let convert_string_list = |list: &Vec| { + Yaml::Array(list.iter().map(|v| Yaml::String(v.to_owned())).collect()) + }; + + let convert_command_args = |value: &TaskCommandArgs| match value { + TaskCommandArgs::String(v) => Yaml::String(v.to_owned()), + TaskCommandArgs::Sequence(vs) => convert_string_list(vs), + }; + + for (id, task_config) in &config.tasks { + let mut task = Hash::new(); + + if let Some(command) = &task_config.command { + task.insert( + Yaml::String("command".to_owned()), + convert_command_args(command), + ); + } + + if let Some(args) = &task_config.args { + task.insert(Yaml::String("args".to_owned()), convert_command_args(args)); + } + + if let Some(outputs) = &task_config.outputs { + task.insert( + Yaml::String("outputs".to_owned()), + convert_string_list(outputs), + ); + } + if let Some(env) = &task_config.env { + let mut env_vars = Hash::new(); + + for (key, value) in env { + env_vars.insert(Yaml::String(key.to_owned()), Yaml::String(value.to_owned())); + } + + task.insert(Yaml::String("env".to_owned()), Yaml::Hash(env_vars)); + } + + if !matches!(task_config.platform, PlatformType::Node) { + task.insert( + Yaml::String("platform".to_owned()), + Yaml::String(to_string(&task_config.platform)?.trim().to_owned()), + ); + } + + if task_config.local { + task.insert(Yaml::String("local".to_owned()), Yaml::Boolean(true)); + } + + tasks.insert(Yaml::String(id.to_owned()), Yaml::Hash(task)); + } + + root.insert(Yaml::String("tasks".to_owned()), Yaml::Hash(tasks)); + } + + let mut out = String::new(); + let mut emitter = YamlEmitter::new(&mut out); + + emitter.dump(&Yaml::Hash(root))?; + + Ok(out) +} pub async fn from_package_json(project_id: &str) -> Result<(), Box> { let workspace = load_workspace().await?; @@ -72,7 +189,7 @@ pub async fn from_package_json(project_id: &str) -> Result<(), Box Result<(), Box { -// let req_str = String::from_utf8_lossy(&buf); -// println!("{}", req_str); -// } -// Err(e) => println!("Unable to read stream: {}", e), -// } -// } - -// fn handle_write(mut stream: TcpStream) { -// let response = b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\nHello world\r\n"; -// match stream.write(response) { -// Ok(_) => println!("Response sent"), -// Err(e) => println!("Failed sending response: {}", e), -// } -// } - -// fn handle_client(stream: TcpStream) { -// // handle_read(&stream); -// // handle_write(stream); -// println!("CLIENT"); -// } - -// fn create_localhost_server() -> TcpListener { -// let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - -// println!("CONNECTED {}", listener.local_addr().unwrap()); - -// tokio::spawn(async { -// for stream in listener.incoming() { -// match stream { -// Ok(stream) => { -// thread::spawn(|| handle_client(stream)); -// } -// Err(e) => { -// println!("Unable to connect: {}", e); -// } -// } -// } -// }); - -// listener -// } - -// #[tokio::test] -// async fn sends_webhooks_to_server() { -// // let server = create_localhost_server(); -// let server = TcpListener::bind("127.0.0.1:0").await.unwrap(); -// let port = server.local_addr().unwrap().port(); - -// dbg!(port); - -// let client = task::spawn(async move { -// let fixture = create_sandbox_with_git("cases"); - -// append_workspace_config( -// fixture.path(), -// &format!("notifier:\n webhookUrl: 'http://127.0.0.1:{}'", port), -// ); - -// let assert = create_moon_command(fixture.path()) -// .arg("run") -// .arg("node:cjs") -// .assert(); - -// moon_utils::test::debug_sandbox(&fixture, &assert); - -// assert.failure(); -// }); - -// server.set_ttl(15).unwrap(); - -// match (server.accept().await.unwrap(), client.await.unwrap()) { -// (conn, _) => { -// dbg!(&conn); -// } -// } - -// drop(server); - -// panic!("hoops"); -// } - -// #[tokio::test] -// async fn sends_webhooks_to_server() { -// // let server = create_localhost_server(); -// let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); -// let port = listener.local_addr().unwrap().port(); - -// dbg!(port); - -// task::spawn(async move { -// let fixture = create_sandbox_with_git("cases"); - -// append_workspace_config( -// fixture.path(), -// &format!("notifier:\n webhookUrl: 'http://127.0.0.1:{}'", port), -// ); - -// let assert = create_moon_command(fixture.path()) -// .arg("run") -// .arg("node:cjs") -// .assert(); - -// moon_utils::test::debug_sandbox(&fixture, &assert); - -// assert.failure(); -// }) -// .await -// .unwrap(); - -// let (server, addr) = listener.accept().await.unwrap(); - -// dbg!(server); - -// drop(listener); - -// panic!("hoops"); -// } +mod utils; + +use moon_notifier::WebhookPayload; +use moon_utils::test::{create_moon_command, create_sandbox_with_git}; +use utils::append_workspace_config; +use wiremock::matchers::{method, path}; +use wiremock::{Mock, MockServer, ResponseTemplate}; + +#[tokio::test] +async fn sends_webhooks() { + let server = MockServer::start().await; + let fixture = create_sandbox_with_git("cases"); + + Mock::given(method("POST")) + .and(path("/webhook")) + .respond_with(ResponseTemplate::new(200)) + .expect(19) + .mount(&server) + .await; + + append_workspace_config( + fixture.path(), + &format!("notifier:\n webhookUrl: '{}/webhook'", server.uri()), + ); + + create_moon_command(fixture.path()) + .arg("run") + .arg("node:cjs") + .assert(); +} + +#[tokio::test] +async fn sends_webhooks_for_cache_events() { + let server = MockServer::start().await; + let fixture = create_sandbox_with_git("cases"); + + Mock::given(method("POST")) + .and(path("/webhook")) + .respond_with(ResponseTemplate::new(200)) + .expect(37) + .mount(&server) + .await; + + append_workspace_config( + fixture.path(), + &format!("notifier:\n webhookUrl: '{}/webhook'", server.uri()), + ); + + create_moon_command(fixture.path()) + .arg("run") + .arg("node:cjs") + .assert(); + + // Run again to hit the cache + create_moon_command(fixture.path()) + .arg("run") + .arg("node:cjs") + .assert(); +} + +#[tokio::test] +async fn doesnt_send_webhooks_if_first_fails() { + let server = MockServer::start().await; + let fixture = create_sandbox_with_git("cases"); + + Mock::given(method("POST")) + .and(path("/webhook")) + .respond_with(ResponseTemplate::new(500)) + .expect(1) + .mount(&server) + .await; + + append_workspace_config( + fixture.path(), + &format!("notifier:\n webhookUrl: '{}/webhook'", server.uri()), + ); + + create_moon_command(fixture.path()) + .arg("run") + .arg("node:cjs") + .assert(); +} + +#[tokio::test] +async fn all_webhooks_have_same_uuid() { + let server = MockServer::start().await; + let fixture = create_sandbox_with_git("cases"); + + Mock::given(method("POST")) + .and(path("/webhook")) + .respond_with(ResponseTemplate::new(200)) + .mount(&server) + .await; + + append_workspace_config( + fixture.path(), + &format!("notifier:\n webhookUrl: '{}/webhook'", server.uri()), + ); + + create_moon_command(fixture.path()) + .arg("run") + .arg("node:cjs") + .assert(); + + let received_requests = server.received_requests().await.unwrap(); + let mut uuid = None; + + for request in received_requests { + let payload: WebhookPayload = + serde_json::from_str(&String::from_utf8(request.body).unwrap()).unwrap(); + + if uuid.is_none() { + uuid = Some(payload.uuid); + } else { + assert_eq!(&payload.uuid, uuid.as_ref().unwrap()); + } + } +} diff --git a/crates/cli/tests/snapshots/migrate_test__from_package_json__converts_scripts-2.snap b/crates/cli/tests/snapshots/migrate_test__from_package_json__converts_scripts-2.snap index 26cf5b3ddbd..96255552641 100644 --- a/crates/cli/tests/snapshots/migrate_test__from_package_json__converts_scripts-2.snap +++ b/crates/cli/tests/snapshots/migrate_test__from_package_json__converts_scripts-2.snap @@ -3,19 +3,17 @@ source: crates/cli/tests/migrate_test.rs assertion_line: 21 expression: "fs::read_to_string(fixture.path().join(\"package-json/common/moon.yml\")).unwrap()" --- +--- language: javascript tasks: lint: command: - - eslint - - . - platform: node + - eslint + - "." lint-fix: command: - - moon - - run - - common:lint - - -- - - --fix - platform: node - + - moon + - run + - "common:lint" + - "--" + - "--fix" diff --git a/crates/cli/tests/snapshots/migrate_test__from_package_json__links_depends_on-2.snap b/crates/cli/tests/snapshots/migrate_test__from_package_json__links_depends_on-2.snap index ee99a23af0e..89e91cb67f1 100644 --- a/crates/cli/tests/snapshots/migrate_test__from_package_json__links_depends_on-2.snap +++ b/crates/cli/tests/snapshots/migrate_test__from_package_json__links_depends_on-2.snap @@ -3,20 +3,18 @@ source: crates/cli/tests/migrate_test.rs assertion_line: 40 expression: "fs::read_to_string(fixture.path().join(\"package-json/deps/moon.yml\")).unwrap()" --- -dependsOn: -- common +--- language: javascript +dependsOn: + - common tasks: build: command: - - babel - - ./src - - --out - - ./lib + - babel + - "./src" + - "--out" + - "./lib" outputs: - - lib - platform: node + - lib test: command: jest - platform: node - diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 916bc7f530d..0adec0c86b4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -7,7 +7,9 @@ mod types; mod validators; mod workspace; -pub use errors::{format_error_line, format_figment_errors, ConfigError}; +pub use errors::{ + format_error_line, format_figment_errors, map_validation_errors_to_figment_errors, ConfigError, +}; pub use project::*; pub use template::*; pub use types::*; diff --git a/crates/config/src/project/local_config.rs b/crates/config/src/project/local_config.rs index 27c78d51457..edcdb249dfe 100644 --- a/crates/config/src/project/local_config.rs +++ b/crates/config/src/project/local_config.rs @@ -7,9 +7,7 @@ use crate::project::dep::DependencyConfig; use crate::project::task::TaskConfig; use crate::project::workspace::ProjectWorkspaceConfig; use crate::types::{FileGroups, ProjectID}; -use crate::validators::{ - skip_if_btree_empty, skip_if_default, skip_if_hash_empty, skip_if_vec_empty, validate_id, -}; +use crate::validators::validate_id; use figment::{ providers::{Format, Serialized, YamlExtended}, Figment, @@ -138,30 +136,23 @@ pub enum ProjectDependsOn { #[schemars(default)] #[serde(default, rename_all = "camelCase")] pub struct ProjectConfig { - #[serde(skip_serializing_if = "skip_if_vec_empty")] pub depends_on: Vec, - #[serde(skip_serializing_if = "skip_if_hash_empty")] #[validate(custom = "validate_file_groups")] pub file_groups: FileGroups, - #[serde(skip_serializing_if = "skip_if_default")] pub language: ProjectLanguage, - #[serde(skip_serializing_if = "Option::is_none")] #[validate] pub project: Option, - #[serde(skip_serializing_if = "skip_if_btree_empty")] #[validate(custom = "validate_tasks")] #[validate] pub tasks: BTreeMap, - #[serde(skip_serializing_if = "skip_if_default")] #[serde(rename = "type")] pub type_of: ProjectType, - #[serde(skip_serializing_if = "skip_if_default")] #[validate] pub workspace: ProjectWorkspaceConfig, diff --git a/crates/config/src/project/task.rs b/crates/config/src/project/task.rs index 9785d99aee8..1c72172c41e 100644 --- a/crates/config/src/project/task.rs +++ b/crates/config/src/project/task.rs @@ -1,9 +1,7 @@ use crate::project::local_config::{ProjectConfig, ProjectLanguage}; use crate::project::task_options::TaskOptionsConfig; use crate::types::{FilePath, InputValue, TargetID}; -use crate::validators::{ - skip_if_default, validate_child_or_root_path, validate_id, validate_target, -}; +use crate::validators::{validate_child_or_root_path, validate_id, validate_target}; use moon_utils::process::split_args; use moon_utils::process::ArgsParseError; use moon_utils::regex::{ENV_VAR, NODE_COMMAND, UNIX_SYSTEM_COMMAND, WINDOWS_SYSTEM_COMMAND}; @@ -76,35 +74,26 @@ pub enum TaskCommandArgs { #[schemars(default)] #[serde(default)] pub struct TaskConfig { - #[serde(skip_serializing_if = "Option::is_none")] pub command: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub args: Option, - #[serde(skip_serializing_if = "Option::is_none")] #[validate(custom = "validate_deps")] pub deps: Option>, - #[serde(skip_serializing_if = "Option::is_none")] pub env: Option>, - #[serde(skip_serializing_if = "Option::is_none")] #[validate(custom = "validate_inputs")] pub inputs: Option>, - #[serde(skip_serializing_if = "skip_if_default")] pub local: bool, - #[serde(skip_serializing_if = "Option::is_none")] #[validate(custom = "validate_outputs")] pub outputs: Option>, - #[serde(skip_serializing_if = "skip_if_default")] #[validate] pub options: TaskOptionsConfig, - #[serde(skip_serializing_if = "skip_if_default")] pub platform: PlatformType, } diff --git a/crates/config/src/project/task_options.rs b/crates/config/src/project/task_options.rs index d657c25684f..a4a5b0e518e 100644 --- a/crates/config/src/project/task_options.rs +++ b/crates/config/src/project/task_options.rs @@ -54,40 +54,29 @@ pub enum TaskOutputStyle { #[schemars(default)] #[serde(default, rename_all = "camelCase")] pub struct TaskOptionsConfig { - #[serde(skip_serializing_if = "Option::is_none")] pub cache: Option, - #[serde(skip_serializing_if = "Option::is_none")] #[validate(custom = "validate_env_file")] pub env_file: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub merge_args: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub merge_deps: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub merge_env: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub merge_inputs: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub merge_outputs: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub output_style: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub retry_count: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub run_deps_in_parallel: Option, - #[serde(rename = "runInCI", skip_serializing_if = "Option::is_none")] + #[serde(rename = "runInCI")] pub run_in_ci: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub run_from_workspace_root: Option, } diff --git a/crates/config/src/project/workspace.rs b/crates/config/src/project/workspace.rs index e6a9bc2d445..deb5e37117b 100644 --- a/crates/config/src/project/workspace.rs +++ b/crates/config/src/project/workspace.rs @@ -1,7 +1,7 @@ // These configs are project-level settings that override those from the workspace! use crate::types::TaskID; -use crate::validators::{skip_if_default, validate_semver_version}; +use crate::validators::validate_semver_version; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -15,7 +15,6 @@ fn validate_node_version(value: &str) -> Result<(), ValidationError> { #[schemars(default)] #[serde(default)] pub struct ProjectWorkspaceNodeConfig { - #[serde(skip_serializing_if = "Option::is_none")] #[validate(custom = "validate_node_version")] pub version: Option, } @@ -24,13 +23,10 @@ pub struct ProjectWorkspaceNodeConfig { #[schemars(default)] #[serde(default)] pub struct ProjectWorkspaceInheritedTasksConfig { - #[serde(skip_serializing_if = "Option::is_none")] pub exclude: Option>, - #[serde(skip_serializing_if = "Option::is_none")] pub include: Option>, - #[serde(skip_serializing_if = "Option::is_none")] pub rename: Option>, } @@ -38,11 +34,9 @@ pub struct ProjectWorkspaceInheritedTasksConfig { #[schemars(default)] #[serde(default, rename_all = "camelCase")] pub struct ProjectWorkspaceConfig { - #[serde(skip_serializing_if = "skip_if_default")] #[validate] pub inherited_tasks: ProjectWorkspaceInheritedTasksConfig, - #[serde(skip_serializing_if = "Option::is_none")] #[validate] pub node: Option, diff --git a/crates/config/src/validators.rs b/crates/config/src/validators.rs index ff712957360..6443ac71119 100644 --- a/crates/config/src/validators.rs +++ b/crates/config/src/validators.rs @@ -1,26 +1,9 @@ use crate::errors::create_validation_error; use moon_utils::regex::{matches_id, matches_target}; use moon_utils::semver::Version; -use std::collections::{BTreeMap, HashMap}; use std::path::Path; use validator::{validate_url as validate_base_url, ValidationError}; -pub fn skip_if_default(value: &T) -> bool { - value == &T::default() -} - -pub fn skip_if_btree_empty(map: &BTreeMap) -> bool { - map.is_empty() -} - -pub fn skip_if_hash_empty(map: &HashMap) -> bool { - map.is_empty() -} - -pub fn skip_if_vec_empty(list: &Vec) -> bool { - list.is_empty() -} - // Validate the value is a valid semver version/range. pub fn validate_semver_version, V: AsRef>( key: K, diff --git a/crates/config/src/workspace/runner.rs b/crates/config/src/workspace/runner.rs index 37dc242b44c..4319d9b9e3a 100644 --- a/crates/config/src/workspace/runner.rs +++ b/crates/config/src/workspace/runner.rs @@ -109,4 +109,42 @@ cacheLifetime: 'bad unit' Ok(()) }); } + + #[test] + #[should_panic(expected = "Must be a valid target format")] + fn invalid_dep_target() { + figment::Jail::expect_with(|jail| { + jail.create_file( + CONFIG_FILENAME, + r#" +implicitDeps: + - '%:task' +"#, + )?; + + load_jailed_config()?; + + Ok(()) + }); + } + + #[test] + #[should_panic( + expected = "Must be a valid ID (accepts A-Z, a-z, 0-9, - (dashes), _ (underscores), /, and must start with a letter)" + )] + fn invalid_dep_target_no_scope() { + figment::Jail::expect_with(|jail| { + jail.create_file( + CONFIG_FILENAME, + r#" +implicitDeps: + - 'foo bar' +"#, + )?; + + load_jailed_config()?; + + Ok(()) + }); + } } diff --git a/crates/config/tests/tasks_test.rs b/crates/config/tests/tasks_test.rs index f65c16256e3..82084351201 100644 --- a/crates/config/tests/tasks_test.rs +++ b/crates/config/tests/tasks_test.rs @@ -2,17 +2,24 @@ use figment::{ providers::{Format, YamlExtended}, Figment, }; +use moon_config::map_validation_errors_to_figment_errors; use moon_config::{TaskCommandArgs, TaskConfig}; use moon_utils::string_vec; use std::path::PathBuf; +use validator::Validate; const CONFIG_FILENAME: &str = "tasks.yml"; // Not a config file, but we want to test in isolation fn load_jailed_config() -> Result { - Figment::new() - .merge(YamlExtended::file(&PathBuf::from(CONFIG_FILENAME))) - .extract() + let figment = Figment::new().merge(YamlExtended::file(&PathBuf::from(CONFIG_FILENAME))); + let config: TaskConfig = figment.extract()?; + + config + .validate() + .map_err(|e| map_validation_errors_to_figment_errors(&figment, &e)[0].clone())?; + + Ok(config) } mod command { @@ -368,26 +375,45 @@ deps: }); } - // #[test] - // #[should_panic( - // expected = "Invalid field deps.0: Expected a string type, received unsigned int `123`." - // )] - // fn invalid_format() { - // figment::Jail::expect_with(|jail| { - // jail.create_file( - // super::CONFIG_FILENAME, - // r#" - // command: foo - // deps: - // - foo - // "#, - // )?; + #[test] + #[should_panic(expected = "Must be a valid target format")] + fn invalid_dep_target() { + figment::Jail::expect_with(|jail| { + jail.create_file( + super::CONFIG_FILENAME, + r#" +command: foo +deps: + - '%:task' +"#, + )?; - // super::load_jailed_config()?; + super::load_jailed_config()?; - // Ok(()) - // }); - // } + Ok(()) + }); + } + + #[test] + #[should_panic( + expected = "Must be a valid ID (accepts A-Z, a-z, 0-9, - (dashes), _ (underscores), /, and must start with a letter)" + )] + fn invalid_dep_target_no_scope() { + figment::Jail::expect_with(|jail| { + jail.create_file( + super::CONFIG_FILENAME, + r#" +command: foo +deps: + - 'foo bar' +"#, + )?; + + super::load_jailed_config()?; + + Ok(()) + }); + } } mod env { diff --git a/crates/notifier/src/webhooks.rs b/crates/notifier/src/webhooks.rs index c1645fa88c1..3c8d3c1c7ec 100644 --- a/crates/notifier/src/webhooks.rs +++ b/crates/notifier/src/webhooks.rs @@ -1,17 +1,21 @@ use moon_emitter::{Event, EventFlow, Subscriber}; use moon_error::MoonError; -use moon_logger::{color, error}; +use moon_logger::{color, error, trace}; use moon_utils::time::chrono::prelude::*; use moon_workspace::Workspace; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use tokio::task::JoinHandle; use uuid::Uuid; -#[derive(Serialize)] +const LOG_TARGET: &str = "moon:notifier:webhooks"; + +#[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct WebhookPayload { pub created_at: DateTime, + // Only for testing! + #[serde(skip_deserializing)] pub event: T, #[serde(rename = "type")] @@ -29,6 +33,8 @@ pub async fn notify_webhook( .body(body) .header("Accept", "application/json") .header("Content-Type", "application/json") + .header("Connection", "keep-alive") + .header("Keep-Alive", "timeout=30, max=120") .send() .await } @@ -62,13 +68,20 @@ impl Subscriber for WebhooksSubscriber { return Ok(EventFlow::Continue); } - let body = serde_json::to_string(&WebhookPayload { + let payload = WebhookPayload { created_at: Utc::now(), event, type_of: event.get_type(), uuid: self.uuid.clone(), - }) - .unwrap(); + }; + + trace!( + target: LOG_TARGET, + "Posting event {} to webhook endpoint", + color::id(&payload.type_of), + ); + + let body = serde_json::to_string(&payload).unwrap(); // For the first event, we want to ensure that the webhook URL is valid // by sending the request and checking for a failure. If failed, @@ -80,7 +93,7 @@ impl Subscriber for WebhooksSubscriber { self.enabled = false; error!( - target: "moon:notifier:webhooks", + target: LOG_TARGET, "Failed to send webhook event to {}. Subsequent webhook requests will be disabled.", color::url(&self.url), ); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 3a8b3145c5a..8775039d192 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2,7 +2,7 @@ use crate::errors::ProjectError; use moon_config::{ format_error_line, format_figment_errors, ConfigError, DependencyConfig, DependencyScope, FilePath, GlobalProjectConfig, PlatformType, ProjectConfig, ProjectDependsOn, ProjectID, - TaskConfig, TaskID, + ProjectLanguage, ProjectType, TaskConfig, TaskID, }; use moon_constants::CONFIG_PROJECT_FILENAME; use moon_logger::{color, debug, trace, Logable}; @@ -282,6 +282,9 @@ pub struct Project { /// Unique ID for the project. Is the LHS of the `projects` setting. pub id: ProjectID, + /// Primary programming language of the project. + pub language: ProjectLanguage, + /// Logging target label. #[serde(skip)] pub log_target: String, @@ -294,6 +297,10 @@ pub struct Project { /// Tasks specific to the project. Inherits all tasks from the global config. pub tasks: TasksMap, + + /// The type of project. + #[serde(rename = "type")] + pub type_of: ProjectType, } impl PartialEq for Project { @@ -342,14 +349,16 @@ impl Project { Ok(Project { alias: None, - config, dependencies, file_groups, id: String::from(id), + language: config.language.clone(), log_target, root, source: String::from(source), tasks, + type_of: config.type_of.clone(), + config, }) } diff --git a/crates/runner/src/dep_graph.rs b/crates/runner/src/dep_graph.rs index 6b967f2b89f..ff2b4d44293 100644 --- a/crates/runner/src/dep_graph.rs +++ b/crates/runner/src/dep_graph.rs @@ -65,7 +65,7 @@ impl DepGraph { project: &Project, project_graph: &ProjectGraph, ) -> Runtime { - match &project.config.language { + match &project.language { ProjectLanguage::JavaScript | ProjectLanguage::TypeScript => { let version = match &project.config.workspace.node { Some(ProjectWorkspaceNodeConfig { diff --git a/crates/task/src/token.rs b/crates/task/src/token.rs index fb0942fa2fd..3e1cd48e690 100644 --- a/crates/task/src/token.rs +++ b/crates/task/src/token.rs @@ -260,7 +260,13 @@ impl<'a> TokenResolver<'a> { } pub fn resolve_var(&self, value: &str, task: &Task) -> Result { - let matches = TOKEN_VAR_PATTERN.captures(value).unwrap(); + let matches = match TOKEN_VAR_PATTERN.captures(value) { + Some(value) => value, + None => { + return Ok(value.to_owned()); + } + }; + let token = matches.get(0).unwrap().as_str(); // $var let var = matches.get(1).unwrap().as_str(); // var @@ -281,17 +287,11 @@ impl<'a> TokenResolver<'a> { "taskType" => task.type_of.to_string(), "workspaceRoot" => path::to_string(workspace_root)?, _ => { - warn!( - target: "moon:project:token", - "Found a token variable in \"{}\" that is not supported, but this may be intentional, so leaving it.", - value - ); - - return Ok(String::from(value)); + return Ok(value.to_owned()); } }; - Ok(String::from(value).replace(token, &var_value)) + Ok(value.to_owned().replace(token, &var_value)) } fn convert_string_to_u8(&self, token: &str, value: String) -> Result { diff --git a/crates/task/tests/token_test.rs b/crates/task/tests/token_test.rs index 463e776fbeb..337129386e8 100644 --- a/crates/task/tests/token_test.rs +++ b/crates/task/tests/token_test.rs @@ -535,6 +535,9 @@ mod args { .unwrap(), "javascript-node-project" ); + + // Unknown var + assert_eq!(resolver.resolve_var("$unknown", &task).unwrap(), "$unknown"); } } diff --git a/packages/types/src/project.ts b/packages/types/src/project.ts index f042a70f38a..ff3aa2d076c 100644 --- a/packages/types/src/project.ts +++ b/packages/types/src/project.ts @@ -2,6 +2,8 @@ import type { Platform } from './common'; import type { DependencyConfig, ProjectConfig, + ProjectLanguage, + ProjectType, TaskMergeStrategy, TaskOutputStyle, } from './project-config'; @@ -56,7 +58,9 @@ export interface Project { dependencies: Record; fileGroups: Record; id: string; + language: ProjectLanguage; root: string; source: string; tasks: Record; + type: ProjectType; } diff --git a/website/blog/2022-10-21_v0.17.mdx b/website/blog/2022-10-21_v0.17.mdx index e40fa49c652..e8559dd35d7 100644 --- a/website/blog/2022-10-21_v0.17.mdx +++ b/website/blog/2022-10-21_v0.17.mdx @@ -2,8 +2,8 @@ slug: v0.17 title: v0.17 - Webhooks, extended YAML, and improved runtime performance authors: [milesj] -tags: [notifier, runner, editors, vscode] -# image: ./img/v0.17.png +tags: [notifier, runner, config, editors, vscode] +image: ./img/v0.17.png --- import Tabs from '@theme/Tabs'; @@ -18,6 +18,20 @@ webhooks! We've also spent some time working on quality of life improvements. To start, we have a few breaking changes this release to be aware of! +### Minor changes to ID formatting + +An ID refers to many things — project names, task names, target segments, so on and so forth. When +parsing these values, we format them to remove unwanted characters, as these IDs are used in many +contexts, many of which need to be strict. + +Previously, we would remove unwanted characters entirely. Instead, we now replace them with dashes +(`-`) for better readability. Take the following for example: + +| ID | Old | New | +| :--------- | :-------- | :--------- | +| domain.com | domaincom | domain-com | +| build:esm | buildesm | build-esm | + ### Task `type` has been renamed to `platform` This setting was renamed for a few reasons. To start, tasks actually have a @@ -59,7 +73,7 @@ tasks: > Because of this change, the `$taskType` token was also renamed to `$taskPlatform`! -## Webhook events +## Webhook events (experimental) Looking to gather metrics for your pipelines? Gain insight into run durations and failures? Maybe you want to send Slack or Discord notifications? With our new notifier system, this is now possible diff --git a/website/blog/img/v0.17.png b/website/blog/img/v0.17.png new file mode 100644 index 00000000000..dd2772602f9 Binary files /dev/null and b/website/blog/img/v0.17.png differ