Release v2.14.2
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m11s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 30s

This commit is contained in:
lzstealth committed 2026-10-12 00:56:09 +01:00
1 parent c3299abaac
commit ddcc665f81
47 files changed
+1209 -66

No files matched your search

+61 -1
View File
@@ -30,6 +30,66 @@ test('buildFontStylesheet keeps disabled fonts available for rendering', () => {
assert.match(stylesheet, /Enabled Font/);
assert.match(stylesheet, /Disabled Font/);
assert.match(stylesheet, /\/media\/assets\/fonts\/enabled-font.ttf/);
});
test('legacy fonts and weather icons migrate into shared assets without losing metadata', async () => {
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pulse-assets-'));
try {
const fonts = path.join(root, 'fonts');
const weather = path.join(root, 'weather-icons', 'abc');
await fs.promises.mkdir(fonts, { recursive: true });
await fs.promises.mkdir(weather, { recursive: true });
await fs.promises.writeFile(path.join(fonts, 'custom.ttf'), 'font-data');
await fs.promises.writeFile(path.join(fonts, 'fonts.json'), JSON.stringify([{ id: 'custom', family: 'Custom', fileName: 'custom.ttf', enabled: false }]));
await fs.promises.writeFile(path.join(fonts, 'fonts.css'), "src:url('/media/fonts/custom.ttf')");
await fs.promises.writeFile(path.join(weather, 'clear-123abc.png'), 'icon-data');
await fs.promises.writeFile(path.join(root, 'weather-icons', 'sets.json'), JSON.stringify([{ id: 'abc', name: 'Custom', icons: { clear: '/media/weather-icons/abc/clear-123abc.png' } }]));
const library = require('../src/web/lib/media/font-library');
await library.ensureFontLibrary(path.join(root, 'uploads'));
const loaded = library.loadFontLibrary(root);
assert.equal(loaded.fonts[0].id, 'custom');
assert.equal(loaded.fonts[0].enabled, false);
assert.equal(loaded.manifestPath, path.join(root, 'assets', 'fonts.json'));
assert.equal(loaded.stylesheetPath, path.join(root, 'assets', 'fonts.css'));
assert.match(loaded.stylesheetHref, /^\/media\/assets\/fonts.css/);
assert.equal(fs.readFileSync(path.join(root, 'assets', 'fonts', 'custom.ttf'), 'utf8'), 'font-data');
assert.match(fs.readFileSync(loaded.stylesheetPath, 'utf8'), /\/media\/assets\/fonts\/custom.ttf/);
const weatherLibrary = require('../src/web/lib/media/weather-icon-library');
assert.equal(weatherLibrary.readWeatherIconSets(root)[0].icons.clear, '/media/assets/weather-icons/abc/clear-123abc.png');
assert.equal(weatherLibrary.getWeatherManifestPath(root), path.join(root, 'assets', 'weather.json'));
assert.equal(fs.existsSync(path.join(root, 'assets', 'weather-icons', 'sets.json')), false);
assert.equal(fs.readFileSync(path.join(root, 'assets', 'weather-icons', 'abc', 'clear-123abc.png'), 'utf8'), 'icon-data');
assert.equal(fs.existsSync(fonts), false);
assert.equal(fs.existsSync(path.join(root, 'weather-icons')), false);
await library.ensureFontLibrary(root);
assert.equal(library.loadFontLibrary(root).fonts.length, 1);
} finally {
await fs.promises.rm(root, { recursive: true, force: true });
}
});
test('shared assets migration tolerates simultaneous service startup', async () => {
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pulse-assets-race-'));
try {
const fonts = path.join(root, 'fonts');
await fs.promises.mkdir(fonts);
await fs.promises.writeFile(path.join(fonts, 'fonts.json'), '[]');
await fs.promises.writeFile(path.join(fonts, 'fonts.css'), "src:url('/media/fonts/custom.ttf')");
await Promise.all(Array.from({ length: 100 }, function (_unused, index) {
return fs.promises.writeFile(path.join(fonts, 'font-' + index + '.ttf'), 'font-data');
}));
const run = require('node:util').promisify(require('node:child_process').execFile);
const modulePath = require.resolve('../src/web/lib/media/assets-storage');
await Promise.all(Array.from({ length: 4 }, function () {
return run(process.execPath, ['-e', 'require(' + JSON.stringify(modulePath) + ').migrateLegacyAssets(' + JSON.stringify(root) + ')']);
}));
assert.equal(fs.existsSync(fonts), false);
assert.equal(fs.readdirSync(path.join(root, 'assets', 'fonts')).length, 100);
assert.equal(JSON.parse(fs.readFileSync(path.join(root, 'assets', 'fonts.json'))).length, 0);
} finally {
await fs.promises.rm(root, { recursive: true, force: true });
}
});
test('collectFontLibrarySyncOperations keeps disabled fonts as puts', async () => {
@@ -43,7 +103,7 @@ test('collectFontLibrarySyncOperations keeps disabled fonts as puts', async () =
const operations = collectFontLibrarySyncOperations(tempRoot);
const fontOps = operations.filter(function (operation) {
return String(operation.uploadPath || '').indexOf('/media/fonts/') === 0 && /\.ttf$/.test(String(operation.uploadPath || ''));
return String(operation.uploadPath || '').indexOf('/media/assets/fonts/') === 0 && /\.ttf$/.test(String(operation.uploadPath || ''));
});
assert.deepEqual(fontOps.map(function (operation) {