merge in develop to 10.0-release

This commit is contained in:
Lachlan Miller
2022-03-07 10:44:26 +10:00
parent de4cb66050
commit 76129e26af
60 changed files with 2191 additions and 859 deletions
@@ -0,0 +1,8 @@
exports['prepare-release-artifacts runs expected commands 1'] = `
$ node ./scripts/prepare-release-artifacts.js --dry-run --sha 57d0a85108fad6f77b39db88b8a7d8a3bfdb51a2 --version 1.2.3
🏗 Running \`move-binaries\`...
🏗 Dry run, not executing: node ./scripts/binary.js move-binaries --sha 57d0a85108fad6f77b39db88b8a7d8a3bfdb51a2 --version 1.2.3
🏗 Running \`create-stable-npm-package\`...
🏗 Dry run, not executing: ./scripts/create-stable-npm-package.sh https://cdn.cypress.io/beta/npm/1.2.3/linux-x64/develop-57d0a85108fad6f77b39db88b8a7d8a3bfdb51a2/cypress.tgz
`
Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

+2 -2
View File
@@ -1,4 +1,4 @@
{
"chrome:beta": "99.0.4844.45",
"chrome:stable": "98.0.4758.102"
"chrome:beta": "100.0.4896.20",
"chrome:stable": "99.0.4844.51"
}
+9 -5
View File
@@ -29,7 +29,7 @@ mainBuildFilters: &mainBuildFilters
only:
- develop
- 10.0-release
- merge-develop-2-28-22
- merge-develop-3-7-22
# 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
@@ -39,7 +39,8 @@ macWorkflowFilters: &mac-workflow-filters
or:
- equal: [ develop, << pipeline.git.branch >> ]
- equal: [ '10.0-release', << pipeline.git.branch >> ]
- equal: [ merge-develop-2-28-22, << pipeline.git.branch >> ]
- equal: [ merge-develop-3-7-22, << pipeline.git.branch >> ]
- equal: [ fix-beta-build-caching, << pipeline.git.branch >> ]
- matches:
pattern: "-release$"
value: << pipeline.git.branch >>
@@ -49,6 +50,7 @@ windowsWorkflowFilters: &windows-workflow-filters
or:
- equal: [ develop, << pipeline.git.branch >> ]
- equal: [ '10.0-release', << pipeline.git.branch >> ]
- equal: [ fix-beta-build-caching, << pipeline.git.branch >> ]
- matches:
pattern: "-release$"
value: << pipeline.git.branch >>
@@ -81,6 +83,7 @@ executors:
macos:
# Executor should have Node >= required version
xcode: "13.0.0"
resource_class: macos.x86.medium.gen2
environment:
PLATFORM: mac
@@ -1675,7 +1678,7 @@ jobs:
- run:
name: Check current branch to persist artifacts
command: |
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "merge-develop-2-28-22" && "$CIRCLE_BRANCH" != "10.0-release" ]]; then
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "merge-develop-3-7-22" && "$CIRCLE_BRANCH" != "10.0-release" ]]; then
echo "Not uploading artifacts or posting install comment for this branch."
circleci-agent step halt
fi
@@ -2405,12 +2408,13 @@ mac-workflow: &mac-workflow
- node_modules_install:
name: darwin-node-modules-install
executor: mac
resource_class: macos.x86.medium.gen2
only-cache-for-root-user: true
- build:
name: darwin-build
executor: mac
resource_class: medium
resource_class: macos.x86.medium.gen2
requires:
- darwin-node-modules-install
@@ -2429,7 +2433,7 @@ mac-workflow: &mac-workflow
- test-runner:upload
- test-runner:commit-status-checks
executor: mac
resource_class: medium
resource_class: macos.x86.medium.gen2
requires:
- darwin-build
+1
View File
@@ -2,6 +2,7 @@ exports['package.json build outputs expected properties 1'] = {
"name": "test",
"engines": "test engines",
"version": "x.y.z",
"buildInfo": "replaced by normalizePackageJson",
"description": "Cypress.io end to end testing tool",
"homepage": "https://github.com/cypress-io/cypress",
"license": "MIT",
+1 -1
View File
@@ -43,4 +43,4 @@ https://download.cypress.io/desktop/0.20.2?platform=OS&arch=ARCH
exports['desktop url from template'] = `
https://download.cypress.io/desktop/0.20.2/darwin-x64/cypress.zip
`
`
+41 -3
View File
@@ -7,7 +7,7 @@ Application Data: /user/app/data/path
Browser Profiles: /user/app/data/path/to/browsers
Binary Caches: /user/path/to/binary/cache
Cypress Version: 0.0.0-development
Cypress Version: 0.0.0-development (stable)
System Platform: linux (Foo-OsVersion)
System Memory: 1.2 GB free 400 MB
@@ -29,7 +29,7 @@ Application Data: /user/app/data/path
Browser Profiles: /user/app/data/path/to/browsers
Binary Caches: /user/path/to/binary/cache
Cypress Version: 0.0.0-development
Cypress Version: 0.0.0-development (stable)
System Platform: linux (Foo-OsVersion)
System Memory: 1.2 GB free 400 MB
@@ -48,8 +48,46 @@ Application Data: /user/app/data/path
Browser Profiles: /user/app/data/path/to/browsers
Binary Caches: /user/path/to/binary/cache
Cypress Version: 0.0.0-development
Cypress Version: 0.0.0-development (stable)
System Platform: linux (Foo-OsVersion)
System Memory: 1.2 GB free 400 MB
`
exports['logs additional info about pre-releases'] = `
Proxy Settings: none detected
Environment Variables: none detected
Application Data: /user/app/data/path
Browser Profiles: /user/app/data/path/to/browsers
Binary Caches: /user/path/to/binary/cache
Cypress Version: 0.0.0-development (pre-release)
System Platform: linux (Foo-OsVersion)
System Memory: 1.2 GB free 400 MB
This is a pre-release build of Cypress.
Build info:
Commit SHA: abc123
Commit Branch: someBranchName
Commit Date: 2022-02-02Txx:xx:xx.000Z
`
exports['logs additional info about development'] = `
Proxy Settings: none detected
Environment Variables: none detected
Application Data: /user/app/data/path
Browser Profiles: /user/app/data/path/to/browsers
Binary Caches: /user/path/to/binary/cache
Cypress Version: 0.0.0-development (pre-release)
System Platform: linux (Foo-OsVersion)
System Memory: 1.2 GB free 400 MB
This is the development (un-built) Cypress CLI.
`
+36
View File
@@ -256,4 +256,40 @@ https://on.cypress.io/guides/getting-started/installing-cypress#system-requireme
Platform: win32-ia32
`
exports['/lib/tasks/install .start non-stable builds logs a warning about installing a pre-release 1'] = `
⚠ Warning: You are installing a pre-release build of Cypress.
Bugs may be present which do not exist in production builds.
This build was created from:
* Commit SHA: abc123
* Commit Branch: aBranchName
* Commit Timestamp: 1996-11-27Txx:xx:xx.000Z
Installing Cypress (version: https://cdn.cypress.io/beta/binary/0.0.0-development/darwin-x64/aBranchName-abc123/cypress.zip)
⠋ Downloaded Cypress
✔ Downloaded Cypress
✔ Downloaded Cypress
⠋ Unzipped Cypress
✔ Downloaded Cypress
✔ Unzipped Cypress
✔ Downloaded Cypress
✔ Unzipped Cypress
⠋ Finished Installation /cache/Cypress/1.2.3
✔ Downloaded Cypress
✔ Unzipped Cypress
✔ Finished Installation /cache/Cypress/1.2.3
✔ Downloaded Cypress
✔ Unzipped Cypress
✔ Finished Installation /cache/Cypress/1.2.3
You can now open Cypress by running: node_modules/.bin/cypress open
https://on.cypress.io/installing-cypress
`
+54 -43
View File
@@ -11,6 +11,7 @@ const _ = require('lodash')
const g = chalk.green
// color for paths
const p = chalk.cyan
const red = chalk.red
// urls
const link = chalk.blue.underline
@@ -43,56 +44,66 @@ const formatCypressVariables = () => {
return maskSensitiveVariables(vars)
}
methods.start = (options = {}) => {
methods.start = async (options = {}) => {
const args = ['--mode=info']
return spawn.start(args, {
await spawn.start(args, {
dev: options.dev,
})
.then(() => {
console.log()
const proxyVars = methods.findProxyEnvironmentVariables()
if (_.isEmpty(proxyVars)) {
console.log('Proxy Settings: none detected')
} else {
console.log('Proxy Settings:')
_.forEach(proxyVars, (value, key) => {
console.log('%s: %s', key, g(value))
})
console.log()
const proxyVars = methods.findProxyEnvironmentVariables()
console.log()
console.log('Learn More: %s', link('https://on.cypress.io/proxy-configuration'))
console.log()
}
})
.then(() => {
const cyVars = formatCypressVariables()
if (_.isEmpty(cyVars)) {
console.log('Environment Variables: none detected')
} else {
console.log('Environment Variables:')
_.forEach(cyVars, (value, key) => {
console.log('%s: %s', key, g(value))
})
}
})
.then(() => {
console.log()
console.log('Application Data:', p(util.getApplicationDataFolder()))
console.log('Browser Profiles:', p(util.getApplicationDataFolder('browsers')))
console.log('Binary Caches: %s', p(state.getCacheDir()))
})
.then(() => {
console.log()
return util.getOsVersionAsync().then((osVersion) => {
console.log('Cypress Version: %s', g(util.pkgVersion()))
console.log('System Platform: %s (%s)', g(os.platform()), g(osVersion))
console.log('System Memory: %s free %s', g(prettyBytes(os.totalmem())), g(prettyBytes(os.freemem())))
if (_.isEmpty(proxyVars)) {
console.log('Proxy Settings: none detected')
} else {
console.log('Proxy Settings:')
_.forEach(proxyVars, (value, key) => {
console.log('%s: %s', key, g(value))
})
})
console.log()
console.log('Learn More: %s', link('https://on.cypress.io/proxy-configuration'))
console.log()
}
const cyVars = formatCypressVariables()
if (_.isEmpty(cyVars)) {
console.log('Environment Variables: none detected')
} else {
console.log('Environment Variables:')
_.forEach(cyVars, (value, key) => {
console.log('%s: %s', key, g(value))
})
}
console.log()
console.log('Application Data:', p(util.getApplicationDataFolder()))
console.log('Browser Profiles:', p(util.getApplicationDataFolder('browsers')))
console.log('Binary Caches: %s', p(state.getCacheDir()))
console.log()
const osVersion = await util.getOsVersionAsync()
const buildInfo = util.pkgBuildInfo()
const isStable = buildInfo && buildInfo.stable
console.log('Cypress Version: %s', g(util.pkgVersion()), isStable ? g('(stable)') : red('(pre-release)'))
console.log('System Platform: %s (%s)', g(os.platform()), g(osVersion))
console.log('System Memory: %s free %s', g(prettyBytes(os.totalmem())), g(prettyBytes(os.freemem())))
if (!buildInfo) {
console.log()
console.log('This is the', red('development'), '(un-built) Cypress CLI.')
} else if (!isStable) {
console.log()
console.log('This is a', red('pre-release'), 'build of Cypress.')
console.log('Build info:')
console.log(' Commit SHA:', g(buildInfo.commitSha))
console.log(' Commit Branch:', g(buildInfo.commitBranch))
console.log(' Commit Date:', g(buildInfo.commitDate))
}
}
module.exports = methods
+8 -1
View File
@@ -41,8 +41,15 @@ const getVersions = () => {
return versions
})
.then((binaryVersions) => {
const buildInfo = util.pkgBuildInfo()
let packageVersion = util.pkgVersion()
if (!buildInfo) packageVersion += ' (development)'
else if (!buildInfo.stable) packageVersion += ' (pre-release)'
const versions = {
package: util.pkgVersion(),
package: packageVersion,
binary: binaryVersions.binary || 'not installed',
electronVersion: binaryVersions.electronVersion || 'not found',
electronNodeVersion: binaryVersions.electronNodeVersion || 'not found',
+138 -217
View File
@@ -1,7 +1,6 @@
const _ = require('lodash')
const arch = require('arch')
const os = require('os')
const url = require('url')
const path = require('path')
const chalk = require('chalk')
const debug = require('debug')('cypress:cli')
@@ -18,97 +17,10 @@ const logger = require('../logger')
const { throwFormErrorText, errors } = require('../errors')
const verbose = require('../VerboseRenderer')
const getNpmArgv = () => {
const json = process.env.npm_config_argv
const { buildInfo, version } = require('../../package.json')
if (!json) {
return
}
debug('found npm argv json %o', json)
try {
return JSON.parse(json).original || []
} catch (e) {
return []
}
}
// attempt to discover the version specifier used to install Cypress
// for example: "^5.0.0", "https://cdn.cypress.io/...", ...
const getVersionSpecifier = (startDir = path.resolve(__dirname, '../..')) => {
const argv = getNpmArgv()
if ((process.env.npm_package_resolved || '').endsWith('cypress.tgz')) {
return process.env.npm_package_resolved
}
if (argv) {
const tgz = _.find(argv, (t) => t.endsWith('cypress.tgz'))
if (tgz) {
return tgz
}
}
const getVersionSpecifierFromPkg = (dir) => {
debug('looking for versionSpecifier %o', { dir })
const tryParent = () => {
const parentPath = path.resolve(dir, '..')
if (parentPath === dir) {
debug('reached FS root with no versionSpecifier found')
return
}
return getVersionSpecifierFromPkg(parentPath)
}
return fs.readJSON(path.join(dir, 'package.json'))
.catch(() => ({}))
.then((pkg) => {
const specifier = _.chain(['dependencies', 'devDependencies', 'optionalDependencies'])
.map((prop) => _.get(pkg, `${prop}.cypress`))
.compact().first().value()
return specifier || tryParent()
})
}
// recurse through parent directories until package.json with `cypress` is found
return getVersionSpecifierFromPkg(startDir)
.then((versionSpecifier) => {
debug('finished looking for versionSpecifier', { versionSpecifier })
return versionSpecifier
})
}
const betaNpmUrlRe = /^\/beta\/npm\/(?<version>[0-9.]+)\/(?<platformSlug>.+?)\/(?<artifactSlug>.+?)\/cypress\.tgz$/
// convert a prerelease NPM package .tgz URL to the corresponding binary .zip URL
const getBinaryUrlFromPrereleaseNpmUrl = (npmUrl) => {
let parsed
try {
parsed = url.parse(npmUrl)
} catch (e) {
return
}
const matches = betaNpmUrlRe.exec(parsed.pathname)
if (parsed.hostname !== 'cdn.cypress.io' || !matches) {
return
}
const { version, artifactSlug } = matches.groups
parsed.pathname = `/beta/binary/${version}/${os.platform()}-${arch()}/${artifactSlug}/cypress.zip`
return parsed.format()
function _getBinaryUrlFromBuildInfo ({ commitSha, commitBranch }) {
return `https://cdn.cypress.io/beta/binary/${version}/${os.platform()}-${arch()}/${commitBranch}-${commitSha}/cypress.zip`
}
const alreadyInstalledMsg = () => {
@@ -227,43 +139,71 @@ const validateOS = () => {
})
}
const start = (options = {}) => {
/**
* Returns the version to install - either a string like `1.2.3` to be fetched
* from the download server or a file path or HTTP URL.
*/
function getVersionOverride ({ envVarVersion, buildInfo }) {
// let this environment variable reset the binary version we need
if (envVarVersion) {
return envVarVersion
}
if (buildInfo && !buildInfo.stable) {
logger.log(
chalk.yellow(stripIndent`
${logSymbols.warning} Warning: You are installing a pre-release build of Cypress.
Bugs may be present which do not exist in production builds.
This build was created from:
* Commit SHA: ${buildInfo.commitSha}
* Commit Branch: ${buildInfo.commitBranch}
* Commit Timestamp: ${buildInfo.commitDate}
`),
)
logger.log()
return _getBinaryUrlFromBuildInfo(buildInfo)
}
}
function getEnvVarVersion () {
if (!util.getEnv('CYPRESS_INSTALL_BINARY')) return
// because passed file paths are often double quoted
// and might have extra whitespace around, be robust and trim the string
const trimAndRemoveDoubleQuotes = true
const envVarVersion = util.getEnv('CYPRESS_INSTALL_BINARY', trimAndRemoveDoubleQuotes)
debug('using environment variable CYPRESS_INSTALL_BINARY "%s"', envVarVersion)
return envVarVersion
}
const start = async (options = {}) => {
debug('installing with options %j', options)
const envVarVersion = getEnvVarVersion()
if (envVarVersion === '0') {
debug('environment variable CYPRESS_INSTALL_BINARY = 0, skipping install')
logger.log(
stripIndent`
${chalk.yellow('Note:')} Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.`,
)
logger.log()
return
}
_.defaults(options, {
force: false,
buildInfo,
})
const pkgVersion = util.pkgVersion()
let needVersion = pkgVersion
let binaryUrlOverride
debug('version in package.json is', needVersion)
// let this environment variable reset the binary version we need
if (util.getEnv('CYPRESS_INSTALL_BINARY')) {
// because passed file paths are often double quoted
// and might have extra whitespace around, be robust and trim the string
const trimAndRemoveDoubleQuotes = true
const envVarVersion = util.getEnv('CYPRESS_INSTALL_BINARY', trimAndRemoveDoubleQuotes)
debug('using environment variable CYPRESS_INSTALL_BINARY "%s"', envVarVersion)
if (envVarVersion === '0') {
debug('environment variable CYPRESS_INSTALL_BINARY = 0, skipping install')
logger.log(
stripIndent`
${chalk.yellow('Note:')} Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.`,
)
logger.log()
return Promise.resolve()
}
binaryUrlOverride = envVarVersion
}
if (util.getEnv('CYPRESS_CACHE_FOLDER')) {
const envCache = util.getEnv('CYPRESS_CACHE_FOLDER')
@@ -278,18 +218,21 @@ const start = (options = {}) => {
logger.log()
}
const installDir = state.getVersionDir(pkgVersion)
const pkgVersion = util.pkgVersion()
const versionOverride = getVersionOverride({ envVarVersion, buildInfo: options.buildInfo })
const versionToInstall = versionOverride || pkgVersion
debug('version in package.json is %s, version to install is %s', pkgVersion, versionToInstall)
const installDir = state.getVersionDir(pkgVersion, options.buildInfo)
const cacheDir = state.getCacheDir()
const binaryDir = state.getBinaryDir(pkgVersion)
return validateOS().then((isValid) => {
if (!isValid) {
return throwFormErrorText(errors.invalidOS)()
}
})
.then(() => {
return fs.ensureDirAsync(cacheDir)
})
if (!(await validateOS())) {
return throwFormErrorText(errors.invalidOS)()
}
await fs.ensureDirAsync(cacheDir)
.catch({ code: 'EACCES' }, (err) => {
return throwFormErrorText(errors.invalidCacheDirectory)(stripIndent`
Failed to access ${chalk.cyan(cacheDir)}:
@@ -297,26 +240,11 @@ const start = (options = {}) => {
${err.message}
`)
})
.then(() => {
return Promise.all([
state.getBinaryPkgAsync(binaryDir).then(state.getBinaryPkgVersion),
getVersionSpecifier(),
])
})
.then(([binaryVersion, versionSpecifier]) => {
if (!binaryUrlOverride && versionSpecifier) {
const computedBinaryUrl = getBinaryUrlFromPrereleaseNpmUrl(versionSpecifier)
if (computedBinaryUrl) {
debug('computed binary url from version specifier %o', { computedBinaryUrl, needVersion })
binaryUrlOverride = computedBinaryUrl
}
}
needVersion = binaryUrlOverride || needVersion
debug('installed version is', binaryVersion, 'version needed is', needVersion)
const binaryPkg = await state.getBinaryPkgAsync(binaryDir)
const binaryVersion = await state.getBinaryPkgVersion(binaryPkg)
const shouldInstall = () => {
if (!binaryVersion) {
debug('no binary installed under cli version')
@@ -336,7 +264,7 @@ const start = (options = {}) => {
return true
}
if ((binaryVersion === needVersion) || !util.isSemver(needVersion)) {
if ((binaryVersion === versionToInstall) || !util.isSemver(versionToInstall)) {
// our version matches, tell the user this is a noop
alreadyInstalledMsg()
@@ -344,96 +272,89 @@ const start = (options = {}) => {
}
return true
})
.then((shouldInstall) => {
// noop if we've been told not to download
if (!shouldInstall) {
debug('Not downloading or installing binary')
}
return
}
// noop if we've been told not to download
if (!shouldInstall()) {
return debug('Not downloading or installing binary')
}
if (needVersion !== pkgVersion) {
logger.log(
chalk.yellow(stripIndent`
${logSymbols.warning} Warning: Forcing a binary version different than the default.
if (envVarVersion) {
logger.log(
chalk.yellow(stripIndent`
${logSymbols.warning} Warning: Forcing a binary version different than the default.
The CLI expected to install version: ${chalk.green(pkgVersion)}
The CLI expected to install version: ${chalk.green(pkgVersion)}
Instead we will install version: ${chalk.green(needVersion)}
Instead we will install version: ${chalk.green(versionToInstall)}
These versions may not work properly together.
`),
)
These versions may not work properly together.
`),
)
logger.log()
}
logger.log()
}
const getLocalFilePath = async () => {
// see if version supplied is a path to a binary
return fs.pathExistsAsync(needVersion)
.then((exists) => {
if (exists) {
return path.extname(needVersion) === '.zip' ? needVersion : false
}
if (await fs.pathExistsAsync(versionToInstall)) {
return path.extname(versionToInstall) === '.zip' ? versionToInstall : false
}
const possibleFile = util.formAbsolutePath(needVersion)
const possibleFile = util.formAbsolutePath(versionToInstall)
debug('checking local file', possibleFile, 'cwd', process.cwd())
debug('checking local file', possibleFile, 'cwd', process.cwd())
return fs.pathExistsAsync(possibleFile)
.then((exists) => {
// if this exists return the path to it
// else false
if (exists && path.extname(possibleFile) === '.zip') {
return possibleFile
}
// if this exists return the path to it
// else false
if ((await fs.pathExistsAsync(possibleFile)) && path.extname(possibleFile) === '.zip') {
return possibleFile
}
return false
})
})
.then((pathToLocalFile) => {
if (pathToLocalFile) {
const absolutePath = path.resolve(needVersion)
return false
}
debug('found local file at', absolutePath)
debug('skipping download')
const pathToLocalFile = await getLocalFilePath()
const rendererOptions = getRendererOptions()
if (pathToLocalFile) {
const absolutePath = path.resolve(versionToInstall)
return new Listr([unzipTask({
progress: {
throttle: 100,
onProgress: null,
},
zipFilePath: absolutePath,
installDir,
rendererOptions,
})], { rendererOptions }).run()
}
debug('found local file at', absolutePath)
debug('skipping download')
if (options.force) {
debug('Cypress already installed at', installDir)
debug('but the installation was forced')
}
const rendererOptions = getRendererOptions()
debug('preparing to download and unzip version ', needVersion, 'to path', installDir)
return new Listr([unzipTask({
progress: {
throttle: 100,
onProgress: null,
},
zipFilePath: absolutePath,
installDir,
rendererOptions,
})], { rendererOptions }).run()
}
const downloadDir = os.tmpdir()
if (options.force) {
debug('Cypress already installed at', installDir)
debug('but the installation was forced')
}
return downloadAndUnzip({ version: needVersion, installDir, downloadDir })
})
// delay 1 sec for UX, unless we are testing
.then(() => {
return Promise.delay(1000)
})
.then(displayCompletionMsg)
})
debug('preparing to download and unzip version ', versionToInstall, 'to path', installDir)
const downloadDir = os.tmpdir()
await downloadAndUnzip({ version: versionToInstall, installDir, downloadDir })
// delay 1 sec for UX, unless we are testing
await Promise.delay(1000)
displayCompletionMsg()
}
module.exports = {
start,
_getVersionSpecifier: getVersionSpecifier,
_getBinaryUrlFromPrereleaseNpmUrl: getBinaryUrlFromPrereleaseNpmUrl,
_getBinaryUrlFromBuildInfo,
}
const unzipTask = ({ zipFilePath, installDir, progress, rendererOptions }) => {
+5 -1
View File
@@ -50,7 +50,11 @@ const getBinaryDir = (version = util.pkgVersion()) => {
return path.join(getVersionDir(version), getPlatFormBinaryFolder())
}
const getVersionDir = (version = util.pkgVersion()) => {
const getVersionDir = (version = util.pkgVersion(), buildInfo = util.pkgBuildInfo()) => {
if (buildInfo && !buildInfo.stable) {
version = ['beta', version, buildInfo.commitBranch, buildInfo.commitSha].join('-')
}
return path.join(getCacheDir(), version)
}
+4
View File
@@ -365,6 +365,10 @@ const util = {
return process.cwd()
},
pkgBuildInfo () {
return pkg.buildInfo
},
pkgVersion () {
return pkg.version
},
+11
View File
@@ -1,5 +1,6 @@
const _ = require('lodash')
const path = require('path')
const shell = require('shelljs')
const fs = require('../lib/fs')
@@ -19,6 +20,10 @@ const {
const packageJsonSrc = path.join('package.json')
const packageJsonDest = path.join('build', 'package.json')
function getStdout (cmd) {
return shell.exec(cmd).trim()
}
function preparePackageForNpmRelease (json) {
// modify the existing package.json
// to prepare it for releasing to npm
@@ -29,6 +34,12 @@ function preparePackageForNpmRelease (json) {
_.extend(json, {
version,
buildInfo: {
commitBranch: process.env.CIRCLE_BRANCH || getStdout('git branch --show-current'),
commitSha: getStdout('git rev-parse HEAD'),
commitDate: new Date(getStdout('git show -s --format=%ci')).toISOString(),
stable: false,
},
description,
homepage,
license,
+12 -2
View File
@@ -10,7 +10,17 @@ const hasVersion = (json) => {
return la(is.semver(json.version), 'cannot find version', json)
}
const changeVersion = (o) => ({ ...o, version: 'x.y.z' })
const normalizePackageJson = (o) => {
expect(o.buildInfo).to.include({ stable: false })
expect(o.buildInfo.commitBranch).to.match(/.+/)
expect(o.buildInfo.commitSha).to.match(/[a-f0-9]+/)
return {
...o,
version: 'x.y.z',
buildInfo: 'replaced by normalizePackageJson',
}
}
describe('package.json build', () => {
beforeEach(function () {
@@ -32,7 +42,7 @@ describe('package.json build', () => {
it('outputs expected properties', () => {
return makeUserPackageFile()
.then(changeVersion)
.then(normalizePackageJson)
.then(snapshot)
})
})
+1
View File
@@ -28,6 +28,7 @@ describe('cli', () => {
os.platform.returns('darwin')
// sinon.stub(util, 'exit')
sinon.stub(util, 'logErrorExit1')
sinon.stub(util, 'pkgBuildInfo').returns({ stable: true })
this.exec = (args) => {
const cliArgs = `node test ${args}`.split(' ')
+21
View File
@@ -25,6 +25,10 @@ describe('exec info', function () {
.withArgs('browsers').returns('/user/app/data/path/to/browsers')
.withArgs().returns('/user/app/data/path')
sinon.stub(util, 'pkgBuildInfo').returns({
stable: true,
})
sinon.stub(state, 'getCacheDir').returns('/user/path/to/binary/cache')
})
@@ -68,4 +72,21 @@ describe('exec info', function () {
await startInfoAndSnapshot('cypress redacts sensitive vars')
})
it('logs additional info about pre-releases', async () => {
util.pkgBuildInfo.returns({
stable: false,
commitSha: 'abc123',
commitBranch: 'someBranchName',
commitDate: new Date('02-02-2022').toISOString(),
})
await startInfoAndSnapshot('logs additional info about pre-releases')
})
it('logs if unbuilt development', async () => {
util.pkgBuildInfo.returns(undefined)
await startInfoAndSnapshot('logs additional info about development')
})
})
+17
View File
@@ -18,6 +18,7 @@ describe('lib/exec/versions', function () {
})
sinon.stub(util, 'pkgVersion').returns('4.5.6')
sinon.stub(util, 'pkgBuildInfo').returns({ stable: true })
})
describe('.getVersions', function () {
@@ -50,6 +51,22 @@ describe('lib/exec/versions', function () {
})
})
it('appends pre-release if not stable', async function () {
util.pkgBuildInfo.returns({ stable: false })
const v = await versions.getVersions()
expect(v.package).to.eql('4.5.6 (pre-release)')
})
it('appends development if missing buildInfo', async function () {
util.pkgBuildInfo.returns(undefined)
const v = await versions.getVersions()
expect(v.package).to.eql('4.5.6 (development)')
})
it('reports default versions if not found', function () {
// imagine package.json only has version there
state.getBinaryPkgAsync.withArgs(binaryDir).resolves({
+35 -138
View File
@@ -1,11 +1,9 @@
require('../../spec_helper')
const _ = require('lodash')
const os = require('os')
const path = require('path')
const chalk = require('chalk')
const Promise = require('bluebird')
const mockfs = require('mock-fs')
const mockedEnv = require('mocked-env')
const snapshot = require('../../support/snapshot')
const stdout = require('../../support/stdout')
@@ -75,6 +73,32 @@ describe('/lib/tasks/install', function () {
})
})
describe('non-stable builds', () => {
function runInstall () {
return install.start({
buildInfo: {
stable: false,
commitSha: 'abc123',
commitBranch: 'aBranchName',
commitDate: new Date('11-27-1996').toISOString(),
},
})
}
it('install from a constructed CDN URL', async function () {
await runInstall()
expect(download.start).to.be.calledWithMatch({
version: 'https://cdn.cypress.io/beta/binary/0.0.0-development/darwin-x64/aBranchName-abc123/cypress.zip',
})
})
it('logs a warning about installing a pre-release', async function () {
await runInstall()
snapshot(normalize(this.stdout.toString()))
})
})
describe('override version', function () {
it('warns when specifying cypress version in env', function () {
const version = '0.12.1'
@@ -460,145 +484,18 @@ describe('/lib/tasks/install', function () {
})
})
context('._getBinaryUrlFromPrereleaseNpmUrl', function () {
beforeEach(() => {
context('._getBinaryUrlFromBuildInfo', function () {
const buildInfo = {
commitSha: 'abc123',
commitBranch: 'aBranchName',
}
it('generates the expected URL', () => {
os.platform.returns('linux')
sinon.stub(os, 'arch').returns('x64')
})
it('returns binary url for prerelease npm url', function () {
expect(install._getBinaryUrlFromPrereleaseNpmUrl('https://cdn.cypress.io/beta/npm/5.1.1/linux-x64/ciprovider-branchname-sha/cypress.tgz'))
.to.eq('https://cdn.cypress.io/beta/binary/5.1.1/linux-x64/ciprovider-branchname-sha/cypress.zip')
expect(install._getBinaryUrlFromPrereleaseNpmUrl('https://cdn.cypress.io/beta/npm/5.1.1/inux-x64/circle-develop-3fdfc3b453eb38ad3c0b079531e4dde6668e3dd0-436710/cypress.tgz'))
.to.eq('https://cdn.cypress.io/beta/binary/5.1.1/linux-x64/circle-develop-3fdfc3b453eb38ad3c0b079531e4dde6668e3dd0-436710/cypress.zip')
expect(install._getBinaryUrlFromPrereleaseNpmUrl('https://cdn.cypress.io/beta/npm/5.1.1/inux-x64/circle-develop/some/branch-3fdfc3b453eb38ad3c0b079531e4dde6668e3dd0-436710/cypress.tgz'))
.to.eq('https://cdn.cypress.io/beta/binary/5.1.1/linux-x64/circle-develop/some/branch-3fdfc3b453eb38ad3c0b079531e4dde6668e3dd0-436710/cypress.zip')
})
it('returns nothing for an invalid url', function () {
expect(install._getBinaryUrlFromPrereleaseNpmUrl('1.2.3')).to.be.undefined
expect(install._getBinaryUrlFromPrereleaseNpmUrl(null)).to.be.undefined
})
})
context('._getVersionSpecifier', function () {
let restoreEnv
beforeEach(function () {
sinon.stub(fs, 'readJSON').rejects()
restoreEnv && restoreEnv()
})
it('resolves undefined if no versionSpecifier found', async function () {
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.be.undefined
})
it('resolves with cypress.tgz URL if specified in npm argv', async function () {
restoreEnv = mockedEnv({
npm_config_argv: JSON.stringify({
original: ['npm', 'i', 'https://foo.com/cypress.tgz'],
}),
})
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('https://foo.com/cypress.tgz')
})
it('resolves with cypress.tgz URL if specified in npm env npm_package_resolved', async function () {
restoreEnv = mockedEnv({
npm_package_resolved: 'https://foo.com/cypress.tgz',
})
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('https://foo.com/cypress.tgz')
})
it('resolves with versionSpecifier from parent pkg.json', async function () {
fs.readJSON.withArgs('/foo/bar/baz/package.json').resolves({
dependencies: {
'cypress': '1.2.3',
},
})
fs.readJSON.withArgs('/foo/bar/package.json').resolves({
dependencies: {
'cypress': 'wrong',
},
})
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('1.2.3')
})
it('resolves with devDependencies too', async function () {
fs.readJSON.withArgs('/foo/bar/baz/package.json').resolves({
devDependencies: {
'cypress': '4.5.6',
},
})
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('4.5.6')
})
it('resolves with optionalDependencies too', async function () {
fs.readJSON.withArgs('/foo/bar/baz/package.json').resolves({
optionalDependencies: {
'cypress': '6.7.8',
},
})
expect(await install._getVersionSpecifier('/foo/bar/baz')).to.eq('6.7.8')
})
context('with win32 path functions and paths', async function () {
const oldPath = _.clone(path)
beforeEach(() => {
_.assign(path, path.win32)
})
afterEach(() => {
_.assign(path, oldPath)
})
it('resolves undefined if no versionSpecifier found', async function () {
expect(await install._getVersionSpecifier('C:\\foo\\bar\\baz')).to.be.undefined
})
it('resolves with versionSpecifier from parent pkg.json', async function () {
fs.readJSON.withArgs('C:\\foo\\bar\\baz\\package.json').resolves({
dependencies: {
'cypress': '1.2.3',
},
})
fs.readJSON.withArgs('C:\\foo\\bar\\package.json').resolves({
dependencies: {
'cypress': 'wrong',
},
})
expect(await install._getVersionSpecifier('C:\\foo\\bar\\baz')).to.eq('1.2.3')
})
it('resolves with devDependencies too', async function () {
fs.readJSON.withArgs('C:\\foo\\bar\\baz\\package.json').resolves({
devDependencies: {
'cypress': '4.5.6',
},
})
expect(await install._getVersionSpecifier('C:\\foo\\bar\\baz')).to.eq('4.5.6')
})
it('resolves with optionalDependencies too', async function () {
fs.readJSON.withArgs('C:\\foo\\bar\\baz\\package.json').resolves({
optionalDependencies: {
'cypress': '6.7.8',
},
})
expect(await install._getVersionSpecifier('C:\\foo\\bar\\baz')).to.eq('6.7.8')
})
expect(install._getBinaryUrlFromBuildInfo(buildInfo))
.to.eq(`https://cdn.cypress.io/beta/binary/0.0.0-development/linux-x64/aBranchName-abc123/cypress.zip`)
})
})
})
+58 -73
View File
@@ -11,45 +11,30 @@ The `@cypress/`-namespaced NPM packages that live inside the [`/npm`](../npm) di
### Prerequisites
- Ensure you have the following permissions set up:
- An AWS account with permission to create AWS access keys for the Cypress CDN.
- An AWS account with permission to access and write to the AWS S3, i.e. the Cypress CDN.
- Permissions for your npm account to publish the `cypress` package.
- Permissions to update releases in ZenHub.
- Set up the following environment variables:
- Cypress AWS access key and secret in `aws_credentials_json`, which looks like this:
- For the `release-automations` steps, you will need setup the following envs:
- GitHub token - generated yourself in github.
- [ZenHub API token](https://app.zenhub.com/dashboard/tokens) to interact with Zenhub. Found in 1Password.
- The `cypress-bot` GitHub app credentials. Found in 1Password.
```text
aws_credentials_json='{"bucket":"cdn.cypress.io","folder":"desktop","key":"...","secret":"..."}'
GITHUB_TOKEN="..."
ZENHUB_API_TOKEN="..."
GITHUB_APP_CYPRESS_INSTALLATION_ID=
GITHUB_APP_ID=
GITHUB_PRIVATE_KEY=
```
- A [GitHub token](https://github.com/settings/tokens) and a [CircleCI token](https://circleci.com/account/api) in `ci_json`:
```text
ci_json='{"githubToken":"...","circleToken":"..."}'
```
- You'll also need to put the GitHub token under its own variable and get a [ZenHub API token](https://app.zenhub.com/dashboard/tokens) for the `release-automations` step.
```text
GITHUB_TOKEN="..."
ZENHUB_API_TOKEN="..."
```
- The `cypress-bot` GitHub app credentials are also needed. Ask another team member who has done a deploy for those.
```text
GITHUB_APP_CYPRESS_INSTALLATION_ID=
GITHUB_APP_ID=
GITHUB_PRIVATE_KEY=
```
- For purging the Cloudflare cache (part of the `move-binaries` step), you'll need `CF_ZONEID` and `CF_TOKEN` set. These can be found in 1Password. If you don't have access, ask a team member who has done a deploy.
- For purging the Cloudflare cache (part of the `move-binaries` step), you'll need `CF_ZONEID` and `CF_TOKEN` set. These can be found in 1Password.
```text
CF_ZONEID="..."
CF_TOKEN="..."
```
- If you don't have access to 1Password, ask a team member who has done a deploy.
- Tip: Use [as-a](https://github.com/bahmutov/as-a) to manage environment variables for different situations.
### Before Publishing a New Version
@@ -67,44 +52,59 @@ of Cypress. You can see the progress of the test projects by opening the status
![Screenshot of status checks](https://i.imgur.com/AsQwzgO.png)
Once the `develop` branch for all test projects are reliably passing with the new changes and the `linux-x64` binary is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.zip`, and the `linux-x64` cypress npm package is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.tgz`, publishing can proceed.
### Steps to Publish a New Version
In the following instructions, "X.Y.Z" is used to denote the [next version of Cypress being published](./next-version.md).
1. `develop` should contain all of the changes made in `master`. However, this occasionally may not be the case. Ensure that `master` does not have any additional commits that are not on `develop` and all auto-generated pull requests designed to merge master into develop have been successfully merged.
1. Confirm that every issue labeled [stage: pending release](https://github.com/cypress-io/cypress/issues?q=label%3A%22stage%3A+pending+release%22+is%3Aclosed) has a ZenHub release set. **Tip:** there is a command in [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to list and check such issues. Without a ZenHub release issues will not be included in the right changelog.
2. If there is a new [`cypress-example-kitchensink`](https://github.com/cypress-io/cypress-example-kitchensink/releases) version, update the corresponding dependency in [`packages/example`](../packages/example) to that new version.
2. Create or review the release-specific documentation and changelog in [cypress-documentation](https://github.com/cypress-io/cypress-documentation). If there is not already a release-specific PR open, create one.
- Use [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to generate a starting point for the changelog, based off of ZenHub:
```shell
cd packages/issues-in-release
yarn do:changelog --release <release label>
```
- Ensure the changelog is up-to-date and has the correct date.
- Merge any release-specific documentation changes into the main release PR.
- You can view the doc's [branch deploy preview](https://github.com/cypress-io/cypress-documentation/blob/master/CONTRIBUTING.md#pull-requests) by clicking 'Details' on the PR's `netlify-cypress-docs/deploy-preview` GitHub status check.
3. Use the `move-binaries` script to move the binaries for `<commit sha>` from `beta` to the `desktop` folder for `<new target version>`. This also purges the cloudflare cache for this version.
3. `develop` should contain all of the changes made in `master`. However, this occasionally may not be the case.
- Ensure that `master` does not have any additional commits that are not on `develop`.
- Ensure all auto-generated pull requests designed to merge master into develop have been successfully merged.
4. If there is a new [`cypress-example-kitchensink`](https://github.com/cypress-io/cypress-example-kitchensink/releases) version, update the corresponding dependency in [`packages/example`](../packages/example) to that new version.
5. Once the `develop` branch is passing for all test projects with the new changes and the `linux-x64` binary is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.zip`, and the `linux-x64` cypress npm package is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.tgz`, publishing can proceed.
6. Log into AWS SSO with `aws sso login --profile <name_of_profile>`. The release scripts assumes you are using the `production` profile. If you have setup your credentials under a different profile, be sure to set the `AWS_PROFILE` environment variable.
7. Use the `prepare-release-artifacts` script (Mac/Linux only) to prepare the latest commit to a stable release. When you run this script, the following happens:
* the binaries for `<commit sha>` are moved from `beta` to the `desktop` folder for `<new target version>` in S3
* the Cloudflare cache for this version is purged
* the pre-prod `cypress.tgz` NPM package is converted to a stable NPM package ready for release
```shell
yarn move-binaries --sha <commit sha> --version <new target version>
yarn prepare-release-artifacts --sha <commit sha> --version <new target version>
```
4. Publish the new npm package under the `dev` tag, using your personal npm account.
- To find the link to the package file `cypress.tgz`:
1. In GitHub, go to the latest commit (the one whose sha you used in the last step).
![commit-link](https://user-images.githubusercontent.com/1157043/80608728-33fe6100-8a05-11ea-8b53-375303757b67.png)
2. Scroll down past the changes to the comments. The first comment should be a `cypress-bot` comment that includes a line beginning `npm install ...`. Grab the `https://cdn.../npm/X.Y.Z/<platform>/<long sha>/cypress.tgz` link.
![commit-bot-comment](../assets/cypress-bot-pre-release-comment.png)
- Make sure the `linux-x64` binary and npm package are present at the commented locations. See [Before Publishing a New Version](#before-publishing-a-new-version).
- Publish the `linux-x64` distribution to the npm registry straight from the URL:
You can pass `--dry-run` to see the commands this would run under the hood.
```shell
npm publish https://cdn.cypress.io/beta/npm/X.Y.Z/<long sha>/cypress.tgz --tag dev
```
:bangbang: Important :bangbang: Be sure to release the `linux-x64` distribution.
8. Validate you are logged in to `npm` with `npm whoami`. Otherwise log in with `npm login`.
5. Double-check that the new version has been published under the `dev` tag using `npm info cypress` or [available-versions](https://github.com/bahmutov/available-versions). `latest` should still point to the previous version. Example output:
9. Publish the generated npm package under the `dev` tag, using your personal npm account.
```shell
npm publish /tmp/cypress-prod.tgz --tag dev
```
10. Double-check that the new version has been published under the `dev` tag using `npm info cypress` or [available-versions](https://github.com/bahmutov/available-versions). `latest` should still point to the previous version. Example output:
```shell
dist-tags:
dev: 3.4.0 latest: 3.3.2
```
6. Test `cypress@X.Y.Z` to make sure everything is working.
11. Test `cypress@X.Y.Z` to make sure everything is working.
- Install the new version: `npm install -g cypress@X.Y.Z`
- Run a quick, manual smoke test:
- `cypress open`
@@ -113,43 +113,28 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
- [cypress-realworld-app](https://github.com/cypress-io/cypress-realworld-app) uses yarn and represents a typical consumer implementation.
- Optionally, do more thorough tests, for example test the new version of Cypress against the Cypress dashboard repo.
7. Confirm that every issue labeled [stage: pending release](https://github.com/cypress-io/cypress/issues?q=label%3A%22stage%3A+pending+release%22+is%3Aclosed) has a ZenHub release set. **Tip:** there is a command in [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to list and check such issues. Without a ZenHub release issues will not be included in the right changelog.
8. Deploy the release-specific documentation and changelog in [cypress-documentation](https://github.com/cypress-io/cypress-documentation).
- If there is not already a release-specific PR open, create one. You can use [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to generate a starting point for the changelog, based off of ZenHub:
```shell
cd packages/issues-in-release
yarn do:changelog --release <release label>
```
- Ensure the changelog is up-to-date and has the correct date.
- Merge any release-specific documentation changes into the main release PR.
- You can view the doc's [branch deploy preview](https://github.com/cypress-io/cypress-documentation/blob/master/CONTRIBUTING.md#pull-requests) by clicking 'Details' on the PR's `netlify-cypress-docs/deploy-preview` GitHub status check.
- Merge this PR into `master` to deploy it to production.
9. Make the new npm version the "latest" version by updating the dist-tag `latest` to point to the new version:
12. Make the new npm version the "latest" version by updating the dist-tag `latest` to point to the new version:
```shell
npm dist-tag add cypress@X.Y.Z
```
10. Run `binary-release` to update the [download server's manifest](https://download.cypress.io/desktop.json). This will also ensure the binary for the version is downloadable for each system.
13. Run `binary-release` to update the [download server's manifest](https://download.cypress.io/desktop.json). This will also ensure the binary for the version is downloadable for each system.
```shell
yarn binary-release --version X.Y.Z
```
11. If needed, push out any updated changes to the links manifest to [`on.cypress.io`](https://github.com/cypress-io/cypress-services/tree/develop/packages/on).
14. If needed, push out any updated changes to the links manifest to [`on.cypress.io`](https://github.com/cypress-io/cypress-services/tree/develop/packages/on).
12. If needed, deploy the updated [`cypress-example-kitchensink`][cypress-example-kitchensink] to `example.cypress.io` by following [these instructions under "Deployment"](../packages/example/README.md).
15. If needed, deploy the updated [`cypress-example-kitchensink`][cypress-example-kitchensink] to `example.cypress.io` by following [these instructions under "Deployment"](../packages/example/README.md).
13. Update the releases in [ZenHub](https://app.zenhub.com/workspaces/test-runner-5c3ea3baeb1e75374f7b0708/reports/release):
16. Update the releases in [ZenHub](https://app.zenhub.com/workspaces/test-runner-5c3ea3baeb1e75374f7b0708/reports/release):
- Close the current release in ZenHub.
- Create a new patch release (and a new minor release, if this is a minor release) in ZenHub, and schedule them both to be completed 2 weeks from the current date.
- Move all issues that are still open from the current release to the appropriate future release.
14. Bump `version` in [`package.json`](package.json), commit it to `develop`, tag it with the version, and push the tag up:
17. Bump `version` in [`package.json`](package.json), commit it to `develop`, tag it with the version, and push the tag up:
```shell
git commit -am "release X.Y.Z [skip ci]"
@@ -159,7 +144,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
git push origin vX.Y.Z
```
15. Merge `develop` into `master` and push both branches up. Note: pushing to `master` will automatically publish any independent npm packages that have not yet been published.
18. Merge `develop` into `master` and push both branches up. Note: pushing to `master` will automatically publish any independent npm packages that have not yet been published.
```shell
git push origin develop
@@ -168,7 +153,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
git push origin master
```
16. Inside of [cypress-io/release-automations][release-automations]:
19. Inside of [cypress-io/release-automations][release-automations]:
- Publish GitHub release to [cypress-io/cypress/releases](https://github.com/cypress-io/cypress/releases) using package `set-releases`:
```shell
@@ -183,9 +168,9 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
- Confirm there are no issues with the label [stage: pending release](https://github.com/cypress-io/cypress/issues?q=label%3A%22stage%3A+pending+release%22+is%3Aclosed) left
17. Publish a new docker image in [`cypress-docker-images`](https://github.com/cypress-io/cypress-docker-images) under `included` for the new cypress version. Note: we use the base image with the Node version matching the bundled Node version. Instructions for updating `cypress-docker-images` can be found [here](https://github.com/cypress-io/cypress-docker-images/blob/master/CONTRIBUTING.md#add-new-included-image).
20. Publish a new docker image in [`cypress-docker-images`](https://github.com/cypress-io/cypress-docker-images) under `included` for the new cypress version. Note: we use the base image with the Node version matching the bundled Node version. Instructions for updating `cypress-docker-images` can be found [here](https://github.com/cypress-io/cypress-docker-images/blob/master/CONTRIBUTING.md#add-new-included-image).
18. Update example projects to the new version. For most projects, you can go to the Renovate dependency issue and check the box next to `Update dependency cypress to X.Y.Z`. It will automatically create a PR. Once it passes, you can merge it. Try updating at least the following projects:
21. Update example projects to the new version. For most projects, you can go to the Renovate dependency issue and check the box next to `Update dependency cypress to X.Y.Z`. It will automatically create a PR. Once it passes, you can merge it. Try updating at least the following projects:
- [cypress-example-todomvc](https://github.com/cypress-io/cypress-example-todomvc/issues/99)
- [cypress-example-todomvc-redux](https://github.com/cypress-io/cypress-example-todomvc-redux/issues/1)
- [cypress-example-realworld](https://github.com/cypress-io/cypress-example-realworld/issues/2)
@@ -195,7 +180,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
- [cypress-documentation](https://github.com/cypress-io/cypress-documentation/issues/1313)
- [cypress-example-docker-compose](https://github.com/cypress-io/cypress-example-docker-compose) - Doesn't have a Renovate issue, but will auto-create and auto-merge non-major Cypress updates as long as the tests pass.
19. Check if any test or example repositories have a branch for testing the features or fixes from the newly published version `x.y.z`. The branch should also be named `x.y.z`. Check all `cypress-test-*` and `cypress-example-*` repositories, and if there is a branch named `x.y.z`, merge it into `master`.
22. Check if any test or example repositories have a branch for testing the features or fixes from the newly published version `x.y.z`. The branch should also be named `x.y.z`. Check all `cypress-test-*` and `cypress-example-*` repositories, and if there is a branch named `x.y.z`, merge it into `master`.
**Test Repos**
+1
View File
@@ -187,3 +187,4 @@ describe('#startDevServer', () => {
})
})
})
.timeout(5000)
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "cypress",
"version": "9.5.0",
"version": "9.5.1",
"description": "Cypress.io end to end testing tool",
"private": true,
"scripts": {
@@ -44,7 +44,7 @@
"jscodeshift": "jscodeshift -t ./node_modules/js-codemod/transforms/arrow-function-arguments.js",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json,.vue .",
"lint-changed": "lint-changed",
"move-binaries": "node ./scripts/binary.js move-binaries",
"prepare-release-artifacts": "node ./scripts/prepare-release-artifacts.js",
"npm-release": "node scripts/npm-release.js",
"prestart": "yarn ensure-deps",
"start": "cypress open --dev --global",
@@ -66,7 +66,7 @@
"test-unit": "lerna exec yarn test-unit --ignore \"'{@packages/{driver,root,static,web-config,net-stubbing,rewriter,ui-components},@cypress/{webpack-dev-server,eslint-plugin-dev}}'\"",
"pretest-watch": "yarn ensure-deps",
"test-watch": "lerna exec yarn test-watch --ignore \"'@packages/{driver,root,static,web-config}'\"",
"type-check": "node scripts/type_check",
"type-check": "yarn lerna exec yarn type-check --scope @tooling/system-tests && node scripts/type_check",
"verify:mocha:results": "node ./scripts/verify_mocha_results",
"watch": "yarn gulp dev:watch",
"prepare": "husky install"
@@ -75,6 +75,7 @@
"nvm": "0.0.4"
},
"devDependencies": {
"@aws-sdk/credential-providers": "3.53.0",
"@cypress/commit-message-install": "3.1.3",
"@cypress/env-or-json-file": "2.0.0",
"@cypress/github-commit-status-check": "1.5.0",
@@ -1,3 +1,5 @@
const stripAnsi = require('strip-ansi')
const { assertLogLength } = require('../../support/utils')
const { Promise } = Cypress
@@ -130,6 +132,10 @@ describe('src/cy/commands/fixtures', () => {
expect(err.message).to.include('A fixture file could not be found')
expect(err.message).to.include('cypress/fixtures/err')
// ensure ansi color codes are not embedded in the error msg
// https://github.com/cypress-io/cypress/issues/20208
expect(err.message).to.eq(stripAnsi(err.message))
done()
})
File diff suppressed because it is too large Load Diff
@@ -243,7 +243,7 @@ describe('per-test config', () => {
})
})
describe('in mulitple nested suites', {
describe('in multiple nested suites', {
foo: false,
}, () => {
describe('config in suite', {
+1 -1
View File
@@ -81,7 +81,7 @@
"text-mask-addons": "3.8.0",
"underscore.string": "3.3.5",
"unfetch": "4.1.0",
"url-parse": "1.5.6",
"url-parse": "1.5.8",
"vanilla-text-mask": "5.1.1",
"vite": "2.5.0",
"webpack": "^4.44.2",
+1 -1
View File
@@ -251,7 +251,7 @@ class $Cypress {
// and those leak out into the stdout formatting.
const errMsg = _.isString(errResult)
? errResult
: `Expected ${format(errResult.key)} to be ${errResult.type}.\n\nInstead the value was: ${stringify(errResult.value)}\``
: `Expected ${format(errResult.key)} to be ${errResult.type}.\n\nInstead the value was: ${stringify(errResult.value)}`
throw new this.state('specWindow').Error(errMsg)
})
@@ -262,6 +262,17 @@ const SetInjectionLevel: ResponseMiddleware = function () {
this.res.wantsInjection = getInjectionLevel()
}
if (this.res.wantsInjection) {
// Chrome plans to make document.domain immutable in Chrome 106, with the default value
// of the Origin-Agent-Cluster header becoming 'true'. We explicitly disable this header
// so that we can continue to support tests that visit multiple subdomains in a single spec.
// https://github.com/cypress-io/cypress/issues/20147
//
// We set the header here only for proxied requests that have scripts injected that set the domain.
// Other proxied requests are ignored.
this.res.setHeader('Origin-Agent-Cluster', '?0')
}
this.res.wantsSecurityRemoved = this.config.modifyObstructiveCode && isReqMatchOriginPolicy && (
(this.res.wantsInjection === 'full')
|| resContentTypeIsJavaScript(this.incomingRes)
@@ -127,4 +127,66 @@ describe('http/response-middleware', function () {
}
}
})
describe('SetInjectionLevel', function () {
const { SetInjectionLevel } = ResponseMiddleware
let ctx
beforeEach(function () {
ctx = {
req: {
proxiedUrl: 'http://proxy.com',
cookies: {
'__cypress.initial': true,
},
headers: {
accept: ['text/html', 'application/xhtml+xml'],
},
},
res: {
setHeader: sinon.stub(),
},
getRemoteState: () => {
return {
strategy: 'http',
props: {
domain: 'proxy',
port: '80',
tld: 'com',
},
}
},
getRenderedHTMLOrigins: () => {
return {}
},
}
})
it('does not set Origin-Agent-Cluster header to false when injection is not expected', function () {
ctx.incomingRes = {
headers: {
'content-type': 'foo/bar',
},
}
return testMiddleware([SetInjectionLevel], ctx)
.then(() => {
expect(ctx.res.setHeader).not.to.be.calledWith('Origin-Agent-Cluster', '?0')
})
})
it('sets Origin-Agent-Cluster header to false when injection is expected', function () {
ctx.incomingRes = {
headers: {
'content-type': 'text/html',
},
}
return testMiddleware([SetInjectionLevel], ctx)
.then(() => {
expect(ctx.res.setHeader).to.be.calledWith('Origin-Agent-Cluster', '?0')
})
})
})
})
@@ -31,6 +31,12 @@ export const iframesController = {
extraOptions,
})
// Chrome plans to make document.domain immutable in Chrome 106, with the default value
// of the Origin-Agent-Cluster header becoming 'true'. We explicitly disable this header
// so that we can continue to support tests that visit multiple subdomains in a single spec.
// https://github.com/cypress-io/cypress/issues/20147
res.setHeader('Origin-Agent-Cluster', '?0')
files.handleIframe(req, res, config, getRemoteState, extraOptions)
},
@@ -33,6 +33,12 @@ export const serveRunner = (runnerPkg: RunnerPkg, config: Cfg, res: Response) =>
const runnerPath = process.env.CYPRESS_INTERNAL_RUNNER_PATH || getPathToIndex(runnerPkg)
// Chrome plans to make document.domain immutable in Chrome 106, with the default value
// of the Origin-Agent-Cluster header becoming 'true'. We explicitly disable this header
// so that we can continue to support tests that visit multiple subdomains in a single spec.
// https://github.com/cypress-io/cypress/issues/20147
res.setHeader('Origin-Agent-Cluster', '?0')
return res.render(runnerPath, {
base64Config,
projectName: config.projectName,
+10 -1
View File
@@ -4,6 +4,8 @@ const debug = require('debug')('cypress:server:fixture')
const coffee = require('coffeescript')
const Promise = require('bluebird')
const jsonlint = require('jsonlint')
const stripAnsi = require('strip-ansi')
const errors = require('./errors')
const { fs } = require('./util/fs')
const glob = require('./util/glob')
@@ -60,7 +62,14 @@ module.exports = {
if (matches.length === 0) {
const relativePath = path.relative('.', p)
errors.throwErr('FIXTURE_NOT_FOUND', relativePath, extensions)
// TODO: there's no reason this error should be in
// the @packages/error list, it should be written in
// the driver since this error can only occur within
// driver commands and not outside of the test runner
const err = errors.get('FIXTURE_NOT_FOUND', relativePath, extensions)
err.message = stripAnsi(err.message)
throw err
}
debug('fixture matches found, using the first', matches)
+2 -1
View File
@@ -53,6 +53,7 @@
"data-uri-to-buffer": "2.0.1",
"dayjs": "^1.9.3",
"debug": "^4.3.2",
"dependency-tree": "8.1.2",
"duplexify": "4.1.1",
"electron-context-menu": "3.1.1",
"errorhandler": "1.5.1",
@@ -119,7 +120,7 @@
"ts-node": "^10.2.1",
"tslib": "2.3.1",
"underscore.string": "3.3.5",
"url-parse": "1.5.6",
"url-parse": "1.5.8",
"uuid": "8.3.2",
"which": "2.0.2",
"widest-line": "3.1.0"
+32
View File
@@ -0,0 +1,32 @@
require('../spec_helper')
const { iframesController } = require(`${root}/lib/controllers/iframes`)
const files = require(`${root}/lib/controllers/files`)
describe('controllers/iframes', () => {
describe('e2e', () => {
it('sets Origin-Agent-Cluster response header to false', () => {
sinon.stub(files, 'handleIframe')
const mockReq = {}
const mockRes = {
setHeader: sinon.stub(),
}
const controllerOptions = {
getSpec: sinon.stub(),
getRemoteState: sinon.stub(),
config: {},
}
iframesController.e2e(controllerOptions, mockReq, mockRes)
expect(mockRes.setHeader).to.have.been.calledWith('Origin-Agent-Cluster', '?0')
expect(files.handleIframe).to.have.been.calledWith(
mockReq, mockRes, controllerOptions.config, controllerOptions.getRemoteState, sinon.match({
specFilter: undefined, specType: 'integration',
}),
)
})
})
})
+19
View File
@@ -0,0 +1,19 @@
require('../spec_helper')
const { serveRunner } = require(`${root}/lib/controllers/runner`)
describe('controllers/runner', () => {
describe('serveRunner', () => {
it('sets Origin-Agent-Cluster response header to false', () => {
const mockRes = {
setHeader: sinon.stub(),
render: sinon.stub(),
}
serveRunner('runner', {}, mockRes)
expect(mockRes.setHeader).to.have.been.calledWith('Origin-Agent-Cluster', '?0')
expect(mockRes.render).to.have.been.called
})
})
})
+1
View File
@@ -114,6 +114,7 @@ export async function buildCypressApp (options: BuildCypressAppOpts) {
meta.distDir('**', 'esprima', 'test'),
meta.distDir('**', 'bmp-js', 'test'),
meta.distDir('**', 'exif-parser', 'test'),
meta.distDir('**', 'app-module-path', 'test'),
], { force: true })
console.log('Deleted excess directories')
+11 -4
View File
@@ -113,6 +113,8 @@ export const prompts = {
export const moveBinaries = async (args = []) => {
debug('moveBinaries with args %o', args)
const options = arg({
'--s3bucket': String,
'--s3folder': String,
'--commit': String,
'--version': String,
// optional, if passed, only the binary for that platform will be moved
@@ -136,8 +138,13 @@ export const moveBinaries = async (args = []) => {
version: options['--version'],
}
const aws = uploadUtils.getS3Credentials()
const s3 = s3helpers.makeS3(aws)
const credentials = await uploadUtils.getS3Credentials()
const aws = {
'bucket': options['--s3bucket'] || uploadUtils.S3Configuration.bucket,
'folder': options['--s3folder'] || uploadUtils.S3Configuration.releaseFolder,
}
const s3 = s3helpers.makeS3(credentials)
// found s3 paths with last build for same commit for all platforms
const lastBuilds: Desktop[] = []
@@ -164,12 +171,12 @@ export const moveBinaries = async (args = []) => {
platformArch,
})
console.log('finding binary for %s in %s', platformArch, uploadDir)
console.log('finding binary in %s for %s in %s', aws.bucket, platformArch, uploadDir)
const list: string[] = await s3helpers.listS3Objects(uploadDir, aws.bucket, s3)
if (debug.enabled) {
console.log('all found subfolders')
console.log('all found sub-folders')
console.log(list.join('\n'))
}
+11 -5
View File
@@ -13,12 +13,18 @@ export const hasOnlyStringValues = (o) => {
*/
export const s3helpers = {
makeS3 (aws) {
la(is.unemptyString(aws.key), 'missing aws key')
la(is.unemptyString(aws.secret), 'missing aws secret')
la(is.unemptyString(aws.accessKeyId), 'missing aws accessKeyId')
la(is.unemptyString(aws.secretAccessKey), 'missing aws secretAccessKey')
if (!process.env.CIRCLECI) {
// sso is not required for CirceCI
la(is.unemptyString(aws.sessionToken), 'missing aws sessionToken')
}
return new S3({
accessKeyId: aws.key,
secretAccessKey: aws.secret,
accessKeyId: aws.accessKeyId,
secretAccessKey: aws.secretAccessKey,
sessionToken: aws.sessionToken,
})
},
@@ -40,7 +46,7 @@ export const s3helpers = {
debug('s3 data for %s', zipFile)
debug(data)
resolve()
resolve(null)
})
})
},
+7 -16
View File
@@ -9,16 +9,7 @@ const upload = require('./upload')
const uploadUtils = require('./util/upload')
const { s3helpers } = require('./s3-api')
const uploadTypes = {
binary: {
uploadFolder: 'binary',
uploadFileName: 'cypress.zip',
},
'npm-package': {
uploadFolder: 'npm',
uploadFileName: 'cypress.tgz',
},
}
const uploadTypes = uploadUtils.S3Configuration.betaUploadTypes
const getCDN = function (uploadPath) {
return [uploadUtils.getUploadUrl(), uploadPath].join('/')
@@ -32,16 +23,16 @@ const getUploadDirForPlatform = function (options) {
// the artifact will be uploaded for every platform and uploaded into under a unique folder
// https://cdn.cypress.io/beta/(binary|npm)/<version>/<platform>/<some unique version info>/cypress.zip
// For binary:
// beta/binary/9.4.2/win32-x64/circle-develop-219138ca4e952edc4af831f2ae16ce659ebdb50b/cypress.zip
// beta/binary/9.4.2/win32-x64/develop-219138ca4e952edc4af831f2ae16ce659ebdb50b/cypress.zip
// For NPM package:
// beta/npm/9.4.2/circle-develop-219138ca4e952edc4af831f2ae16ce659ebdb50b/cypress.tgz
// beta/npm/9.4.2/develop-219138ca4e952edc4af831f2ae16ce659ebdb50b/cypress.tgz
const getUploadPath = function (options) {
const { hash, uploadFileName } = options
return [getUploadDirForPlatform(options), hash, uploadFileName].join('/')
}
const setChecksum = (filename, key) => {
const setChecksum = async (filename, key) => {
console.log('setting checksum for file %s', filename)
console.log('on s3 object %s', key)
@@ -56,7 +47,7 @@ const setChecksum = (filename, key) => {
console.log('SHA256 checksum %s', checksum)
console.log('size', size)
const aws = uploadUtils.getS3Credentials()
const aws = await uploadUtils.getS3Credentials()
const s3 = s3helpers.makeS3(aws)
// S3 object metadata can only have string values
const metadata = {
@@ -66,7 +57,7 @@ const setChecksum = (filename, key) => {
// by default s3.copyObject does not preserve ACL when copying
// thus we need to reset it for our public files
return s3helpers.setUserMetadata(aws.bucket, key, metadata,
return s3helpers.setUserMetadata(uploadUtils.S3Configuration.bucket, key, metadata,
'application/zip', 'public-read', s3)
}
@@ -128,7 +119,7 @@ const uploadArtifactToS3 = function (args = []) {
.then(uploadUtils.saveUrl(`${options.type}-url.json`))
.catch((e) => {
console.error('There was an issue uploading the artifact.')
console.error(e)
throw e
})
}
+46 -49
View File
@@ -15,17 +15,13 @@ fs = Promise.promisifyAll(fs)
// TODO: refactor this
// system expects desktop application to be inside a file
// with this name
const zipName = 'cypress.zip'
const zipName = uploadUtils.S3Configuration.binaryZipName
module.exports = {
zipName,
getPublisher () {
return uploadUtils.getPublisher(this.getAwsObj)
},
getAwsObj () {
return uploadUtils.getS3Credentials()
async getPublisher () {
return uploadUtils.getPublisher()
},
// returns desktop folder for a given folder without platform
@@ -43,7 +39,7 @@ module.exports = {
let { folder, version, platformArch, name } = options
if (!folder) {
folder = this.getAwsObj().folder
folder = uploadUtils.S3Configuration.releaseFolder
}
la(check.unemptyString(folder), 'missing folder', options)
@@ -104,34 +100,34 @@ module.exports = {
},
s3Manifest (version) {
const publisher = this.getPublisher()
return this.getPublisher()
.then((publisher) => {
const { releaseFolder } = uploadUtils.S3Configuration
const aws = this.getAwsObj()
const headers = {
'Cache-Control': 'no-cache',
}
let manifest = null
const headers = {}
return new Promise((resolve, reject) => {
return this.createRemoteManifest(releaseFolder, version)
.then((src) => {
manifest = src
headers['Cache-Control'] = 'no-cache'
return gulp.src(src)
.pipe(rename((p) => {
p.dirname = `${releaseFolder}/${p.dirname}`
let manifest = null
return new Promise((resolve, reject) => {
return this.createRemoteManifest(aws.folder, version)
.then((src) => {
manifest = src
return gulp.src(src)
.pipe(rename((p) => {
p.dirname = `${aws.folder}/${p.dirname}`
return p
})).pipe(gulpDebug())
.pipe(publisher.publish(headers))
.pipe(awspublish.reporter())
.on('error', reject)
.on('end', resolve)
return p
})).pipe(gulpDebug())
.pipe(publisher.publish(headers))
.pipe(awspublish.reporter())
.on('error', reject)
.on('end', resolve)
})
}).finally(() => {
return fs.removeAsync(manifest)
})
}).finally(() => {
return fs.removeAsync(manifest)
})
},
@@ -144,26 +140,27 @@ module.exports = {
la(check.extension(path.extname(uploadPath))(file),
'invalid file to upload extension', file)
return new Promise((resolve, reject) => {
const publisher = this.getPublisher()
return this.getPublisher()
.then((publisher) => {
const headers = {
'Cache-Control': 'no-cache',
}
const headers = {}
return new Promise((resolve, reject) => {
return gulp.src(file)
.pipe(rename((p) => {
// rename to standard filename for upload
p.basename = path.basename(uploadPath, path.extname(uploadPath))
p.dirname = path.dirname(uploadPath)
headers['Cache-Control'] = 'no-cache'
return gulp.src(file)
.pipe(rename((p) => {
// rename to standard filename for upload
p.basename = path.basename(uploadPath, path.extname(uploadPath))
p.dirname = path.dirname(uploadPath)
return p
}))
.pipe(gulpDebug())
.pipe(publisher.publish(headers))
.pipe(awspublish.reporter())
.on('error', reject)
.on('end', resolve)
return p
}))
.pipe(gulpDebug())
.pipe(publisher.publish(headers))
.pipe(awspublish.reporter())
.on('error', reject)
.on('end', resolve)
})
})
},
}
+33 -29
View File
@@ -1,5 +1,4 @@
const _ = require('lodash')
const path = require('path')
const awspublish = require('gulp-awspublish')
const human = require('human-interval')
const la = require('lazy-ass')
@@ -7,7 +6,8 @@ const check = require('check-more-types')
const fse = require('fs-extra')
const os = require('os')
const Promise = require('bluebird')
const { configFromEnvOrJsonFile, filenameToShellVariable } = require('@cypress/env-or-json-file')
const { fromSSO, fromEnv } = require('@aws-sdk/credential-providers')
const konfig = require('../get-config')()
const { purgeCloudflareCache } = require('./purge-cloudflare-cache')
@@ -25,47 +25,50 @@ const formHashFromEnvironment = function () {
} = process
if (env.CIRCLECI) {
return `circle-${env.CIRCLE_BRANCH}-${env.CIRCLE_SHA1}`
return `${env.CIRCLE_BRANCH}-${env.CIRCLE_SHA1}`
}
throw new Error('Do not know how to form unique build hash on this CI')
}
const getS3Credentials = function () {
const key = path.join('scripts', 'support', 'aws-credentials.json')
const config = configFromEnvOrJsonFile(key)
if (!config) {
console.error('⛔️ Cannot find AWS credentials')
console.error('Using @cypress/env-or-json-file module')
console.error('and filename', key)
console.error('which is environment variable', filenameToShellVariable(key))
console.error('available environment variable keys')
console.error(Object.keys(process.env))
throw new Error('AWS config not found')
}
la(check.unemptyString(config.bucket), 'missing AWS config bucket')
la(check.unemptyString(config.folder), 'missing AWS config folder')
la(check.unemptyString(config.key), 'missing AWS key')
la(check.unemptyString(config.secret), 'missing AWS secret key')
return config
const S3Configuration = {
bucket: 'cdn.cypress.io',
releaseFolder: 'desktop',
binaryZipName: 'cypress.zip',
betaUploadTypes: {
binary: {
uploadFolder: 'binary',
uploadFileName: 'cypress.zip',
},
'npm-package': {
uploadFolder: 'npm',
uploadFileName: 'cypress.tgz',
},
},
}
const getPublisher = function (getAwsObj = getS3Credentials) {
const aws = getAwsObj()
const getS3Credentials = async function () {
// sso is not required for CirceCI
if (process.env.CIRCLECI) {
return fromEnv()()
}
return fromSSO({ profile: process.env.AWS_PROFILE || 'production' })()
}
const getPublisher = async function () {
const aws = await getS3Credentials()
// console.log("aws.bucket", aws.bucket)
return awspublish.create({
httpOptions: {
timeout: human('10 minutes'),
},
params: {
Bucket: aws.bucket,
Bucket: S3Configuration.bucket,
},
accessKeyId: aws.key,
secretAccessKey: aws.secret,
accessKeyId: aws.accessKeyId,
secretAccessKey: aws.secretAccessKey,
sessionToken: aws.sessionToken,
})
}
@@ -156,6 +159,7 @@ const saveUrl = (filename) => {
}
module.exports = {
S3Configuration,
getS3Credentials,
getPublisher,
purgeDesktopAppFromCache,
+57
View File
@@ -0,0 +1,57 @@
#!/bin/bash
set -e # exit on error
PLATFORM=$(node -p 'process.platform')
if [[ $PLATFORM != "linux" && $PLATFORM != "darwin" ]]; then
echo "Currently, create-stable-npm-package is only supported on Linux and MacOS."
echo "See https://github.com/cypress-io/cypress/pull/20296#discussion_r817115583"
exit 1
fi
if [[ ! $1 ]]; then
echo "publish-npm-package takes the .tgz URL as the first argument"
exit 1
fi
if [[ $1 != *"linux-x64"* ]]; then
echo "Only publish the 'linux-x64' .tgz. A non-linux-x64 .tgz was passed."
exit 1
fi
set -x # log commands
TGZ_URL=$1
PREPROD_TGZ_PATH=/tmp/cypress-preprod.tgz
UNPACKED_PATH=/tmp/unpacked-cypress
PROD_TGZ_PATH=/tmp/cypress-prod.tgz
echo "Downloading tgz from TGZ_URL=$TGZ_URL"
curl $TGZ_URL -o $PREPROD_TGZ_PATH
echo "Untarring PREPROD_TGZ_PATH=$PREPROD_TGZ_PATH"
rm -rf $UNPACKED_PATH || true
mkdir $UNPACKED_PATH
tar -xzvf $PREPROD_TGZ_PATH -C $UNPACKED_PATH
export PKG_JSON_PATH=$UNPACKED_PATH/package/package.json
echo "Patching stable: true to package.json"
node <<EOF
const fs = require('fs')
const pkg = require("$PKG_JSON_PATH")
pkg.buildInfo.stable = true
const json = JSON.stringify(pkg, null, 2)
fs.writeFileSync("$PKG_JSON_PATH", json)
EOF
echo "New package.json:"
cat $UNPACKED_PATH/package/package.json
echo "Tarring..."
cd $UNPACKED_PATH
tar -czvf $PROD_TGZ_PATH *
set +x
echo "Prod NPM package built at:"
echo " $PROD_TGZ_PATH"
+27
View File
@@ -0,0 +1,27 @@
const minimist = require('minimist')
const shelljs = require('shelljs')
const args = minimist(process.argv.slice(2))
if (!/^[a-z0-9]{40}$/.test(args.sha)) {
throw new Error('A valid (40 character) commit SHA must be passed in `--sha`.')
}
if (!/^\d+\.\d+\.\d+$/.test(args.version)) {
throw new Error('A valid semantic version (X.Y.Z) must be passed in `--version`.')
}
// eslint-disable-next-line no-console
const log = (...args) => console.log('🏗', ...args)
const exec = args['dry-run'] ?
(...args) => log('Dry run, not executing:', ...args)
: (...args) => shelljs.exec(...args)
log('Running `move-binaries`...')
exec(`node ./scripts/binary.js move-binaries --sha ${args.sha} --version ${args.version}`)
const prereleaseNpmUrl = `https://cdn.cypress.io/beta/npm/${args.version}/linux-x64/develop-${args.sha}/cypress.tgz`
log('Running `create-stable-npm-package`...')
exec(`./scripts/create-stable-npm-package.sh ${prereleaseNpmUrl}`)
@@ -0,0 +1,10 @@
const shelljs = require('shelljs')
const snapshot = require('snap-shot-it')
describe('prepare-release-artifacts', () => {
it('runs expected commands', () => {
const stdout = shelljs.exec('yarn prepare-release-artifacts --dry-run --sha 57d0a85108fad6f77b39db88b8a7d8a3bfdb51a2 --version 1.2.3')
snapshot(stdout)
})
})
@@ -182,13 +182,13 @@ exports['testConfigOverrides / fails when passing invalid config values - [chrom
1) inline test config override throws error:
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`""\`\`
Instead the value was: \`""\`
[stack trace lines]
2) inline test config override throws error when executed within cy cmd:
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`"null"\`\`
Instead the value was: \`"null"\`
[stack trace lines]
3) context config overrides throws error
@@ -197,7 +197,7 @@ Instead the value was: \`"null"\`\`
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
Error
@@ -209,7 +209,7 @@ https://on.cypress.io/config
Expected \`defaultCommandTimeout\` to be a number.
Instead the value was: \`"500"\`\`
Instead the value was: \`"500"\`
https://on.cypress.io/config
Error
@@ -221,7 +221,7 @@ https://on.cypress.io/config
Expected \`defaultCommandTimeout\` to be a number.
Instead the value was: \`"500"\`\`
Instead the value was: \`"500"\`
https://on.cypress.io/config
Error
@@ -234,7 +234,7 @@ https://on.cypress.io/config
Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`"not_an_http_url"\`\`
Instead the value was: \`"not_an_http_url"\`
https://on.cypress.io/config
Error
@@ -246,7 +246,7 @@ https://on.cypress.io/config
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
@@ -260,7 +260,7 @@ Because this error occurred during a \`before all\` hook we are skipping the rem
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
Error
@@ -350,14 +350,14 @@ exports['testConfigOverrides / fails when passing invalid config values with bef
inline test config override throws error:
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`""\`\`
Instead the value was: \`""\`
[stack trace lines]
2) runs all tests
inline test config override throws error when executed within cy cmd:
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`"null"\`\`
Instead the value was: \`"null"\`
[stack trace lines]
3) runs all tests
@@ -367,7 +367,7 @@ Instead the value was: \`"null"\`\`
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
Error
@@ -380,7 +380,7 @@ https://on.cypress.io/config
Expected \`defaultCommandTimeout\` to be a number.
Instead the value was: \`"500"\`\`
Instead the value was: \`"500"\`
https://on.cypress.io/config
Error
@@ -393,7 +393,7 @@ https://on.cypress.io/config
Expected \`defaultCommandTimeout\` to be a number.
Instead the value was: \`"500"\`\`
Instead the value was: \`"500"\`
https://on.cypress.io/config
Error
@@ -407,7 +407,7 @@ https://on.cypress.io/config
Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`"not_an_http_url"\`\`
Instead the value was: \`"not_an_http_url"\`
https://on.cypress.io/config
Error
@@ -420,7 +420,7 @@ https://on.cypress.io/config
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
@@ -435,7 +435,7 @@ Because this error occurred during a \`before all\` hook we are skipping the rem
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
Error
@@ -517,7 +517,7 @@ exports['testConfigOverrides / correctly fails when invalid config values for it
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
Error
@@ -601,13 +601,13 @@ exports['testConfigOverrides / fails when passing invalid config values - [firef
1) inline test config override throws error:
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`""\`\`
Instead the value was: \`""\`
[stack trace lines]
2) inline test config override throws error when executed within cy cmd:
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`"null"\`\`
Instead the value was: \`"null"\`
[stack trace lines]
3) context config overrides throws error
@@ -616,7 +616,7 @@ Instead the value was: \`"null"\`\`
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
[stack trace lines]
@@ -627,7 +627,7 @@ https://on.cypress.io/config
Expected \`defaultCommandTimeout\` to be a number.
Instead the value was: \`"500"\`\`
Instead the value was: \`"500"\`
https://on.cypress.io/config
[stack trace lines]
@@ -638,7 +638,7 @@ https://on.cypress.io/config
Expected \`defaultCommandTimeout\` to be a number.
Instead the value was: \`"500"\`\`
Instead the value was: \`"500"\`
https://on.cypress.io/config
[stack trace lines]
@@ -650,7 +650,7 @@ https://on.cypress.io/config
Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`"not_an_http_url"\`\`
Instead the value was: \`"not_an_http_url"\`
https://on.cypress.io/config
[stack trace lines]
@@ -661,7 +661,7 @@ https://on.cypress.io/config
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
@@ -674,7 +674,7 @@ Because this error occurred during a \`before all\` hook we are skipping the rem
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
[stack trace lines]
@@ -763,14 +763,14 @@ exports['testConfigOverrides / fails when passing invalid config values with bef
inline test config override throws error:
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`""\`\`
Instead the value was: \`""\`
[stack trace lines]
2) runs all tests
inline test config override throws error when executed within cy cmd:
Error: Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`"null"\`\`
Instead the value was: \`"null"\`
[stack trace lines]
3) runs all tests
@@ -780,7 +780,7 @@ Instead the value was: \`"null"\`\`
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
[stack trace lines]
@@ -792,7 +792,7 @@ https://on.cypress.io/config
Expected \`defaultCommandTimeout\` to be a number.
Instead the value was: \`"500"\`\`
Instead the value was: \`"500"\`
https://on.cypress.io/config
[stack trace lines]
@@ -804,7 +804,7 @@ https://on.cypress.io/config
Expected \`defaultCommandTimeout\` to be a number.
Instead the value was: \`"500"\`\`
Instead the value was: \`"500"\`
https://on.cypress.io/config
[stack trace lines]
@@ -817,7 +817,7 @@ https://on.cypress.io/config
Expected \`baseUrl\` to be a fully qualified URL (starting with \`http://\` or \`https://\`).
Instead the value was: \`"not_an_http_url"\`\`
Instead the value was: \`"not_an_http_url"\`
https://on.cypress.io/config
[stack trace lines]
@@ -829,7 +829,7 @@ https://on.cypress.io/config
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
@@ -843,7 +843,7 @@ Because this error occurred during a \`before all\` hook we are skipping the rem
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
[stack trace lines]
@@ -924,7 +924,7 @@ exports['testConfigOverrides / correctly fails when invalid config values for it
Expected \`retries\` to be a positive number or null or an object with keys "openMode" and "runMode" with values of numbers or nulls.
Instead the value was: \`"1"\`\`
Instead the value was: \`"1"\`
https://on.cypress.io/config
[stack trace lines]
+8
View File
@@ -0,0 +1,8 @@
import { expect as _expect } from 'chai'
import _sinon from 'sinon'
declare global {
// these are made global in `spec_helper`
const expect: typeof _expect
const sinon: typeof _sinon
}
+2 -2
View File
@@ -351,8 +351,8 @@ export function removeProject (name) {
// returns the path to project fixture
// in the cyTmpDir
export function project (...args) {
return this.projectPath.apply(this, args)
export function project (name) {
return projectPath(name)
}
export function projectPath (name) {
-2
View File
@@ -2,8 +2,6 @@ import systemTests from './system-tests'
import dayjs from 'dayjs'
import _ from 'lodash'
const expect = global.expect as unknown as Chai.ExpectStatic
const STATIC_DATE = '2018-02-01T20:14:19.323Z'
const expectDurationWithin = function (obj, duration, low, high, reset) {
+1 -1
View File
@@ -166,7 +166,7 @@ const getResponse = function (responseSchema) {
}
const sendResponse = function (req, res, responseBody) {
return new Promise((resolve) => {
return new Promise<void>((resolve) => {
const _writeRaw = res._writeRaw
res._writeRaw = function () {
+7 -5
View File
@@ -1,9 +1,10 @@
const snapshot = require('snap-shot-it')
import { SpawnOptions } from 'child_process'
import type { SpawnOptions } from 'child_process'
import stream from 'stream'
import { expect } from './spec_helper'
import { dockerSpawner } from './docker'
import Express from 'express'
const isCi = require('is-ci')
@@ -15,7 +16,6 @@ const path = require('path')
const http = require('http')
const human = require('human-interval')
const morgan = require('morgan')
const express = require('express')
const Bluebird = require('bluebird')
const debug = require('debug')('cypress:system-tests')
const httpsProxy = require('@packages/https-proxy')
@@ -32,7 +32,8 @@ require(`@packages/server/lib/project-base`)
type CypressConfig = { [key: string]: any }
type BrowserName = 'electron' | 'firefox' | 'chrome'
export type BrowserName = 'electron' | 'firefox' | 'chrome'
| '!electron' | '!chrome' | '!firefox'
type ExecResult = {
code: number
@@ -503,7 +504,7 @@ const startServer = function (obj) {
ensurePort(port)
const app = express()
const app = Express()
const srv = https ? httpsProxy.httpsServer(app) : new http.Server(app)
@@ -516,7 +517,7 @@ const startServer = function (obj) {
}
if (obj.static) {
app.use(express.static(path.join(__dirname, '../projects/e2e'), {}))
app.use(Express.static(path.join(__dirname, '../projects/e2e'), {}) as Express.RequestHandler)
}
return new Bluebird((resolve) => {
@@ -705,6 +706,7 @@ const systemTests = {
args = _.compact(args)
// avoid snapshot cwd issue - see /patches/snap-shot* for more information
// @ts-ignore
global.CACHED_CWD_FOR_SNAP_SHOT_IT = path.join(__dirname, '..')
return snapshot.apply(null, args)
+4 -1
View File
@@ -5,6 +5,7 @@
"private": true,
"main": "index.js",
"scripts": {
"type-check": "tsc --project .",
"projects:yarn:install": "node ./scripts/projects-yarn-install.js",
"test": "node ./scripts/run.js --glob-in-dir='{test,test-binary}'",
"test:ci": "node ./scripts/run.js"
@@ -28,6 +29,8 @@
"@packages/socket": "0.0.0-development",
"@packages/ts": "0.0.0-development",
"@storybook/testing-vue3": "0.0.1",
"@types/chai": "4.2.15",
"@types/mocha": "9.1.0",
"babel-loader": "8.1.0",
"bluebird": "3.7.2",
"body-parser": "1.19.0",
@@ -45,7 +48,7 @@
"dayjs": "^1.9.3",
"debug": "^4.3.2",
"dockerode": "3.3.1",
"execa": "1.0.0",
"execa": "4",
"express": "4.17.1",
"express-session": "1.16.1",
"express-useragent": "1.0.15",
@@ -1,5 +1,5 @@
const _ = require('lodash')
const execa = require('execa')
const { execa } = require('execa')
const util = require('util')
const si = require('systeminformation')
+19 -8
View File
@@ -2,6 +2,18 @@ import systemTests from '../lib/system-tests'
const beforeBrowserLaunchProject = 'plugin-before-browser-launch-deprecation'
const includesString = (s: string) => {
return (stdout: string) => {
expect(stdout).to.include(s)
}
}
const excludesString = (s: string) => {
return (stdout: string) => {
expect(stdout).to.not.include(s)
}
}
describe('deprecated before:browser:launch args', () => {
systemTests.setup()
@@ -28,8 +40,7 @@ describe('deprecated before:browser:launch args', () => {
project: beforeBrowserLaunchProject,
spec: 'app.cy.js',
snapshot: true,
stdoutInclude: 'Deprecation Warning:',
psInclude: ['--foo', '--bar'],
onStdout: includesString('Deprecation Warning:'),
})
systemTests.it('using non-deprecated API - no warning', {
@@ -46,8 +57,7 @@ describe('deprecated before:browser:launch args', () => {
project: beforeBrowserLaunchProject,
spec: 'app.cy.js',
snapshot: true,
stdoutExclude: 'Deprecation Warning:',
psInclude: ['--foo', '--bar'],
onStdout: excludesString('Deprecation Warning:'),
})
systemTests.it('concat return returns once', {
@@ -71,10 +81,12 @@ describe('deprecated before:browser:launch args', () => {
return exec({ originalTitle: `deprecated before:browser:launch args / concat return returns once per test run - [firefox,chromium]` })
},
stdoutInclude: 'Deprecation Warning:',
onStdout: includesString('Deprecation Warning:'),
})
systemTests.it('no mutate return', {
// TODO: fix/remove this test, it should be warning but is not
// https://github.com/cypress-io/cypress/issues/20436
systemTests.it.skip('no mutate return', {
// TODO: implement webPreferences.additionalArgs here
// once we decide if/what we're going to make the implemenation
// SUGGESTION: add this to Cypress.browser.args which will capture
@@ -88,8 +100,7 @@ describe('deprecated before:browser:launch args', () => {
project: beforeBrowserLaunchProject,
spec: 'app.cy.js',
snapshot: true,
stdoutInclude: 'Deprecation Warning:',
psInclude: '--foo',
onStdout: includesString('Deprecation Warning:'),
})
// TODO: these errors could be greatly improved by the code frame
-1
View File
@@ -25,7 +25,6 @@ describe('e2e firefox', function () {
config: {
video: false,
},
exit: false,
onRun: (exec) => {
return exec()
.then(() => {
+4 -4
View File
@@ -1,4 +1,4 @@
import systemTests from '../lib/system-tests'
import systemTests, { BrowserName } from '../lib/system-tests'
describe('e2e headless', function () {
systemTests.setup()
@@ -25,7 +25,7 @@ describe('e2e headless', function () {
systemTests.it('pass for browsers that do not need xvfb', {
...baseSpec,
browser: ['chrome', 'chrome-beta', 'firefox'],
browser: ['chrome', 'firefox'],
expectedExitCode: 0,
onRun (exec) {
return exec().then(({ stderr }) => {
@@ -60,10 +60,10 @@ describe('e2e headless', function () {
// "can not record video in headed mode" error
// this trick allows us to have 1 snapshot for electron
// and 1 for every other browser
;[
;([
'electron',
'!electron',
].map((b) => {
] as BrowserName[]).map((b) => {
systemTests.it(`tests in headed mode pass in ${b}`, {
spec: 'headless.cy.js',
config: {
+1 -1
View File
@@ -2,7 +2,7 @@ import bodyParser from 'body-parser'
import cookieParser from 'cookie-parser'
import systemTests from '../lib/system-tests'
let counts = null
let counts: Record<string, number> | null = null
const urlencodedParser = bodyParser.urlencoded({ extended: false })
const jsonParser = bodyParser.json()
+3 -1
View File
@@ -90,7 +90,9 @@ describe('e2e screenshots', () => {
fs.statAsync(screenshot4).get('size'),
fs.statAsync(screenshot5).get('size'),
fs.statAsync(screenshot6).get('size'),
fs.statAsync(screenshot7).get('size'),
// Ignore comparing 6 and 7 since they can sometimes be the same since we take the screenshot as close to the failure as possible and
// the test run error may not have displayed yet. Leaving this commented in case we want to change this behavior in the future
// fs.statAsync(screenshot7).get('size'),
fs.statAsync(screenshot8).get('size'),
fs.statAsync(screenshot9).get('size'),
])
@@ -1,6 +1,6 @@
import fs from 'fs-extra'
import path from 'path'
import systemTests, { expect } from '../lib/system-tests'
import systemTests, { expect, BrowserName } from '../lib/system-tests'
import Fixtures from '../lib/fixtures'
const e2ePath = Fixtures.projectPath('e2e')
@@ -35,7 +35,7 @@ describe('testConfigOverrides', () => {
// window.Error throws differently for firefox. break into
// browser permutations for snapshot comparisons
const permutations = [
const permutations: BrowserName[][] = [
['chrome', 'electron'],
['firefox'],
]
+23
View File
@@ -0,0 +1,23 @@
{
"extends": "../packages/ts/tsconfig.json",
"include": [
"test",
"lib",
],
"files": [
"../packages/ts/index.d.ts",
"./globals.d.ts"
],
"compilerOptions": {
"types": [
"mocha",
"node",
"chai"
],
"noEmit": true,
"lib": ["esnext"],
"skipLibCheck": true,
"noImplicitReturns": false,
"strictNullChecks": false
}
}
+969 -45
View File
File diff suppressed because it is too large Load Diff