Repository navigation
build: move openjd-model, openjd-sessions and openjd-cli (Python) into openjd-rs - #433
crowecawcaw wants to merge 785 commits into
Conversation
…ption/openjd-model-for-python#242) Signed-off-by: Josh Usiskin <56369778+jusiskin@users.noreply.github.com>
…ption/openjd-sessions-for-python#279) Signed-off-by: Josh Usiskin <56369778+jusiskin@users.noreply.github.com>
Currently, the `openjd run` and other commands will fail if the job template they receive has an extension different from .json, .yml, or .yaml. This change treats all non-.json extensions as YAML, so that job templates can be named with different extensions like `.ojdt` that clearly distinguish them from generic JSON or YAML. Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
When running in Python 3.14, importing pydantic.v1 produces a warning message. The code was porting it specifically for a Python 3.9 compatibility issue, so this change is to only do that import in Python 3.9 and for 3.10+ only use the newer pydantic. Added Python versions 3.13 and 3.14 to the github action test matrix Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
Some of the sample code in the README.md no longer worked after the change for RFC 0002. https://github.com/OpenJobDescription/openjd-specifications/blob/mainline/rfcs/0002-model-extensions.md updated OpenJD to make the fields and validation of template components depend on an 'extensions' property at the top level of the template. Implementing this in the model library involved adding a model parsing context that objects look at during construction and validation. This change adds a docstring for the parse_model function and updates the broken examples to explain how the extensions fit in and how to create model objects. Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…eters Because a job template is runnable code similar to a script, we can think of `openjd run <templatefile>` the same as `bash <scriptfile>`. Part of this is printing help output of the options available. This change makes it so `openjd run <templatefile> -h` prints help that is specific about the template file, displaying the job name, description, and all its parameters. Similarly, if there are missing required parameters, the error message says that and then displays this same help text. Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…ion class (OpenJobDescription/openjd-sessions-for-python#281) Signed-off-by: Godot Bian <13778003+godobyte@users.noreply.github.com>
…#283) Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
… to ==10.5.* (OpenJobDescription/openjd-cli#197) chore(deps): update python-semantic-release requirement Updates the requirements on [python-semantic-release](https://github.com/python-semantic-release/python-semantic-release) to permit the latest version. - [Release notes](https://github.com/python-semantic-release/python-semantic-release/releases) - [Changelog](https://github.com/python-semantic-release/python-semantic-release/blob/master/CHANGELOG.rst) - [Commits](python-semantic-release/python-semantic-release@v10.4...v10.5) --- updated-dependencies: - dependency-name: python-semantic-release dependency-version: 10.5.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… to ==10.5.* (OpenJobDescription/openjd-model-for-python#246) chore(deps): update python-semantic-release requirement Updates the requirements on [python-semantic-release](https://github.com/python-semantic-release/python-semantic-release) to permit the latest version. - [Release notes](https://github.com/python-semantic-release/python-semantic-release/releases) - [Changelog](https://github.com/python-semantic-release/python-semantic-release/blob/master/CHANGELOG.rst) - [Commits](python-semantic-release/python-semantic-release@v10.4...v10.5.1) --- updated-dependencies: - dependency-name: python-semantic-release dependency-version: 10.5.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… to ==10.5.* (OpenJobDescription/openjd-sessions-for-python#284) chore(deps): update python-semantic-release requirement Updates the requirements on [python-semantic-release](https://github.com/python-semantic-release/python-semantic-release) to permit the latest version. - [Release notes](https://github.com/python-semantic-release/python-semantic-release/releases) - [Changelog](https://github.com/python-semantic-release/python-semantic-release/blob/master/CHANGELOG.rst) - [Commits](python-semantic-release/python-semantic-release@v10.4...v10.5.1) --- updated-dependencies: - dependency-name: python-semantic-release dependency-version: 10.5.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
…openjd-model-for-python#248) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…openjd-cli#199) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…lder envs (OpenJobDescription/openjd-model-for-python#249) Signed-off-by: Morgan Epp <60796713+epmog@users.noreply.github.com>
…lder envs (OpenJobDescription/openjd-sessions-for-python#288) Signed-off-by: Morgan Epp <60796713+epmog@users.noreply.github.com>
…lder envs (OpenJobDescription/openjd-cli#200) Signed-off-by: Morgan Epp <60796713+epmog@users.noreply.github.com>
…escription/openjd-sessions-for-python#287) Signed-off-by: Charles Moore <122481442+moorec-aws@users.noreply.github.com>
…escription/openjd-cli#201) Signed-off-by: Charles Moore <122481442+moorec-aws@users.noreply.github.com>
…escription/openjd-model-for-python#250) Signed-off-by: Charles Moore <122481442+moorec-aws@users.noreply.github.com>
…openjd-sessions-for-python#286) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This new function provides the ability to run an ad hoc command within the session environment, choosing whether to use the environment variables from the session enter actions or to ignore them. The parameters for the command are direct, so you don't have to construct a temporary Action or StepAction object to run a simple command. Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…obDescription/openjd-sessions-for-python#289) Updates the requirements on [mypy](https://github.com/python/mypy) to permit the latest version. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](python/mypy@v1.18.1...v1.19.0) --- updated-dependencies: - dependency-name: mypy dependency-version: 1.19.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…obDescription/openjd-cli#202) Updates the requirements on [mypy](https://github.com/python/mypy) to permit the latest version. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](python/mypy@v1.18.1...v1.19.0) --- updated-dependencies: - dependency-name: mypy dependency-version: 1.19.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#290) Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
…model-for-python#254) fix: handle None value for environment field in EnvironmentTemplate Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
…obDescription/openjd-model-for-python#251) Updates the requirements on [mypy](https://github.com/python/mypy) to permit the latest version. - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](python/mypy@v1.18.1...v1.19.0) --- updated-dependencies: - dependency-name: mypy dependency-version: 1.19.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ttblanchard <55503092+ttblanchard@users.noreply.github.com>
…penjd-model-for-python#365) openjd-expr 0.7.0 -> 0.8.0, openjd-model 0.7.1 -> 0.8.0, and openjd-sessions 0.5.8 -> 0.6.0 (openjd-rs release PR OpenJobDescription/openjd-model-for-python#395). Six upstream changes land, reconciled against a source diff of the published crates. The only new public API is two methods on openjd_expr::FormatString, segment_count and literal_segments, which the model crate's validator uses (OpenJobDescription/openjd-model-for-python#383). Both are now exposed on openjd.expr.FormatString, with the stub and specs/python-expr-interface.md updated. openjd-model and openjd-sessions have no public signature changes; the two [breaking] changelog entries are behavioural. Each change exercised through this package's API, v1 and v0 side by side where both apply: * openjd-rs#396 type-checks an `in`/`not in` item against the list's element type. `'a' in [1, 2]` now raises where it returned False. Int/float and path/string coercion kept; `x in []` is False for any x. * openjd-rs#383 checks resolved-value constraints at template validation. A job name resolving to 129 characters, and a chunks defaultTaskCount naming an undeclared parameter, are now rejected at decode. v0 already rejected both. The whole-field chunks expression is resolved with target type int, so a STRING value fails inside the evaluator: test_chunk_default_task_count_invalid_int's message match moves from "not a valid integer" to "Cannot convert ... to int"; the exception class it pins is unchanged. * openjd-rs#397: a single-valued allOf with one literal and one whole-field expression is now accepted at validation (0.7.1 rejected it) because the expression can null-skip, and job creation re-checks the resolved count. A control character in a resolved job name is rejected at create_job; 0.7.1 produced the job. * openjd-rs#398 renders NONCONTIGUOUS chunks like the v0 reference: [1,2,4,6] is "1,2,4,6", not "1,2-6:2". Both paths agree on all four probed shapes. * openjd-rs#384: preprocess_job_parameters accepts its own output for a LIST[PATH] parameter defaulting to []. 0.7.1 refused the second pass. * openjd-rs#359 rejects embedded filenames ".", ".." and any containing a null byte at template validation. v0 accepts all three. The sessions-side check for Windows drive anchors ("C:", "D:relative") is not reachable from this repo's tests: the model layer accepts them and the check is Windows-only. One test template was invalid and passed only because 0.7.1 did not validate chunk fields: test_chunks_with_format_string_default_task_count now declares the parameter it references. Old-pins mutant: with the three pins reverted and the two new bindings removed, 30 tests fail, all in the new or updated classes, and the whole-field allOf deferral was measured as rejected on 0.7.1. Two new cases (a tab in a literal run of an interpolated name, two certain allOf elements) were already rejected on 0.7.1 and are labelled controls. THIRD-PARTY-LICENSES.txt regenerated; only the three crate versions changed. Cargo.lock moved only those three packages. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
* chore(release): 0.11.13 Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com> * chore(release): Fill in the 0.11.13 release notes The generated block was empty: the only commit in the release is a chore(deps), which the changelog template does not render. Frame it as the openjd-rs 0.8.0 bump and index the six upstream fixes it carries, one line each, plus the two new FormatString methods it exposes. Versions taken from the rust-bindings/Cargo.toml diff in 710ee13; each line corresponds to a test added in that commit. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> --------- Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com> Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> Co-authored-by: David Leong <116610336+leongdl@users.noreply.github.com>
…n/openjd-model-for-python#367) * fix: Allow '_' in a combination expression Template Schemas §3.4.3 constraint 1 gives a <CombinationExpr> the characters allowed in an <Identifier> plus the space and the operators, and §7.1 puts the underscore among an <Identifier>'s characters. The character class here omitted it, so a task parameter named Frame_Range was rejected by the pattern before the expression parser saw it: steps[0] -> parameterSpace -> combination: String should match pattern '(?-m:^[A-Za-z0-9\*\(\), ]+\z)' The name itself was legal, so such a parameter could be declared and never referenced. Its own comment already claimed the intended relationship to <Identifier>, so bind both classes to one constant rather than restating it: the two cannot drift again. The rendered _identifier_regex is unchanged. The Rust path accepted these all along, so the two lanes disagreed. Add a control on the v1 side pinning its half of the parity. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> * fix: Address review on the combination expression class Two findings from the automated review. Scope the comment's claim. It said the shared constant meant Identifier and CombinationExpr "cannot drift", which is stronger than the code provides: only the character class is shared, not the leading-character rule. Say what is actually shared, and record why the space is U+0020 alone. Whitespace stays narrow, deliberately. The shared TokenStream folds every whitespace run to a space before lexing, so CombinationExpressionParser on its own accepts a newline that the pattern refuses. §3.4.3 allows "the space", and openjd-rs refuses a newline too, so the pattern is the conformant side and widening it to \s would create a divergence rather than close one. Pin it with a newline case instead of leaving the decision implicit. Assert the shared constant, not the rendered pattern. Hardcoding the expanded regex meant a deliberate widening of _identifier_chars would break a test about hyphens, which is the opposite of what factoring the constant out was for. Verified: widening the constant now leaves the negative controls green, while reverting it, widening to any character, and widening the space to \s are each caught. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> --------- Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
…bDescription/openjd-model-for-python#368) fix: apply the template's extensions in the job parameter merge preprocess_job_parameters re-validates each merged job parameter definition through parse_model, and it passed no supported_extensions. The parsing context that reached NameIdentifierLengthMixin therefore carried an empty extension set, so the mixin enforced the base 64-character limit on a parameter name that decode had already accepted at up to 512 under FEATURE_BUNDLE_1. create_job calls preprocess_job_parameters itself, so both entry points rejected the name. Measured before the change, for a template declaring FEATURE_BUNDLE_1 with a 512-character STRING parameter name: decode_job_template OK preprocess_job_parameters DecodeValidationError: 1 validation errors for JobStringParameterDefinition name: name must be at most 64 characters long create_job same merge_job_parameter_definitions now derives the union of the extensions declared by the job template and any environment templates, and threads it to merge_job_parameter_definitions_for_one and on into parse_model. The union is sound because a definition was decoded under its own template's extension set and every definition of one job parameter carries the same name, so the union cannot admit a value decode did not already accept for the template that declared it. The public signature of merge_job_parameter_definitions is unchanged: it already receives the templates that carry the extensions, so the set is derived rather than passed. This follows the shape of OpenJobDescription/openjd-model-for-python#362, which fixed the sibling defect in instantiate_model, and it keeps a caller from declaring an extension the template did not. merge_job_parameter_definitions_for_one takes a new keyword-only supported_extensions argument that defaults to None, preserving its behaviour for any existing caller. Tests cover all four FEATURE_BUNDLE_1 length ceilings at job-creation time on both lanes. v0 gains group H in test_feature_bundle_1.py: the parameter name through the merge, preprocess_job_parameters and create_job for each of STRING, PATH, INT and FLOAT, a genuine two-source merge, the environment-template-only case, the EXPR-typed model_copy branch, and controls for the base 64 limit and the static 512 identifier ceiling. v1 gains an equivalent class in model_v1/test_create_job.py, which had no coverage of these ceilings at all; the Rust implementation validates in a single pass and already passes, so those tests are a ratchet against a re-validation pass being added upstream unnoticed. Every v0 test was mutation-checked. Seven mutants, each caught: dropping the threading at any of its three points, hardcoding FEATURE_BUNDLE_1 instead of deriving it, ignoring environment templates, applying the threading only to STRING, and applying it only to single-source merges. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
…penjd-model-for-python#369) openjd-rs released on 2026-09-18: openjd-expr 0.9.0, openjd-model 0.9.0, openjd-sessions 0.7.0 (OpenJobDescription#401). The release is one feature plumbed through all three crates -- opt-in resolved-value caps and evaluation budgets (OpenJobDescription/openjd-model-for-python#399) -- plus a job-creation re-check on carried-forward fields (OpenJobDescription/openjd-model-for-python#404) and an MSRV/dependency sweep (OpenJobDescription/openjd-model-for-python#403). Four breaking signature changes reach this package's bindings: FormatString::validate_expressions now takes a FormatStringOptions, decode_environment_template takes CallerLimits, evaluate_let_bindings takes the two budgets, and CallerLimits / SessionConfig gained fields. CallerLimits grows from six fields to ten, and the four new ones are threaded to every entry point upstream threads them to: the environment template decoders, evaluate_let_bindings, and Session, which is the run-time enforcement boundary for the resolved-value caps. Separately, max_template_size was inert in this package. It is checked only inside document_string_to_object, and the parse_string helper passed a default CallerLimits, so a caller asking for a byte ceiling on decode_job_template_str got none. parse_string now takes the caller's own limits. Found while documenting the field. Cargo.lock moved the three crates plus the OpenJobDescription/openjd-model-for-python#403 sweep; THIRD-PARTY-LICENSES regenerated to match. Verified: 6139 passed / 24 skipped / 3 xfailed, coverage 94.16%; ruff, black, mypy, cargo fmt and clippy clean. Six mutants covering each piece of new plumbing were each caught by the new tests. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
The capability name constraints and the uniqueness of names within hostRequirements amounts and attributes apply to the names after their format strings are resolved (OpenJobDescription/openjd-specifications#189). Template validation only compares the raw names, so two different format strings that resolved to the same name, or a name that resolved to more than 100 characters, created a job. Check the 100-character limit on the resolved amount and attribute names, and the case-insensitive uniqueness of the resolved names, when the job is created. The messages match the ones for literal names. Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…idation (OpenJobDescription/openjd-model-for-python#372) The EXPR extension makes job and task parameter type names case-insensitive (RFC 0007 §2), so `type: string` is exactly as valid as `type: STRING`. The model accepted such a declaration but then rejected every reference to the parameter: steps[0] -> script -> actions -> onRun -> args[0]: Variable Param.Msg does not exist at this location. The variable-reference prevalidation pass runs on raw template values, before the field validator that folds the `type` discriminator to upper case, and it resolved the type-discriminated parameter-definition union with an exact string comparison. A lowercase or mixed-case type name matched no member of the union, so the definition contributed none of its `Param.*` / `RawParam.*` / `Task.Param.*` symbols and none of its EXPR type information. Compare the discriminator with an ASCII-only case fold when EXPR is active, in both the reference-validation and the definition-collection traversal. Job, task, and environment-template parameters now define their symbols and types whatever the case of the type name. The fold is ASCII-only so Unicode look-alikes such as `ıNT` stay rejected, matching the field validator. Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…ption/openjd-model-for-python#373) * chore(deps): Bump openjd-* Rust crates to the 0.10.0 release openjd-rs released on 2026-09-28: openjd-expr 0.10.0, openjd-model 0.10.0, openjd-sessions 0.7.1 (OpenJobDescription#408). This package pinned 0.9.0 / 0.9.0 / 0.7.0. openjd-sessions 0.7.1 is a transitive re-pin with no source change. Two breaking changes, both features: - OpenJobDescription/openjd-model-for-python#409 makes template::AmountRequirement::name and template::AttributeRequirement::name a FormatString instead of a String, so a capability name may contain expressions (openjd-specifications#189). The §3.3.1.1 / §3.3.2.1 constraints now apply to the resolved name. - OpenJobDescription/openjd-model-for-python#407 requires create_job's context to cover the template's declared extensions. With mismatch impossible, the job-creation resolved-value checks report every evaluation error instead of silently skipping it. Only OpenJobDescription/openjd-model-for-python#409 broke compilation, in four places: the two template-type constructors and the two name getters. The Python-facing `name` stays a `str` holding the raw template text. v0 models these as AmountCapabilityName / AttributeCapabilityName, both subclasses of v0's FormatString, which subclasses str -- so exposing an openjd.expr .FormatString here would make v0 and v1 diverge where they agree, and .raw() is what OpenJobDescription/openjd-model-for-python#409 prescribes for reading these fields. One behaviour change falls out of the adaptation rather than upstream: the constructor parses, so a malformed format string now raises ExpressionError. create_job needed no code change. The binding derives its context from job_template.default_validation_context() when the caller passes none, which covers the template's extensions by construction; a caller-supplied mismatched context is what OpenJobDescription/openjd-model-for-python#407 now rejects, and the binding surfaces it. OpenJobDescription/openjd-model-for-python#407 is a squash of five commits whose message documents three behaviour changes the release changelog and the PR body do not name: - eval_boolop no longer suppresses budget errors in operands after an unresolved one, the same bypass eval_ifexp had. - A list comprehension over a *concrete* iterable whose filter evaluates unresolved concludes unresolved[list[T]] instead of hard-erroring. This was a defect that predated the release, masked by the lenient policy OpenJobDescription/openjd-model-for-python#407 deleted, and reachable only at job creation -- the one stage where the iterable is concrete and the filter is not. - Template validation and create_job's re-checks evaluate under PathFormat::Posix, so validation outcomes no longer depend on the host OS. Windows-only in effect; unverified locally. Verified: 6243 passed / 24 skipped / 3 xfailed, coverage 94.22%. The three xfails are the pre-existing openjd.expr known gaps; none flipped. ruff, black, mypy, cargo fmt and clippy clean. Every behaviour change above was measured through this package's public API against both 0.9.0 and 0.10.0, and the openjd.expr expectations are taken from the upstream assertions in test_unresolved_eval.rs and test_memory.rs. Four mutants, each rebuilt and each caught: reverting the three pins kills 53 of the new cases, the two __repr__ sites kill 2, the two name getters kill 11, and swallowing the name parse error kills 2. Each was restored byte-for-byte with the bytecode cache cleared between runs. THIRD-PARTY-LICENSES.txt moves only the three version lines; the release pulled in no new transitive crates. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> * fix: Expose the capability name as a FormatString, not a str Review feedback on OpenJobDescription/openjd-model-for-python#373: consistency with the other v1 fields beats consistency with v0 here. openjd-rs#409 made template::AmountRequirement::name and template::AttributeRequirement::name a FormatString. The first pass kept the Python-facing `name` a `str` and parsed on the way in, on the grounds that v0 models these as AmountCapabilityName / AttributeCapabilityName, both subclasses of v0's FormatString, which subclasses str. That argument looks at the wrong neighbour: every other FormatString-typed field on the v1 template types -- `min`, `max`, `anyOf`, `allOf`, `Action.command` -- is an openjd.expr.FormatString and refuses a bare str. `name` now matches them. This deletes more than it adds. parse_capability_name and its doc comment go away, because the FormatString arrives already parsed, and both constructors go back to being infallible. Validation moves to where it belongs: a malformed name is rejected by FormatString's own constructor rather than by a str-typed field that parses behind the caller's back. __repr__ keeps .raw(), matching Action's treatment of its command. This is a breaking change to the v1 Python API, and two pre-existing tests prove it: TestPickle::test_amount_requirement and test_attribute_requirement both passed `name=` a str, and TestStepTemplate::test_host_requirements compared `.name` to one. All three are updated, so this is a behaviour change and not a refactor. Also from review: the spec edit that changed the documented ModelProfile repr to `revision=v2023_09` was wrong -- PyModelProfile::__repr__ still emits the uppercase-V form (rust-bindings/src/model/profile.rs:397), measured as `ModelProfile(revision=V2023_09, extensions=[EXPR])`. Reverted. The two `SpecificationRevision.v2023_09` corrections in the same block stand: that repr is lowercase (profile.rs:63) and `V2023_09` is not an attribute at all. The divergence between the two reprs is pre-existing and left alone here. Verified: 6245 passed / 24 skipped / 3 xfailed, coverage 94.32%. ruff, black, mypy, cargo fmt and clippy clean. Four mutants, each rebuilt and each caught: reverting the pins to 0.9.0 kills 68 cases, formatting the FormatString in both __repr__ sites kills 2, both getters returning a constant kills 12, and both constructors discarding the supplied name kills 10. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> --------- Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
…penJobDescription/openjd-model-for-python#375) * feat!: Bump openjd-* Rust crates to the openjd-model 0.11.0 release openjd-rs released openjd-expr 0.10.1, openjd-model 0.10.1 and openjd-sessions 0.7.2 on 2026-09-30 (OpenJobDescription#416), then openjd-model 0.11.0 and openjd-sessions 0.7.3 on 2026-10-03 (OpenJobDescription/openjd-model-for-python#420). This package pinned 0.10.0 / 0.10.0 / 0.7.1. Cargo.lock moves only the three crates; no transitive dependency changed. openjd-sessions has no source change in either release. One public Rust API change breaks compilation: OpenJobDescription/openjd-model-for-python#419 makes template::SimpleAction::script a FormatString. The constructor and the getter of PySimpleAction were the two errors. Following OpenJobDescription/openjd-model-for-python#373's capability-name precedent, the Python-facing SimpleAction.script is now an openjd.expr.FormatString in both directions, like its args/timeout siblings and the EmbeddedFile.data it desugars to. The new Rust-side SimpleActionKind / simple_actions / simple_action helpers are not exposed: nothing here consumes them, and step.bash..node already cover introspection. resolve_syntax_sugar changed signature but the bindings never call it. Behaviour changes, each measured through this package's Python API against both pins, with expectations copied from upstream assertions: - OpenJobDescription/openjd-model-for-python#419: bash/python/cmd/powershell/node steps are validated at decode with errors at the authored path (steps[0] -> bash -> args[1]). On 0.10.0, 17 of 21 upstream rejection cases decoded; 9 failed later at create_job at the desugared path, 8 were never rejected. A malformed script now fails at parse (DecodeValidationError). Non-ASCII step names no longer raise PanicException (ASCII-only sanitizer, named in the squash body but not the changelog). - OpenJobDescription/openjd-model-for-python#419, also named only in the squash body: the comprehension-variable check now covers a step script's authored embeddedFiles[].data. A comprehension over a let-bound name in data decoded on 0.10.0 and is now rejected at steps[0] -> script -> embeddedFiles[0] -> data. The same check on command and args is unchanged. v0 agrees on outcome and path in 4/4 cases. - OpenJobDescription/openjd-model-for-python#421: LIST[PATH] defaults must be relative and stay inside the template directory, are joined to it and normalized, element by element; relative submitted PATH / LIST[PATH] values are joined and normalized; defaults are constraint-checked after the join, including environment-template parameters. Absolute submitted values are returned as written. create_job without a preceding preprocess is unchanged (measured, 7 cases identical on both pins). - OpenJobDescription/openjd-model-for-python#410: a job environment's let failure now reads like a step script's ("script let binding 'q': ..."); the missing-extension message lists extensions sorted. - OpenJobDescription/openjd-model-for-python#410/OpenJobDescription#417/OpenJobDescription#418: openjd.expr memory accounting; every upstream byte figure reproduces. Some expressions that fit now exceed the limit (coercion to the target type, string slices, attribute base lookups) and some that failed now fit. No existing Python test pinned a moved figure. Unreachable from Python: URI-allowed LIST[PATH] rows (the binding hard-codes allow_uri_path_values=false, create_job_fns.rs:158; upstream test_create_job.rs covers them). The Windows rows of OpenJobDescription/openjd-model-for-python#421 are added as win32-only tests and were not run locally. v0 divergence: v0 preprocess_job_parameters returns LIST[PATH] defaults unjoined and does not normalize submitted '..'. openjd-specifications#191 (Template Schemas 2.2, 2.12) makes v1's behaviour normative. Recorded as two strict xfails in test_known_gaps.py and in the model report; fixing v0 is a separate change. On the OpenJobDescription/openjd-model-for-python#419 decode cases v0 agrees with v1 on outcome in 22/24 and on field path in 16/24; the two outcome differences are v0 rejecting Task.File.<step>_script, which predates this bump. Verified: 6323 passed / 26 skipped / 5 xfailed, coverage 94.22%. The +2 skipped are the Windows-only tests, the +2 xfailed the v0 gaps; no strict xfail flipped. ruff, black, mypy, cargo fmt, build, clippy -D warnings, test and test --doc clean. THIRD-PARTY-LICENSES.txt regenerated (three version lines) and verify mode passes. Mutants, each rebuilt, bytecode cleared, restored by checksum: reverting the pins and the adaptation kills 62 of 79 new or edited cases (the 17 survivors are controls whose behaviour 0.10.0 shares); the getter returning str kills 4; the constructor accepting str kills 4. Not done: the three-auditor review of the new tests (regression-workflow section 3) was not dispatched; the mutation results above are the author's own. TestExtremeSliceSteps pins the OpenJobDescription/openjd-model-for-python#418 overflow fix only in a build with overflow checks; a hatch env install of 0.10.0 already returned the right answer. BREAKING CHANGE: SimpleAction.script is an openjd.expr.FormatString instead of str, and SimpleAction(script=...) requires one. LIST[PATH] defaults must be relative and are joined with the job template directory; relative submitted PATH values are normalized. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> * chore(deps): Bump pyo3 to 0.29.3 for clippy 1.99 Rust 1.99.0 (stable since 2026-10-01) makes clippy flag clone_on_copy inside pyo3 0.29.2's #[pyclass(from_py_object)] expansion on Copy enums: 13 errors, which fail Rust Quality's clippy -D warnings step on every PR. pyo3 0.29.3 fixes the generated code (PyO3/pyo3#6309). Raise the floor in rust-bindings/Cargo.toml to 0.29.3 so a fresh resolve cannot pick the affected release. THIRD-PARTY-LICENSES.txt regenerated; only the four pyo3 version lines changed. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> --------- Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
…for-python#368) Update to the latest openjd-model release. Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…#369) Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
Imported from https://github.com/OpenJobDescription/openjd-model-for-python (mainline) with git filter-repo. Paths are moved under python/openjd-model/, tags are renamed to legacy/openjd-model-for-python/<version>, and PR references are rewritten to OpenJobDescription/openjd-model-for-python#<n>. Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
…essions Imported from https://github.com/OpenJobDescription/openjd-sessions-for-python (mainline) with git filter-repo. Paths are moved under python/openjd-sessions/, tags are renamed to legacy/openjd-sessions-for-python/<version>, and PR references are rewritten to OpenJobDescription/openjd-sessions-for-python#<n>. Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Imported from https://github.com/OpenJobDescription/openjd-cli (mainline) with git filter-repo. Paths are moved under python/openjd-cli/, tags are renamed to legacy/openjd-cli/<version>, and PR references are rewritten to OpenJobDescription/openjd-cli#<n>. Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Make python/openjd-model, python/openjd-sessions and python/openjd-cli part
of the openjd-rs workspace so a change to the Rust crates and the Python
packages that expose them lands, is tested, and is released together.
- openjd-model's PyO3 bindings crate (renamed openjd-model-py) moves its
manifest to the Python package root and depends on openjd-expr,
openjd-model and openjd-sessions by path instead of from crates.io. The
nested Cargo workspace, Cargo.lock and deny.toml are removed in favor of
the root ones.
- openjd-sessions and openjd-cli get an empty `publish = false` release
anchor crate whose version is the Python package version.
- Python versions now come from each package's Cargo.toml (maturin reads it
natively; hatch reads it with a regex) instead of from git tags. This
removes the in-tree PEP 517 backend, generate_version.py,
maturin_build.py and the hatch version-copy hooks. _version.py is now a
committed file that reads the installed distribution's metadata.
- sessions and cli hatch environments install the in-repo openjd-model
(and openjd-sessions) before themselves, so their tests run against the
code in this repo rather than the PyPI releases. openjd-cli's pins move
to the in-repo 0.13 versions, which its test suite passes against.
- Per-repo GitHub configuration, semantic-release configuration and
duplicated CODE_OF_CONDUCT/CONTRIBUTING files are removed; the
repo-level ones apply.
Building against the in-repo crates picks up behavior changes from
openjd-expr 0.11 and openjd-model 0.12 that Python users can see. The
affected tests are updated:
- Syntax errors use CPython's wording, e.g. "'(' was never closed" instead
of "unexpected EOF while parsing" (ruff parser 0.16.10, openjd-rs#429).
- A failing `let` binding in create_job raises ModelValidationError at the
binding's template path instead of ExpressionError (openjd-rs#428).
BREAKING CHANGE: openjd-model's create_job raises ModelValidationError,
not ExpressionError, when a `let` binding fails to evaluate, and reports
every job-creation error at once.
Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
pydantic 2.14 annotates FieldInfo.annotation as TypeForm[Any], which mypy rejects where the variable-reference validators expect type[Any]. Cast to the runtime type the annotation holds. This also fails on openjd-model-for-python's mainline with current dependencies. Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Add a Python workflow that lints, builds, and tests openjd-model, openjd-sessions and openjd-cli on Linux, Windows and macOS across the Python versions each package supported in its own repository, each against the in-repo versions of the others. It also runs openjd-sessions' macOS cross-user tests, checks openjd-model's THIRD-PARTY-LICENSES.txt, and checks the pins between the Python packages with the new scripts/sync_python_pins.py. CodeQL now analyzes Python as well as Rust, and Dependabot updates the Python packages' pip requirements. Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
release-plz now versions openjd-model, openjd-sessions and openjd-cli alongside the crates, so one Release PR covers every package that changed and merging it releases all of them. - release-plz.toml: the three Python packages are `git_only` (their last release comes from a python-<distribution>-v<version> tag, not crates.io) and are never published to crates.io. - release-plz.yml: when `release` creates a Python tag, build the openjd-model wheels (six platforms, abi3) and sdist and the openjd-sessions/openjd-cli wheel and sdist from that tag, PGP-sign them as the per-package repositories did, attach them to the GitHub Release, and upload them to PyPI in dependency order. - On the Release PR, run scripts/sync_python_pins.py after release-plz so the pyproject.toml pins between the Python packages follow the version bumps (the Cargo dependencies between them cascade the bumps). - Python releases no longer get the Rust THIRD-PARTY-LICENSES attachment; openjd-model ships its own. Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Describe the python/ packages in the README, AGENTS.md and RELEASING.md (including the one-time PyPI trusted-publishing and signing setup), and update python/openjd-model's AGENTS.md, generate_stubs.sh and the package READMEs for the workspace layout and the release-plz flow. Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
…li 0.7.8 Set the first versions released from this repository and write their changelog entries. With no python-<distribution>-v<version> tag for these versions yet, the release-plz `release` job on the merge commit tags and publishes them; later Release PRs start from those tags. - openjd-model 0.14.0: built from the in-repo openjd-expr 0.11.0, openjd-model 0.12.0 and openjd-sessions 0.8.0, a breaking change for create_job's let-binding errors. - openjd-sessions 0.13.1 and openjd-cli 0.7.8: require the new versions (pins updated with scripts/sync_python_pins.py). Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
| echo "Creating ephemeral venv..." | ||
| python -m venv "$venv_dir" | ||
| "$venv_dir/bin/pip" install --quiet --upgrade pip >/dev/null | ||
| "$venv_dir/bin/pip" install --quiet "$wheel_dir"/openjd_model-*.whl pip-licenses >/dev/null |
There was a problem hiding this comment.
Should fix: The Python half of this check depends on whatever PyPI serves at run time, not on anything in the repo. The step installs the wheel with pydantic >= 2.10, < 3 and pyyaml == 6.0.* unpinned, and pip-licenses writes each resolved version into the output (** name; version X).
Failure: pydantic, pydantic-core, typing-extensions, annotated-types or typing-inspection ships a new release. The regenerated file then no longer matches the committed THIRD-PARTY-LICENSES.txt, so the Python package checks job in python.yml fails on every PR that touches python/ or the expr/model/sessions crates, even when the PR doesn't change dependencies.
Fix: resolve the Python section from a pinned constraints/lock file that gets updated together with the committed file. Or compare only package names and licenses, not versions.
|
Claude review · advisory Reviewed Open: 0 blocking · 1 should-fix · 0 nit. ❌ Address or reply to the open threads. Fix or reply to each thread; the next revision's review re-checks open threads and resolves those it agrees are handled. Resolving a thread yourself also closes it. Later revisions review only what changed. |
|
Useful to see we can do it. Not for right now. |
Fixes: none
Important
Before merging
branch-protectionruleset allows only squash merges and requires linear history, so landing this with history needs an admin to bypass the ruleset (or relax it) for this one PR. Squashing works, but the imported history is lost andgit blameunderpython/points at the squash commit.Signed-off-by(7 model, 10 sessions, 8 cli). They're other people's historical commits, so this PR doesn't rewrite them. A maintainer can mark DCO as passing for them. Every new commit in this PR is signed off.openjd-model0.14.0,openjd-sessions0.13.1 andopenjd-cli0.7.8 to PyPI. Before merging, set up PyPI trusted publishing and the signing secrets (see "One-time setup" below). Otherwise the PyPI step fails; it can be re-run after the setup.What was the problem/requirement? (What/Why)
OpenJD development is centered here, but the Python packages live in three other repositories, and each has its own release process. A Rust change that Python users should get takes a chain of releases and PRs:
The pins drift along the way. On
mainlinetoday, openjd-cli still requiresopenjd-model < 0.12andopenjd-sessions < 0.13, while both are at 0.13.0.What was the solution? (How)
Move the three Python packages into this repository with their history, build and test them from the Cargo workspace, and release them with the same release-plz Release PR as the crates.
Layout
python/openjd-model,python/openjd-sessionsandpython/openjd-cliare the old repositories, imported withgit filter-repo:python/<pkg>/.legacy/<repository>/<version>. Push them separately if you want them upstream (git push origin 'refs/tags/legacy/*'); release-plz ignores them.(#123)PR references are rewritten toOpenJobDescription/<repository>#123, so they don't link to the wrong PRs here.openjd-model-py, is a workspace member. It depends onopenjd-expr,openjd-modelandopenjd-sessionsby path instead of from crates.io. This removes the bindings' nested Cargo workspace, its Cargo.lock and deny.toml, and the[patch.crates-io]dance.publish = false"release anchor" crate (openjd-sessions-py,openjd-cli-py), so release-plz can version them.python/crates aren't indefault-members, so plaincargo build/cargo teststill cover only the Rust crates.Versions
versionin itsCargo.toml. maturin reads it natively, hatch reads it with a regex, and_version.pyreads it back from the installed package's metadata.generate_version.py,maturin_build.pyand the hatch VCS/copy hooks.Releasing (
release-plz.toml,release-plz.yml)git_only: their last release comes from apython-<distribution>-v<version>tag, and they're never published to crates.io. release-plz bumps them from conventional commits that touch their directories.openjd-expr,openjd-modeloropenjd-sessionstherefore releasesopenjd-modeltoo, and then the packages that depend on it, in the same Release PR.scripts/sync_python_pins.pyon the PR branch to move the pins between the Python packages.openjd-modelwheels and sdist, and theopenjd-sessions/openjd-cliwheel and sdist, from the tag;CI
python.ymlrunshatch run lint,hatch buildandhatch run testfor each package on Linux, Windows and macOS. Each package keeps the Python versions it was tested on before: 3.9–3.14, and 3.9–3.12 for cli.THIRD-PARTY-LICENSES.txtcheck, andsync_python_pins.py --check. It's path-filtered topython/**,crates/openjd-{expr,model,sessions}/**, Cargo files and the workflow itself.What is the impact of this change?
python/here, and their open issues moved withgh issue transfer. Their open PRs are Dependabot bumps and two draft sessions PRs (fix(sessions): prune exited environment variables from WrappedAction.Environment #362, docs(expr): warn that is_relative_to is a lexical prefix test, not containment #367), which would need re-opening here.One-time setup (also in RELEASING.md)
openjd-model,openjd-sessionsandopenjd-cli: repositoryOpenJobDescription/openjd-rs, workflowrelease-plz.yml, environmentrelease.releaseenvironment needsAWS_PGP_KEY_SECRET_ROLE,AWS_PGP_KEY_SECRETandPGP_USER, as the old repositories had.How was this change tested?
< 0.12/< 0.13).hatch run lintis clean for all three.cargo clippy --workspace --all-targets -D warnings,cargo +nightly fmt --check,cargo doc -D warningsandcargo deny checkpass.mainwith a fork-only commit that points the repository guards at the fork, skips the crates, and drops signing and PyPI (which need upstream secrets):python-*tags and GitHub Releases on the merge commit, with the changelog entries as release notes.openjd checkandopenjd runsucceed.fix(model):commit produced a Release PR bumping openjd-model to 0.14.1, which cascaded to openjd-sessions 0.13.2 and openjd-cli 0.7.9. The workflow added the "sync Python package pins" commit, and merging that PR released all three.0.7.1entry in the sessions changelog stopped release-plz from parsing it, which left the release notes empty.reusable_release.ymland the PyPA action, but their first run is the real release.Was this change documented?
python/openjd-model/AGENTS.mdis updated for the workspace layout. The package READMEs link to this repository's releases.Is this a breaking change?
For
openjd-model(Python), yes. Building against the in-repo crates picks up openjd-expr 0.11.0, openjd-model 0.12.0 and openjd-sessions 0.8.0. The PyPI 0.13.0 release used 0.10.1, 0.11.0 and 0.7.3. Hence 0.14.0:create_jobraisesModelValidationErrorat the binding's template path (e.g.steps[0] -> script -> let[0]), notExpressionError, when aletbinding fails. It also reports every job-creation error at once (feat(model)!: report all job-creation errors at once, with paths #428).'(' was never closed(fix(expr)!: update ruff parser to 0.16.10, pin exact version, raise MSRV to 1.97 #429).C:are treated as filesystem paths (fix(expr): treat single-letter URI schemes as filesystem paths #424).The five affected tests are updated. openjd-sessions and openjd-cli only move their pins.
Two other changes in the imported code:
fix(model): mypy failed onmainlinewith pydantic 2.14 (FieldInfo.annotationis nowTypeForm[Any]). This also fails in openjd-model-for-python today.scripts/generate_stubs.shhas its paths updated for the new layout. I couldn't run it end to end: buildingpyo3-stub-gen0.23 underabi3-py39fails onmainlinetoo (the/tmppatch the script clones isn't wired in).Does this change impact security?
It doesn't change runtime code paths. It changes release automation: PyPI uploads move to this repository's
releaseenvironment via trusted publishing, and PGP signing uses the same secrets as before. Third-party actions are pinned to commit SHAs (PyO3/maturin-action,pypa/gh-action-pypi-publish,aws-actions/configure-aws-credentials).By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.