Skip to content

Commit

Permalink
fix: resolve files outside the project directory to the bundled direc…
Browse files Browse the repository at this point in the history
…tory (#30067)

* fix: resolve files outside the project directory to the bundled directory via the directory path difference [run ci]

* update common ancestor code

---------

Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
  • Loading branch information
AtofStryker and jennifer-shehane authored Aug 22, 2024
1 parent 699fec5 commit d79c99e
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 11 deletions.
9 changes: 4 additions & 5 deletions .circleci/workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ mainBuildFilters: &mainBuildFilters
- 'update-v8-snapshot-cache-on-develop'
- 'jquery-patch-remove-unload'
- 'publish-binary'
- 'feat/experimentalJustInTimeCompile'
- 'fix/8599'

# 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
Expand All @@ -43,7 +43,6 @@ 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: [ 'feat/experimentalJustInTimeCompile', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand All @@ -54,7 +53,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: [ 'feat/experimentalJustInTimeCompile', << pipeline.git.branch >> ]
- equal: [ 'jquery-patch-remove-unload', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand All @@ -77,7 +76,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: [ 'feat/experimentalJustInTimeCompile', << pipeline.git.branch >> ]
- equal: [ 'fix/8599', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand Down Expand Up @@ -153,7 +152,7 @@ commands:
name: Set environment variable to determine whether or not to persist artifacts
command: |
echo "Setting SHOULD_PERSIST_ARTIFACTS variable"
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "feat/experimentalJustInTimeCompile" ]]; then
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "fix/8599" ]]; then
export SHOULD_PERSIST_ARTIFACTS=true
fi' >> "$BASH_ENV"
# You must run `setup_should_persist_artifacts` command and be using bash before running this command
Expand Down
4 changes: 4 additions & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ _Released 8/27/2024 (PENDING)_
- Added a `CYPRESS_SKIP_VERIFY` flag to enable suppressing Cypress verification checks. Addresses [#22243](https://github.com/cypress-io/cypress/issues/22243).
- Updated the protocol to allow making Cloud API requests. Addressed in [#30066](https://github.com/cypress-io/cypress/pull/30066).

**Bugfixes:**

- Fixed an issue where files outside the Cypress project directory were not calculating the bundle output path correctly for the `file:preprocessor`. Addresses [#8599](https://github.com/cypress-io/cypress/issues/8599).

**Dependency Updates:**

- Updated `detect-port` from `1.3.0` to `1.6.1`. Addressed in [#30038](https://github.com/cypress-io/cypress/pull/30038).
Expand Down
72 changes: 66 additions & 6 deletions packages/server/lib/util/app_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,37 @@ const { fs } = require('../util/fs')
const cwd = require('../cwd')
const md5 = require('md5')
const sanitize = require('sanitize-filename')
const replace = require('lodash/replace')

const PRODUCT_NAME = pkg.productName || pkg.name
const OS_DATA_PATH = ospath.data()

const ELECTRON_APP_DATA_PATH = path.join(OS_DATA_PATH, PRODUCT_NAME)
const findCommonAncestor = (path1, path2) => {
const sep = os.platform() === 'win32' ? '\\' : '/'

function* commonArrayMembersGenerator (path1, path2) {
const [longer, shorter] = path1.length > path2.length ? [path1, path2] : [path2, path1]

// find when the paths eventually differ.
for (const pathSegment of shorter) {
if (pathSegment === longer.shift()) {
yield pathSegment
} else {
break
}
}
}

return path1 === path2 ? path1
: path.parse(path1).root !== path.parse(path2).root ? null
: [...commonArrayMembersGenerator(path.normalize(path1).split(sep), path.normalize(path2).split(sep))].join(sep)
}

const getElectronAppDataPath = () => {
const OS_DATA_PATH = ospath.data()
const ELECTRON_APP_DATA_PATH = path.join(OS_DATA_PATH, PRODUCT_NAME)

return ELECTRON_APP_DATA_PATH
}

if (!PRODUCT_NAME) {
throw new Error('Root package is missing name')
Expand Down Expand Up @@ -47,11 +73,45 @@ const toHashName = (projectRoot) => {
return `${name}-${hash}`
}

const modifyFileIfOutsideProjectDirectory = (projectRoot, filePath) => {
/**
* files that live outside of the project directory
* do not resolve correctly on Windows as we are trying to resolve the file to the project directory.
* This issue is only noticeable on windows since the absolute path gets appended to the project bundle
* path. In Unix based systems, this goes unnoticed because:
* /Users/foo/project/nested/hash-bundle/Users/foo/project/file.js
* is a valid path in Unix, but
* C:\\Users\\foo\\project\\nested\\hash-bundleC:\\Users\\foo\\project\\file.js
* is not a valid path in Windows
*
* To resolve this issue, we find the common ancestor directory between the project and file,
* take the path AFTER the common ancestor directory of the file, and append it to the project bundle directory.
* Effectively:
* C:\\Users\\foo\\project\\nested\\hash-bundleC:\\Users\\foo\\project\\file.js
* will become
* C:\\Users\\foo\\project\\nested\\hash-bundle\\file.js
* @see https://github.com/cypress-io/cypress/issues/8599
*/

const relative = path.relative(projectRoot, filePath)
const isSubDirectory = relative && !relative.startsWith('..') && !path.isAbsolute(relative)

// if the file does NOT live inside the project directory,
// find the common ancestor of the project and file to get the file subpath to append to the project bundle directory
if (!isSubDirectory) {
const commonDirectoryPath = findCommonAncestor(projectRoot, filePath)

filePath = replace(filePath, commonDirectoryPath, '')
}

return filePath
}

module.exports = {
toHashName,

findCommonAncestor,
getBundledFilePath (projectRoot, filePath) {
return this.projectsPath(toHashName(projectRoot), 'bundles', filePath)
return this.projectsPath(toHashName(projectRoot), 'bundles', modifyFileIfOutsideProjectDirectory(projectRoot, filePath))
},

ensure () {
Expand Down Expand Up @@ -98,15 +158,15 @@ module.exports = {
folder = `${folder}-e2e-test`
}

const p = path.join(ELECTRON_APP_DATA_PATH, 'cy', folder, ...paths)
const p = path.join(getElectronAppDataPath(), 'cy', folder, ...paths)

log('path: %s', p)

return p
},

electronPartitionsPath () {
return path.join(ELECTRON_APP_DATA_PATH, 'Partitions')
return path.join(getElectronAppDataPath(), 'Partitions')
},

projectsPath (...paths) {
Expand Down
87 changes: 87 additions & 0 deletions packages/server/test/unit/util/app_data_spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
require('../../spec_helper')
const os = require('os')
const osPath = require('ospath')
const path = require('path')

const AppData = require(`../../../lib/util/app_data`)

Expand Down Expand Up @@ -28,6 +31,28 @@ describe('lib/util/app_data', () => {
})
})

context('#findCommonAncestor', () => {
it('posix', () => {
expect(AppData.findCommonAncestor('/a/b/c/d', '/a/b/c/d/')).to.equal('/a/b/c/d')
expect(AppData.findCommonAncestor('/a/b/c', '/a/x/y')).to.equal('/a')
expect(AppData.findCommonAncestor('/a/b/', '/a/b/')).to.equal('/a/b/')
expect(AppData.findCommonAncestor('/a', '/a/b/c')).to.equal('/a')
expect(AppData.findCommonAncestor('/a/b/c', '/a')).to.equal('/a')
})

it('win32', () => {
sinon.stub(os, 'platform')
os.platform.returns('win32')

expect(AppData.findCommonAncestor('c:\\a\\b\\c\\d', 'c:\\a\\b\\c\\d\\')).to.equal('c:\\a\\b\\c\\d')
expect(AppData.findCommonAncestor('c:\\a\\b\\c', 'c:\\a\\x\\y')).to.equal('c:\\a')
expect(AppData.findCommonAncestor('c:\\a\\b\\', 'c:\\a\\b\\')).to.equal('c:\\a\\b\\')
expect(AppData.findCommonAncestor('c:\\a\\b\\', 'd:\\a\\b\\')).to.equal('')
expect(AppData.findCommonAncestor('c:\\a', 'c:\\a\\b\\c')).to.equal('c:\\a')
expect(AppData.findCommonAncestor('c:\\a\\b\\c', 'c:\\a')).to.equal('c:\\a')
})
})

context('#getBundledFilePath', () => {
it('provides an absolute path to the bundled file', () => {
const projectRoot = '/foo/bar'
Expand All @@ -38,5 +63,67 @@ describe('lib/util/app_data', () => {
expect(result).to.contain(expectedPrefix)
expect(result).to.contain(imagePath)
})

// @see https://github.com/cypress-io/cypress/issues/8599
describe('issue #8599: can find a path to bundle preprocessor files that live outside the project directory', () => {
it('on windows', () => {
// mock / stub out path and os variables as if we were on Windows
sinon.stub(os, 'platform')
sinon.stub(osPath, 'data')
sinon.stub(path, 'basename')
sinon.stub(path, 'dirname')
sinon.stub(path, 'isAbsolute')
sinon.stub(path, 'join')
sinon.stub(path, 'parse')
sinon.stub(path, 'normalize')

os.platform.returns('win32')
osPath.data.returns(`C:\\Users\\foo\\AppData\\Roaming`)

path.basename.callsFake((...args) => path.win32.basename(...args))
path.dirname.callsFake((...args) => path.win32.dirname(...args))
path.isAbsolute.callsFake((...args) => path.win32.isAbsolute(...args))
path.join.callsFake((...args) => path.win32.join(...args))
path.parse.callsFake((...args) => path.win32.parse(...args))
path.normalize.callsFake((...args) => path.win32.normalize(...args))
const filePathNotInProjectDirectory = `C:\\Users\\foo\\project\\support\\index.js`

const projectRoot = `C:\\Users\\foo\\project\\nested-project`

const result = AppData.getBundledFilePath(projectRoot, filePathNotInProjectDirectory)

expect(result).to.equal(`C:\\Users\\foo\\AppData\\Roaming\\Cypress\\cy\\test\\projects\\nested-project-5ddfc54488859fd4a685e789cc5259c9\\bundles\\support\\index.js`)
})

it('on linux', () => {
sinon.stub(os, 'platform')
sinon.stub(osPath, 'data')

os.platform.returns('linux')
osPath.data.returns(`/Users/foo/.cache`)

const filePathNotInProjectDirectory = `/Users/foo/project/support/index.js`
const projectRoot = `/Users/foo/project/nested-project`

const result = AppData.getBundledFilePath(projectRoot, filePathNotInProjectDirectory)

expect(result).to.equal(`/Users/foo/.cache/Cypress/cy/test/projects/nested-project-48bc0cf1ee4dff159065e8a5813d3c7f/bundles/support/index.js`)
})

it('on darwin/mac', () => {
sinon.stub(os, 'platform')
sinon.stub(osPath, 'data')

os.platform.returns('linux')
osPath.data.returns(`/Users/foo/Library/Caches`)

const filePathNotInProjectDirectory = `/Users/foo/project/support/index.js`
const projectRoot = `/Users/foo/project/nested-project`

const result = AppData.getBundledFilePath(projectRoot, filePathNotInProjectDirectory)

expect(result).to.equal(`/Users/foo/Library/Caches/Cypress/cy/test/projects/nested-project-48bc0cf1ee4dff159065e8a5813d3c7f/bundles/support/index.js`)
})
})
})
})

5 comments on commit d79c99e

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on d79c99e Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.14.0/linux-arm64/develop-d79c99e324e9d9588679d9d79402f9f528ba4c27/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on d79c99e Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.14.0/linux-x64/develop-d79c99e324e9d9588679d9d79402f9f528ba4c27/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on d79c99e Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.14.0/win32-x64/develop-d79c99e324e9d9588679d9d79402f9f528ba4c27/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on d79c99e Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.14.0/darwin-arm64/develop-d79c99e324e9d9588679d9d79402f9f528ba4c27/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on d79c99e Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.14.0/darwin-x64/develop-d79c99e324e9d9588679d9d79402f9f528ba4c27/cypress.tgz

Please sign in to comment.