diff --git a/docs/netsuke-design.md b/docs/netsuke-design.md index 863d1628..b5707908 100644 --- a/docs/netsuke-design.md +++ b/docs/netsuke-design.md @@ -591,6 +591,9 @@ The manifest version is parsed using the `semver` crate to validate that it follows semantic versioning rules. Global and target variable maps now share the `HashMap` type for consistency. This keeps YAML manifests concise while ensuring forward compatibility. +Targets also accept optional `phony` and `always` booleans. They default to +`false`, making it explicit when a step should run regardless of file +timestamps. ### 3.5 Testing diff --git a/docs/roadmap.md b/docs/roadmap.md index bdb1c2c8..3f66073f 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -27,7 +27,7 @@ compilation pipeline from parsing to execution. - [ ] Implement parsing for the netsuke_version field and validate it using the semver crate. - - [ ] Support `phony` and `always` boolean flags on targets. + - [x] Support `phony` and `always` boolean flags on targets. *(done)* - [ ] Parse the optional steps list, treating each entry as a target with phony: true by default. diff --git a/tests/ast_tests.rs b/tests/ast_tests.rs index 45d5e19d..3c659448 100644 --- a/tests/ast_tests.rs +++ b/tests/ast_tests.rs @@ -213,3 +213,34 @@ fn invalid_enum_variants() { "#; assert!(serde_yml::from_str::(yaml).is_err()); } + +#[test] +fn phony_and_always_flags() { + let yaml = r#" + netsuke_version: "1.0.0" + targets: + - name: clean + recipe: + kind: command + command: rm -rf build + phony: true + always: true + "#; + let manifest = serde_yml::from_str::(yaml).expect("parse"); + let target = manifest.targets.first().expect("target"); + assert!(target.phony); + assert!(target.always); + + let yaml = r#" + netsuke_version: "1.0.0" + targets: + - name: clean + recipe: + kind: command + command: rm -rf build + "#; + let manifest = serde_yml::from_str::(yaml).expect("parse"); + let target = manifest.targets.first().expect("target"); + assert!(!target.phony); + assert!(!target.always); +} diff --git a/tests/data/phony.yml b/tests/data/phony.yml new file mode 100644 index 00000000..3bb42ac9 --- /dev/null +++ b/tests/data/phony.yml @@ -0,0 +1,8 @@ +netsuke_version: "1.0.0" +targets: + - name: clean + recipe: + kind: command + command: "rm -rf build" + phony: true + always: true diff --git a/tests/features/manifest.feature b/tests/features/manifest.feature index 431fae80..8176e057 100644 --- a/tests/features/manifest.feature +++ b/tests/features/manifest.feature @@ -4,3 +4,8 @@ Feature: Manifest parsing When the manifest file "tests/data/minimal.yml" is parsed Then the manifest version is "1.0.0" And the first target name is "hello" + + Scenario: Parse phony and always flags + When the manifest file "tests/data/phony.yml" is parsed + Then the first target is phony + And the first target is always rebuilt diff --git a/tests/steps/manifest_steps.rs b/tests/steps/manifest_steps.rs index eaf692e7..8f0775f4 100644 --- a/tests/steps/manifest_steps.rs +++ b/tests/steps/manifest_steps.rs @@ -54,3 +54,17 @@ fn first_target_name(world: &mut CliWorld, name: String) { other => panic!("Expected StringOrList::String, got: {other:?}"), } } + +#[then("the first target is phony")] +fn first_target_phony(world: &mut CliWorld) { + let manifest = world.manifest.as_ref().expect("manifest"); + let first = manifest.targets.first().expect("targets"); + assert!(first.phony); +} + +#[then("the first target is always rebuilt")] +fn first_target_always(world: &mut CliWorld) { + let manifest = world.manifest.as_ref().expect("manifest"); + let first = manifest.targets.first().expect("targets"); + assert!(first.always); +}