Refactor build configs and improve CI/CD checks

This commit makes the build process more robust, simplifies
configurations and reduce the risk of incomplete or erroneous
deployments.

- Centralize output directory definitions by introducing
  `dist-dirs.json`.
- Add `verify-build-artifacts` utility to ensure correct build outputs
  and `print-dist-dir` to determine distribution directory.
- Add steps in CI/CD pipeline to verify build artifacts.
- Migrate Electron Builder config from YAML to CJS for capability to
  read JSON.
- Fix `release-site.yaml` failing due to pointing to wrong distribution
  directory, change it to use `print-dist-dir`.
- Improve `check-desktop-runtime-errors` to verify build artifacts for
  more reliable builds. Ensure tests fail and succeed reliably.
- Update `.gitignore` and configure ESLint to use it to define and
  ignore build artifact directories from one place, remove
  `.eslintignore` that does not add anything after this change.
- Keep `"main"` field in `package.json` as `electron-vite` depends on it
  (alex8088/electron-vite#270).
- Improve documentation
This commit is contained in:
undergroundwires
2023-09-03 14:50:31 +02:00
parent eb096d07e2
commit 0a2a1a026b
16 changed files with 364 additions and 66 deletions

View File

@@ -1,7 +1,13 @@
import { join } from 'path';
import distDirs from '@/../dist-dirs.json' assert { type: 'json' };
export const DESKTOP_BUILD_COMMAND = 'npm run electron:prebuild && npm run electron:build -- --publish never';
export const DESKTOP_BUILD_COMMAND = [
'npm run electron:prebuild',
'npm run check:verify-build-artifacts -- --electron-unbundled',
'npm run electron:build -- --publish never',
'npm run check:verify-build-artifacts -- --electron-bundled',
].join(' && ');
export const PROJECT_DIR = process.cwd();
export const DESKTOP_DIST_PATH = join(PROJECT_DIR, 'dist');
export const DESKTOP_DIST_PATH = join(PROJECT_DIR, distDirs.electronBundled);
export const APP_EXECUTION_DURATION_IN_SECONDS = 60; // Long enough for CI runners
export const SCREENSHOT_PATH = join(PROJECT_DIR, 'screenshot.png');

View File

@@ -60,7 +60,11 @@ export async function npmBuild(
cwd: projectDir,
});
if (error) {
log(error, LogLevel.Warn); // Cannot disable Vue CLI errors, stderr contains false-positives.
die(error);
}
if (await isDirMissingOrEmpty(distDir)) {
die(`The desktop application build process did not produce the expected artifacts. The output directory "${distDir}" is empty or missing.`);
}
}

View File

@@ -1,23 +1,67 @@
import { test } from 'vitest';
import {
describe, it, beforeAll, afterAll,
} from 'vitest';
import { main } from './check-desktop-runtime-errors/main';
import { COMMAND_LINE_FLAGS, CommandLineFlag } from './check-desktop-runtime-errors/cli-args';
test('should have no desktop runtime errors', async () => {
// arrange
setCommandLineFlagsFromEnvironmentVariables();
let exitCode: number;
global.process.exit = (code?: number): never => {
exitCode = code;
return undefined as never;
};
// act
await main();
// assert
expect(exitCode).to.equal(0);
}, {
timeout: 60 /* minutes */ * 10000,
describe('desktop runtime error checks', () => {
const { waitForExitCode } = useInterceptedProcessExitOrCompletion(beforeAll, afterAll);
it('should successfully execute the main function and exit with a zero status code', async () => {
// arrange
setCommandLineFlagsFromEnvironmentVariables();
// act
const exitCode = await waitForExitCode(
() => main(),
);
// assert
expect(exitCode).to.equal(0);
}, {
timeout: 60 /* minutes */ * 60000,
});
});
function useInterceptedProcessExitOrCompletion(
beforeTest: (callback: () => void) => void,
afterTest: (callback: () => void) => void,
) {
const originalFunction = global.process.exit;
let isExitCodeReceived = false;
let exitCodeResolver: (value: number | undefined) => void;
const waitForExitCode = (runner: () => Promise<void>) => new Promise<number | undefined>(
(resolve, reject) => {
exitCodeResolver = resolve;
runner()
.catch((error) => {
if (isExitCodeReceived) {
return;
}
console.error('Process did not call `process.exit` but threw an error:', error);
reject(error);
})
.then(() => {
if (isExitCodeReceived) {
return;
}
console.log('Process completed without calling `process.exit`. Treating as `0` exit code.');
exitCodeResolver(0);
});
},
);
beforeTest(() => {
global.process.exit = (code?: number): never => {
exitCodeResolver(code);
isExitCodeReceived = true;
return undefined as never;
};
});
afterTest(() => {
global.process.exit = originalFunction;
});
return {
waitForExitCode,
};
}
/*
Map environment variables to CLI arguments for compatibility with Vitest.
*/