From 732a97ba0cce84352e6fc691195700c0fac6bd27 Mon Sep 17 00:00:00 2001 From: Koen Kanters Date: Thu, 3 Oct 2019 20:06:31 +0200 Subject: [PATCH] =?UTF-8?q?Don=E2=80=99t=20write=20to=20configuration.yaml?= =?UTF-8?q?=20when=20it=20didn=E2=80=99t=20change.=20https://github.com/Ko?= =?UTF-8?q?enkk/zigbee2mqtt/issues/2071?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/util/settings.js | 6 +++--- lib/util/yaml.js | 10 +++++++--- package.json | 1 + test/controller.test.js | 7 +++++++ test/settings.test.js | 11 +++++++++++ test/stub/data.js | 4 ++-- 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/lib/util/settings.js b/lib/util/settings.js index 03671472fa..bed07bf3ac 100644 --- a/lib/util/settings.js +++ b/lib/util/settings.js @@ -244,16 +244,16 @@ function write() { // Read settings to check if we have to split devices/groups into separate file. const actual = yaml.read(file); if (typeof actual.devices === 'string') { - yaml.write(data.joinPath(actual.devices), settings.devices); + yaml.writeIfChanged(data.joinPath(actual.devices), settings.devices); toWrite.devices = actual.devices; } if (typeof actual.groups === 'string') { - yaml.write(data.joinPath(actual.groups), settings.groups); + yaml.writeIfChanged(data.joinPath(actual.groups), settings.groups); toWrite.groups = actual.groups; } - yaml.write(file, toWrite); + yaml.writeIfChanged(file, toWrite); _settings = read(); _settingsWithDefaults = objectAssignDeep.noMutate(defaults, get()); diff --git a/lib/util/yaml.js b/lib/util/yaml.js index 9d2ab8a41d..a3a64c1c1c 100644 --- a/lib/util/yaml.js +++ b/lib/util/yaml.js @@ -1,5 +1,6 @@ const yaml = require('js-yaml'); const fs = require('fs'); +const equals = require('fast-deep-equal'); function read(file) { try { @@ -25,8 +26,11 @@ function readIfExists(file) { return fs.existsSync(file) ? read(file) : null; } -function write(file, content) { - fs.writeFileSync(file, yaml.safeDump(content)); +function writeIfChanged(file, content) { + const before = readIfExists(file); + if (!equals(before, content)) { + fs.writeFileSync(file, yaml.safeDump(content)); + } } -module.exports = {read, readIfExists, write}; +module.exports = {read, readIfExists, writeIfChanged}; diff --git a/package.json b/package.json index a6381b822c..03b4a2d18c 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "ajv": "*", "debounce": "*", + "fast-deep-equal": "*", "git-last-commit": "*", "js-yaml": "*", "mkdir-recursive": "*", diff --git a/test/controller.test.js b/test/controller.test.js index b84d2d9e45..7bb05838cd 100644 --- a/test/controller.test.js +++ b/test/controller.test.js @@ -380,4 +380,11 @@ describe('Controller', () => { await flushPromises(); expect(MQTT.publish).toHaveBeenCalledTimes(0); }); + + it('Should start when state is corrupted', async () => { + fs.writeFileSync(path.join(data.mockDir, 'state.json'), 'corrupted'); + await controller.start(); + await flushPromises(); + expect(controller.state.state).toStrictEqual({}); + }); }); diff --git a/test/settings.test.js b/test/settings.test.js index e3af2b0356..7d8d0e0484 100644 --- a/test/settings.test.js +++ b/test/settings.test.js @@ -463,4 +463,15 @@ describe('Settings', () => { settings.changeFriendlyName('myname1', 'myname'); }).toThrowError(`friendly_name 'myname' is already in use`); }); + + it('Shouldnt write to configuration.yaml when there are no changes in it', () => { + const contentConfiguration = {devices: 'devices.yaml'}; + const contentDevices = {}; + write(configurationFile, contentConfiguration); + const before = fs.statSync(configurationFile).mtimeMs; + write(devicesFile, contentDevices); + settings.addDevice('0x1234'); + const after = fs.statSync(configurationFile).mtimeMs; + expect(before).toBe(after); + }); }); diff --git a/test/stub/data.js b/test/stub/data.js index de867d5942..f764802327 100644 --- a/test/stub/data.js +++ b/test/stub/data.js @@ -132,11 +132,11 @@ function writeDefaultConfiguration() { } }; - yaml.write(path.join(mockDir, 'configuration.yaml'), config); + yaml.writeIfChanged(path.join(mockDir, 'configuration.yaml'), config); } function writeEmptyState() { - yaml.write(path.join(mockDir, 'state.json'), JSON.stringify({})); + fs.writeFileSync(path.join(mockDir, 'state.json'), JSON.stringify({})); } function writeDefaultState() {