Skip to content

Allow an environment to define only onExit - #346

Merged
mwiebe merged 2 commits into
OpenJobDescription:mainfrom
leongdl:fix/environment-onexit-only
Aug 25, 2026
Merged

Allow an environment to define only onExit#346
mwiebe merged 2 commits into
OpenJobDescription:mainfrom
leongdl:fix/environment-onexit-only

Conversation

@leongdl

@leongdl leongdl commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

What was the problem/requirement? (What/Why)

An environment script that defines onExit without onEnter is rejected during template validation:

environment -> script -> actions:
	onEnter is required.

This blocks environments whose only work is cleanup. The motivating case is a queue environment that tears down state established elsewhere in the session. The same <Environment> type backs standalone environment templates, jobEnvironments, and step environments, so all three were affected.

The rule came from reading the 2023-09 wiki literally: line 1572 declares onEnter: <Action> with no @optional annotation. That annotation was never present and was never removed, so the requirement has been in the document since the specification was first imported in November 2023. openjd-specifications PR 178 corrects the wiki, annotating both onEnter and onExit as optional and stating the at-least-one constraint explicitly.

What was the solution? (How)

Apply the existing "at least one action" rule regardless of whether WRAP_ACTIONS is enabled, in crates/openjd-model/src/template/validate_v2023_09/structure.rs. Either ordinary action alone now satisfies validation.

The error message still names the wrap hooks only when the extension is enabled, because they are not a valid choice otherwise:

  • without WRAP_ACTIONS: must define at least one of onEnter or onExit.
  • with WRAP_ACTIONS: unchanged, naming the complete hook set as a third option.

Unchanged: an actions object defining no action is still rejected, and the all-or-nothing wrap-hook rule in wrap_actions.rs is untouched.

Runtime safety. No session change was needed. on_enter is already Option<Action>. Session::enter_environment guards on the action being present before running it, so the expect inside that branch stays unreachable. EnvScriptRunner::run_env_action returns Success for a None action, which is the path a variables:-only environment already takes. An onExit-only environment therefore enters as a no-op and runs its exit action normally.

What is the impact of this change?

One verdict changes, and it widens what is accepted. Nothing that validated before now fails.

Environment actions Before After
onEnter only Accepted Accepted
onEnter and onExit Accepted Accepted
onExit only Rejected Accepted
No action defined Rejected Rejected
Complete wrap-hook set Accepted Accepted
Partial or ungated wrap hooks Rejected Rejected

The error text for an empty actions object without WRAP_ACTIONS changes from onEnter is required. to must define at least one of onEnter or onExit. Callers matching on that string need updating.

This change aligns openjd-rs with openjd-model-for-python PR 338. Without it the two implementations disagree on onExit-only input.

How was this change tested?

  • cargo fmt --all
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo test --workspace: 7263 passed, 0 failed, 24 ignored
  • 2023-09 conformance suite with the rebuilt release binary: 1161 passed, 1 failed. The single failure is base/job_templates/3.5--env-script-onexit-only.invalid.yaml, which asserts the old rejection and is reclassified as valid by openjd-specifications PR 178. Before this change the same suite reported 1162 passed against that fixture tree.

Tests changed:

  • test_environment_template.rs: replaced the previous onEnter-required test with onExit-only accepted, added onEnter-only accepted, and added empty actions rejected asserting the full message
  • test_actions_and_steps.rs: test_env_action_on_exit now expects success, and test_env_actions_empty asserts the new message

Both new behaviors were mutation-checked. Restoring the strict requirement fails the two onExit-only tests. Disabling the at-least-one check fails the two empty-actions tests. The mutants were removed and the source restored before committing.

Was this change documented?

specs/model/public-api.md documents on_enter: Option<Action>, which is unchanged. No spec document states the validation rule, so no update was required. The code comment explaining the rule was rewritten.

Is this a breaking change?

Not for templates, since the change only widens acceptance. It does change one validation error string, which is a behavior change for any caller matching on the old text.

Does this change impact security?

No. No files, directories, permissions, or process boundaries are affected. The runtime executes the same actions it did before, and an absent onEnter runs nothing.

Related

  • openjd-specifications PR 178 corrects the wiki and reclassifies the job template fixture
  • openjd-model-for-python PR 338 makes the same change in the Python model

@leongdl
leongdl requested a review from a team as a code owner August 25, 2026 20:32
Comment thread crates/openjd-model/src/template/validate_v2023_09/structure.rs
Comment thread crates/openjd-model/tests/integration/test_environment_template.rs
seant-aws
seant-aws previously approved these changes Aug 25, 2026
Comment thread crates/openjd-model/src/template/validate_v2023_09/structure.rs
@leongdl

leongdl commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Review feedback status

Both code findings are fixed in c862472. I have resolved those two threads and left the third open, because it is a dependency rather than a defect.

Finding Status
Stale wrap_actions_enabled doc comment Fixed, thread resolved
Untested WRAP_ACTIONS message arm Fixed, thread resolved
Conformance fails until the spec change lands Open by design, see below

Fixed: stale doc comment

The doc comment on EffectiveRules::wrap_actions_enabled still said onEnter is required when the extension is absent, which this PR made untrue. It now describes what the flag decides, which is whether the wrap hooks count as an acceptable choice and which wording the error uses. It also states that the flag does not make onEnter required, so the removed branch does not get reintroduced to match a stale comment.

Fixed: missing test for the WRAP_ACTIONS wording

Added empty_env_actions_rejected_with_wrap_hooks_named in test_wrap_actions.rs, asserting the complete error text with WRAP_ACTIONS and EXPR enabled.

I confirmed the gap was real instead of assuming it. Swapping the two arms of the if rules.wrap_actions_enabled message selection passed the previous test set on the wrap arm. After this change the same swap fails three tests.

Verification after the fix:

  • cargo fmt --all --check clean
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo test --workspace: 7264 passed, 0 failed, 24 ignored

Open: merge order

The three Conformance failures are one case, 3.5--env-script-onexit-only.invalid.yaml, at 1115 passed and 1 failed on each platform. That fixture asserts the rejection this PR removes, so no code change here can make it pass.

openjd-specifications#178 renames the fixture to drop the .invalid. infix. The Conformance job checks out the spec repo unpinned at its default branch, so this PR goes green once that merges.

This PR should merge after openjd-specifications#178. Merging first would turn main red on all three platforms. I have not disabled or special-cased the fixture, because that would hide the coupling rather than resolve it.

Related: openjd-model-for-python#338 makes the same change in the Python model.

An environment script that defined onExit without onEnter was rejected during
template validation with "onEnter is required." This blocked environments whose
only work is cleanup, including queue environments that tear down state
established elsewhere in the session.

Apply the same "at least one action" rule regardless of whether WRAP_ACTIONS is
enabled, so either ordinary action alone is sufficient. The error message keeps
naming the wrap hooks only when the extension is enabled, since they are not a
valid choice otherwise.

An actions object that defines no action is still rejected, and the
all-or-nothing wrap-hook rule in wrap_actions.rs is unchanged.

The session runtime already handles an absent onEnter. enter_environment guards
on the action being present, and the environment script runner treats a missing
action as a successful no-op, which is the path a variables-only environment
already takes.

This matches openjd-model-for-python PR 338 and the wiki correction in
openjd-specifications PR 178.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
… wording

Address review feedback on the at-least-one-action change.

The doc comment on EffectiveRules::wrap_actions_enabled still said onEnter is
required when the extension is absent, which the previous commit made untrue.
Describe what the flag now decides: whether the wrap hooks count as an
acceptable choice, and which wording the error uses. A future reader could
otherwise reintroduce the removed branch to match the comment.

Add the missing test for the WRAP_ACTIONS wording. Only the plain arm was
asserted, so swapping the two message arms went undetected. The new test
asserts the full error text for an empty actions object with the extension
enabled, and that swap now fails three tests.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
@mwiebe
mwiebe force-pushed the fix/environment-onexit-only branch from c862472 to 585184c Compare August 25, 2026 22:45
@mwiebe
mwiebe enabled auto-merge (squash) August 25, 2026 23:00
@mwiebe
mwiebe merged commit 8d22c78 into OpenJobDescription:main Aug 25, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants