From 57a11616a3d6d9d489507f4adaccaed5bcd0a7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Wed, 22 Jan 2025 11:01:35 +0100 Subject: [PATCH 1/4] Add skipCleanup option --- packages/compass-smoke-tests/src/cli.ts | 13 +++++++++++-- packages/compass-smoke-tests/src/context.ts | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/compass-smoke-tests/src/cli.ts b/packages/compass-smoke-tests/src/cli.ts index d1d5fbdd33c..44aadfec9cf 100755 --- a/packages/compass-smoke-tests/src/cli.ts +++ b/packages/compass-smoke-tests/src/cli.ts @@ -98,6 +98,11 @@ const argv = yargs(hideBin(process.argv)) .option('localPackage', { type: 'boolean', description: 'Use the local package instead of downloading', + }) + .option('skipCleanup', { + type: 'boolean', + description: 'Do not delete the sandbox after a run', + default: false, }); type TestSubject = PackageDetails & { @@ -195,8 +200,12 @@ async function run() { await uninstall(); } } finally { - console.log('Cleaning up sandbox'); - fs.rmSync(context.sandboxPath, { recursive: true }); + if (context.skipCleanup) { + console.log(`Skipped cleaning up sandbox: ${context.sandboxPath}`); + } else { + console.log(`Cleaning up sandbox: ${context.sandboxPath}`); + fs.rmSync(context.sandboxPath, { recursive: true }); + } } } diff --git a/packages/compass-smoke-tests/src/context.ts b/packages/compass-smoke-tests/src/context.ts index 77b63580ee7..483c7c50e49 100644 --- a/packages/compass-smoke-tests/src/context.ts +++ b/packages/compass-smoke-tests/src/context.ts @@ -9,4 +9,5 @@ export type SmokeTestsContext = { forceDownload?: boolean; localPackage?: boolean; sandboxPath: string; + skipCleanup: boolean; }; From 897a01aab1f7f25597ee86baddc514718786bc8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Wed, 22 Jan 2025 11:01:41 +0100 Subject: [PATCH 2/4] Throw ExecuteFailure with status and signal --- packages/compass-smoke-tests/src/execute.ts | 24 +++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/compass-smoke-tests/src/execute.ts b/packages/compass-smoke-tests/src/execute.ts index 1073d99967e..3b23f1283e3 100644 --- a/packages/compass-smoke-tests/src/execute.ts +++ b/packages/compass-smoke-tests/src/execute.ts @@ -1,6 +1,19 @@ -import assert from 'node:assert/strict'; import { spawnSync, type SpawnOptions } from 'node:child_process'; +export class ExecuteFailure extends Error { + constructor( + public command: string, + public args: string[], + public status: number | null, + public signal: NodeJS.Signals | null + ) { + const commandDetails = `${command} ${args.join(' ')}`; + const statusDetails = `status = ${status || 'null'}`; + const signalDetails = `signal = ${signal || 'null'})`; + super(`${commandDetails} exited with ${statusDetails} ${signalDetails}`); + } +} + export function execute( command: string, args: string[], @@ -10,10 +23,7 @@ export function execute( stdio: 'inherit', ...options, }); - assert( - status === 0 && signal === null, - `${command} ${args.join(' ')} exited with (status = ${ - status || 'null' - }, signal = ${signal || 'null'})` - ); + if (status !== 0 || signal !== null) { + throw new ExecuteFailure(command, args, status, signal); + } } From 9a00ffcd1c4a3e87068b7ae14fb3bc2019ffa08d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Wed, 22 Jan 2025 11:03:33 +0100 Subject: [PATCH 3/4] Add MSI installer --- packages/compass-smoke-tests/src/cli.ts | 3 ++ .../src/installers/types.ts | 2 +- .../src/installers/windows-msi.ts | 49 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 packages/compass-smoke-tests/src/installers/windows-msi.ts diff --git a/packages/compass-smoke-tests/src/cli.ts b/packages/compass-smoke-tests/src/cli.ts index 44aadfec9cf..b1678aaf40b 100755 --- a/packages/compass-smoke-tests/src/cli.ts +++ b/packages/compass-smoke-tests/src/cli.ts @@ -20,6 +20,7 @@ import { type SmokeTestsContext } from './context'; import { installMacDMG } from './installers/mac-dmg'; import { installMacZIP } from './installers/mac-zip'; import { installWindowsZIP } from './installers/windows-zip'; +import { installWindowsMSI } from './installers/windows-msi'; const SUPPORTED_PLATFORMS = ['win32', 'darwin', 'linux'] as const; const SUPPORTED_ARCHS = ['x64', 'arm64'] as const; @@ -159,6 +160,8 @@ function getInstaller(kind: PackageKind) { return installMacZIP; } else if (kind === 'windows_zip') { return installWindowsZIP; + } else if (kind === 'windows_msi') { + return installWindowsMSI; } else { throw new Error(`Installer for '${kind}' is not yet implemented`); } diff --git a/packages/compass-smoke-tests/src/installers/types.ts b/packages/compass-smoke-tests/src/installers/types.ts index c9c7d73d06e..3d22429ab84 100644 --- a/packages/compass-smoke-tests/src/installers/types.ts +++ b/packages/compass-smoke-tests/src/installers/types.ts @@ -16,5 +16,5 @@ export type InstallablePackage = { export type InstalledAppInfo = { appPath: string; - uninstall: () => Promise; + uninstall: () => void | Promise; }; diff --git a/packages/compass-smoke-tests/src/installers/windows-msi.ts b/packages/compass-smoke-tests/src/installers/windows-msi.ts new file mode 100644 index 00000000000..c5a93d15ab5 --- /dev/null +++ b/packages/compass-smoke-tests/src/installers/windows-msi.ts @@ -0,0 +1,49 @@ +import path from 'node:path'; + +import type { InstalledAppInfo, InstallablePackage } from './types'; +import { execute, ExecuteFailure } from '../execute'; + +// See https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/msiexec + +export function installWindowsMSI({ + appName, + filepath, + destinationPath, +}: InstallablePackage): InstalledAppInfo { + const installDirectory = path.resolve(destinationPath, appName); + const appPath = path.resolve(installDirectory, `${appName}.exe`); + + function uninstall() { + execute('msiexec', ['/uninstall', filepath, '/passive']); + } + + // Installing an MSI which is already installed is a no-op + // So we uninstall the MSI first (which will use the PackageCode to find the installed application) + // It is fine if the uninstall exists with status 1605, as this happens if the app wasn't already installed. + try { + uninstall(); + } catch (err) { + if (err instanceof ExecuteFailure && err.status === 1605) { + console.log( + "Uninstalling before installing failed, which is expected if the app wasn't already installed" + ); + } else { + throw err; + } + } + + execute('msiexec', [ + '/package', + filepath, + '/passive', + `APPLICATIONROOTDIRECTORY=${installDirectory}`, + ]); + + // Check that the executable will run without being quarantined or similar + execute(appPath, ['--version']); + + return { + appPath: installDirectory, + uninstall, + }; +} From c0cfb239ab330330723d21b7fd6e9e2127d391f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Wed, 22 Jan 2025 11:11:06 +0100 Subject: [PATCH 4/4] Update CI to run windows_msi --- .evergreen/functions.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.evergreen/functions.yml b/.evergreen/functions.yml index 7ef78460f5b..662ea19f282 100644 --- a/.evergreen/functions.yml +++ b/.evergreen/functions.yml @@ -679,9 +679,8 @@ functions: eval $(.evergreen/print-compass-env.sh) if [[ "$IS_WINDOWS" == "true" ]]; then - # TODO: windows_setup - # TODO: windows_msi npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_zip + npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_msi fi if [[ "$IS_OSX" == "true" ]]; then