fix: Switch to biome & vitest (#726)

This commit is contained in:
Nerivec
2025-03-23 21:30:14 +01:00
committed by GitHub
parent 44c5bbcc1c
commit 94b76a910a
48 changed files with 1762 additions and 4075 deletions

View File

@@ -1,10 +1,10 @@
import {getJson, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage} from '../process_firmware_image.js';
import {getJson, readCacheJson, writeCacheJson} from "../common.js";
import {processFirmwareImage} from "../process_firmware_image.js";
type ImagesJsonBuildPart = {
path: string; // .bin
offset: number;
type?: 'app' | 'storage';
type?: "app" | "storage";
ota?: string; // .ota
};
type ImagesJsonBuild = {
@@ -21,11 +21,11 @@ type ImagesJson = {
builds: ImagesJsonBuild[];
};
const NAME = 'GammaTroniques';
const NAME = "GammaTroniques";
// const LOG_PREFIX = `[${NAME}]`;
const BASE_URL = 'https://update.gammatroniques.fr/';
const MANIFEST_URL_PATH = `/manifest.json`;
const MODEL_IDS: [urlId: string, modelId: string][] = [['ticmeter', 'TICMeter']];
const BASE_URL = "https://update.gammatroniques.fr/";
const MANIFEST_URL_PATH = "/manifest.json";
const MODEL_IDS: [urlId: string, modelId: string][] = [["ticmeter", "TICMeter"]];
function isDifferent(newData: ImagesJson, cachedData?: ImagesJson): boolean {
return Boolean(process.env.IGNORE_CACHE) || !cachedData || cachedData.version !== newData.version;
@@ -62,14 +62,14 @@ export async function download(): Promise<void> {
writeCacheJson(cacheFileName, page);
const appUrl: ImagesJsonBuildPart | undefined = page.builds[0].parts.find((part) => part.type === 'app');
const appUrl: ImagesJsonBuildPart | undefined = page.builds[0].parts.find((part) => part.type === "app");
if (!appUrl || !appUrl.ota) {
console.error(`${logPrefix} No image found.`);
continue;
}
const firmwareFileName = appUrl.ota.split('/').pop()!;
const firmwareFileName = appUrl.ota.split("/").pop()!;
await processFirmwareImage(NAME, firmwareFileName, appUrl.ota, {modelId});
}

View File

@@ -1,7 +1,7 @@
import type {ExtraMetas} from '../types.js';
import type {ExtraMetas} from "../types.js";
import {getJson, getLatestImage, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage} from '../process_firmware_image.js';
import {getJson, getLatestImage, readCacheJson, writeCacheJson} from "../common.js";
import {processFirmwareImage} from "../process_firmware_image.js";
type ReleaseAssetJson = {
url: string;

View File

@@ -1,5 +1,5 @@
import {getJson, getLatestImage, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage} from '../process_firmware_image.js';
import {getJson, getLatestImage, readCacheJson, writeCacheJson} from "../common.js";
import {processFirmwareImage} from "../process_firmware_image.js";
type ImageJson = {
createdAt: string;
@@ -13,34 +13,34 @@ type ImageJson = {
};
type PageJson = {updates: ImageJson[]};
const NAME = 'Hue';
const BASE_URL = 'https://firmware.meethue.com/v1/checkupdate?version=0&deviceTypeId=';
const NAME = "Hue";
const BASE_URL = "https://firmware.meethue.com/v1/checkupdate?version=0&deviceTypeId=";
const DEVICE_TYPE_IDS: string[] = [
'100b-111',
'100b-112',
"100b-111",
"100b-112",
// '100b-113',
'100b-114',
'100b-115',
"100b-114",
"100b-115",
// '100b-116',
'100b-117',
'100b-118',
"100b-117",
"100b-118",
// '100b-119',
'100b-11a',
"100b-11a",
// '100b-11b',
// '100b-11c',
'100b-11d',
'100b-11e',
'100b-11f',
'100b-120',
"100b-11d",
"100b-11e",
"100b-11f",
"100b-120",
// '100b-121',
// '100b-122',
'100b-123',
"100b-123",
// '100b-124',
'100b-125',
"100b-125",
// '100b-126',
'100b-127',
"100b-127",
// '100b-128',
'100b-129',
"100b-129",
// '100b-12a',
// '100b-12b',
// '100b-12c',
@@ -98,7 +98,7 @@ export async function download(): Promise<void> {
continue;
}
const firmwareFileName = image.binaryUrl.split('/').pop()!;
const firmwareFileName = image.binaryUrl.split("/").pop()!;
await processFirmwareImage(NAME, firmwareFileName, image.binaryUrl, {releaseNotes: image.releaseNotes || undefined});
}

View File

@@ -1,5 +1,5 @@
import {getJson, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage} from '../process_firmware_image.js';
import {getJson, readCacheJson, writeCacheJson} from "../common.js";
import {processFirmwareImage} from "../process_firmware_image.js";
type GatewayImageJson = {
fw_binary_url: string;
@@ -16,7 +16,9 @@ type GatewayImageJson = {
};
type DeviceImageJson = {
fw_binary_url: string;
// biome-ignore lint/style/useNamingConvention: <explanation>
fw_file_version_LSB: number;
// biome-ignore lint/style/useNamingConvention: <explanation>
fw_file_version_MSB: number;
fw_filesize: number;
fw_image_type: number;
@@ -25,16 +27,16 @@ type DeviceImageJson = {
};
type ImagesJson = (GatewayImageJson | DeviceImageJson)[];
const NAME = 'IKEA';
const NAME = "IKEA";
const LOG_PREFIX = `[${NAME}]`;
const PRODUCTION_FIRMWARE_URL = 'http://fw.ota.homesmart.ikea.net/feed/version_info.json';
const PRODUCTION_FIRMWARE_URL = "http://fw.ota.homesmart.ikea.net/feed/version_info.json";
// const TEST_FIRMWARE_URL = 'http://fw.test.ota.homesmart.ikea.net/feed/version_info.json';
export const RELEASE_NOTES_URL = 'https://ww8.ikea.com/ikeahomesmart/releasenotes/releasenotes.html';
export const RELEASE_NOTES_URL = "https://ww8.ikea.com/ikeahomesmart/releasenotes/releasenotes.html";
function findInCache(image: DeviceImageJson, cachedData?: ImagesJson): DeviceImageJson | undefined {
// `fw_type` compare ensures always `DeviceImagesJson`
return cachedData?.find(
(d) => d.fw_type == image.fw_type && d.fw_image_type == image.fw_image_type && d.fw_manufacturer_id == image.fw_manufacturer_id,
(d) => d.fw_type === image.fw_type && d.fw_image_type === image.fw_image_type && d.fw_manufacturer_id === image.fw_manufacturer_id,
) as DeviceImageJson | undefined;
}
@@ -67,7 +69,7 @@ export async function download(): Promise<void> {
continue;
}
const firmwareFileName = image.fw_binary_url.split('/').pop()!;
const firmwareFileName = image.fw_binary_url.split("/").pop()!;
if (!isDifferent(image, findInCache(image, cachedData))) {
console.log(`[${NAME}:${firmwareFileName}] No change from last run.`);

View File

@@ -1,6 +1,6 @@
import {getJson, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage} from '../process_firmware_image.js';
import {RELEASE_NOTES_URL} from './ikea.js';
import {getJson, readCacheJson, writeCacheJson} from "../common.js";
import {processFirmwareImage} from "../process_firmware_image.js";
import {RELEASE_NOTES_URL} from "./ikea.js";
type GatewayImageJson = {
fw_type: 3;
@@ -23,15 +23,15 @@ type DeviceImageJson = {
type ImagesJson = (GatewayImageJson | DeviceImageJson)[];
// same name as `ikea.ts` to keep everything in same folder
const NAME = 'IKEA';
const NAME = "IKEA";
const CACHE_FILENAME = `${NAME}_new`;
const LOG_PREFIX = `[${NAME}_new]`;
// requires cacerts/ikea_new.pem
const FIRMWARE_URL = 'https://fw.ota.homesmart.ikea.com/check/update/prod';
const FIRMWARE_URL = "https://fw.ota.homesmart.ikea.com/check/update/prod";
function findInCache(image: DeviceImageJson, cachedData?: ImagesJson): DeviceImageJson | undefined {
// `fw_type` compare ensures always `DeviceImagesJson`
return cachedData?.find((d) => d.fw_type == image.fw_type && d.fw_image_type == image.fw_image_type) as DeviceImageJson | undefined;
return cachedData?.find((d) => d.fw_type === image.fw_type && d.fw_image_type === image.fw_image_type) as DeviceImageJson | undefined;
}
function isDifferent(newData: DeviceImageJson, cachedData?: DeviceImageJson): boolean {
@@ -58,7 +58,7 @@ export async function download(): Promise<void> {
continue;
}
const firmwareFileName = image.fw_binary_url.split('/').pop()!;
const firmwareFileName = image.fw_binary_url.split("/").pop()!;
if (!isDifferent(image, findInCache(image, cachedData))) {
console.log(`[${NAME}:${firmwareFileName}] No change from last run.`);

View File

@@ -1,9 +1,9 @@
import {getJson, getLatestImage, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage} from '../process_firmware_image.js';
import {getJson, getLatestImage, readCacheJson, writeCacheJson} from "../common.js";
import {processFirmwareImage} from "../process_firmware_image.js";
type DeviceImageJson = {
version: string;
channel: 'beta' | 'production';
channel: "beta" | "production";
firmware: string;
manufacturer_id: number;
image_type: number;
@@ -12,15 +12,15 @@ type ModelsJson = {
[k: string]: DeviceImageJson[];
};
const NAME = 'Inovelli';
const NAME = "Inovelli";
const LOG_PREFIX = `[${NAME}]`;
const FIRMWARE_URL = 'https://files.inovelli.com/firmware/firmware.json';
const FIRMWARE_URL = "https://files.inovelli.com/firmware/firmware.json";
function sortByVersion(a: DeviceImageJson, b: DeviceImageJson): number {
const aRadix = a.version.match(/[a-fA-F]/) ? 16 : 10;
const bRadix = b.version.match(/[a-fA-F]/) ? 16 : 10;
const aVersion = parseInt(a.version, aRadix);
const bVersion = parseInt(b.version, bRadix);
const aVersion = Number.parseInt(a.version, aRadix);
const bVersion = Number.parseInt(b.version, bRadix);
return aVersion < bVersion ? -1 : aVersion > bVersion ? 1 : 0;
}
@@ -44,7 +44,7 @@ export async function download(): Promise<void> {
const cachedData = readCacheJson<ModelsJson>(NAME);
for (const model in models) {
if (model == '') {
if (model === "") {
// ignore empty key (bug)
continue;
}
@@ -55,7 +55,7 @@ export async function download(): Promise<void> {
continue;
}
const firmwareFileName = image.firmware.split('/').pop()!;
const firmwareFileName = image.firmware.split("/").pop()!;
if (cachedData && !isDifferent(image, getLatestImage(cachedData[model], sortByVersion))) {
console.log(`[${NAME}:${firmwareFileName}] No change from last run.`);

View File

@@ -1,5 +1,5 @@
import {getJson, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage} from '../process_firmware_image.js';
import {getJson, readCacheJson, writeCacheJson} from "../common.js";
import {processFirmwareImage} from "../process_firmware_image.js";
type ImageJson = {
vendor: string;
@@ -13,12 +13,12 @@ type ImageJson = {
version: string;
date: string;
images: {
'zigbee.ota': {
"zigbee.ota": {
url: string;
hash: string;
filesize: number;
};
'zigbee.bin': {
"zigbee.bin": {
url: string;
hash: string;
filesize: number;
@@ -29,12 +29,12 @@ type ImageJson = {
};
};
const NAME = 'JetHome';
const NAME = "JetHome";
const LOG_PREFIX = `[${NAME}]`;
const BASE_URL = 'https://fw.jethome.ru';
const BASE_URL = "https://fw.jethome.ru";
const DEVICE_URL = `${BASE_URL}/api/devices/`;
const MODEL_IDS = ['WS7'];
const MODEL_IDS = ["WS7"];
function getCacheFileName(modelId: string): string {
return `${NAME}_${modelId}`;
@@ -62,14 +62,14 @@ export async function download(): Promise<void> {
// XXX: this is assumed to always be present even for devices that support OTA but without images yet available?
if (image?.latest_firmware?.release?.images) {
const firmware = image.latest_firmware.release.images['zigbee.ota'];
const firmware = image.latest_firmware.release.images["zigbee.ota"];
if (!firmware) {
continue;
}
const firmwareUrl = BASE_URL + firmware.url;
const firmwareFileName = firmwareUrl.split('/').pop()!;
const firmwareFileName = firmwareUrl.split("/").pop()!;
const cacheFileName = getCacheFileName(modelId);
if (!isDifferent(image, readCacheJson(cacheFileName))) {
@@ -85,7 +85,6 @@ export async function download(): Promise<void> {
});
} else {
console.error(`${LOG_PREFIX} No image data for ${modelId}.`);
continue;
}
}
}

View File

@@ -1,5 +1,5 @@
import {getJson, getLatestImage, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage, ProcessFirmwareImageStatus} from '../process_firmware_image.js';
import {getJson, getLatestImage, readCacheJson, writeCacheJson} from "../common.js";
import {ProcessFirmwareImageStatus, processFirmwareImage} from "../process_firmware_image.js";
type FirmwareJson = {
blob: null;
@@ -34,12 +34,12 @@ type ImagesJson = {
};
type GroupedImagesJson = Record<string, FirmwareJson[]>;
const NAME = 'LEDVANCE';
const NAME = "LEDVANCE";
const LOG_PREFIX = `[${NAME}]`;
const FIRMWARE_URL = 'https://api.update.ledvance.com/v1/zigbee/firmwares/';
const FIRMWARE_URL = "https://api.update.ledvance.com/v1/zigbee/firmwares/";
// const UPDATE_CHECK_URL = 'https://api.update.ledvance.com/v1/zigbee/firmwares/newer';
// const UPDATE_CHECK_PARAMS = `?company=${manufCode}&product=${imageType}&version=0.0.0`;
const UPDATE_DOWNLOAD_URL = 'https://api.update.ledvance.com/v1/zigbee/firmwares/download';
const UPDATE_DOWNLOAD_URL = "https://api.update.ledvance.com/v1/zigbee/firmwares/download";
/** XXX: getting 429 after a few downloads, force more throttling. Seems to trigger after around ~20 requests. */
const FETCH_FAILED_THROTTLE_MS = 60000;
const FETCH_FAILED_RETRIES = 3;
@@ -98,7 +98,7 @@ export async function download(): Promise<void> {
// const fileVersion = parseInt(fileVersionMatch[1], 16);
const firmwareUrl = `${UPDATE_DOWNLOAD_URL}?company=${firmware.identity.company}&product=${firmware.identity.product}&version=${getVersionString(firmware)}`;
const firmwareFileName = firmware.fullName.split('/').pop()!;
const firmwareFileName = firmware.fullName.split("/").pop()!;
if (cachedDataByProduct && !isDifferent(firmware, getLatestImage(cachedDataByProduct[product], sortByReleased))) {
console.log(`[${NAME}:${firmwareFileName}] No change from last run.`);
@@ -112,7 +112,7 @@ export async function download(): Promise<void> {
releaseNotes: firmware.releaseNotes,
});
if (status === ProcessFirmwareImageStatus.REQUEST_FAILED) {
if (status === ProcessFirmwareImageStatus.RequestFailed) {
await new Promise((resolve) => setTimeout(resolve, FETCH_FAILED_THROTTLE_MS));
} else {
break;

View File

@@ -1,9 +1,9 @@
import * as github from './github.js';
import * as github from "./github.js";
const NAME = 'LiXee';
const FIRMWARE_URL = 'https://api.github.com/repos/fairecasoimeme/Zlinky_TIC/releases';
const NAME = "LiXee";
const FIRMWARE_URL = "https://api.github.com/repos/fairecasoimeme/Zlinky_TIC/releases";
/** @see https://github.com/fairecasoimeme/Zlinky_TIC?tab=readme-ov-file#route-or-limited-route-from-v7 */
const FIRMWARE_EXT = '.ota';
const FIRMWARE_EXT = ".ota";
const FIRMWARE_LIMITED = `limited${FIRMWARE_EXT}`;
export async function writeCache(): Promise<void> {

View File

@@ -1,5 +1,5 @@
import {getJson, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage} from '../process_firmware_image.js';
import {getJson, readCacheJson, writeCacheJson} from "../common.js";
import {processFirmwareImage} from "../process_firmware_image.js";
type ImageJson = {
model: string;
@@ -10,12 +10,12 @@ type ImagesJson = {
versions: ImageJson[];
};
const NAME = 'SalusControls';
const NAME = "SalusControls";
const LOG_PREFIX = `[${NAME}]`;
const FIRMWARE_URL = 'https://eu.salusconnect.io/demo/default/status/firmware?timestamp=0';
const FIRMWARE_URL = "https://eu.salusconnect.io/demo/default/status/firmware?timestamp=0";
function findInCache(image: ImageJson, cachedData?: ImagesJson): ImageJson | undefined {
return cachedData?.versions?.find((d) => d.model == image.model);
return cachedData?.versions?.find((d) => d.model === image.model);
}
function isDifferent(newData: ImageJson, cachedData?: ImageJson): boolean {
@@ -38,14 +38,14 @@ export async function download(): Promise<void> {
for (const image of images.versions) {
const archiveUrl = image.url; //.replace(/^http:\/\//, 'https://');
const archiveFileName = archiveUrl.split('/').pop()!;
const archiveFileName = archiveUrl.split("/").pop()!;
if (!isDifferent(image, findInCache(image, cachedData))) {
console.log(`[${NAME}:${archiveFileName}] No change from last run.`);
continue;
}
await processFirmwareImage(NAME, archiveFileName, archiveUrl, {manufacturerName: [NAME]}, true, (fileName) => fileName.endsWith('.ota'));
await processFirmwareImage(NAME, archiveFileName, archiveUrl, {manufacturerName: [NAME]}, true, (fileName) => fileName.endsWith(".ota"));
}
writeCacheJson(NAME, images);

View File

@@ -1,7 +1,7 @@
import url from 'url';
import url from "node:url";
import {getLatestImage, getText, readCacheJson, writeCacheJson} from '../common.js';
import {processFirmwareImage} from '../process_firmware_image.js';
import {getLatestImage, getText, readCacheJson, writeCacheJson} from "../common.js";
import {processFirmwareImage} from "../process_firmware_image.js";
type Image = {
fileName: string;
@@ -14,14 +14,14 @@ type GroupedImages = {
[k: string]: Image[];
};
const NAME = 'Ubisys';
const NAME = "Ubisys";
const LOG_PREFIX = `[${NAME}]`;
const FIRMWARE_HTML_URL = 'http://fwu.ubisys.de/smarthome/OTA/release/index';
const FIRMWARE_HTML_URL = "http://fwu.ubisys.de/smarthome/OTA/release/index";
function groupByImageType(arr: Image[]): GroupedImages {
return arr.reduce<GroupedImages>((acc, cur) => {
acc[cur.imageType + (cur.hardwareVersionMax ? cur.hardwareVersionMax : '')] = [
...(acc[cur.imageType + (cur.hardwareVersionMax ? cur.hardwareVersionMax : '')] || []),
acc[cur.imageType + (cur.hardwareVersionMax ? cur.hardwareVersionMax : "")] = [
...(acc[cur.imageType + (cur.hardwareVersionMax ? cur.hardwareVersionMax : "")] || []),
cur,
];
return acc;
@@ -37,7 +37,7 @@ function isDifferent(newData: Image, cachedData?: Image): boolean {
}
function parseText(pageText: string): Image[] {
const lines = pageText.split('\n');
const lines = pageText.split("\n");
const images: Image[] = [];
for (const line of lines) {
@@ -48,9 +48,9 @@ function parseText(pageText: string): Image[] {
images.push({
fileName: imageMatch[0],
imageType: imageMatch[1],
hardwareVersionMin: parseInt(imageMatch[2], 16),
hardwareVersionMax: parseInt(imageMatch[3], 16),
fileVersion: parseInt(imageMatch[4], 16),
hardwareVersionMin: Number.parseInt(imageMatch[2], 16),
hardwareVersionMax: Number.parseInt(imageMatch[3], 16),
fileVersion: Number.parseInt(imageMatch[4], 16),
});
}
}

View File

@@ -1,13 +1,13 @@
import * as github from './github.js';
import * as github from "./github.js";
const NAME = 'xyzroe';
const FIRMWARE_URL = 'https://api.github.com/repos/xyzroe/ZigUSB_C6/releases';
const FIRMWARE_EXT = '.ota';
const NAME = "xyzroe";
const FIRMWARE_URL = "https://api.github.com/repos/xyzroe/ZigUSB_C6/releases";
const FIRMWARE_EXT = ".ota";
export async function writeCache(): Promise<void> {
await github.writeCache(NAME, FIRMWARE_URL);
}
export async function download(): Promise<void> {
await github.download(NAME, FIRMWARE_URL, [(a): boolean => a.name.endsWith(FIRMWARE_EXT)], {modelId: 'ZigUSB_C6'});
await github.download(NAME, FIRMWARE_URL, [(a): boolean => a.name.endsWith(FIRMWARE_EXT)], {modelId: "ZigUSB_C6"});
}

View File

@@ -1,28 +1,28 @@
import type {ExtraMetas, ExtraMetasWithFileName, ImageHeader, RepoImageMeta} from './types';
import type {ExtraMetas, ExtraMetasWithFileName, ImageHeader, RepoImageMeta} from "./types";
import assert from 'assert';
import {exec} from 'child_process';
import {createHash} from 'crypto';
import {existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync} from 'fs';
import path from 'path';
import assert from "node:assert";
import {exec} from "node:child_process";
import {createHash} from "node:crypto";
import {existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync} from "node:fs";
import path from "node:path";
export const UPGRADE_FILE_IDENTIFIER = Buffer.from([0x1e, 0xf1, 0xee, 0x0b]);
export const BASE_REPO_URL = `https://raw.githubusercontent.com/Koenkk/zigbee-OTA/`;
export const REPO_BRANCH = 'master';
export const BASE_REPO_URL = "https://raw.githubusercontent.com/Koenkk/zigbee-OTA/";
export const REPO_BRANCH = "master";
/** Images used by OTA upgrade process */
export const BASE_IMAGES_DIR = 'images';
export const BASE_IMAGES_DIR = "images";
/** Images used by OTA downgrade process */
export const PREV_IMAGES_DIR = 'images1';
export const PREV_IMAGES_DIR = "images1";
/** Manifest used by OTA upgrade process */
export const BASE_INDEX_MANIFEST_FILENAME = 'index.json';
export const BASE_INDEX_MANIFEST_FILENAME = "index.json";
/** Manifest used by OTA downgrade process */
export const PREV_INDEX_MANIFEST_FILENAME = 'index1.json';
export const CACHE_DIR = '.cache';
export const TMP_DIR = 'tmp';
export const PR_ARTIFACT_DIR = 'pr';
export const PR_DIFF_FILENAME = 'PR_DIFF';
export const PR_ERROR_FILENAME = 'PR_ERROR';
export const PR_NUMBER_FILENAME = 'PR_NUMBER';
export const PREV_INDEX_MANIFEST_FILENAME = "index1.json";
export const CACHE_DIR = ".cache";
export const TMP_DIR = "tmp";
export const PR_ARTIFACT_DIR = "pr";
export const PR_DIFF_FILENAME = "PR_DIFF";
export const PR_ERROR_FILENAME = "PR_ERROR";
export const PR_NUMBER_FILENAME = "PR_NUMBER";
export const PR_ARTIFACT_DIFF_FILEPATH = path.join(PR_ARTIFACT_DIR, PR_DIFF_FILENAME);
export const PR_ARTIFACT_ERROR_FILEPATH = path.join(PR_ARTIFACT_DIR, PR_ERROR_FILENAME);
export const PR_ARTIFACT_NUMBER_FILEPATH = path.join(PR_ARTIFACT_DIR, PR_NUMBER_FILENAME);
@@ -30,17 +30,17 @@ export const PR_ARTIFACT_NUMBER_FILEPATH = path.join(PR_ARTIFACT_DIR, PR_NUMBER_
* 'ikea_new' first, to prioritize downloads from new URL
*/
export const ALL_AUTODL_MANUFACTURERS = [
'gammatroniques',
'hue',
'ikea_new',
'ikea',
'inovelli',
'jethome',
'ledvance',
'lixee',
'salus',
'ubisys',
'xyzroe',
"gammatroniques",
"hue",
"ikea_new",
"ikea",
"inovelli",
"jethome",
"ledvance",
"lixee",
"salus",
"ubisys",
"xyzroe",
];
export async function execute(command: string): Promise<string> {
@@ -60,11 +60,11 @@ export function primitivesArrayEquals(a: (string | number | boolean)[], b: (stri
}
export function computeSHA512(buffer: Buffer): string {
const hash = createHash('sha512');
const hash = createHash("sha512");
hash.update(buffer);
return hash.digest('hex');
return hash.digest("hex");
}
export function getOutDir(folderName: string, basePath: string = BASE_IMAGES_DIR): string {
@@ -82,21 +82,21 @@ export function getRepoFirmwareFileUrl(folderName: string, fileName: string, bas
}
export function writeManifest(fileName: string, firmwareList: RepoImageMeta[]): void {
writeFileSync(fileName, JSON.stringify(firmwareList, undefined, 2), 'utf8');
writeFileSync(fileName, JSON.stringify(firmwareList, undefined, 2), "utf8");
}
export function readManifest(fileName: string): RepoImageMeta[] {
return JSON.parse(readFileSync(fileName, 'utf8'));
return JSON.parse(readFileSync(fileName, "utf8"));
}
export function writeCacheJson<T>(fileName: string, contents: T, basePath: string = CACHE_DIR): void {
writeFileSync(path.join(basePath, `${fileName}.json`), JSON.stringify(contents), 'utf8');
writeFileSync(path.join(basePath, `${fileName}.json`), JSON.stringify(contents), "utf8");
}
export function readCacheJson<T>(fileName: string, basePath: string = CACHE_DIR): T | undefined {
const filePath = path.join(basePath, `${fileName}.json`);
return existsSync(filePath) ? JSON.parse(readFileSync(filePath, 'utf8')) : undefined;
return existsSync(filePath) ? JSON.parse(readFileSync(filePath, "utf8")) : undefined;
}
export function parseImageHeader(buffer: Buffer): ImageHeader {
@@ -110,7 +110,7 @@ export function parseImageHeader(buffer: Buffer): ImageHeader {
imageType: buffer.readUInt16LE(12),
fileVersion: buffer.readUInt32LE(14),
zigbeeStackVersion: buffer.readUInt16LE(18),
otaHeaderString: buffer.toString('utf8', 20, 52),
otaHeaderString: buffer.toString("utf8", 20, 52),
totalImageSize: buffer.readUInt32LE(52),
};
let headerPos = 56;
@@ -132,7 +132,7 @@ export function parseImageHeader(buffer: Buffer): ImageHeader {
headerPos += 2;
}
assert(UPGRADE_FILE_IDENTIFIER.equals(header.otaUpgradeFileIdentifier), `Invalid upgrade file identifier`);
assert(UPGRADE_FILE_IDENTIFIER.equals(header.otaUpgradeFileIdentifier), "Invalid upgrade file identifier");
return header;
} catch (error) {
@@ -199,25 +199,27 @@ export function getLatestImage<T>(list: T[] | undefined, compareFn: (a: T, b: T)
return sortedList.slice(0, sortedList.length > 1 && process.env.PREV ? -1 : undefined).pop();
}
export const enum ParsedImageStatus {
NEW = 0,
NEWER = 1,
OLDER = 2,
IDENTICAL = 3,
export enum ParsedImageStatus {
New = 0,
Newer = 1,
Older = 2,
Identical = 3,
}
export function getParsedImageStatus(parsedImage: ImageHeader, match?: RepoImageMeta): ParsedImageStatus {
if (match) {
if (match.fileVersion > parsedImage.fileVersion) {
return ParsedImageStatus.OLDER;
} else if (match.fileVersion < parsedImage.fileVersion) {
return ParsedImageStatus.NEWER;
} else {
return ParsedImageStatus.IDENTICAL;
return ParsedImageStatus.Older;
}
} else {
return ParsedImageStatus.NEW;
if (match.fileVersion < parsedImage.fileVersion) {
return ParsedImageStatus.Newer;
}
return ParsedImageStatus.Identical;
}
return ParsedImageStatus.New;
}
/**
@@ -231,8 +233,8 @@ export function getValidMetas(metas: Partial<ExtraMetas & ExtraMetasWithFileName
const validMetas: ExtraMetasWithFileName = {};
if (!ignoreFileName) {
if (metas.fileName != undefined) {
if (typeof metas.fileName != 'string') {
if (metas.fileName != null) {
if (typeof metas.fileName !== "string") {
throw new Error(`Invalid format for 'fileName', expected 'string' type.`);
}
@@ -240,72 +242,76 @@ export function getValidMetas(metas: Partial<ExtraMetas & ExtraMetasWithFileName
}
}
if (metas.originalUrl != undefined) {
if (typeof metas.originalUrl != 'string') {
if (metas.originalUrl != null) {
if (typeof metas.originalUrl !== "string") {
throw new Error(`Invalid format for 'originalUrl', expected 'string' type.`);
}
validMetas.originalUrl = metas.originalUrl;
}
if (metas.force != undefined) {
if (typeof metas.force != 'boolean') {
if (metas.force != null) {
if (typeof metas.force !== "boolean") {
throw new Error(`Invalid format for 'force', expected 'boolean' type.`);
}
validMetas.force = metas.force;
}
if (metas.hardwareVersionMax != undefined) {
if (typeof metas.hardwareVersionMax != 'number') {
if (metas.hardwareVersionMax != null) {
if (typeof metas.hardwareVersionMax !== "number") {
throw new Error(`Invalid format for 'hardwareVersionMax', expected 'number' type.`);
}
validMetas.hardwareVersionMax = metas.hardwareVersionMax;
}
if (metas.hardwareVersionMin != undefined) {
if (typeof metas.hardwareVersionMin != 'number') {
if (metas.hardwareVersionMin != null) {
if (typeof metas.hardwareVersionMin !== "number") {
throw new Error(`Invalid format for 'hardwareVersionMin', expected 'number' type.`);
}
validMetas.hardwareVersionMin = metas.hardwareVersionMin;
}
if (metas.manufacturerName != undefined) {
if (!Array.isArray(metas.manufacturerName) || metas.manufacturerName.length < 1 || metas.manufacturerName.some((m) => typeof m != 'string')) {
if (metas.manufacturerName != null) {
if (
!Array.isArray(metas.manufacturerName) ||
metas.manufacturerName.length < 1 ||
metas.manufacturerName.some((m) => typeof m !== "string")
) {
throw new Error(`Invalid format for 'manufacturerName', expected 'array of string' type.`);
}
validMetas.manufacturerName = metas.manufacturerName;
}
if (metas.maxFileVersion != undefined) {
if (typeof metas.maxFileVersion != 'number') {
if (metas.maxFileVersion != null) {
if (typeof metas.maxFileVersion !== "number") {
throw new Error(`Invalid format for 'maxFileVersion', expected 'number' type.`);
}
validMetas.maxFileVersion = metas.maxFileVersion;
}
if (metas.minFileVersion != undefined) {
if (typeof metas.minFileVersion != 'number') {
if (metas.minFileVersion != null) {
if (typeof metas.minFileVersion !== "number") {
throw new Error(`Invalid format for 'minFileVersion', expected 'number' type.`);
}
validMetas.minFileVersion = metas.minFileVersion;
}
if (metas.modelId != undefined) {
if (typeof metas.modelId != 'string') {
if (metas.modelId != null) {
if (typeof metas.modelId !== "string") {
throw new Error(`Invalid format for 'modelId', expected 'string' type.`);
}
validMetas.modelId = metas.modelId;
}
if (metas.releaseNotes != undefined) {
if (typeof metas.releaseNotes != 'string') {
if (metas.releaseNotes != null) {
if (typeof metas.releaseNotes !== "string") {
throw new Error(`Invalid format for 'releaseNotes', expected 'string' type.`);
}
@@ -337,7 +343,7 @@ export function addImageToPrev(
prevManifest.splice(prevMatchIndex, 1);
// make sure fileName exists for migration from old system
const prevFileName = prevMatch.fileName ? prevMatch.fileName : prevMatch.url.split('/').pop()!;
const prevFileName = prevMatch.fileName ? prevMatch.fileName : prevMatch.url.split("/").pop()!;
rmSync(path.join(prevOutDir, prevFileName), {force: true});
}
@@ -380,23 +386,23 @@ export function addImageToBase(
const [prevMatchIndex, prevMatch] = findMatchImage(parsedImage, prevManifest, extraMetas);
const prevStatus = getParsedImageStatus(parsedImage, prevMatch);
if (prevStatus !== ParsedImageStatus.OLDER && prevStatus !== ParsedImageStatus.NEW) {
if (prevStatus !== ParsedImageStatus.Older && prevStatus !== ParsedImageStatus.New) {
console.warn(`${logPrefix} Base image is new/newer but prev image is not older/non-existing.`);
}
if (prevStatus !== ParsedImageStatus.NEW) {
if (prevStatus !== ParsedImageStatus.New) {
console.log(`${logPrefix} Removing prev image.`);
prevManifest.splice(prevMatchIndex, 1);
// make sure fileName exists for migration from old system
const prevFileName = prevMatch!.fileName ? prevMatch!.fileName : prevMatch!.url.split('/').pop()!;
const prevFileName = prevMatch!.fileName ? prevMatch!.fileName : prevMatch!.url.split("/").pop()!;
rmSync(path.join(prevOutDir, prevFileName), {force: true});
}
// relocate base to prev
// make sure fileName exists for migration from old system
const baseFileName = baseMatch.fileName ? baseMatch.fileName : baseMatch.url.split('/').pop()!;
const baseFileName = baseMatch.fileName ? baseMatch.fileName : baseMatch.url.split("/").pop()!;
const baseFilePath = path.join(baseOutDir, baseFileName);
// if for some reason the file is no longer present (should not happen), don't add it to prev since link is broken

View File

@@ -1,23 +1,23 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import assert from 'assert';
import {existsSync, mkdirSync, writeFileSync} from 'fs';
import assert from "node:assert";
import {existsSync, mkdirSync, writeFileSync} from "node:fs";
import {
BASE_INDEX_MANIFEST_FILENAME,
execute,
PREV_INDEX_MANIFEST_FILENAME,
PR_ARTIFACT_DIFF_FILEPATH,
PR_ARTIFACT_DIR,
PR_ARTIFACT_ERROR_FILEPATH,
PR_ARTIFACT_NUMBER_FILEPATH,
PREV_INDEX_MANIFEST_FILENAME,
execute,
readManifest,
writeManifest,
} from './common.js';
import {getChangedOtaFiles} from './ghw_get_changed_ota_files.js';
import {processOtaFiles} from './ghw_process_ota_files.js';
} from "./common.js";
import {getChangedOtaFiles} from "./ghw_get_changed_ota_files.js";
import {processOtaFiles} from "./ghw_process_ota_files.js";
function throwError(comment: string): void {
writeFileSync(PR_ARTIFACT_ERROR_FILEPATH, comment);
@@ -26,14 +26,14 @@ function throwError(comment: string): void {
}
export async function checkOtaPR(github: Octokit, core: typeof CoreApi, context: Context): Promise<void> {
assert(context.payload.pull_request, 'Not a pull request');
assert(!context.payload.pull_request.merged, 'Should not be executed on a merged pull request');
assert(context.payload.pull_request, "Not a pull request");
assert(!context.payload.pull_request.merged, "Should not be executed on a merged pull request");
if (!existsSync(PR_ARTIFACT_DIR)) {
mkdirSync(PR_ARTIFACT_DIR, {recursive: true});
}
writeFileSync(PR_ARTIFACT_NUMBER_FILEPATH, context.issue.number.toString(10), 'utf8');
writeFileSync(PR_ARTIFACT_NUMBER_FILEPATH, context.issue.number.toString(10), "utf8");
const baseManifest = readManifest(BASE_INDEX_MANIFEST_FILENAME);
const prevManifest = readManifest(PREV_INDEX_MANIFEST_FILENAME);
@@ -58,9 +58,9 @@ export async function checkOtaPR(github: Octokit, core: typeof CoreApi, context:
core.info(`Prev manifest has ${prevManifest.length} images.`);
core.info(`Base manifest has ${baseManifest.length} images.`);
const diff = await execute(`git diff`);
const diff = await execute("git diff");
core.startGroup('diff');
core.startGroup("diff");
core.info(diff);
core.endGroup();

View File

@@ -1,29 +1,29 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import {readdirSync, readFileSync, writeFileSync} from 'fs';
import path from 'path';
import {readFileSync, readdirSync, writeFileSync} from "node:fs";
import path from "node:path";
export const CACERTS_DIR = 'cacerts';
export const CACERTS_CONCAT_FILEPATH = 'cacerts.pem';
export const CACERTS_DIR = "cacerts";
export const CACERTS_CONCAT_FILEPATH = "cacerts.pem";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export async function concatCaCerts(github: Octokit, core: typeof CoreApi, context: Context): Promise<void> {
let pemContents: string = '';
export function concatCaCerts(github: Octokit, core: typeof CoreApi, context: Context): void {
let pemContents = "";
for (const pem of readdirSync(CACERTS_DIR)) {
if (!pem.endsWith('.pem')) {
if (!pem.endsWith(".pem")) {
continue;
}
core.startGroup(pem);
pemContents += readFileSync(path.join(CACERTS_DIR, pem), 'utf8');
pemContents += '\n';
pemContents += readFileSync(path.join(CACERTS_DIR, pem), "utf8");
pemContents += "\n";
core.endGroup();
}
writeFileSync(CACERTS_CONCAT_FILEPATH, pemContents, 'utf8');
writeFileSync(CACERTS_CONCAT_FILEPATH, pemContents, "utf8");
}

View File

@@ -1,10 +1,10 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import type {RepoImageMeta} from './types.js';
import type {RepoImageMeta} from "./types.js";
import {BASE_IMAGES_DIR, BASE_INDEX_MANIFEST_FILENAME, execute, PREV_IMAGES_DIR, PREV_INDEX_MANIFEST_FILENAME, readManifest} from './common.js';
import {BASE_IMAGES_DIR, BASE_INDEX_MANIFEST_FILENAME, PREV_IMAGES_DIR, PREV_INDEX_MANIFEST_FILENAME, execute, readManifest} from "./common.js";
// about 3 lines
const MAX_RELEASE_NOTES_LENGTH = 380;
@@ -19,7 +19,7 @@ function listItemWithReleaseNotes(imagePath: string, releaseNotes?: string): str
let listItem = `* ${imagePath}`;
if (releaseNotes) {
let notes = releaseNotes.replace(/[#*\r\n]+/g, '').replaceAll('-', '|');
let notes = releaseNotes.replace(/[#*\r\n]+/g, "").replaceAll("-", "|");
if (notes.length > MAX_RELEASE_NOTES_LENGTH) {
notes = `${notes.slice(0, MAX_RELEASE_NOTES_LENGTH)}...`;
@@ -33,7 +33,7 @@ function listItemWithReleaseNotes(imagePath: string, releaseNotes?: string): str
}
export async function createAutodlRelease(github: Octokit, core: typeof CoreApi, context: Context): Promise<void> {
const tagName = new Date().toISOString().replace(/[:.]/g, '');
const tagName = new Date().toISOString().replace(/[:.]/g, "");
// --exclude-standard => Add the standard Git exclusions: .git/info/exclude, .gitignore in each directory, and the users global exclusion file.
// --others => Show other (i.e. untracked) files in the output.
// -z => \0 line termination on output and do not quote filenames.
@@ -44,8 +44,8 @@ export async function createAutodlRelease(github: Octokit, core: typeof CoreApi,
core.debug(`git ls-files for ${PREV_IMAGES_DIR}: ${downgradeImagesStr}`);
// -1 to remove empty string at end due to \0 termination
const upgradeImages = upgradeImagesStr.split('\0').slice(0, -1);
const downgradeImages = downgradeImagesStr.split('\0').slice(0, -1);
const upgradeImages = upgradeImagesStr.split("\0").slice(0, -1);
const downgradeImages = downgradeImagesStr.split("\0").slice(0, -1);
core.info(`Upgrade Images List: ${upgradeImages}`);
core.info(`Downgrade Images List: ${downgradeImages}`);
@@ -56,12 +56,12 @@ export async function createAutodlRelease(github: Octokit, core: typeof CoreApi,
let body: string | undefined;
if (upgradeImages.length > 0 || downgradeImages.length > 0) {
body = '';
body = "";
if (upgradeImages.length > 0) {
const listWithReleaseNotes = upgradeImages.map((v) => listItemWithReleaseNotes(v, findReleaseNotes(v, baseManifest)));
body += `## New upgrade images from automatic download:
${listWithReleaseNotes.join('\n')}
${listWithReleaseNotes.join("\n")}
`;
}
@@ -69,7 +69,7 @@ ${listWithReleaseNotes.join('\n')}
if (downgradeImages.length > 0) {
const listWithReleaseNotes = downgradeImages.map((v) => listItemWithReleaseNotes(v, findReleaseNotes(v, prevManifest)));
body += `## New downgrade images from automatic download:
${listWithReleaseNotes.join('\n')}
${listWithReleaseNotes.join("\n")}
`;
}
@@ -85,6 +85,6 @@ ${listWithReleaseNotes.join('\n')}
prerelease: false,
// get changes from PRs
generate_release_notes: true,
make_latest: 'true',
make_latest: "true",
});
}

View File

@@ -1,10 +1,10 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import assert from 'assert';
import assert from "node:assert";
const IGNORE_OTA_WORKFLOW_LABEL = 'ignore-ota-workflow';
const IGNORE_OTA_WORKFLOW_LABEL = "ignore-ota-workflow";
export async function createPRToDefault(
github: Octokit,
@@ -45,7 +45,7 @@ export async function createPRToDefault(
ref: `heads/${fromBranchName}`,
});
core.notice(`Nothing needed re-processing.`);
core.notice("Nothing needed re-processing.");
// don't fail if no commits
return;

View File

@@ -1,10 +1,10 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import assert from 'assert';
import assert from "node:assert";
import {BASE_IMAGES_DIR} from './common.js';
import {BASE_IMAGES_DIR} from "./common.js";
export async function getChangedOtaFiles(
github: Octokit,
@@ -20,18 +20,18 @@ export async function getChangedOtaFiles(
basehead,
});
assert(compare.data.files && compare.data.files.length > 0, 'No file');
assert(compare.data.files && compare.data.files.length > 0, "No file");
core.info(`Changed files: ${compare.data.files.map((f) => f.filename).join(', ')}`);
core.info(`Changed files: ${compare.data.files.map((f) => f.filename).join(", ")}`);
const fileList = compare.data.files.filter((f) => f.filename.startsWith(`${BASE_IMAGES_DIR}/`));
if (throwIfFilesOutsideOfImages && fileList.length !== compare.data.files.length) {
if (context.payload.pull_request) {
throw new Error(`Detected changes in files outside of \`images\` directory. This is not allowed for a pull request with OTA files.`);
} else {
throw new Error(`Cannot run with files outside of \`images\` directory.`);
throw new Error("Detected changes in files outside of `images` directory. This is not allowed for a pull request with OTA files.");
}
throw new Error("Cannot run with files outside of `images` directory.");
}
return fileList.map((f) => f.filename);

View File

@@ -1,17 +1,17 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import {existsSync, mkdirSync} from 'fs';
import {existsSync, mkdirSync} from "node:fs";
import {ALL_AUTODL_MANUFACTURERS, CACHE_DIR} from './common.js';
import {ALL_AUTODL_MANUFACTURERS, CACHE_DIR} from "./common.js";
export async function overwriteCache(github: Octokit, core: typeof CoreApi, context: Context, manufacturersCSV?: string): Promise<void> {
if (!existsSync(CACHE_DIR)) {
mkdirSync(CACHE_DIR, {recursive: true});
}
const manufacturers = manufacturersCSV ? manufacturersCSV.trim().split(',') : ALL_AUTODL_MANUFACTURERS;
const manufacturers = manufacturersCSV ? manufacturersCSV.trim().split(",") : ALL_AUTODL_MANUFACTURERS;
for (const manufacturer of manufacturers) {
// ignore empty strings

View File

@@ -1,34 +1,35 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import type {ExtraMetas, GHExtraMetas, RepoImageMeta} from './types.js';
import type {ExtraMetas, GHExtraMetas, RepoImageMeta} from "./types.js";
import assert from 'assert';
import {readFileSync, renameSync} from 'fs';
import path from 'path';
import assert from "node:assert";
import {readFileSync, renameSync} from "node:fs";
import path from "node:path";
import {
BASE_IMAGES_DIR,
PREV_IMAGES_DIR,
ParsedImageStatus,
UPGRADE_FILE_IDENTIFIER,
addImageToBase,
addImageToPrev,
BASE_IMAGES_DIR,
findMatchImage,
getOutDir,
getParsedImageStatus,
getValidMetas,
ParsedImageStatus,
parseImageHeader,
PREV_IMAGES_DIR,
UPGRADE_FILE_IDENTIFIER,
} from './common.js';
} from "./common.js";
const EXTRA_METAS_PR_BODY_START_TAG = '```json';
const EXTRA_METAS_PR_BODY_END_TAG = '```';
const EXTRA_METAS_PR_BODY_START_TAG = "```json";
const EXTRA_METAS_PR_BODY_END_TAG = "```";
function getFileExtraMetas(extraMetas: GHExtraMetas, fileName: string): ExtraMetas {
if (Array.isArray(extraMetas)) {
const fileExtraMetas = extraMetas.find((m) => m.fileName === fileName) ?? {};
/** @see getValidMetas */
// biome-ignore lint/performance/noDelete: <explanation>
delete fileExtraMetas.fileName;
return fileExtraMetas;
@@ -39,16 +40,18 @@ function getFileExtraMetas(extraMetas: GHExtraMetas, fileName: string): ExtraMet
}
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) {
return context.payload.pull_request.body;
} else if (context.eventName === 'push') {
}
if (context.eventName === "push") {
const pushMsg = context.payload.head_commit.message as string;
const prMatch = pushMsg.match(/\(#(\d+)\)/);
if (prMatch) {
const prNumber = parseInt(prMatch[1], 10);
const prNumber = Number.parseInt(prMatch[1], 10);
try {
const pr = await github.rest.pulls.get({
@@ -78,15 +81,15 @@ async function parsePRBodyExtraMetas(github: Octokit, core: typeof CoreApi, cont
if (metasStart !== -1 && metasEnd > metasStart) {
const metas = JSON.parse(prBody.slice(metasStart + EXTRA_METAS_PR_BODY_START_TAG.length, metasEnd)) as GHExtraMetas;
core.info(`Extra metas from PR body:`);
core.info("Extra metas from PR body:");
core.info(JSON.stringify(metas, undefined, 2));
if (Array.isArray(metas)) {
extraMetas = [];
for (const meta of metas) {
if (!meta.fileName || typeof meta.fileName != 'string') {
core.info(`Ignoring meta in array with missing/invalid fileName:`);
if (!meta.fileName || typeof meta.fileName !== "string") {
core.info("Ignoring meta in array with missing/invalid fileName:");
core.info(JSON.stringify(meta, undefined, 2));
continue;
}
@@ -119,14 +122,14 @@ export async function processOtaFiles(
core.startGroup(filePath);
const logPrefix = `[${filePath}]`;
let failureComment: string = '';
let failureComment = "";
try {
const firmwareFileName = path.basename(filePath);
const manufacturer = filePath.replace(BASE_IMAGES_DIR, '').replace(firmwareFileName, '').replaceAll('/', '').trim();
const manufacturer = filePath.replace(BASE_IMAGES_DIR, "").replace(firmwareFileName, "").replaceAll("/", "").trim();
if (!manufacturer) {
throw new Error(`File should be in its associated manufacturer subfolder`);
throw new Error("File should be in its associated manufacturer subfolder");
}
const firmwareBuffer = Buffer.from(readFileSync(filePath));
@@ -146,14 +149,14 @@ export async function processOtaFiles(
const statusToBase = getParsedImageStatus(parsedImage, baseMatch);
switch (statusToBase) {
case ParsedImageStatus.OLDER: {
case ParsedImageStatus.Older: {
// if prev doesn't have a match, move to prev
const [prevMatchIndex, prevMatch] = findMatchImage(parsedImage, prevManifest, fileExtraMetas);
const statusToPrev = getParsedImageStatus(parsedImage, prevMatch);
switch (statusToPrev) {
case ParsedImageStatus.OLDER:
case ParsedImageStatus.IDENTICAL: {
case ParsedImageStatus.Older:
case ParsedImageStatus.Identical: {
failureComment = `Base manifest has higher version:
\`\`\`json
${JSON.stringify(baseMatch, undefined, 2)}
@@ -169,11 +172,11 @@ ${JSON.stringify(parsedImage, undefined, 2)}
break;
}
case ParsedImageStatus.NEWER:
case ParsedImageStatus.NEW: {
case ParsedImageStatus.Newer:
case ParsedImageStatus.New: {
addImageToPrev(
logPrefix,
statusToPrev === ParsedImageStatus.NEWER,
statusToPrev === ParsedImageStatus.Newer,
prevManifest,
prevMatchIndex,
prevMatch!,
@@ -197,7 +200,7 @@ ${JSON.stringify(parsedImage, undefined, 2)}
break;
}
case ParsedImageStatus.IDENTICAL: {
case ParsedImageStatus.Identical: {
failureComment = `Conflict with image at index \`${baseMatchIndex}\`:
\`\`\`json
${JSON.stringify(baseMatch, undefined, 2)}
@@ -209,11 +212,11 @@ ${JSON.stringify(parsedImage, undefined, 2)}
break;
}
case ParsedImageStatus.NEWER:
case ParsedImageStatus.NEW: {
case ParsedImageStatus.Newer:
case ParsedImageStatus.New: {
addImageToBase(
logPrefix,
statusToBase === ParsedImageStatus.NEWER,
statusToBase === ParsedImageStatus.Newer,
prevManifest,
prevOutDir,
baseManifest,

View File

@@ -1,21 +1,21 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import assert from 'assert';
import {existsSync, readFileSync, writeFileSync} from 'fs';
import assert from "node:assert";
import {existsSync, readFileSync, writeFileSync} from "node:fs";
import {execute, PR_ARTIFACT_DIR, PR_DIFF_FILENAME, PR_ERROR_FILENAME, PR_NUMBER_FILENAME} from './common.js';
import {PR_ARTIFACT_DIR, PR_DIFF_FILENAME, PR_ERROR_FILENAME, PR_NUMBER_FILENAME, execute} from "./common.js";
export async function reportOtaPR(github: Octokit, core: typeof CoreApi, context: Context): Promise<void> {
assert(context.payload.workflow_run, 'Not a workflow run');
assert(context.payload.workflow_run, "Not a workflow run");
// XXX: context.payload.workflow_run is not typed...
const workflow_run = context.payload.workflow_run as Awaited<ReturnType<typeof github.rest.actions.getWorkflowRun>>['data'];
const workflowRun = context.payload.workflow_run as Awaited<ReturnType<typeof github.rest.actions.getWorkflowRun>>["data"];
// workflow_run.conclusion: action_required, cancelled, failure, neutral, skipped, stale, success, timed_out, startup_failure, null
if (workflow_run.conclusion !== 'success' && workflow_run.conclusion !== 'failure') {
core.info(`Ignoring workflow run ${workflow_run.html_url} with conclusion ${workflow_run.conclusion}.`);
if (workflowRun.conclusion !== "success" && workflowRun.conclusion !== "failure") {
core.info(`Ignoring workflow run ${workflowRun.html_url} with conclusion ${workflowRun.conclusion}.`);
return;
}
@@ -23,17 +23,17 @@ export async function reportOtaPR(github: Octokit, core: typeof CoreApi, context
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: workflow_run.id,
run_id: workflowRun.id,
});
const matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name == PR_ARTIFACT_DIR);
const matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === PR_ARTIFACT_DIR);
assert(matchArtifact, `No artifact found for ${workflow_run.url}`);
assert(matchArtifact, `No artifact found for ${workflowRun.url}`);
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
archive_format: "zip",
});
const artifactZipFileName = `${PR_ARTIFACT_DIR}.zip`;
@@ -43,16 +43,16 @@ export async function reportOtaPR(github: Octokit, core: typeof CoreApi, context
core.info(unzipOutput);
assert(existsSync(PR_NUMBER_FILENAME), `Invalid artifact for ${workflow_run.html_url}`);
assert(existsSync(PR_NUMBER_FILENAME), `Invalid artifact for ${workflowRun.html_url}`);
const prNumber = parseInt(readFileSync(PR_NUMBER_FILENAME, 'utf8'), 10);
const prNumber = Number.parseInt(readFileSync(PR_NUMBER_FILENAME, "utf8"), 10);
core.info(`Running for pr#${prNumber} for ${workflow_run.html_url}`);
core.info(`Running for pr#${prNumber} for ${workflowRun.html_url}`);
if (workflow_run.conclusion === 'failure') {
assert(existsSync(PR_ERROR_FILENAME), `Workflow failed but could not find ${PR_ERROR_FILENAME} for ${workflow_run.html_url}`);
if (workflowRun.conclusion === "failure") {
assert(existsSync(PR_ERROR_FILENAME), `Workflow failed but could not find ${PR_ERROR_FILENAME} for ${workflowRun.html_url}`);
const prError = readFileSync(PR_ERROR_FILENAME, 'utf8');
const prError = readFileSync(PR_ERROR_FILENAME, "utf8");
await github.rest.issues.createComment({
owner: context.repo.owner,
@@ -62,10 +62,12 @@ export async function reportOtaPR(github: Octokit, core: typeof CoreApi, context
});
throw new Error(prError);
} else if (workflow_run.conclusion === 'success') {
assert(existsSync(PR_DIFF_FILENAME), `Workflow succeeded but could not find ${PR_DIFF_FILENAME} for ${workflow_run.html_url}`);
}
const prDiff = readFileSync(PR_DIFF_FILENAME, 'utf8');
if (workflowRun.conclusion === "success") {
assert(existsSync(PR_DIFF_FILENAME), `Workflow succeeded but could not find ${PR_DIFF_FILENAME} for ${workflowRun.html_url}`);
const prDiff = readFileSync(PR_DIFF_FILENAME, "utf8");
core.info(prDiff);
await github.rest.issues.createComment({

View File

@@ -1,49 +1,49 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import type {RepoImageMeta} from './types';
import type {RepoImageMeta} from "./types";
import {existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync} from 'fs';
import path from 'path';
import {existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync} from "node:fs";
import path from "node:path";
import {
addImageToBase,
addImageToPrev,
BASE_IMAGES_DIR,
BASE_INDEX_MANIFEST_FILENAME,
BASE_REPO_URL,
PREV_IMAGES_DIR,
PREV_INDEX_MANIFEST_FILENAME,
ParsedImageStatus,
REPO_BRANCH,
UPGRADE_FILE_IDENTIFIER,
addImageToBase,
addImageToPrev,
computeSHA512,
findMatchImage,
getOutDir,
getParsedImageStatus,
getRepoFirmwareFileUrl,
getValidMetas,
ParsedImageStatus,
parseImageHeader,
PREV_IMAGES_DIR,
PREV_INDEX_MANIFEST_FILENAME,
readManifest,
REPO_BRANCH,
UPGRADE_FILE_IDENTIFIER,
writeManifest,
} from './common.js';
} from "./common.js";
/** These are now handled by autodl */
const IGNORE_3RD_PARTIES = ['https://github.com/fairecasoimeme/', 'https://github.com/xyzroe/'];
const IGNORE_3RD_PARTIES = ["https://github.com/fairecasoimeme/", "https://github.com/xyzroe/"];
const DIR_3RD_PARTIES = {
'https://otau.meethue.com/': 'Hue',
'https://images.tuyaeu.com/': 'Tuya',
'https://tr-zha.s3.amazonaws.com/': 'ThirdReality',
"https://otau.meethue.com/": "Hue",
"https://images.tuyaeu.com/": "Tuya",
"https://tr-zha.s3.amazonaws.com/": "ThirdReality",
// NOTE: no longer valid / unable to access via script
// 'https://www.elektroimportoren.no/docs/lib/4512772-Firmware-35.ota': 'Namron',
// 'https://deconz.dresden-elektronik.de/': 'DresdenElektronik',
};
export const NOT_IN_BASE_MANIFEST_IMAGES_DIR = 'not-in-manifest-images';
export const NOT_IN_PREV_MANIFEST_IMAGES_DIR = 'not-in-manifest-images1';
export const NOT_IN_MANIFEST_FILENAME = 'not-in-manifest.json';
export const NOT_IN_BASE_MANIFEST_IMAGES_DIR = "not-in-manifest-images";
export const NOT_IN_PREV_MANIFEST_IMAGES_DIR = "not-in-manifest-images1";
export const NOT_IN_MANIFEST_FILENAME = "not-in-manifest.json";
function ignore3rdParty(meta: RepoImageMeta): boolean {
for (const ignore of IGNORE_3RD_PARTIES) {
@@ -67,11 +67,10 @@ async function download3rdParties(
github: Octokit,
core: typeof CoreApi,
context: Context,
/* istanbul ignore next */
outDirFinder = get3rdPartyDir,
/* v8 ignore next */ outDirFinder = get3rdPartyDir,
): Promise<void> {
if (!process.env.NODE_EXTRA_CA_CERTS) {
throw new Error(`Download 3rd Parties requires \`NODE_EXTRA_CA_CERTS=cacerts.pem\`.`);
throw new Error("Download 3rd Parties requires `NODE_EXTRA_CA_CERTS=cacerts.pem`.");
}
const baseManifest = readManifest(BASE_INDEX_MANIFEST_FILENAME);
@@ -101,7 +100,7 @@ async function download3rdParties(
continue;
}
const fileName = decodeURIComponent(meta.url.split('/').pop()!);
const fileName = decodeURIComponent(meta.url.split("/").pop()!);
const outDirName = outDirFinder(meta);
if (outDirName) {
@@ -130,7 +129,7 @@ async function download3rdParties(
const statusToBase = getParsedImageStatus(parsedImage, baseMatch);
switch (statusToBase) {
case ParsedImageStatus.OLDER: {
case ParsedImageStatus.Older: {
addImageToPrev(
`[${fileName}]`,
false, // no prev existed before
@@ -158,16 +157,16 @@ async function download3rdParties(
break;
}
case ParsedImageStatus.IDENTICAL: {
case ParsedImageStatus.Identical: {
core.warning(`Conflict with image at index \`${baseMatchIndex}\`: ${JSON.stringify(baseMatch)}`);
continue;
}
case ParsedImageStatus.NEWER:
case ParsedImageStatus.NEW: {
case ParsedImageStatus.Newer:
case ParsedImageStatus.New: {
addImageToBase(
`[${fileName}]`,
statusToBase === ParsedImageStatus.NEWER,
statusToBase === ParsedImageStatus.Newer,
prevManifest,
prevOutDir,
baseManifest,
@@ -196,12 +195,11 @@ async function download3rdParties(
} catch (error) {
core.error(`Ignoring ${fileName}: ${error}`);
/* istanbul ignore next */
/* v8 ignore start */
if (firmwareFilePath) {
rmSync(firmwareFilePath, {force: true});
}
continue;
/* v8 ignore stop */
}
} else {
core.warning(`Ignoring '${fileName}' with no out dir specified.`);
@@ -230,12 +228,13 @@ function checkImagesAgainstManifests(github: Octokit, core: typeof CoreApi, cont
core.info(`Checking ${manifestName} (currently ${manifest.length} images)...`);
for (const subfolderName of readdirSync(imagesDir)) {
// skip removal of anything not desired while running jest tests
// skip removal of anything not desired while running tests
// compare should match data.test.ts > IMAGES_TEST_DIR
/* istanbul ignore if */
if (process.env.JEST_WORKER_ID && subfolderName !== 'jest-tmp') {
/* v8 ignore start */
if (process.env.VITEST_WORKER_ID && subfolderName !== "test-tmp") {
continue;
}
/* v8 ignore stop */
const subfolderPath = path.join(imagesDir, subfolderName);
@@ -365,25 +364,29 @@ export async function reProcessAllImages(
throw new Error(`${NOT_IN_PREV_MANIFEST_IMAGES_DIR} is not empty. Cannot run.`);
}
/* istanbul ignore if */
/* v8 ignore start */
if (!existsSync(BASE_IMAGES_DIR)) {
mkdirSync(BASE_IMAGES_DIR, {recursive: true});
}
/* v8 ignore stop */
/* istanbul ignore if */
/* v8 ignore start */
if (!existsSync(PREV_IMAGES_DIR)) {
mkdirSync(PREV_IMAGES_DIR, {recursive: true});
}
/* v8 ignore stop */
/* istanbul ignore if */
/* v8 ignore start */
if (!existsSync(BASE_INDEX_MANIFEST_FILENAME)) {
writeManifest(BASE_INDEX_MANIFEST_FILENAME, []);
}
/* v8 ignore stop */
/* istanbul ignore if */
/* v8 ignore start */
if (!existsSync(PREV_INDEX_MANIFEST_FILENAME)) {
writeManifest(PREV_INDEX_MANIFEST_FILENAME, []);
}
/* v8 ignore stop */
if (!skipDownload3rdParties) {
await download3rdParties(github, core, context, downloadOutDirFinder);

View File

@@ -1,15 +1,15 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import {existsSync, mkdirSync, rmSync} from 'fs';
import {existsSync, mkdirSync, rmSync} from "node:fs";
import {ALL_AUTODL_MANUFACTURERS, BASE_INDEX_MANIFEST_FILENAME, CACHE_DIR, PREV_INDEX_MANIFEST_FILENAME, TMP_DIR, writeManifest} from './common.js';
import {ALL_AUTODL_MANUFACTURERS, BASE_INDEX_MANIFEST_FILENAME, CACHE_DIR, PREV_INDEX_MANIFEST_FILENAME, TMP_DIR, writeManifest} from "./common.js";
export async function runAutodl(github: Octokit, core: typeof CoreApi, context: Context, manufacturersCSV?: string): Promise<void> {
const manufacturers = manufacturersCSV ? manufacturersCSV.trim().split(',') : ALL_AUTODL_MANUFACTURERS;
const manufacturers = manufacturersCSV ? manufacturersCSV.trim().split(",") : ALL_AUTODL_MANUFACTURERS;
core.info(`Setup...`);
core.info("Setup...");
if (!existsSync(CACHE_DIR)) {
mkdirSync(CACHE_DIR, {recursive: true});
@@ -52,7 +52,7 @@ export async function runAutodl(github: Octokit, core: typeof CoreApi, context:
core.endGroup();
}
core.info(`Teardown...`);
core.info("Teardown...");
rmSync(TMP_DIR, {recursive: true, force: true});
}

View File

@@ -1,15 +1,15 @@
import type CoreApi from '@actions/core';
import type {Context} from '@actions/github/lib/context';
import type {Octokit} from '@octokit/rest';
import type CoreApi from "@actions/core";
import type {Context} from "@actions/github/lib/context";
import type {Octokit} from "@octokit/rest";
import assert from 'assert';
import assert from "node:assert";
import {BASE_INDEX_MANIFEST_FILENAME, PREV_INDEX_MANIFEST_FILENAME, readManifest, writeManifest} from './common.js';
import {getChangedOtaFiles} from './ghw_get_changed_ota_files.js';
import {processOtaFiles} from './ghw_process_ota_files.js';
import {BASE_INDEX_MANIFEST_FILENAME, PREV_INDEX_MANIFEST_FILENAME, readManifest, writeManifest} from "./common.js";
import {getChangedOtaFiles} from "./ghw_get_changed_ota_files.js";
import {processOtaFiles} from "./ghw_process_ota_files.js";
export async function updateManifests(github: Octokit, core: typeof CoreApi, context: Context): Promise<void> {
assert(context.eventName === 'push', 'Not a push');
assert(context.eventName === "push", "Not a push");
const filePaths = await getChangedOtaFiles(github, core, context, `${context.payload.before}...${context.payload.after}`, true);
const baseManifest = readManifest(BASE_INDEX_MANIFEST_FILENAME);

View File

@@ -1,13 +1,13 @@
export * as common from './common.js';
export {checkOtaPR} from './ghw_check_ota_pr.js';
export {concatCaCerts} from './ghw_concat_cacerts.js';
export {createAutodlRelease} from './ghw_create_autodl_release.js';
export {createPRToDefault} from './ghw_create_pr_to_default.js';
export {getChangedOtaFiles} from './ghw_get_changed_ota_files.js';
export {overwriteCache} from './ghw_overwrite_cache.js';
export {processOtaFiles} from './ghw_process_ota_files.js';
export {reportOtaPR} from './ghw_report_ota_pr.js';
export {reProcessAllImages} from './ghw_reprocess_all_images.js';
export {runAutodl} from './ghw_run_autodl.js';
export {updateManifests} from './ghw_update_manifests.js';
export {processFirmwareImage} from './process_firmware_image.js';
export * as common from "./common.js";
export {checkOtaPR} from "./ghw_check_ota_pr.js";
export {concatCaCerts} from "./ghw_concat_cacerts.js";
export {createAutodlRelease} from "./ghw_create_autodl_release.js";
export {createPRToDefault} from "./ghw_create_pr_to_default.js";
export {getChangedOtaFiles} from "./ghw_get_changed_ota_files.js";
export {overwriteCache} from "./ghw_overwrite_cache.js";
export {processOtaFiles} from "./ghw_process_ota_files.js";
export {reportOtaPR} from "./ghw_report_ota_pr.js";
export {reProcessAllImages} from "./ghw_reprocess_all_images.js";
export {runAutodl} from "./ghw_run_autodl.js";
export {updateManifests} from "./ghw_update_manifests.js";
export {processFirmwareImage} from "./process_firmware_image.js";

View File

@@ -1,5 +1,5 @@
import {readFileSync} from 'fs';
import {readFileSync} from "node:fs";
import {parseImageHeader} from './common.js';
import {parseImageHeader} from "./common.js";
console.log(parseImageHeader(readFileSync(process.argv[2])));

View File

@@ -1,34 +1,34 @@
import type {ExtraMetas} from './types';
import type {ExtraMetas} from "./types";
import assert from 'assert';
import {readdirSync, readFileSync, renameSync, rmSync, writeFileSync} from 'fs';
import path from 'path';
import assert from "node:assert";
import {readFileSync, readdirSync, renameSync, rmSync, writeFileSync} from "node:fs";
import path from "node:path";
import {extract} from 'tar';
import {extract} from "tar";
import {
addImageToBase,
addImageToPrev,
BASE_IMAGES_DIR,
BASE_INDEX_MANIFEST_FILENAME,
PREV_IMAGES_DIR,
PREV_INDEX_MANIFEST_FILENAME,
ParsedImageStatus,
TMP_DIR,
UPGRADE_FILE_IDENTIFIER,
addImageToBase,
addImageToPrev,
findMatchImage,
getOutDir,
getParsedImageStatus,
ParsedImageStatus,
parseImageHeader,
PREV_IMAGES_DIR,
PREV_INDEX_MANIFEST_FILENAME,
readManifest,
TMP_DIR,
UPGRADE_FILE_IDENTIFIER,
writeManifest,
} from './common.js';
} from "./common.js";
export const enum ProcessFirmwareImageStatus {
ERROR = -1,
SUCCESS = 0,
REQUEST_FAILED = 1,
TAR_NO_IMAGE = 2,
export enum ProcessFirmwareImageStatus {
Error = -1,
Success = 0,
RequestFailed = 1,
TarNoImage = 2,
}
async function tarExtract(filePath: string, outDir: string, tarImageFinder: (fileName: string) => boolean): Promise<string> {
@@ -71,7 +71,7 @@ export async function processFirmwareImage(
firmwareFileName: string,
firmwareFileUrl: string,
extraMetas: ExtraMetas = {},
tar: boolean = false,
tar = false,
tarImageFinder?: (fileName: string) => boolean,
): Promise<ProcessFirmwareImageStatus> {
// throttle requests (this is done at the top to ensure always executed)
@@ -80,9 +80,9 @@ export async function processFirmwareImage(
let firmwareFilePath: string | undefined;
const logPrefix = `[${manufacturer}:${firmwareFileName}]`;
if (tar && !firmwareFileName.endsWith('.tar.gz')) {
if (tar && !firmwareFileName.endsWith(".tar.gz")) {
// ignore non-archive
return ProcessFirmwareImageStatus.TAR_NO_IMAGE;
return ProcessFirmwareImageStatus.TarNoImage;
}
const prevManifest = readManifest(PREV_INDEX_MANIFEST_FILENAME);
@@ -95,11 +95,11 @@ export async function processFirmwareImage(
if (!firmwareFile.ok || !firmwareFile.body) {
console.error(`${logPrefix} Invalid response from ${firmwareFileUrl} status=${firmwareFile.status}.`);
return ProcessFirmwareImageStatus.REQUEST_FAILED;
return ProcessFirmwareImageStatus.RequestFailed;
}
if (tar) {
assert(tarImageFinder, `No image finder function supplied for tar.`);
assert(tarImageFinder, "No image finder function supplied for tar.");
const archiveBuffer = Buffer.from(await firmwareFile.arrayBuffer());
const archiveFilePath = path.join(baseOutDir, firmwareFileName);
@@ -110,7 +110,7 @@ export async function processFirmwareImage(
firmwareFileName = await tarExtract(archiveFilePath, baseOutDir, tarImageFinder);
} catch {
console.error(`${logPrefix} No image found for ${firmwareFileUrl}.`);
return ProcessFirmwareImageStatus.TAR_NO_IMAGE;
return ProcessFirmwareImageStatus.TarNoImage;
}
}
@@ -121,14 +121,14 @@ export async function processFirmwareImage(
const statusToBase = getParsedImageStatus(parsedImage, baseMatch);
switch (statusToBase) {
case ParsedImageStatus.OLDER: {
case ParsedImageStatus.Older: {
// if prev doesn't have a match, move to prev
const [prevMatchIndex, prevMatch] = findMatchImage(parsedImage, prevManifest, extraMetas);
const statusToPrev = getParsedImageStatus(parsedImage, prevMatch);
switch (statusToPrev) {
case ParsedImageStatus.OLDER:
case ParsedImageStatus.IDENTICAL: {
case ParsedImageStatus.Older:
case ParsedImageStatus.Identical: {
console.log(
`${logPrefix} Base manifest has higher version and an equal or better match is already present in prev manifest. Ignoring.`,
);
@@ -136,11 +136,11 @@ export async function processFirmwareImage(
break;
}
case ParsedImageStatus.NEWER:
case ParsedImageStatus.NEW: {
case ParsedImageStatus.Newer:
case ParsedImageStatus.New: {
addImageToPrev(
logPrefix,
statusToPrev === ParsedImageStatus.NEWER,
statusToPrev === ParsedImageStatus.Newer,
prevManifest,
prevMatchIndex,
prevMatch!,
@@ -166,17 +166,17 @@ export async function processFirmwareImage(
break;
}
case ParsedImageStatus.IDENTICAL: {
case ParsedImageStatus.Identical: {
console.log(`${logPrefix} Base manifest already has version ${parsedImage.fileVersion}. Ignoring.`);
break;
}
case ParsedImageStatus.NEWER:
case ParsedImageStatus.NEW: {
case ParsedImageStatus.Newer:
case ParsedImageStatus.New: {
addImageToBase(
logPrefix,
statusToBase === ParsedImageStatus.NEWER,
statusToBase === ParsedImageStatus.Newer,
prevManifest,
prevOutDir,
baseManifest,
@@ -203,12 +203,13 @@ export async function processFirmwareImage(
} catch (error) {
console.error(`${logPrefix} Failed to save firmware file ${firmwareFileName}: ${(error as Error).stack!}.`);
/* istanbul ignore if */
/* v8 ignore start */
if (firmwareFilePath) {
rmSync(firmwareFilePath, {force: true});
}
/* v8 ignore stop */
return ProcessFirmwareImageStatus.ERROR;
return ProcessFirmwareImageStatus.Error;
}
writeManifest(PREV_INDEX_MANIFEST_FILENAME, prevManifest);
@@ -217,5 +218,5 @@ export async function processFirmwareImage(
console.log(`Prev manifest has ${prevManifest.length} images.`);
console.log(`Base manifest has ${baseManifest.length} images.`);
return ProcessFirmwareImageStatus.SUCCESS;
return ProcessFirmwareImageStatus.Success;
}

View File

@@ -65,10 +65,10 @@ export interface RepoImageMeta extends ImageInfo, ImageMeta {
export type ExtraMetas = Omit<
RepoImageMeta,
'fileName' | 'fileVersion' | 'fileSize' | 'url' | 'imageType' | 'manufacturerCode' | 'sha512' | 'otaHeaderString'
"fileName" | "fileVersion" | "fileSize" | "url" | "imageType" | "manufacturerCode" | "sha512" | "otaHeaderString"
>;
export type ExtraMetasWithFileName = Omit<
RepoImageMeta,
'fileName' | 'fileVersion' | 'fileSize' | 'url' | 'imageType' | 'manufacturerCode' | 'sha512' | 'otaHeaderString'
"fileName" | "fileVersion" | "fileSize" | "url" | "imageType" | "manufacturerCode" | "sha512" | "otaHeaderString"
> & {fileName?: string};
export type GHExtraMetas = ExtraMetas | ExtraMetasWithFileName[];