Add metadata checks for Tuya and Gledopto (#867)

This commit is contained in:
Koen Kanters
2025-09-11 21:02:02 +02:00
committed by GitHub
parent baac87f000
commit 5ab4317878
5 changed files with 48 additions and 3 deletions

View File

@@ -19,6 +19,10 @@ import {
} from "./common.js"; } from "./common.js";
import type {ExtraMetas, GHExtraMetas, RepoImageMeta} from "./types.js"; import type {ExtraMetas, GHExtraMetas, RepoImageMeta} from "./types.js";
const GLEDOPTO_MANUFACTURER_CODE = 4687;
const TUYA_MANUFACTURER_CODE_1 = 4098;
const TUYA_MANUFACTURER_CODE_2 = 4417;
const EXTRA_METAS_PR_BODY_START_TAG = "```json"; const EXTRA_METAS_PR_BODY_START_TAG = "```json";
const EXTRA_METAS_PR_BODY_END_TAG = "```"; const EXTRA_METAS_PR_BODY_END_TAG = "```";
@@ -26,7 +30,6 @@ function getFileExtraMetas(extraMetas: GHExtraMetas, fileName: string): ExtraMet
if (Array.isArray(extraMetas)) { if (Array.isArray(extraMetas)) {
const fileExtraMetas = extraMetas.find((m) => m.fileName === fileName) ?? {}; const fileExtraMetas = extraMetas.find((m) => m.fileName === fileName) ?? {};
/** @see getValidMetas */ /** @see getValidMetas */
// biome-ignore lint/performance/noDelete: <explanation>
delete fileExtraMetas.fileName; delete fileExtraMetas.fileName;
return fileExtraMetas; return fileExtraMetas;
@@ -36,7 +39,7 @@ function getFileExtraMetas(extraMetas: GHExtraMetas, fileName: string): ExtraMet
return extraMetas; return extraMetas;
} }
async function getPRBody(github: Octokit, core: typeof CoreApi, context: Context): Promise<string | undefined> { async function getPRBody(github: Octokit, _core: typeof CoreApi, context: Context): Promise<string | undefined> {
assert(context.payload.pull_request || context.eventName === "push"); assert(context.payload.pull_request || context.eventName === "push");
if (context.payload.pull_request) { if (context.payload.pull_request) {
@@ -119,7 +122,7 @@ export async function processOtaFiles(
core.startGroup(filePath); core.startGroup(filePath);
const logPrefix = `[${filePath}]`; const logPrefix = `[${filePath}]`;
let failureComment = ""; let failureComment: string | undefined;
try { try {
const firmwareFileName = path.basename(filePath); const firmwareFileName = path.basename(filePath);
@@ -145,6 +148,20 @@ export async function processOtaFiles(
const [baseMatchIndex, baseMatch] = findMatchImage(parsedImage, baseManifest, fileExtraMetas); const [baseMatchIndex, baseMatch] = findMatchImage(parsedImage, baseManifest, fileExtraMetas);
const statusToBase = getParsedImageStatus(parsedImage, baseMatch); const statusToBase = getParsedImageStatus(parsedImage, baseMatch);
// Manufacturer specific checks
if (parsedImage.manufacturerCode === GLEDOPTO_MANUFACTURER_CODE && !fileExtraMetas.modelId) {
// Gledopto uses the same imageType for every device, force modelId to be present.
// https://github.com/Koenkk/zigbee-OTA/pull/864
throw new Error("Gledopto image requires extra `modelId` metadata");
}
if (
(TUYA_MANUFACTURER_CODE_1 === parsedImage.manufacturerCode || TUYA_MANUFACTURER_CODE_2 === parsedImage.manufacturerCode) &&
!fileExtraMetas.manufacturerName
) {
// Tuya uses the same imageType for every device, force manufacturerName to be present.
throw new Error("Tuya image requires extra `manufacturerName` metadata");
}
switch (statusToBase) { switch (statusToBase) {
case ParsedImageStatus.Older: { case ParsedImageStatus.Older: {
// if prev doesn't have a match, move to prev // if prev doesn't have a match, move to prev

View File

@@ -11,6 +11,8 @@ import {it} from "vitest";
import * as common from "../src/common"; import * as common from "../src/common";
import type {ExtraMetas, RepoImageMeta} from "../src/types"; import type {ExtraMetas, RepoImageMeta} from "../src/types";
export const IMAGE_GLEDOPTO = "GL-B-008P_V17A1_OTAV7.ota";
export const IMAGE_TUYA = "1662693814-oem_mg21_zg_nh_win_cover_relay_OTA_1.0.7.bin";
export const IMAGE_V14_1 = "ZLinky_router_v14.ota"; export const IMAGE_V14_1 = "ZLinky_router_v14.ota";
export const IMAGE_V14_2 = "ZLinky_router_v14_limited.ota"; export const IMAGE_V14_2 = "ZLinky_router_v14_limited.ota";
export const IMAGE_V13_1 = "ZLinky_router_v13.ota"; export const IMAGE_V13_1 = "ZLinky_router_v13.ota";

View File

@@ -10,7 +10,9 @@ import type {RepoImageMeta} from "../src/types";
import { import {
BASE_IMAGES_TEST_DIR_PATH, BASE_IMAGES_TEST_DIR_PATH,
getAdjustedContent, getAdjustedContent,
IMAGE_GLEDOPTO,
IMAGE_INVALID, IMAGE_INVALID,
IMAGE_TUYA,
IMAGE_V12_1, IMAGE_V12_1,
IMAGE_V12_1_METAS, IMAGE_V12_1_METAS,
IMAGE_V13_1, IMAGE_V13_1,
@@ -626,4 +628,28 @@ Text after end tag`);
]); ]);
expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V12_1_METAS]); expect(writeManifestSpy).toHaveBeenCalledWith(common.PREV_INDEX_MANIFEST_FILENAME, [IMAGE_V12_1_METAS]);
}); });
it.each([
['"manufacturerName": ["_TZ3000"]', IMAGE_TUYA, undefined],
["", IMAGE_TUYA, "Tuya image requires extra `manufacturerName` metadata"],
['"modelId": "GL-C-008"', IMAGE_GLEDOPTO, undefined],
["", IMAGE_GLEDOPTO, "Gledopto image requires extra `modelId` metadata"],
])("manufacturer specific checks", async (body: string, image: string, error: string | undefined) => {
filePaths = [useImage(image)];
const newContext = withBody(`\`\`\`json
{
${body}
}
\`\`\``);
if (error === undefined) {
// @ts-expect-error mock
await checkOtaPR(github, core, newContext);
} else {
await expect(async () => {
// @ts-expect-error mock
await checkOtaPR(github, core, newContext);
}).rejects.toThrow(expect.objectContaining({message: expect.stringContaining(error)}));
}
});
}); });

Binary file not shown.