From 8775ceb0c1d2bac552a345751935d0191410bfa3 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Mon, 3 Mar 2025 21:12:57 +0100 Subject: [PATCH] fix: General cleanup (#26595) --- index.js | 76 ++++++++++++++++++++++++-------------------- lib/types/types.d.ts | 2 +- lib/util/utils.ts | 21 +++++------- package.json | 1 - pnpm-lock.yaml | 8 ----- test/utils.test.ts | 47 +++++++++++++++++++++------ 6 files changed, 88 insertions(+), 67 deletions(-) diff --git a/index.js b/index.js index 244b7b962..bf4d39b38 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,6 @@ const fs = require('fs'); const os = require('os'); const path = require('path'); const {exec} = require('child_process'); -const {rimrafSync} = require('rimraf'); require('source-map-support').install(); let controller; @@ -16,7 +15,7 @@ let unsolicitedStop = false; let watchdogDelays = [2000, 60000, 300000, 900000, 1800000, 3600000]; if (watchdog && process.env.Z2M_WATCHDOG !== 'default') { - if (/^(?:(?:[0-9]*[.])?[0-9]+)+(?:,?(?:[0-9]*[.])?[0-9]+)*$/.test(process.env.Z2M_WATCHDOG)) { + if (/^\d+(.\d+)?(,\d+(.\d+)?)*$/.test(process.env.Z2M_WATCHDOG)) { watchdogDelays = process.env.Z2M_WATCHDOG.split(',').map((v) => parseFloat(v) * 60000); } else { console.log(`Invalid watchdog delays (must use number-only CSV format representing minutes, example: 'Z2M_WATCHDOG=1,5,15,30,60'.`); @@ -58,10 +57,16 @@ async function exit(code, restart = false) { } async function currentHash() { - const git = require('git-last-commit'); + return await new Promise((resolve) => { + exec('git rev-parse --short=8 HEAD', (error, stdout) => { + const commitHash = stdout.trim(); - return new Promise((resolve) => { - git.getLastCommit((err, commit) => (err ? resolve('unknown') : resolve(commit.shortHash))); + if (error || commitHash === '') { + resolve('unknown'); + } else { + resolve(commitHash); + } + }); }); } @@ -72,10 +77,9 @@ async function writeHash() { } async function build(reason) { - return new Promise((resolve, reject) => { - process.stdout.write(`Building Zigbee2MQTT... (${reason})`); - rimrafSync('dist'); + process.stdout.write(`Building Zigbee2MQTT... (${reason})`); + return await new Promise((resolve, reject) => { const env = {...process.env}; const _600mb = 629145600; @@ -85,7 +89,8 @@ async function build(reason) { env.NODE_OPTIONS = '--max_old_space_size=256'; } - exec('pnpm run build', {env, cwd: __dirname}, async (err, stdout, stderr) => { + // clean build, prevent failures due to tsc incremental building + exec('pnpm run prepack', {env, cwd: __dirname}, async (err, stdout, stderr) => { if (err) { process.stdout.write(', failed\n'); @@ -107,8 +112,9 @@ async function checkDist() { await build('initial build'); } - const distHash = fs.readFileSync(hashFile, 'utf-8'); + const distHash = fs.readFileSync(hashFile, 'utf8'); const hash = await currentHash(); + if (hash !== 'unknown' && distHash !== hash) { await build('hash changed'); } @@ -118,41 +124,41 @@ async function start() { console.log(`Starting Zigbee2MQTT ${watchdog ? `with watchdog (${watchdogDelays})` : `without watchdog`}.`); await checkDist(); - const version = engines.node; - - if (!semver.satisfies(process.version, version)) { - console.log(`\t\tZigbee2MQTT requires node version ${version}, you are running ${process.version}!\n`); - } - - // Validate settings - const settings = require('./dist/util/settings'); - - settings.reRead(); - // gc { + const version = engines.node; + + if (!semver.satisfies(process.version, version)) { + console.log(`\t\tZigbee2MQTT requires node version ${version}, you are running ${process.version}!\n`); + } + + // Validate settings + const settings = require('./dist/util/settings'); + + settings.reRead(); + const settingsMigration = require('./dist/util/settingsMigration'); settingsMigration.migrateIfNecessary(); - } - const errors = settings.validate(); + const errors = settings.validate(); - if (errors.length > 0) { - unsolicitedStop = false; + if (errors.length > 0) { + unsolicitedStop = false; - console.log(`\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`); - console.log(' READ THIS CAREFULLY\n'); - console.log(`Refusing to start because configuration is not valid, found the following errors:`); + console.log(`\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`); + console.log(' READ THIS CAREFULLY\n'); + console.log(`Refusing to start because configuration is not valid, found the following errors:`); - for (const error of errors) { - console.log(`- ${error}`); + for (const error of errors) { + console.log(`- ${error}`); + } + + console.log(`\nIf you don't know how to solve this, read https://www.zigbee2mqtt.io/guide/configuration`); + console.log(`\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n`); + + return await exit(1); } - - console.log(`\nIf you don't know how to solve this, read https://www.zigbee2mqtt.io/guide/configuration`); - console.log(`\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n`); - - return exit(1); } const {Controller} = require('./dist/controller'); diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts index b9bfe2441..609a2bfa7 100644 --- a/lib/types/types.d.ts +++ b/lib/types/types.d.ts @@ -120,7 +120,7 @@ declare global { serial: { disable_led: boolean; port?: string; - adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate' | 'ember'; + adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate' | 'ember' | 'zboss'; baudrate?: number; rtscts?: boolean; }; diff --git a/lib/util/utils.ts b/lib/util/utils.ts index f3a1666ca..b971bc3e0 100644 --- a/lib/util/utils.ts +++ b/lib/util/utils.ts @@ -1,6 +1,7 @@ import type {Zigbee2MQTTAPI, Zigbee2MQTTResponse, Zigbee2MQTTResponseEndpoints, Zigbee2MQTTScene} from 'lib/types/api'; import type * as zhc from 'zigbee-herdsman-converters'; +import {exec} from 'child_process'; import assert from 'node:assert'; import crypto from 'node:crypto'; import fs from 'node:fs'; @@ -50,32 +51,26 @@ function capitalize(s: string): string { } async function getZigbee2MQTTVersion(includeCommitHash = true): Promise<{commitHash?: string; version: string}> { - const git = await import('git-last-commit'); - const packageJSON = await import('../..' + '/package.json'); + const packageJSON = await import('../../package.json'); + const version = packageJSON.version; + let commitHash: string | undefined; if (!includeCommitHash) { - return {version: packageJSON.version, commitHash: undefined}; + return {version, commitHash}; } return await new Promise((resolve) => { - const version = packageJSON.version; + exec('git rev-parse --short=8 HEAD', (error, stdout) => { + commitHash = stdout.trim(); - git.getLastCommit((err: Error, commit: {shortHash: string}) => { - let commitHash = undefined; - - if (err) { + if (error || commitHash === '') { try { commitHash = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', '.hash'), 'utf-8'); - /* v8 ignore start */ } catch { commitHash = 'unknown'; } - /* v8 ignore stop */ - } else { - commitHash = commit.shortHash; } - commitHash = commitHash.trim(); resolve({commitHash, version}); }); }); diff --git a/package.json b/package.json index bd299ba38..9ccaa0ee1 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "express-static-gzip": "^2.2.0", "fast-deep-equal": "^3.1.3", "finalhandler": "^1.3.1", - "git-last-commit": "^1.0.1", "humanize-duration": "^3.32.1", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 340a5102a..aa1ba0954 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,9 +29,6 @@ importers: finalhandler: specifier: ^1.3.1 version: 1.3.1 - git-last-commit: - specifier: ^1.0.1 - version: 1.0.1 humanize-duration: specifier: ^3.32.1 version: 3.32.1 @@ -1062,9 +1059,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - git-last-commit@1.0.1: - resolution: {integrity: sha512-FDSgeMqa7GnJDxt/q0AbrxbfeTyxp4ImxEw1e4nw6NUHA5FMhFUq33dTXI4Xdgcj1VQ1q5QLWF6WxFrJ8KCBOg==} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2730,8 +2724,6 @@ snapshots: fsevents@2.3.3: optional: true - git-last-commit@1.0.1: {} - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 diff --git a/test/utils.test.ts b/test/utils.test.ts index f7b66ced3..496f59e71 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -1,13 +1,11 @@ +import {exec} from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import utils from '../lib/util/utils'; -const mockGetLastCommit = vi.fn<() => [boolean, {shortHash: string} | null]>(() => [false, {shortHash: '123'}]); - -vi.mock('git-last-commit', () => ({ - getLastCommit: vi.fn((cb) => cb(...mockGetLastCommit())), -})); +// keep the implementations, just spy +vi.mock('node:child_process', {spy: true}); describe('Utils', () => { it('Object is empty', () => { @@ -20,13 +18,44 @@ describe('Utils', () => { expect(utils.objectHasProperties({a: 1, b: 2, c: 3}, ['a', 'b', 'd'])).toBeFalsy(); }); - it('git last commit', async () => { + it('get Z2M version', async () => { + const readFileSyncSpy = vi.spyOn(fs, 'readFileSync'); const version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version; - expect(await utils.getZigbee2MQTTVersion()).toStrictEqual({commitHash: '123', version: version}); + expect(await utils.getZigbee2MQTTVersion()).toStrictEqual({commitHash: expect.stringMatching(/^(?!unknown)[a-z0-9]{8}$/), version}); + expect(exec).toHaveBeenCalledTimes(1); - mockGetLastCommit.mockReturnValueOnce([true, null]); - expect(await utils.getZigbee2MQTTVersion()).toStrictEqual({commitHash: expect.any(String), version: version}); + // @ts-expect-error mock spy + exec.mockImplementationOnce((cmd, cb) => { + cb(null, 'abcd1234'); + }); + expect(await utils.getZigbee2MQTTVersion()).toStrictEqual({commitHash: 'abcd1234', version}); + + // @ts-expect-error mock spy + exec.mockImplementationOnce((cmd, cb) => { + cb(null, ''); + }); + // hash file may or may not be present during testing, don't failing matching if not + expect(await utils.getZigbee2MQTTVersion()).toStrictEqual({commitHash: expect.stringMatching(/^(unknown|([a-z0-9]{8}))$/), version}); + + readFileSyncSpy.mockImplementationOnce(() => { + throw new Error('no hash file'); + }); + // @ts-expect-error mock spy + exec.mockImplementationOnce((cmd, cb) => { + cb(null, ''); + }); + expect(await utils.getZigbee2MQTTVersion()).toStrictEqual({commitHash: 'unknown', version}); + + readFileSyncSpy.mockImplementationOnce(() => { + throw new Error('no hash file'); + }); + // @ts-expect-error mock spy + exec.mockImplementationOnce((cmd, cb) => { + cb(new Error('invalid'), ''); + }); + expect(await utils.getZigbee2MQTTVersion()).toStrictEqual({commitHash: 'unknown', version}); + expect(exec).toHaveBeenCalledTimes(5); }); it('Check dependency version', async () => {