Skip to content

Commit ccef796

Browse files
CopilotAlCalzone
andauthored
Add non-semver version detection to config validation; fix ZWA005 $if conditions (#285)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: AlCalzone <17641229+AlCalzone@users.noreply.github.com>
1 parent 9e354a0 commit ccef796

File tree

4 files changed

+69
-4
lines changed

4 files changed

+69
-4
lines changed

firmwares/aeotec/ZWA005-A.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"upgrades": [
1212
//firmware V2.00 to V2.20
1313
{
14-
"$if": "firmwareVersion >= 2.00 && firmwareVersion < 2.21",
14+
"$if": "firmwareVersion >= 2.0 && firmwareVersion < 2.21",
1515
"version": "2.21",
1616
"channel": "stable",
1717
"changelog": "Bug Fixes:\n* Resolved random white LED flash when re-powering\n* Fixes lux reporting 0\n* Fixes sensor crash\n* Changes Parameter 100 coefficient for calibration\n* Adjusts battery calibration used for factory production",

firmwares/aeotec/ZWA005-B.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"upgrades": [
1212
//firmware V2.00 to V2.20
1313
{
14-
"$if": "firmwareVersion >= 2.00 && firmwareVersion < 2.21",
14+
"$if": "firmwareVersion >= 2.0 && firmwareVersion < 2.21",
1515
"version": "2.21",
1616
"channel": "stable",
1717
"changelog": "Bug Fixes:\n* Resolved random white LED flash when re-powering\n* Fixes lux reporting 0\n* Fixes sensor crash\n* Changes Parameter 100 coefficient for calibration\n* Adjusts battery calibration used for factory production",

firmwares/aeotec/ZWA005-C.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"upgrades": [
1212
//firmware V2.00 to V2.20
1313
{
14-
"$if": "firmwareVersion >= 2.00 && firmwareVersion < 2.21",
14+
"$if": "firmwareVersion >= 2.0 && firmwareVersion < 2.21",
1515
"version": "2.21",
1616
"channel": "stable",
1717
"changelog": "Bug Fixes:\n* Resolved random white LED flash when re-powering\n* Fixes lux reporting 0\n* Fixes sensor crash\n* Changes Parameter 100 coefficient for calibration\n* Adjusts battery calibration used for factory production",

src/maintenance/check_configs.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import JSON5 from "json5";
22
import path from "path-browserify";
33
import { ConditionalUpdateConfig } from "../lib/config.js";
4+
import { parseLogic } from "../lib/Logic.js";
45
import { getErrorMessage } from "../lib/shared.js";
56
import { NodeFS } from "./nodeFS.js";
67

@@ -11,6 +12,47 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
1112

1213
const configDir = path.resolve(__dirname, "../../firmwares");
1314

15+
const VERSION_OPERATORS = new Set([
16+
"ver >=",
17+
"ver >",
18+
"ver <=",
19+
"ver <",
20+
"ver ===",
21+
]);
22+
23+
/** Returns true if a version string has a part with a leading zero, e.g. "2.00" */
24+
function hasLeadingZeroVersionPart(version: string): boolean {
25+
return version
26+
.split(".")
27+
.some((part) => part.length > 1 && part.startsWith("0"));
28+
}
29+
30+
/** Recursively walks a JSON Logic object and collects version strings that have leading zeros */
31+
function findNonSemverVersionsInLogic(logic: unknown): string[] {
32+
if (!logic || typeof logic !== "object") return [];
33+
const results: string[] = [];
34+
for (const [key, value] of Object.entries(logic)) {
35+
if (VERSION_OPERATORS.has(key)) {
36+
if (Array.isArray(value) && value.length >= 2) {
37+
const version = value[1];
38+
if (
39+
typeof version === "string" &&
40+
hasLeadingZeroVersionPart(version)
41+
) {
42+
results.push(version);
43+
}
44+
}
45+
} else if (Array.isArray(value)) {
46+
for (const item of value) {
47+
results.push(...findNonSemverVersionsInLogic(item));
48+
}
49+
} else if (typeof value === "object") {
50+
results.push(...findNonSemverVersionsInLogic(value));
51+
}
52+
}
53+
return results;
54+
}
55+
1456
interface ValidationResult {
1557
filename: string;
1658
errors: string[];
@@ -29,7 +71,30 @@ async function validateConfigFile(filePath: string): Promise<ValidationResult> {
2971
const definition = JSON5.parse(fileContent);
3072

3173
// Create ConditionalUpdateConfig which validates the schema and runs sanity checks
32-
new ConditionalUpdateConfig(definition);
74+
const config = new ConditionalUpdateConfig(definition);
75+
76+
// Check $if conditions for non-semver versions (e.g. leading zeros like "2.00")
77+
for (let i = 0; i < config.upgrades.length; i++) {
78+
const upgrade = config.upgrades[i];
79+
if (upgrade.$if) {
80+
let logic: unknown;
81+
try {
82+
logic = parseLogic(upgrade.$if);
83+
} catch (e) {
84+
errors.push(
85+
`upgrades[${i}].$if is not valid logic: ${getErrorMessage(e)}`,
86+
);
87+
continue;
88+
}
89+
const nonSemverVersions =
90+
findNonSemverVersionsInLogic(logic);
91+
for (const version of nonSemverVersions) {
92+
errors.push(
93+
`upgrades[${i}].$if contains non-semver version "${version}" (has leading zeros)`,
94+
);
95+
}
96+
}
97+
}
3398
} catch (parseError) {
3499
if (
35100
parseError instanceof Error &&

0 commit comments

Comments
 (0)