mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-19 08:03:16 +00:00
fix(cli): accept server-default retry/stop_after_if fields in lint
The CLI linter rejected valid flow YAML that the server itself emits.
A module with a constant retry and no early-stop error message serialises
its unset defaults as `seconds: 0`, `random_factor: null` and
`error_message: null`, which the OpenFlow-derived schema rejected:
- `exponential.seconds` had `minimum: 1`, but the Rust default is 0
- `exponential.random_factor` (`Option<i8>`) was not nullable
- typed nullable fields relied on AJV's non-standard `nullable` keyword
Fixes:
- openflow.openapi.yaml: `exponential.seconds` minimum 1 -> 0; add
`nullable: true` to `exponential.random_factor`.
- gen_openflow_schema.sh: rewrite typed (non-enum) `{ type, nullable: true }`
fields to standard JSON Schema `{ type: ["<T>", "null"] }` so the generated
schemas accept null regardless of validator nullable support.
- bump windmill-yaml-validator 1.1.1 -> 1.1.2 and the CLI dependency.
Adds a regression test covering a flow with default retry and stop_after_if.
Fixes WIN-2048
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3cf4083960
commit
8b9c2a52c2
Generated
+3
-4
@@ -32,7 +32,7 @@
|
||||
"windmill-parser-wasm-rust": "1.647.1",
|
||||
"windmill-parser-wasm-ts": "1.695.0",
|
||||
"windmill-parser-wasm-yaml": "1.593.0",
|
||||
"windmill-yaml-validator": "1.1.1",
|
||||
"windmill-yaml-validator": "1.1.2",
|
||||
"ws": "8.18.0",
|
||||
"yaml": "^2.7.0"
|
||||
},
|
||||
@@ -1470,9 +1470,8 @@
|
||||
"integrity": "sha512-Gyx4aR2jsJYuDrD3mCNTmz7LWOQQXPw5yKNCC1xRgUOPfjsD/tINAFfsBLwVOSmlQQcFZO+wHm4KtDtXOcnGVw=="
|
||||
},
|
||||
"node_modules/windmill-yaml-validator": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-yaml-validator/-/windmill-yaml-validator-1.1.1.tgz",
|
||||
"integrity": "sha512-CVgAwEoBdJhF39q2N012QffhlGPRIyIWd8gj7NnfG+/lMWgH2k5CBLtKIt6cPF8Bxz+6DGC3st1ARSsecDtbTg==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/windmill-yaml-validator/-/windmill-yaml-validator-1.1.2.tgz",
|
||||
"license": "Apache 2.0",
|
||||
"dependencies": {
|
||||
"@stoplight/yaml": "^4.3.0",
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@
|
||||
"windmill-parser-wasm-rust": "1.647.1",
|
||||
"windmill-parser-wasm-ts": "1.695.0",
|
||||
"windmill-parser-wasm-yaml": "1.593.0",
|
||||
"windmill-yaml-validator": "1.1.1",
|
||||
"windmill-yaml-validator": "1.1.2",
|
||||
"ws": "8.18.0",
|
||||
"yaml": "^2.7.0"
|
||||
},
|
||||
|
||||
@@ -152,10 +152,11 @@ components:
|
||||
description: Multiplier for exponential backoff
|
||||
seconds:
|
||||
type: integer
|
||||
minimum: 1
|
||||
minimum: 0
|
||||
description: Initial delay in seconds
|
||||
random_factor:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
description: Random jitter percentage (0-100) to avoid thundering herd
|
||||
|
||||
@@ -94,3 +94,35 @@ for (const file of files) {
|
||||
}
|
||||
console.log('Added null to nullable enums in generated schemas');
|
||||
"
|
||||
|
||||
# OpenAPI 3.0 expresses nullable typed fields as { type: \"<T>\", nullable: true },
|
||||
# which is not standard JSON Schema. For typed (non-enum) fields, rewrite them to
|
||||
# the standard { type: [\"<T>\", \"null\"] } form so any JSON Schema validator accepts
|
||||
# null. Enum-nullable fields are handled by the pass above and are left untouched.
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function convertNullableTypesToArrays(obj) {
|
||||
if (!obj || typeof obj !== 'object') return;
|
||||
if (Array.isArray(obj)) { obj.forEach(convertNullableTypesToArrays); return; }
|
||||
if (obj.nullable === true && typeof obj.type === 'string' && !Array.isArray(obj.enum)) {
|
||||
obj.type = [obj.type, 'null'];
|
||||
delete obj.nullable;
|
||||
}
|
||||
for (const v of Object.values(obj)) convertNullableTypesToArrays(v);
|
||||
}
|
||||
|
||||
const files = [
|
||||
'${output_dirpath}/openflow.json',
|
||||
'${output_dirpath}/schedule.json',
|
||||
...fs.readdirSync('${output_dirpath}/triggers').map(f => path.join('${output_dirpath}/triggers', f))
|
||||
].filter(f => f.endsWith('.json'));
|
||||
|
||||
for (const file of files) {
|
||||
const schema = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
convertNullableTypesToArrays(schema);
|
||||
fs.writeFileSync(file, JSON.stringify(schema, null, 2) + '\n');
|
||||
}
|
||||
console.log('Converted nullable typed fields to type arrays in generated schemas');
|
||||
"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-yaml-validator",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"description": "YAML validator for Windmill flow, schedule, and trigger files",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -88,6 +88,16 @@ describe("WindmillYamlValidator", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
it("should validate a flow with default retry and stop_after_if fields (WIN-2048)", () => {
|
||||
// The server serialises unset retry/stop_after_if defaults as
|
||||
// seconds: 0, random_factor: null and error_message: null. These must
|
||||
// round-trip through the linter without errors.
|
||||
const validFlow = readSample("valid-default-retry-stop-after-if.yaml");
|
||||
|
||||
const result = validator.validate(validFlow, { type: "flow" });
|
||||
|
||||
expect(result.errors).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("invalid flows", () => {
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# Mirrors the YAML the server emits for a module with a constant retry and a
|
||||
# stop_after_if without an error message. These defaults (seconds: 0,
|
||||
# random_factor: null, error_message: null) used to be rejected by the linter.
|
||||
summary: Flow with default retry and stop_after_if
|
||||
value:
|
||||
modules:
|
||||
- id: script_step
|
||||
value:
|
||||
type: rawscript
|
||||
input_transforms: {}
|
||||
content: |
|
||||
export async function main() {
|
||||
return "ok";
|
||||
}
|
||||
language: deno
|
||||
retry:
|
||||
constant:
|
||||
attempts: 2
|
||||
seconds: 5
|
||||
exponential:
|
||||
attempts: 0
|
||||
multiplier: 1
|
||||
seconds: 0
|
||||
random_factor: null
|
||||
stop_after_if:
|
||||
expr: "false"
|
||||
error_message: null
|
||||
Reference in New Issue
Block a user