From 8fc59f442b8837f526b9a13179f65113cc077fc9 Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Mon, 17 Jul 2023 11:19:13 -0400 Subject: [PATCH 1/6] fix: Fix web worker creation within spec frame (#27313) --- cli/CHANGELOG.md | 1 + .../cypress/e2e/e2e/privileged_commands.cy.ts | 15 +++++++++++++++ packages/driver/src/util/privileged_channel.ts | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index ac2728f2a6..5807c32015 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -6,6 +6,7 @@ _Released 07/18/2023 (PENDING)_ **Bugfixes:** - Fixed an issue where `cy.writeFile()` would erroneously fail with the error `cy.writeFile() must only be invoked from the spec file or support file`. Fixes [#27097](https://github.com/cypress-io/cypress/issues/27097). +- Fixed an issue where web workers could not be created within a spec. Fixes [#27298](https://github.com/cypress-io/cypress/issues/27298). ## 12.17.1 diff --git a/packages/driver/cypress/e2e/e2e/privileged_commands.cy.ts b/packages/driver/cypress/e2e/e2e/privileged_commands.cy.ts index d797e47249..6dd9c19332 100644 --- a/packages/driver/cypress/e2e/e2e/privileged_commands.cy.ts +++ b/packages/driver/cypress/e2e/e2e/privileged_commands.cy.ts @@ -132,6 +132,21 @@ describe('privileged commands', () => { `) }) + it('allows web workers in spec frame', () => { + const workerScript = `postMessage('hello from worker')` + const blob = new Blob([workerScript], { type: 'application/javascript' }) + const workerURL = URL.createObjectURL(blob) + const worker = new Worker(workerURL) + + return new Promise((resolve) => { + worker.onmessage = ({ data }) => { + expect(data).to.equal('hello from worker') + + resolve() + } + }) + }) + it('passes in test body .then() callback', () => { cy.then(() => { cy.exec('echo "hello"') diff --git a/packages/driver/src/util/privileged_channel.ts b/packages/driver/src/util/privileged_channel.ts index 4ec32ce80d..97f6106072 100644 --- a/packages/driver/src/util/privileged_channel.ts +++ b/packages/driver/src/util/privileged_channel.ts @@ -10,7 +10,7 @@ export function setSpecContentSecurityPolicy (specWindow) { const metaEl = specWindow.document.createElement('meta') metaEl.setAttribute('http-equiv', 'Content-Security-Policy') - metaEl.setAttribute('content', `script-src 'unsafe-eval'`) + metaEl.setAttribute('content', `script-src 'unsafe-eval'; worker-src * data: blob: 'unsafe-eval' 'unsafe-inline'`) specWindow.document.querySelector('head')!.appendChild(metaEl) } From ddec92d8da9fac92a7556867cb0258a4dbe8f1ec Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Mon, 17 Jul 2023 12:19:23 -0400 Subject: [PATCH 2/6] fix: Handle privileged commands arg mutation (#27282) --- cli/CHANGELOG.md | 1 + .../driver/cypress/e2e/commands/exec.cy.js | 4 +- .../driver/cypress/e2e/commands/files.cy.js | 16 +++---- .../driver/cypress/e2e/commands/task.cy.js | 4 +- .../e2e/e2e/origin/commands/files.cy.ts | 2 +- .../cypress/e2e/e2e/privileged_commands.cy.ts | 11 +++++ .../src/cy/commands/actions/selectFile.ts | 14 ++---- packages/driver/src/cy/commands/exec.ts | 6 --- packages/driver/src/cy/commands/files.ts | 12 ----- .../driver/src/cy/commands/origin/index.ts | 15 ------ packages/driver/src/cy/commands/task.ts | 6 --- packages/driver/src/cypress/chainer.ts | 4 +- packages/driver/src/cypress/cy.ts | 8 ++-- .../driver/src/util/privileged_channel.ts | 47 ++----------------- .../privileged-commands/privileged-channel.js | 9 +++- .../privileged-commands-manager.ts | 4 +- 16 files changed, 49 insertions(+), 114 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 5807c32015..b3881e9bf7 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -5,6 +5,7 @@ _Released 07/18/2023 (PENDING)_ **Bugfixes:** +- Fixed an issue where commands would fail with the error `must only be invoked from the spec file or support file` if their arguments were mutated. Fixes [#27200](https://github.com/cypress-io/cypress/issues/27200). - Fixed an issue where `cy.writeFile()` would erroneously fail with the error `cy.writeFile() must only be invoked from the spec file or support file`. Fixes [#27097](https://github.com/cypress-io/cypress/issues/27097). - Fixed an issue where web workers could not be created within a spec. Fixes [#27298](https://github.com/cypress-io/cypress/issues/27298). diff --git a/packages/driver/cypress/e2e/commands/exec.cy.js b/packages/driver/cypress/e2e/commands/exec.cy.js index 9dbcce705d..61e622f890 100644 --- a/packages/driver/cypress/e2e/commands/exec.cy.js +++ b/packages/driver/cypress/e2e/commands/exec.cy.js @@ -17,8 +17,8 @@ describe('src/cy/commands/exec', () => { cy.exec('ls').then(() => { expect(Cypress.backend).to.be.calledWith('run:privileged', { + args: ['8374177128052794'], commandName: 'exec', - userArgs: ['8374177128052794'], options: { cmd: 'ls', timeout: 2500, @@ -33,8 +33,8 @@ describe('src/cy/commands/exec', () => { cy.exec('ls', { env: { FOO: 'foo' } }).then(() => { expect(Cypress.backend).to.be.calledWith('run:privileged', { + args: ['8374177128052794', '6419589148408857'], commandName: 'exec', - userArgs: ['8374177128052794', '6419589148408857'], options: { cmd: 'ls', timeout: 2500, diff --git a/packages/driver/cypress/e2e/commands/files.cy.js b/packages/driver/cypress/e2e/commands/files.cy.js index 7e04ef45be..d72094e834 100644 --- a/packages/driver/cypress/e2e/commands/files.cy.js +++ b/packages/driver/cypress/e2e/commands/files.cy.js @@ -21,8 +21,8 @@ describe('src/cy/commands/files', () => { expect(Cypress.backend).to.be.calledWith( 'run:privileged', { + args: ['6998637248317671'], commandName: 'readFile', - userArgs: ['6998637248317671'], options: { file: 'foo.json', encoding: 'utf8', @@ -39,8 +39,8 @@ describe('src/cy/commands/files', () => { expect(Cypress.backend).to.be.calledWith( 'run:privileged', { + args: ['6998637248317671', '2573904513237804'], commandName: 'readFile', - userArgs: ['6998637248317671', '2573904513237804'], options: { file: 'foo.json', encoding: 'ascii', @@ -61,8 +61,8 @@ describe('src/cy/commands/files', () => { expect(Cypress.backend).to.be.calledWith( 'run:privileged', { + args: ['6998637248317671', '6158203196586298'], commandName: 'readFile', - userArgs: ['6998637248317671', '6158203196586298'], options: { file: 'foo.json', encoding: null, @@ -451,8 +451,8 @@ describe('src/cy/commands/files', () => { expect(Cypress.backend).to.be.calledWith( 'run:privileged', { + args: ['2916834115813688', '4891975990226114'], commandName: 'writeFile', - userArgs: ['2916834115813688', '4891975990226114'], options: { fileName: 'foo.txt', contents: 'contents', @@ -471,8 +471,8 @@ describe('src/cy/commands/files', () => { expect(Cypress.backend).to.be.calledWith( 'run:privileged', { + args: ['2916834115813688', '4891975990226114', '2573904513237804'], commandName: 'writeFile', - userArgs: ['2916834115813688', '4891975990226114', '2573904513237804'], options: { fileName: 'foo.txt', contents: 'contents', @@ -494,8 +494,8 @@ describe('src/cy/commands/files', () => { expect(Cypress.backend).to.be.calledWith( 'run:privileged', { + args: ['2916834115813688', '6309890104324788', '6158203196586298'], commandName: 'writeFile', - userArgs: ['2916834115813688', '6309890104324788', '6158203196586298'], options: { fileName: 'foo.txt', contents: buffer, @@ -514,8 +514,8 @@ describe('src/cy/commands/files', () => { expect(Cypress.backend).to.be.calledWith( 'run:privileged', { + args: ['2916834115813688', '4891975990226114', '4694939291947123'], commandName: 'writeFile', - userArgs: ['2916834115813688', '4891975990226114', '4694939291947123'], options: { fileName: 'foo.txt', contents: 'contents', @@ -569,8 +569,8 @@ describe('src/cy/commands/files', () => { expect(Cypress.backend).to.be.calledWith( 'run:privileged', { + args: ['2916834115813688', '4891975990226114', '2343101193011749'], commandName: 'writeFile', - userArgs: ['2916834115813688', '4891975990226114', '2343101193011749'], options: { fileName: 'foo.txt', contents: 'contents', diff --git a/packages/driver/cypress/e2e/commands/task.cy.js b/packages/driver/cypress/e2e/commands/task.cy.js index dd00ecddc7..4675016e57 100644 --- a/packages/driver/cypress/e2e/commands/task.cy.js +++ b/packages/driver/cypress/e2e/commands/task.cy.js @@ -14,8 +14,8 @@ describe('src/cy/commands/task', () => { cy.task('foo').then(() => { expect(Cypress.backend).to.be.calledWith('run:privileged', { + args: ['338657716278786'], commandName: 'task', - userArgs: ['338657716278786'], options: { task: 'foo', timeout: 2500, @@ -30,8 +30,8 @@ describe('src/cy/commands/task', () => { cy.task('foo', { foo: 'foo' }).then(() => { expect(Cypress.backend).to.be.calledWith('run:privileged', { + args: ['338657716278786', '4940328425038888'], commandName: 'task', - userArgs: ['338657716278786', '4940328425038888'], options: { task: 'foo', timeout: 2500, diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/files.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/files.cy.ts index 495e4725cd..771aa27edf 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/files.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/files.cy.ts @@ -37,8 +37,8 @@ context('cy.origin files', { browser: '!webkit' }, () => { expect(Cypress.backend).to.be.calledWith( 'run:privileged', { + args: ['6998637248317671', '4581875909943693'], commandName: 'writeFile', - userArgs: ['6998637248317671', '4581875909943693'], options: { fileName: 'foo.json', contents, diff --git a/packages/driver/cypress/e2e/e2e/privileged_commands.cy.ts b/packages/driver/cypress/e2e/e2e/privileged_commands.cy.ts index 6dd9c19332..6dc09d2bcc 100644 --- a/packages/driver/cypress/e2e/e2e/privileged_commands.cy.ts +++ b/packages/driver/cypress/e2e/e2e/privileged_commands.cy.ts @@ -115,6 +115,17 @@ describe('privileged commands', () => { cy.get('#basic').selectFile(Uint8Array.from([98, 97, 122])) }) + it('handles args being mutated', () => { + const obj = { foo: 'bar' } + + cy.wait(10).then(() => { + obj.foo = 'baz' + }) + + cy.task('return:arg', obj) + cy.writeFile('cypress/_test-output/written.json', obj) + }) + it('handles evaled code', () => { window.eval(` cy.task('return:arg', 'eval arg') diff --git a/packages/driver/src/cy/commands/actions/selectFile.ts b/packages/driver/src/cy/commands/actions/selectFile.ts index e47c6ec354..c7ace22945 100644 --- a/packages/driver/src/cy/commands/actions/selectFile.ts +++ b/packages/driver/src/cy/commands/actions/selectFile.ts @@ -163,7 +163,7 @@ export default (Commands, Cypress, cy, state, config) => { } } - const readFiles = async (filePaths, options, userArgs) => { + const readFiles = async (filePaths, options) => { if (!filePaths.length) return [] // This reads the file with privileged access in the same manner as @@ -177,7 +177,6 @@ export default (Commands, Cypress, cy, state, config) => { options: { files: filePaths, }, - userArgs, }) .then((results) => { return results.map((result) => { @@ -268,11 +267,11 @@ export default (Commands, Cypress, cy, state, config) => { } } - async function collectFiles (files, options, userArgs) { + async function collectFiles (files, options) { const filesCollection = ([] as (Cypress.FileReference | FilePathObject)[]).concat(files).map(parseFile(options)) // if there are any file paths, read them from the server in one go const filePaths = filesCollection.filter((file) => (file as FilePathObject).isFilePath) - const filePathResults = await readFiles(filePaths, options, userArgs) + const filePathResults = await readFiles(filePaths, options) // stitch them back into the collection filePathResults.forEach((filePathResult) => { @@ -284,11 +283,6 @@ export default (Commands, Cypress, cy, state, config) => { Commands.addAll({ prevSubject: 'element' }, { async selectFile (subject: JQuery, files: Cypress.FileReference | Cypress.FileReference[], options: Partial, ...extras: never[]): Promise { - // privileged commands need to send any and all args, even if not part - // of their API, so they can be compared to the args collected when the - // command is invoked - const userArgs = [files, _.isObject(options) ? { ...options } : undefined, ...extras] - options = _.defaults({}, options, { action: 'select', log: true, @@ -351,7 +345,7 @@ export default (Commands, Cypress, cy, state, config) => { } // Make sure files is an array even if the user only passed in one - const filesArray = await collectFiles(files, options, userArgs) + const filesArray = await collectFiles(files, options) const subjectChain = cy.subjectChain() // We verify actionability on the subject, rather than the eventTarget, diff --git a/packages/driver/src/cy/commands/exec.ts b/packages/driver/src/cy/commands/exec.ts index ec64de3e40..55efcd8bc0 100644 --- a/packages/driver/src/cy/commands/exec.ts +++ b/packages/driver/src/cy/commands/exec.ts @@ -14,11 +14,6 @@ interface InternalExecOptions extends Partial { export default (Commands, Cypress, cy) => { Commands.addAll({ exec (cmd: string, userOptions: Partial, ...extras: never[]) { - // privileged commands need to send any and all args, even if not part - // of their API, so they can be compared to the args collected when the - // command is invoked - const userArgs = [cmd, userOptions, ...extras] - userOptions = userOptions || {} const options: InternalExecOptions = _.defaults({}, userOptions, { @@ -60,7 +55,6 @@ export default (Commands, Cypress, cy) => { cy, Cypress: (Cypress as unknown) as InternalCypress.Cypress, options: _.pick(options, 'cmd', 'timeout', 'env'), - userArgs, }) .timeout(options.timeout) .then((result) => { diff --git a/packages/driver/src/cy/commands/files.ts b/packages/driver/src/cy/commands/files.ts index 34c7ed7a95..ec94f6220f 100644 --- a/packages/driver/src/cy/commands/files.ts +++ b/packages/driver/src/cy/commands/files.ts @@ -22,11 +22,6 @@ type WriteFileOptions = Partial export default (Commands, Cypress, cy, state) => { Commands.addAll({ readFile (file: string, encoding: Cypress.Encodings | ReadFileOptions | undefined, userOptions?: ReadFileOptions, ...extras: never[]) { - // privileged commands need to send any and all args, even if not part - // of their API, so they can be compared to the args collected when the - // command is invoked - const userArgs = [file, encoding, _.isObject(userOptions) ? { ...userOptions } : undefined, ...extras] - if (_.isObject(encoding)) { userOptions = encoding encoding = undefined @@ -77,7 +72,6 @@ export default (Commands, Cypress, cy, state) => { file, encoding: options.encoding, }, - userArgs, }) .timeout(options.timeout) .catch((err) => { @@ -146,11 +140,6 @@ export default (Commands, Cypress, cy, state) => { }, writeFile (fileName: string, contents: string, encoding: Cypress.Encodings | WriteFileOptions | undefined, userOptions: WriteFileOptions, ...extras: never[]) { - // privileged commands need to send any and all args, even if not part - // of their API, so they can be compared to the args collected when the - // command is invoked - const userArgs = [fileName, contents, encoding, _.isObject(userOptions) ? { ...userOptions } : undefined, ...extras] - if (_.isObject(encoding)) { userOptions = encoding encoding = undefined @@ -214,7 +203,6 @@ export default (Commands, Cypress, cy, state) => { encoding: options.encoding, flag: options.flag, }, - userArgs, }) .timeout(options.timeout) .then(({ filePath, contents }) => { diff --git a/packages/driver/src/cy/commands/origin/index.ts b/packages/driver/src/cy/commands/origin/index.ts index 262a9912e4..8426dcd237 100644 --- a/packages/driver/src/cy/commands/origin/index.ts +++ b/packages/driver/src/cy/commands/origin/index.ts @@ -27,15 +27,6 @@ const normalizeOrigin = (urlOrDomain) => { type OptionsOrFn = { args: T } | (() => {}) type Fn = (args?: T) => {} -function getUserArgs (urlOrDomain: string, optionsOrFn: OptionsOrFn, extras: never[], fn?: Fn) { - return [ - urlOrDomain, - fn && _.isObject(optionsOrFn) ? { ...optionsOrFn } : optionsOrFn, - fn ? fn : undefined, - ...extras, - ] -} - export default (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, state: StateFunc, config: Cypress.InternalConfig) => { const communicator = Cypress.primaryOriginCommunicator @@ -45,11 +36,6 @@ export default (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, state: State return $errUtils.throwErrByPath('webkit.origin') } - // privileged commands need to send any and all args, even if not part - // of their API, so they can be compared to the args collected when the - // command is invoked - const userArgs = getUserArgs(urlOrDomain, optionsOrFn, extras, fn) - const userInvocationStack = state('current').get('userInvocationStack') // store the invocation stack in the case that `cy.origin` errors @@ -213,7 +199,6 @@ export default (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, state: State options: { specBridgeOrigin, }, - userArgs, }) // once the secondary origin page loads, send along the diff --git a/packages/driver/src/cy/commands/task.ts b/packages/driver/src/cy/commands/task.ts index e6cb6e56d3..9c20cb9061 100644 --- a/packages/driver/src/cy/commands/task.ts +++ b/packages/driver/src/cy/commands/task.ts @@ -15,11 +15,6 @@ interface InternalTaskOptions extends Partial { Commands.addAll({ task (task, arg, userOptions: Partial, ...extras: never[]) { - // privileged commands need to send any and all args, even if not part - // of their API, so they can be compared to the args collected when the - // command is invoked - const userArgs = [task, arg, _.isObject(userOptions) ? { ...userOptions } : undefined, ...extras] - userOptions = userOptions || {} const options: InternalTaskOptions = _.defaults({}, userOptions, { @@ -65,7 +60,6 @@ export default (Commands, Cypress, cy) => { commandName: 'task', cy, Cypress: (Cypress as unknown) as InternalCypress.Cypress, - userArgs, options: { task, arg, diff --git a/packages/driver/src/cypress/chainer.ts b/packages/driver/src/cypress/chainer.ts index a54a1a1de6..869d57901b 100644 --- a/packages/driver/src/cypress/chainer.ts +++ b/packages/driver/src/cypress/chainer.ts @@ -20,7 +20,7 @@ export class $Chainer { static add (key, fn) { $Chainer.prototype[key] = function (...args) { - const verificationPromise = Cypress.emitMap('command:invocation', { name: key, args }) + const privilegeVerification = Cypress.emitMap('command:invocation', { name: key, args }) const userInvocationStack = $stackUtils.normalizedUserInvocationStack( (new this.specWindow.Error('command invocation stack')).stack, @@ -28,7 +28,7 @@ export class $Chainer { // call back the original function with our new args // pass args an as array and not a destructured invocation - fn(this, userInvocationStack, args, verificationPromise) + fn(this, userInvocationStack, args, privilegeVerification) // return the chainer so additional calls // are slurped up by the chainer instead of cy diff --git a/packages/driver/src/cypress/cy.ts b/packages/driver/src/cypress/cy.ts index a01a7d9096..4c6b6c02c4 100644 --- a/packages/driver/src/cypress/cy.ts +++ b/packages/driver/src/cypress/cy.ts @@ -683,7 +683,7 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert const cyFn = wrap(true) const chainerFn = wrap(false) - const callback = (chainer, userInvocationStack, args, verificationPromise, firstCall = false) => { + const callback = (chainer, userInvocationStack, args, privilegeVerification, firstCall = false) => { // dont enqueue / inject any new commands if // onInjectCommand returns false const onInjectCommand = cy.state('onInjectCommand') @@ -699,7 +699,7 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert chainerId: chainer.chainerId, userInvocationStack, fn: firstCall ? cyFn : chainerFn, - verificationPromise, + privilegeVerification, })) } @@ -715,7 +715,7 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert // websocket message for running the command to ensure prevent a race // condition where running the command happens before the command is // verified - const verificationPromise = Cypress.emitMap('command:invocation', { name, args }) + const privilegeVerification = Cypress.emitMap('command:invocation', { name, args }) // this is the first call on cypress // so create a new chainer instance @@ -727,7 +727,7 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert const userInvocationStack = $stackUtils.captureUserInvocationStack(cy.specWindow.Error) - callback(chainer, userInvocationStack, args, verificationPromise, true) + callback(chainer, userInvocationStack, args, privilegeVerification, true) // if we are in the middle of a command // and its return value is a promise diff --git a/packages/driver/src/util/privileged_channel.ts b/packages/driver/src/util/privileged_channel.ts index 97f6106072..b30987b778 100644 --- a/packages/driver/src/util/privileged_channel.ts +++ b/packages/driver/src/util/privileged_channel.ts @@ -1,4 +1,3 @@ -import _ from 'lodash' import Bluebird from 'bluebird' /** @@ -19,54 +18,18 @@ interface RunPrivilegedCommandOptions { cy: Cypress.cy Cypress: InternalCypress.Cypress options: any - userArgs: any[] } -// hashes a string in the same manner as is in the privileged channel. -// unfortunately this can't be shared because we want to reduce the surface -// area in the privileged channel, which uses closured references to -// globally-accessible functions -// source: https://github.com/bryc/code/blob/d0dac1c607a005679799024ff66166e13601d397/jshash/experimental/cyrb53.js -function hash (str) { - const seed = 0 - let h1 = 0xdeadbeef ^ seed - let h2 = 0x41c6ce57 ^ seed +export function runPrivilegedCommand ({ commandName, cy, Cypress, options }: RunPrivilegedCommandOptions): Bluebird { + const { args, promise } = (cy.state('current').get('privilegeVerification') || [])[0] || {} - for (let i = 0, ch; i < str.length; i++) { - ch = str.charCodeAt(i) - h1 = Math.imul(h1 ^ ch, 2654435761) - h2 = Math.imul(h2 ^ ch, 1597334677) - } - h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) - h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909) - h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) - h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909) - - return `${4294967296 * (2097151 & h2) + (h1 >>> 0)}` -} - -export function runPrivilegedCommand ({ commandName, cy, Cypress, options, userArgs }: RunPrivilegedCommandOptions): Bluebird { - const hashedArgs = _.dropRightWhile(userArgs || [], _.isUndefined) - .map((arg) => { - if (arg === undefined) { - arg = null - } - - if (typeof arg === 'function') { - arg = arg.toString() - } - - return hash(JSON.stringify(arg)) - }) - - return Bluebird.try(() => { - return cy.state('current').get('verificationPromise')[0] - }) + return Bluebird + .try(() => promise) .then(() => { return Cypress.backend('run:privileged', { commandName, options, - userArgs: hashedArgs, + args, }) }) } diff --git a/packages/server/lib/privileged-commands/privileged-channel.js b/packages/server/lib/privileged-commands/privileged-channel.js index 5f908d0d28..b89b882d4e 100644 --- a/packages/server/lib/privileged-commands/privileged-channel.js +++ b/packages/server/lib/privileged-commands/privileged-channel.js @@ -160,7 +160,7 @@ return slice.call(array, 0, index + 1) } - async function onCommandInvocation (command) { + function onCommandInvocation (command) { // message doesn't really matter since we're only interested in the stack const err = new Err('command stack error') @@ -199,7 +199,7 @@ // send it to the server, where it's stored in state. when the command is // run and it sends its message to the server via websocket, we check // that verified status before allowing the command to continue running - await fetch(`/${namespace}/add-verified-command`, { + const promise = fetch(`/${namespace}/add-verified-command`, { body: stringify({ args, name: command.name, @@ -215,6 +215,11 @@ // verified command failed, the default behavior is NOT to allow // the privileged command to run }) + + return { + args, + promise, + } } win.Cypress.on('command:invocation', onCommandInvocation) diff --git a/packages/server/lib/privileged-commands/privileged-commands-manager.ts b/packages/server/lib/privileged-commands/privileged-commands-manager.ts index 9d6845fdc6..5f6ddea243 100644 --- a/packages/server/lib/privileged-commands/privileged-commands-manager.ts +++ b/packages/server/lib/privileged-commands/privileged-commands-manager.ts @@ -78,10 +78,10 @@ class PrivilegedCommandsManager { return !!matchingCommand } - runPrivilegedCommand (config, { commandName, options, userArgs }) { + runPrivilegedCommand (config, { commandName, options, args }) { // the presence of the command within the verifiedCommands array indicates // the command being run is verified - const hasCommand = this.hasVerifiedCommand({ name: commandName, args: userArgs }) + const hasCommand = this.hasVerifiedCommand({ name: commandName, args }) if (config.testingType === 'e2e' && !hasCommand) { // this error message doesn't really matter as each command will catch it From 376795f125054c4a70cb1a9b75b01c1a42cccf93 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 17 Jul 2023 21:01:47 -0400 Subject: [PATCH 3/6] fix(webpack-dev-server): add typeRoots to generated tsconfig for angular (#27117) * feat(webpack-dev-server): generate a local tsconfig file * chore: remove comments * chore: use relative paths * test(webpack-dev-server): update tsconfig tests * test(webpack-dev-server): update tsconfig tests * test(webpack-dev-server): update tsconfig tests * test(webpack-dev-server): upt outDir path * chore: build binary * Update npm/webpack-dev-server/src/helpers/angularHandler.ts Co-authored-by: Stokes Player * fix(webpack-dev-server): use tmp but add typeRoots * chore: update workflow to build binary * remove redundant node_modules/types * remove node_modules/types from assertions --------- Co-authored-by: Stokes Player --- .circleci/workflows.yml | 11 +++--- .../src/helpers/angularHandler.ts | 10 ++++-- .../test/handlers/angularHandler.spec.ts | 35 +++++++------------ 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index de3ab3abf8..c8973d48a5 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -30,7 +30,8 @@ mainBuildFilters: &mainBuildFilters - /^release\/\d+\.\d+\.\d+$/ # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - 'update-v8-snapshot-cache-on-develop' - - 'chore/use_cloud_m1_runners' + - 'jordanpowell88/angular-tsconfig' + - 'fix/chrome_crash_recovery' # usually we don't build Mac app - it takes a long time # but sometimes we want to really confirm we are doing the right thing @@ -41,7 +42,8 @@ macWorkflowFilters: &darwin-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'chore/use_cloud_m1_runners', << pipeline.git.branch >> ] + - equal: ['jordanpowell88/angular-tsconfig', << pipeline.git.branch >> ] + - equal: [ 'mschile/issue-26900_browserCriClient', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -52,7 +54,8 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'chore/use_cloud_m1_runners', << pipeline.git.branch >> ] + - equal: [ 'jordanpowell88/angular-tsconfig', << pipeline.git.branch >> ] + - equal: [ 'mschile/issue-26900_browserCriClient', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -142,7 +145,7 @@ commands: - run: name: Check current branch to persist artifacts command: | - if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" && "$CIRCLE_BRANCH" != "mschile/issue-26900_browserCriClient" ]]; then + if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" && "$CIRCLE_BRANCH" != "jordanpowell88/angular-tsconfig" ]]; then echo "Not uploading artifacts or posting install comment for this branch." circleci-agent step halt fi diff --git a/npm/webpack-dev-server/src/helpers/angularHandler.ts b/npm/webpack-dev-server/src/helpers/angularHandler.ts index 413278d503..03d0046465 100644 --- a/npm/webpack-dev-server/src/helpers/angularHandler.ts +++ b/npm/webpack-dev-server/src/helpers/angularHandler.ts @@ -136,9 +136,11 @@ export async function generateTsConfig (devServerConfig: AngularWebpackDevServer includePaths.push(...polyfills.map((p: string) => getProjectFilePath(workspaceRoot, p))) } - const cypressTypes = getProjectFilePath(workspaceRoot, 'node_modules', 'cypress', 'types', 'index.d.ts') + const typeRoots = [ + getProjectFilePath(workspaceRoot, 'node_modules'), + ] - includePaths.push(cypressTypes) + const types = ['cypress'] const tsConfigContent = JSON.stringify({ extends: getProjectFilePath(projectRoot, buildOptions.tsConfig ?? 'tsconfig.json'), @@ -146,9 +148,11 @@ export async function generateTsConfig (devServerConfig: AngularWebpackDevServer outDir: getProjectFilePath(projectRoot, 'out-tsc/cy'), allowSyntheticDefaultImports: true, skipLibCheck: true, + types, + typeRoots, }, include: includePaths, - }) + }, null, 2) const tsConfigPath = path.join(await getTempDir(), 'tsconfig.json') diff --git a/npm/webpack-dev-server/test/handlers/angularHandler.spec.ts b/npm/webpack-dev-server/test/handlers/angularHandler.spec.ts index 5199fa24a8..b382f279da 100644 --- a/npm/webpack-dev-server/test/handlers/angularHandler.spec.ts +++ b/npm/webpack-dev-server/test/handlers/angularHandler.spec.ts @@ -19,15 +19,12 @@ import '../support' import { scaffoldMigrationProject } from '../test-helpers/scaffoldProject' chai.use(chaiPromise) - describe('angularHandler', function () { this.timeout(1000 * 60) - it('sources the config from angular-13', async () => { const projectRoot = await scaffoldMigrationProject('angular-13') process.chdir(projectRoot) - const devServerConfig = { cypressConfig: { projectRoot, @@ -35,13 +32,11 @@ describe('angularHandler', function () { } as Cypress.PluginConfigOptions, framework: 'angular', } as AngularWebpackDevServerConfig - const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await angularHandler(devServerConfig) expect(webpackConfig).to.exist expect((webpackConfig?.entry as any).main).to.be.undefined expect(sourceWebpackModulesResult.framework?.importPath).to.include(path.join('@angular-devkit', 'build-angular')) - const { buildOptions } = await expectNormalizeProjectConfig(projectRoot) await expectLoadsAngularJson(projectRoot) @@ -54,7 +49,6 @@ describe('angularHandler', function () { const projectRoot = await scaffoldMigrationProject('angular-14') process.chdir(projectRoot) - const devServerConfig = { cypressConfig: { projectRoot, @@ -62,13 +56,11 @@ describe('angularHandler', function () { } as Cypress.PluginConfigOptions, framework: 'angular', } as AngularWebpackDevServerConfig - const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await angularHandler(devServerConfig) expect(webpackConfig).to.exist expect((webpackConfig?.entry as any).main).to.be.undefined expect(sourceWebpackModulesResult.framework?.importPath).to.include(path.join('@angular-devkit', 'build-angular')) - const { buildOptions } = await expectNormalizeProjectConfig(projectRoot) await expectLoadsAngularJson(projectRoot) @@ -81,7 +73,6 @@ describe('angularHandler', function () { const projectRoot = await scaffoldMigrationProject('angular-15') process.chdir(projectRoot) - const devServerConfig = { cypressConfig: { projectRoot, @@ -89,13 +80,11 @@ describe('angularHandler', function () { } as Cypress.PluginConfigOptions, framework: 'angular', } as AngularWebpackDevServerConfig - const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await angularHandler(devServerConfig) expect(webpackConfig).to.exist expect((webpackConfig?.entry as any).main).to.be.undefined expect(sourceWebpackModulesResult.framework?.importPath).to.include(path.join('@angular-devkit', 'build-angular')) - const { buildOptions } = await expectNormalizeProjectConfig(projectRoot) await expectLoadsAngularJson(projectRoot) @@ -129,7 +118,6 @@ describe('angularHandler', function () { const projectRoot = await scaffoldMigrationProject('angular-custom-config') process.chdir(projectRoot) - const devServerConfig = { framework: 'angular', cypressConfig: { @@ -140,13 +128,11 @@ describe('angularHandler', function () { projectConfig: customProjectConfig, }, } as unknown as AngularWebpackDevServerConfig - const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await angularHandler(devServerConfig) expect(webpackConfig).to.exist expect((webpackConfig?.entry as any).main).to.be.undefined expect(sourceWebpackModulesResult.framework?.importPath).to.include(path.join('@angular-devkit', 'build-angular')) - await expectLoadsAngularJson(projectRoot) await expectLoadsAngularCLiModules(projectRoot) await expectGeneratesTsConfig(devServerConfig, customProjectConfig.buildOptions) @@ -181,28 +167,22 @@ const expectNormalizeProjectConfig = async (projectRoot: string) => { return projectConfig } - const expectLoadsAngularJson = async (projectRoot: string) => { const angularJson = await getAngularJson(projectRoot) expect(angularJson).to.not.be.null - await expect(getAngularJson(path.join('..', projectRoot))).to.be.rejected } - const expectLoadsAngularCLiModules = async (projectRoot: string) => { const angularCliModules = await getAngularCliModules(projectRoot) expect(angularCliModules.generateBrowserWebpackConfigFromContext).to.not.be.null expect(angularCliModules.getStylesConfig).to.not.be.null expect(angularCliModules.getCommonConfig).to.not.be.null - await expect(getAngularCliModules(path.join('..', projectRoot))).to.be.rejected } - const expectLoadsAngularBuildOptions = (buildOptions: BuildOptions) => { const tsConfig = 'tsconfig.cypress.json' - let finalBuildOptions = getAngularBuildOptions(buildOptions, tsConfig) expect(finalBuildOptions.aot).to.be.false @@ -211,7 +191,6 @@ const expectLoadsAngularBuildOptions = (buildOptions: BuildOptions) => { expect(finalBuildOptions.outputHashing).to.equal('none') expect(finalBuildOptions.budgets).to.be.undefined } - const expectGeneratesTsConfig = async (devServerConfig: AngularWebpackDevServerConfig, buildOptions: any) => { const { projectRoot } = devServerConfig.cypressConfig let tsConfigPath = await generateTsConfig(devServerConfig, buildOptions) @@ -228,11 +207,16 @@ const expectGeneratesTsConfig = async (devServerConfig: AngularWebpackDevServerC outDir: toPosix(path.join(projectRoot, 'out-tsc/cy')), allowSyntheticDefaultImports: true, skipLibCheck: true, + typeRoots: [ + toPosix(path.join(projectRoot, 'node_modules')), + ], + types: [ + 'cypress', + ], }, include: [ toPosix(path.join(projectRoot, 'src/**/*.cy.ts')), toPosix(path.join(projectRoot, 'src/polyfills.ts')), - toPosix(path.join(projectRoot, 'node_modules/cypress/types/index.d.ts')), ], }) @@ -256,11 +240,16 @@ const expectGeneratesTsConfig = async (devServerConfig: AngularWebpackDevServerC outDir: toPosix(path.join(projectRoot, 'out-tsc/cy')), allowSyntheticDefaultImports: true, skipLibCheck: true, + typeRoots: [ + toPosix(path.join(projectRoot, 'node_modules')), + ], + types: [ + 'cypress', + ], }, include: [ toPosix(path.join(projectRoot, 'src/**/*.cy.ts')), toPosix(supportFile), - toPosix(path.join(projectRoot, 'node_modules/cypress/types/index.d.ts')), ], }) } From 1d6246b4e62b859bf13701fe836c78cc865a0031 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 17 Jul 2023 21:37:08 -0400 Subject: [PATCH 4/6] chore: release @cypress/webpack-dev-server-v3.5.1 [skip ci] --- npm/webpack-dev-server/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/npm/webpack-dev-server/CHANGELOG.md b/npm/webpack-dev-server/CHANGELOG.md index 1883c9c60b..475259e463 100644 --- a/npm/webpack-dev-server/CHANGELOG.md +++ b/npm/webpack-dev-server/CHANGELOG.md @@ -1,3 +1,10 @@ +# [@cypress/webpack-dev-server-v3.5.1](https://github.com/cypress-io/cypress/compare/@cypress/webpack-dev-server-v3.5.0...@cypress/webpack-dev-server-v3.5.1) (2023-07-18) + + +### Bug Fixes + +* **webpack-dev-server:** add typeRoots to generated tsconfig for angular ([#27117](https://github.com/cypress-io/cypress/issues/27117)) ([376795f](https://github.com/cypress-io/cypress/commit/376795f125054c4a70cb1a9b75b01c1a42cccf93)) + # [@cypress/webpack-dev-server-v3.5.0](https://github.com/cypress-io/cypress/compare/@cypress/webpack-dev-server-v3.4.1...@cypress/webpack-dev-server-v3.5.0) (2023-06-26) From cb6152503a9d1f538351066af97c89736c7ec568 Mon Sep 17 00:00:00 2001 From: Matt Henkes Date: Tue, 18 Jul 2023 09:34:15 -0500 Subject: [PATCH 5/6] chore: fix memory import (#27324) --- packages/server/lib/browsers/memory/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/lib/browsers/memory/index.ts b/packages/server/lib/browsers/memory/index.ts index 8cab2934db..7024a7ae0e 100644 --- a/packages/server/lib/browsers/memory/index.ts +++ b/packages/server/lib/browsers/memory/index.ts @@ -8,7 +8,7 @@ import path from 'path' import pid from 'pidusage' import { groupCyProcesses, Process } from '../../util/process_profiler' import browsers from '..' -import { telemetry } from '@packages/telemetry/src/browser' +import { telemetry } from '@packages/telemetry' import type { Automation } from '../../automation' import type { BrowserInstance } from '../types' From b0315b19a7a7142c1a08382ab02befe75aa2daef Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Wed, 19 Jul 2023 09:26:17 -0400 Subject: [PATCH 6/6] chore: revert circle M1 runners for MacStadium VM (#27328) * Revert "chore: use cloud hosted m1 runners for apple silicon devices (#27257)" This reverts commit 78ad0d53ef23641e773ca389960d171911ab479e. * chore: reverted runner and trying new m1 runner on macstadium [run ci] [run ci] run ci run ci chore: bump cache (uising person over admin) [run ci] chore: test build artifacts [run ci] bump cache [run ci] update cache --- .circleci/cache-version.txt | 2 +- .circleci/workflows.yml | 57 ++++++++++++++++++++++--------------- scripts/circle-cache.js | 2 ++ 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/.circleci/cache-version.txt b/.circleci/cache-version.txt index 81c26f678b..c620bbdae2 100644 --- a/.circleci/cache-version.txt +++ b/.circleci/cache-version.txt @@ -1,3 +1,3 @@ # Bump this version to force CI to re-create the cache from scratch. -06-07-23 +07-19-23 \ No newline at end of file diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index c8973d48a5..9359b05f9b 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -30,8 +30,7 @@ mainBuildFilters: &mainBuildFilters - /^release\/\d+\.\d+\.\d+$/ # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - 'update-v8-snapshot-cache-on-develop' - - 'jordanpowell88/angular-tsconfig' - - 'fix/chrome_crash_recovery' + - 'revert-runner' # usually we don't build Mac app - it takes a long time # but sometimes we want to really confirm we are doing the right thing @@ -42,8 +41,7 @@ macWorkflowFilters: &darwin-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: ['jordanpowell88/angular-tsconfig', << pipeline.git.branch >> ] - - equal: [ 'mschile/issue-26900_browserCriClient', << pipeline.git.branch >> ] + - equal: [ 'revert-runner', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -54,8 +52,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'jordanpowell88/angular-tsconfig', << pipeline.git.branch >> ] - - equal: [ 'mschile/issue-26900_browserCriClient', << pipeline.git.branch >> ] + - equal: [ 'revert-runner', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -75,7 +72,7 @@ windowsWorkflowFilters: &windows-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'chore/use_cloud_m1_runners', << pipeline.git.branch >> ] + - equal: [ 'revert-runner', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -122,11 +119,8 @@ executors: environment: PLATFORM: windows - # see https://circleci.com/docs/configuration-reference/#macos-execution-environment darwin-arm64: &darwin-arm64-executor - macos: - xcode: "14.2.0" - resource_class: macos.m1.large.gen1 + machine: true environment: PLATFORM: darwin @@ -145,7 +139,7 @@ commands: - run: name: Check current branch to persist artifacts command: | - if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" && "$CIRCLE_BRANCH" != "jordanpowell88/angular-tsconfig" ]]; then + if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" && "$CIRCLE_BRANCH" != "revert-runner" ]]; then echo "Not uploading artifacts or posting install comment for this branch." circleci-agent step halt fi @@ -435,7 +429,7 @@ commands: node_version=$(cat .node-version) source ./scripts/ensure-node.sh echo "Installing Yarn" - npm install yarn -g # ensure yarn is installed with the correct node engine + npm install yarn --location=global # ensure yarn is installed with the correct node engine yarn check-node-version - run: name: Check Node @@ -1361,18 +1355,12 @@ jobs: steps: - restore_cached_workspace - restore_cached_system_tests_deps + # TODO: Remove this once we switch off self-hosted M1 runners - when: condition: - or: - - equal: [ *linux-arm64-executor, << parameters.executor >> ] # TODO: Figure out how to support linux-arm64 when we get to linux arm64 build: https://github.com/cypress-io/cypress/issues/23557 + equal: [ *darwin-arm64-executor, << parameters.executor >> ] steps: - - run: - name: Run v8 integration tests - command: | - source ./scripts/ensure-node.sh - yarn test-integration --scope "'@tooling/packherd'" - - verify-mocha-results: - expectedResultCount: 1 + - run: rm -f /tmp/cypress/junit/* - unless: condition: or: @@ -1385,6 +1373,18 @@ jobs: yarn test-integration --scope "'@tooling/{packherd,v8-snapshot,electron-mksnapshot}'" - verify-mocha-results: expectedResultCount: 3 + - when: + condition: + or: + - equal: [ *linux-arm64-executor, << parameters.executor >> ] + steps: + - run: + name: Run v8 integration tests + command: | + source ./scripts/ensure-node.sh + yarn test-integration --scope "'@tooling/packherd'" + - verify-mocha-results: + expectedResultCount: 1 - store_test_results: path: /tmp/cypress - store-npm-logs @@ -1520,6 +1520,12 @@ jobs: parallelism: 1 steps: - restore_cached_workspace + # TODO: Remove this once we switch off self-hosted M1 runners + - when: + condition: + equal: [ *darwin-arm64-executor, << parameters.executor >> ] + steps: + - run: rm -f /tmp/cypress/junit/* - run: yarn workspace @packages/server test-unit cloud/environment_spec.ts - verify-mocha-results: expectedResultCount: 1 @@ -2859,11 +2865,13 @@ darwin-arm64-workflow: &darwin-arm64-workflow - node_modules_install: name: darwin-arm64-node-modules-install executor: darwin-arm64 + resource_class: cypress-io/m1-macstadium only-cache-for-root-user: true - build: name: darwin-arm64-build executor: darwin-arm64 + resource_class: cypress-io/m1-macstadium requires: - darwin-arm64-node-modules-install @@ -2875,23 +2883,26 @@ darwin-arm64-workflow: &darwin-arm64-workflow - test-runner:commit-status-checks - test-runner:build-binary executor: darwin-arm64 - resource_class: large + resource_class: cypress-io/m1-macstadium requires: - darwin-arm64-build - v8-integration-tests: name: darwin-arm64-v8-integration-tests executor: darwin-arm64 + resource_class: cypress-io/m1-macstadium requires: - darwin-arm64-build - driver-integration-memory-tests: name: darwin-arm64-driver-integration-memory-tests executor: darwin-arm64 + resource_class: cypress-io/m1-macstadium requires: - darwin-arm64-build - server-unit-tests-cloud-environment: name: darwin-arm64-server-unit-tests-cloud-environment executor: darwin-arm64 + resource_class: cypress-io/m1-macstadium requires: - darwin-arm64-build diff --git a/scripts/circle-cache.js b/scripts/circle-cache.js index 921b53d416..5b3692c7ac 100644 --- a/scripts/circle-cache.js +++ b/scripts/circle-cache.js @@ -69,6 +69,8 @@ async function prepareCircleCache () { .replace(/(.*?)\/node_modules/, '$1_node_modules') .replace(BASE_DIR, CACHE_DIR) + // self-hosted M1 doesn't always clear this directory between runs, so remove it + await fsExtra.remove(dest) await fsExtra.move(src, dest) }), )