diff --git a/.evergreen/buildvariants-and-tasks.in.yml b/.evergreen/buildvariants-and-tasks.in.yml index e84b998402e..62c21158bfb 100644 --- a/.evergreen/buildvariants-and-tasks.in.yml +++ b/.evergreen/buildvariants-and-tasks.in.yml @@ -469,6 +469,7 @@ tasks: - func: bootstrap vars: scope: 'compass-e2e-tests' + - func: get-compass-mongodb-com - func: smoketest-packaged-app vars: mongodb_version: latest-enterprise diff --git a/.evergreen/buildvariants-and-tasks.yml b/.evergreen/buildvariants-and-tasks.yml index c92fe9e8ab5..2d5af0d53fd 100644 --- a/.evergreen/buildvariants-and-tasks.yml +++ b/.evergreen/buildvariants-and-tasks.yml @@ -495,6 +495,7 @@ tasks: - func: bootstrap vars: scope: compass-e2e-tests + - func: get-compass-mongodb-com - func: smoketest-packaged-app vars: mongodb_version: latest-enterprise diff --git a/.evergreen/functions.yml b/.evergreen/functions.yml index 8b2ce8b7672..3c30305448a 100644 --- a/.evergreen/functions.yml +++ b/.evergreen/functions.yml @@ -659,6 +659,36 @@ functions: npm run --unsafe-perm --workspace compass-e2e-tests test-packaged-ci + get-compass-mongodb-com: + - command: github.generate_token + params: + owner: 10gen + repo: compass-mongodb-com + expansion_name: generated_token + permissions: # optional + contents: read + - command: shell.exec + type: setup + params: + working_dir: src + shell: bash + env: + <<: *compass-env + script: | + set -e + eval $(.evergreen/print-compass-env.sh) + git clone https://x-access-token:${generated_token}@github.com/10gen/compass-mongodb-com.git ../compass-mongodb-com + cd ../compass-mongodb-com + echo "Using node version:"; + node -v; + echo "Using npm version:"; + npm -v; + echo "Using gcc version:" + gcc --version; + echo "Using g++ version:" + g++ --version; + npm ci --engine-strict=false + smoketest-packaged-app: - command: shell.exec # Fail the task if it's idle for 10 mins diff --git a/.evergreen/preinstall.sh b/.evergreen/preinstall.sh index 3093b9063c0..41a3511ef2a 100755 --- a/.evergreen/preinstall.sh +++ b/.evergreen/preinstall.sh @@ -13,9 +13,9 @@ echo "APPDATA: $APPDATA" echo "PATH: $PATH" # these are super useful if you want to run the smoke tests locally -echo "DEV_VERSION_IDENTIFIER: $DEV_VERSION_IDENTIFIER" -echo "EVERGREEN_BUCKET_NAME: $EVERGREEN_BUCKET_NAME" -echo "EVERGREEN_BUCKET_KEY_PREFIX: $EVERGREEN_BUCKET_KEY_PREFIX" +echo "export DEV_VERSION_IDENTIFIER=$DEV_VERSION_IDENTIFIER" +echo "export EVERGREEN_BUCKET_KEY_PREFIX=$EVERGREEN_BUCKET_KEY_PREFIX" +echo "export EVERGREEN_BUCKET_NAME=$EVERGREEN_BUCKET_NAME" echo "IS_OSX: $IS_OSX" echo "IS_LINUX: $IS_LINUX" diff --git a/package-lock.json b/package-lock.json index c76564de9db..1dba8b884ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46045,11 +46045,13 @@ "@mongodb-js/prettier-config-compass": "^1.1.3", "@mongodb-js/tsconfig-compass": "^1.1.3", "@types/node": "^20", + "cross-spawn": "^7.0.5", "depcheck": "^1.4.1", "eslint": "^7.25.0", "hadron-build": "^25.6.3", "lodash": "^4.17.21", "prettier": "^2.7.1", + "tree-kill": "^1.2.2", "typescript": "^5.0.4", "yargs": "^17.7.2" } @@ -57117,11 +57119,13 @@ "@mongodb-js/prettier-config-compass": "^1.1.3", "@mongodb-js/tsconfig-compass": "^1.1.3", "@types/node": "^20", + "cross-spawn": "^7.0.5", "depcheck": "^1.4.1", "eslint": "^7.25.0", "hadron-build": "^25.6.3", "lodash": "^4.17.21", "prettier": "^2.7.1", + "tree-kill": "^1.2.2", "typescript": "^5.0.4", "yargs": "^17.7.2" }, diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index 1f17ed7748b..b13b1773714 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -636,8 +636,10 @@ async function startCompassElectron( process.env.APP_ENV = 'webdriverio'; // For webdriverio env we are changing appName so that keychain records do not - // overlap with anything else - process.env.HADRON_PRODUCT_NAME_OVERRIDE = 'MongoDB Compass WebdriverIO'; + // overlap with anything else. But leave it alone if + if (!process.env.HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE) { + process.env.HADRON_PRODUCT_NAME_OVERRIDE = 'MongoDB Compass WebdriverIO'; + } // Guide cues might affect too many tests in a way where the auto showing of the cue prevents // clicks from working on elements. Dealing with this case-by-case is way too much work, so diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 449bf6590f3..7ed8ee95ff4 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -1380,3 +1380,12 @@ export const GlobalWrites = { SampleFindingDocuments: '[data-testid="sample-finding-documents"]', SampleInsertingDocuments: '[data-testid="sample-inserting-documents"]', }; + +// Auto-update toasts +export const AutoUpdateToast = '[data-testid="toast-compass-update"]'; +export const AutoUpdateRestartButton = + '[data-testid="auto-update-restart-button"]'; +export const AutoUpdateDownloadLink = + '[data-testid="auto-update-download-link"]'; +export const AutoUpdateReleaseNotesLink = + '[data-testid="auto-update-release-notes-link"]'; diff --git a/packages/compass-e2e-tests/tests/auto-update.test.ts b/packages/compass-e2e-tests/tests/auto-update.test.ts new file mode 100644 index 00000000000..781ac0ef162 --- /dev/null +++ b/packages/compass-e2e-tests/tests/auto-update.test.ts @@ -0,0 +1,87 @@ +import fs from 'fs'; +import path from 'path'; +import { expect } from 'chai'; +import { + init, + cleanup, + Selectors, + screenshotPathName, +} from '../helpers/compass'; +import { LOG_PATH } from '../helpers/test-runner-paths'; + +describe('Auto-update', function () { + it('auto-update from', async function () { + if (process.env.TEST_NAME !== 'AUTO_UPDATE_FROM') { + // we don't want this test to execute along with all the others under + // normal circumstances because it is destructive - it overwrites Compass + // itself + this.skip(); + } + + // run the app and wait for it to auto-update + console.log('starting compass the first time'); + const compass = await init('auto-update from', { firstRun: true }); + const { browser } = compass; + try { + await browser.$(Selectors.AutoUpdateToast).waitForDisplayed(); + + if (process.env.AUTO_UPDATE_UPDATABLE === 'true') { + const restartButton = browser.$(Selectors.AutoUpdateRestartButton); + await restartButton.waitForDisplayed(); + + // We could click the restart button to apply the update and restart the + // app, but restarting the app confuses webdriverio or at least our test + // helpers. So we're going to just restart the app manually. + await browser.pause(1000); + } else { + // When auto-update is not supported the toast contains a link to + // download + const linkElement = browser.$(Selectors.AutoUpdateDownloadLink); + await linkElement.waitForDisplayed(); + expect(await linkElement.getAttribute('href')).to.equal( + 'https://www.mongodb.com/try/download/compass?utm_source=compass&utm_medium=product' + ); + } + } finally { + await browser.screenshot(screenshotPathName('auto-update-from')); + await cleanup(compass); + + if (process.platform === 'darwin' && process.env.HOME) { + console.log('copying ShipIt dir if it exits'); + const shipitDir = path.resolve( + process.env.HOME, + 'Library', + 'Caches', + 'com.mongodb.compass.dev.ShipIt' + ); + + if (fs.existsSync(shipitDir)) { + console.log(`copying ${shipitDir}`); + fs.cpSync(shipitDir, `${LOG_PATH}/ShipIt`, { recursive: true }); + } else { + console.log(`${shipitDir} does not exist`); + } + } + } + + if (process.env.AUTO_UPDATE_UPDATABLE === 'true') { + console.log('starting compass a second time'); + // run the app again and check that the version changed + const compass = await init('auto-update from restart', { + firstRun: false, + }); + const { browser } = compass; + try { + await browser.$(Selectors.AutoUpdateToast).waitForDisplayed(); + await browser + .$(Selectors.AutoUpdateReleaseNotesLink) + .waitForDisplayed(); + } finally { + await browser.screenshot( + screenshotPathName('auto-update-from-restart') + ); + await cleanup(compass); + } + } + }); +}); diff --git a/packages/compass-smoke-tests/package.json b/packages/compass-smoke-tests/package.json index 762636cfc64..0177840bfcb 100644 --- a/packages/compass-smoke-tests/package.json +++ b/packages/compass-smoke-tests/package.json @@ -34,11 +34,13 @@ "@mongodb-js/eslint-config-compass": "^1.2.3", "@mongodb-js/prettier-config-compass": "^1.1.3", "@mongodb-js/tsconfig-compass": "^1.1.3", + "cross-spawn": "^7.0.5", "depcheck": "^1.4.1", "eslint": "^7.25.0", "hadron-build": "^25.6.3", "lodash": "^4.17.21", "prettier": "^2.7.1", + "tree-kill": "^1.2.2", "typescript": "^5.0.4", "yargs": "^17.7.2" } diff --git a/packages/compass-smoke-tests/src/cli.ts b/packages/compass-smoke-tests/src/cli.ts index d1d5fbdd33c..143572f2703 100755 --- a/packages/compass-smoke-tests/src/cli.ts +++ b/packages/compass-smoke-tests/src/cli.ts @@ -3,6 +3,8 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; +//import crossSpawn from 'cross-spawn'; +//import kill from 'tree-kill'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { pick } from 'lodash'; @@ -21,6 +23,11 @@ import { installMacDMG } from './installers/mac-dmg'; import { installMacZIP } from './installers/mac-zip'; import { installWindowsZIP } from './installers/windows-zip'; +// hardcode to yesterday's nightly +process.env.DEV_VERSION_IDENTIFIER = '25.1.22-dev.10011'; +process.env.EVERGREEN_BUCKET_KEY_PREFIX = + '10gen-compass-main/e231969720181ae6e978f0ce3799b954d99ed9a2_7797'; + const SUPPORTED_PLATFORMS = ['win32', 'darwin', 'linux'] as const; const SUPPORTED_ARCHS = ['x64', 'arm64'] as const; @@ -179,7 +186,9 @@ async function run() { ]) ); - const { kind, filepath, appName } = await getTestSubject(context); + const { kind, filepath, appName, autoUpdatable } = await getTestSubject( + context + ); const install = getInstaller(kind); try { @@ -190,7 +199,25 @@ async function run() { }); try { - runTest({ appName, appPath }); + //const server = startAutoUpdateServer({ + // allowDowngrades: true, + // port: 8080, + //}); + try { + runTest({ + appName, + appPath, + autoUpdatable, + testName: 'AUTO_UPDATE_FROM', + }); + } finally { + //if (server.pid) { + // console.log('Stopping auto-update server'); + // kill(server.pid, 'SIGINT'); + //} else { + // console.log('cannnot stop auto-update server because no pid'); + //} + } } finally { await uninstall(); } @@ -200,12 +227,62 @@ async function run() { } } +/* +type AutoUpdateServerOptions = { + port: number; + allowDowngrades?: boolean; +}; + +function startAutoUpdateServer({ + port, + allowDowngrades, +}: AutoUpdateServerOptions) { + const env: Record = { + ...process.env, + PORT: port.toString(), + }; + if (allowDowngrades) { + env.UPDATE_CHECKER_ALLOW_DOWNGRADES = 'true'; + } + + // a git repo that is not published to npm that evergreen clones for us in CI + // next to the Compass code + const cwd = path.join( + __dirname, + '..', + '..', + '..', + '..', + 'compass-mongodb-com' + ); + + if (!fs.existsSync(cwd)) { + throw new Error(`compass-mongodb-com does not exist: ${cwd}`); + } + + console.log('Starting auto-update server', cwd); + return crossSpawn.spawn('npm', ['run', 'start'], { + env, + cwd, + stdio: 'inherit', + shell: true, + }); +} +*/ + type RunTestOptions = { appName: string; appPath: string; + autoUpdatable?: boolean; + testName: string; }; -function runTest({ appName, appPath }: RunTestOptions) { +function runTest({ + appName, + appPath, + autoUpdatable, + testName, +}: RunTestOptions) { execute( 'npm', [ @@ -215,13 +292,22 @@ function runTest({ appName, appPath }: RunTestOptions) { '--workspace', 'compass-e2e-tests', '--', - '--test-filter=time-to-first-query', + '--test-filter=auto-update', ], { // We need to use a shell to get environment variables setup correctly shell: true, env: { ...process.env, + //HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE: 'http://localhost:8080', + + // this is the usual one, just setting + // HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE so the app will set up the + // auto-update code even though it is running in CI + HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE: 'https://compass.mongodb.com', + + AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), + TEST_NAME: testName, COMPASS_APP_NAME: appName, COMPASS_APP_PATH: appPath, }, diff --git a/packages/compass-smoke-tests/src/execute.ts b/packages/compass-smoke-tests/src/execute.ts index 1073d99967e..7e97e988297 100644 --- a/packages/compass-smoke-tests/src/execute.ts +++ b/packages/compass-smoke-tests/src/execute.ts @@ -6,6 +6,10 @@ export function execute( args: string[], options?: SpawnOptions ) { + // print the command so that when it outputs to stdout we can see where it + // comes from + console.log(command, args); + const { status, signal } = spawnSync(command, args, { stdio: 'inherit', ...options, diff --git a/packages/compass-smoke-tests/src/installers/mac-dmg.ts b/packages/compass-smoke-tests/src/installers/mac-dmg.ts index 4cccabd71cf..dae0ccccd90 100644 --- a/packages/compass-smoke-tests/src/installers/mac-dmg.ts +++ b/packages/compass-smoke-tests/src/installers/mac-dmg.ts @@ -33,16 +33,29 @@ export function installMacDMG({ 'Application Support', appName ); + const shipitDir = path.resolve( + process.env.HOME, + 'Library', + 'Caches', + 'com.mongodb.compass.dev.ShipIt' + ); - if (fs.existsSync(settingsDir)) { - console.log(`${settingsDir} already exists. Removing.`); - fs.rmSync(settingsDir, { recursive: true }); + for (const dir of [settingsDir, shipitDir]) { + if (fs.existsSync(dir)) { + console.log(`${dir} already exists. Removing.`); + fs.rmSync(dir, { recursive: true }); + } } } + const executablePath = path.resolve(appPath, 'Contents/MacOS', appName); + + execute('xattr', ['-l', appPath]); + execute('xattr', ['-l', executablePath]); + // see if the executable will run without being quarantined or similar // TODO: Move this somewhere shared between mac installers - execute(path.resolve(appPath, 'Contents/MacOS', appName), ['--version']); + execute(executablePath, ['--version']); return { appPath: appPath, diff --git a/packages/compass-smoke-tests/src/installers/mac-zip.ts b/packages/compass-smoke-tests/src/installers/mac-zip.ts index 083ba5869c4..0036525535f 100644 --- a/packages/compass-smoke-tests/src/installers/mac-zip.ts +++ b/packages/compass-smoke-tests/src/installers/mac-zip.ts @@ -23,10 +23,18 @@ export function installMacZIP({ 'Application Support', appName ); + const shipitDir = path.resolve( + process.env.HOME, + 'Library', + 'Caches', + 'com.mongodb.compass.dev.ShipIt' + ); - if (fs.existsSync(settingsDir)) { - console.log(`${settingsDir} already exists. Removing.`); - fs.rmSync(settingsDir, { recursive: true }); + for (const dir of [settingsDir, shipitDir]) { + if (fs.existsSync(dir)) { + console.log(`${dir} already exists. Removing.`); + fs.rmSync(dir, { recursive: true }); + } } } diff --git a/packages/compass/src/main/application.ts b/packages/compass/src/main/application.ts index be6e8e8e605..4166c9bfdf4 100644 --- a/packages/compass/src/main/application.ts +++ b/packages/compass/src/main/application.ts @@ -160,9 +160,7 @@ class CompassApplication { await this.setupCORSBypass(); void this.setupCompassAuthService(); - // TODO(COMPASS-7618): For now don't setup auto-update in CI because the - // toasts will obscure other things which we don't expect yet. - if (!process.env.CI) { + if (!process.env.CI || process.env.HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE) { this.setupAutoUpdate(); } await setupCSFLELibrary(); diff --git a/packages/compass/src/main/auto-update-manager.ts b/packages/compass/src/main/auto-update-manager.ts index 46c2a6772a2..7b82e179eef 100644 --- a/packages/compass/src/main/auto-update-manager.ts +++ b/packages/compass/src/main/auto-update-manager.ts @@ -478,7 +478,10 @@ const STATE_UPDATE: Record< mongoLogId(1001000129), 'AutoUpdateManager', 'Error Downloading Update', - { message: error.message } + { + message: error.message, + attr: { code: error.code, domain: error.domain }, + } ); }, },