- Bump Node.js to version 18. This change is necessary as Node.js v16 will reach end-of-life on 2023-09-11. It also ensure compatibility with dependencies requiring minimum of Node.js v18, such as `vite`, `@vitejs`plugin-legacy` and `icon-gen`. - Bump `setup-node` action to v4. - Recommend using the `nvm` tool for managing Node.js versions in the documentation. - Update documentation to point to code reference for required Node.js version. This removes duplication of information, and keeps the code as single source of truth for required Node.js version. - Refactor code to adopt the `node:` protocol for Node API imports as per Node.js 18 standards. This change addresses ambiguities and aligns with Node.js best practices (nodejs/node#38343). Currently, there is no ESLint rule to enforce this protocol, as noted in import-js/eslint-plugin-import#2717. - Replace `cross-fetch` dependency with the native Node.js fetch API introduced in Node.js 18. Adjust type casting for async iterable read streams to align with the latest Node.js APIs, based on discussions in DefinitelyTyped/DefinitelyTyped#65542.
134 lines
4.1 KiB
JavaScript
134 lines
4.1 KiB
JavaScript
/**
|
|
* Description:
|
|
* This script verifies the existence and content of build artifacts based on the
|
|
* provided CLI flags. It exists with exit code `0` if all verifications pass, otherwise
|
|
* with exit code `1`.
|
|
*
|
|
* Usage:
|
|
* node scripts/verify-build-artifacts.js [options]
|
|
*
|
|
* Options:
|
|
* --electron-unbundled Verify artifacts for the unbundled Electron application.
|
|
* --electron-bundled Verify artifacts for the bundled Electron application.
|
|
* --web Verify artifacts for the web application.
|
|
*/
|
|
|
|
import { access, readdir } from 'node:fs/promises';
|
|
import { exec } from 'node:child_process';
|
|
import { resolve } from 'node:path';
|
|
|
|
const PROCESS_ARGUMENTS = process.argv.slice(2);
|
|
const PRINT_DIST_DIR_SCRIPT_BASE_COMMAND = 'node scripts/print-dist-dir';
|
|
|
|
async function main() {
|
|
const buildConfigs = getBuildVerificationConfigs();
|
|
if (!anyCommandsFound(Object.keys(buildConfigs))) {
|
|
die(`No valid command found in process arguments. Expected one of: ${Object.keys(buildConfigs).join(', ')}`);
|
|
}
|
|
/* eslint-disable no-await-in-loop */
|
|
for (const [command, config] of Object.entries(buildConfigs)) {
|
|
if (PROCESS_ARGUMENTS.includes(command)) {
|
|
const distDir = await executePrintDistDirScript(config.printDistDirScriptArgument);
|
|
await verifyDirectoryExists(distDir);
|
|
await verifyNonEmptyDirectory(distDir);
|
|
await verifyFilesExist(distDir, config.filePatterns);
|
|
}
|
|
}
|
|
/* eslint-enable no-await-in-loop */
|
|
console.log('✅ Build completed successfully and all expected artifacts are in place.');
|
|
process.exit(0);
|
|
}
|
|
|
|
function getBuildVerificationConfigs() {
|
|
return {
|
|
'--electron-unbundled': {
|
|
printDistDirScriptArgument: '--electron-unbundled',
|
|
filePatterns: [
|
|
/main[/\\]index\.cjs/,
|
|
/preload[/\\]index\.cjs/,
|
|
/renderer[/\\]index\.htm(l)?/,
|
|
],
|
|
},
|
|
'--electron-bundled': {
|
|
printDistDirScriptArgument: '--electron-bundled',
|
|
filePatterns: [
|
|
/latest.*\.yml/, // generates latest.yml for auto-updates
|
|
/.*-\d+\.\d+\.\d+\..*/, // a file with extension and semantic version (packaged application)
|
|
],
|
|
},
|
|
'--web': {
|
|
printDistDirScriptArgument: '--web',
|
|
filePatterns: [
|
|
/index\.htm(l)?/,
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
function anyCommandsFound(commands) {
|
|
return PROCESS_ARGUMENTS.some((arg) => commands.includes(arg));
|
|
}
|
|
|
|
async function verifyDirectoryExists(directoryPath) {
|
|
try {
|
|
await access(directoryPath);
|
|
} catch (error) {
|
|
die(`Directory does not exist at \`${directoryPath}\`:\n\t${error.message}`);
|
|
}
|
|
}
|
|
|
|
async function verifyNonEmptyDirectory(directoryPath) {
|
|
const files = await readdir(directoryPath);
|
|
if (files.length === 0) {
|
|
die(`Directory is empty at \`${directoryPath}\``);
|
|
}
|
|
}
|
|
|
|
async function verifyFilesExist(directoryPath, filePatterns) {
|
|
const files = await listAllFilesRecursively(directoryPath);
|
|
for (const pattern of filePatterns) {
|
|
const match = files.some((file) => pattern.test(file));
|
|
if (!match) {
|
|
die(
|
|
`No file matches the pattern ${pattern.source} in directory \`${directoryPath}\``,
|
|
`\nFiles in directory:\n${files.map((file) => `\t- ${file}`).join('\n')}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function listAllFilesRecursively(directoryPath) {
|
|
const dir = await readdir(directoryPath, { withFileTypes: true });
|
|
const files = await Promise.all(dir.map(async (dirent) => {
|
|
const absolutePath = resolve(directoryPath, dirent.name);
|
|
if (dirent.isDirectory()) {
|
|
return listAllFilesRecursively(absolutePath);
|
|
}
|
|
return absolutePath;
|
|
}));
|
|
return files.flat();
|
|
}
|
|
|
|
async function executePrintDistDirScript(flag) {
|
|
return new Promise((resolve, reject) => {
|
|
const commandToRun = `${PRINT_DIST_DIR_SCRIPT_BASE_COMMAND} ${flag}`;
|
|
|
|
exec(commandToRun, (error, stdout, stderr) => {
|
|
if (error) {
|
|
reject(new Error(`Execution failed with error: ${error}`));
|
|
} else if (stderr) {
|
|
reject(new Error(`Execution failed with stderr: ${stderr}`));
|
|
} else {
|
|
resolve(stdout.trim());
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function die(...message) {
|
|
console.error(...message);
|
|
process.exit(1);
|
|
}
|
|
|
|
await main();
|