From a693687134781286ea2d5c897dabe9e283b36552 Mon Sep 17 00:00:00 2001 From: Brian Mann Date: Wed, 28 Dec 2022 18:01:38 -0500 Subject: [PATCH 1/2] chore(electron): when in dev, build the arm64 electron binary when cpu arch supports it (#25274) * bump version of systeminformation used for m1 support * when in dev, build the arm64 electron binary when cpu arch supports it * fix debug statements --- packages/electron/lib/install.js | 109 +++++++++++++++++++++++-------- packages/electron/package.json | 3 +- packages/server/package.json | 2 +- system-tests/package.json | 2 +- yarn.lock | 8 +-- 5 files changed, 88 insertions(+), 36 deletions(-) diff --git a/packages/electron/lib/install.js b/packages/electron/lib/install.js index cd29d714554e..b67ac7601491 100644 --- a/packages/electron/lib/install.js +++ b/packages/electron/lib/install.js @@ -2,8 +2,11 @@ const _ = require('lodash') const os = require('os') const path = require('path') +const systeminformation = require('systeminformation') +const execa = require('execa') + const paths = require('./paths') -const log = require('debug')('cypress:electron') +const debug = require('debug')('cypress:electron') const fs = require('fs-extra') const crypto = require('crypto') const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses') @@ -27,9 +30,7 @@ module.exports = { return require('@packages/icons') }, - checkCurrentVersion () { - const pathToVersion = paths.getPathToVersion() - + checkCurrentVersion (pathToVersion) { // read in the version file return fs.readFile(pathToVersion, 'utf8') .then((str) => { @@ -55,6 +56,7 @@ module.exports = { }, checkIconVersion () { + // TODO: this seems wrong, it's hard coding the check only for OSX and not windows or linux (!?) const mainIconsPath = this.icons().getPathToIcon('cypress.icns') const cachedIconsPath = path.join(__dirname, '../dist/Cypress/Cypress.app/Contents/Resources/electron.icns') @@ -66,8 +68,30 @@ module.exports = { }) }, - checkExecExistence () { - return fs.stat(paths.getPathToExec()) + checkExecExistence (pathToExec) { + return fs.stat(pathToExec) + }, + + async checkBinaryArchCpuArch (pathToExec, platform, arch) { + if (platform === 'darwin' && arch === 'x64') { + return Promise.all([ + // get the current arch of the binary + execa('lipo', ['-archs', pathToExec]) + .then(({ stdout }) => { + return stdout + }), + + // get the real arch of the system + this.getRealArch(platform, arch), + ]) + .then(([binaryArch, cpuArch]) => { + debug('archs detected %o', { binaryArch, cpuArch }) + + if (binaryArch !== cpuArch) { + throw new Error(`built binary arch: '${binaryArch}' does not match system CPU arch: '${cpuArch}', binary needs rebuilding`) + } + }) + } }, move (src, dest) { @@ -94,44 +118,62 @@ module.exports = { }) }, + async getRealArch (platform, arch) { + if (platform === 'darwin' && arch === 'x64') { + // see this comment for explanation of x64 -> arm64 translation + // https://github.com/cypress-io/cypress/pull/25014/files#diff-85c4db7620ed2731baf5669a9c9993e61e620693a008199ca7c584e621b6a1fdR11 + return systeminformation.cpu() + .then(({ manufacturer }) => { + // if the cpu is apple then return arm64 as the arch + return manufacturer === 'Apple' ? 'arm64' : arch + }) + } + + return arch + }, + package (options = {}) { const pkgr = require('electron-packager') const icons = require('@packages/icons') const iconPath = icons.getPathToIcon('cypress') - log('package icon', iconPath) + debug('package icon', iconPath) const platform = os.platform() const arch = os.arch() - _.defaults(options, { - dist: paths.getPathToDist(), - dir: 'app', - out: 'tmp', - name: 'Cypress', - platform, - arch, - asar: false, - prune: true, - overwrite: true, - electronVersion, - icon: iconPath, + return this.getRealArch(platform, arch) + .then((arch) => { + _.defaults(options, { + dist: paths.getPathToDist(), + dir: 'app', + out: 'tmp', + name: 'Cypress', + platform, + arch, + asar: false, + prune: true, + overwrite: true, + electronVersion, + icon: iconPath, + }) + + debug('packager options %j', options) + + return pkgr(options) }) - - log('packager options %j', options) - - return pkgr(options) .then((appPaths) => { return appPaths[0] }) // Promise.resolve("tmp\\Cypress-win32-x64") .then((appPath) => { + const { dist } = options + // and now move the tmp into dist - console.log('moving created file from', appPath) - console.log('to', options.dist) + debug('moving created file %o', { from: appPath, to: dist }) - return this.move(appPath, options.dist) + return this.move(appPath, dist) }) .then(() => { return !['1', 'true'].includes(process.env.DISABLE_SNAPSHOT_REQUIRE) ? flipFuses( @@ -150,22 +192,31 @@ module.exports = { }, ensure () { + const arch = os.arch() + const platform = os.platform() + const pathToExec = paths.getPathToExec() + const pathToVersion = paths.getPathToVersion() + return Promise.all([ // check the version of electron and re-build if updated - this.checkCurrentVersion(), + this.checkCurrentVersion(pathToVersion), // check if the dist folder exist and re-build if not - this.checkExecExistence(), + this.checkExecExistence(pathToExec), // Compare the icon in dist with the one in the icons // package. If different, force the re-build. this.checkIconVersion(), ]) + .then(() => { + // check that the arch of the built binary matches our CPU + return this.checkBinaryArchCpuArch(pathToExec, platform, arch) + }) // if all is good, then return without packaging a new electron app }, check () { return this.ensure() - .catch(() => { + .catch((err) => { this.packageAndExit() }) }, diff --git a/packages/electron/package.json b/packages/electron/package.json index 00818e8e1a6d..6985d0cf1cd5 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -27,7 +27,8 @@ "electron-packager": "15.4.0", "execa": "4.1.0", "mocha": "3.5.3", - "rimraf": "3.0.2" + "rimraf": "3.0.2", + "systeminformation": "5.16.9" }, "files": [ "dist", diff --git a/packages/server/package.json b/packages/server/package.json index 68a02101b111..95faf99fb88e 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -111,7 +111,7 @@ "squirrelly": "7.9.2", "strip-ansi": "6.0.0", "syntax-error": "1.4.0", - "systeminformation": "5.6.4", + "systeminformation": "5.16.9", "term-size": "2.1.0", "through": "2.3.8", "tough-cookie": "4.0.0", diff --git a/system-tests/package.json b/system-tests/package.json index 123b605c39e8..c8247f34c752 100644 --- a/system-tests/package.json +++ b/system-tests/package.json @@ -80,7 +80,7 @@ "snap-shot-it": "7.9.3", "ssestream": "1.0.1", "supertest": "4.0.2", - "systeminformation": "5.6.4", + "systeminformation": "5.16.9", "temp-dir": "^2.0.0", "webpack": "^4.44.2", "ws": "5.2.3" diff --git a/yarn.lock b/yarn.lock index d3ed62811663..4cee602cf12e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32343,10 +32343,10 @@ syntax-error@1.4.0: dependencies: acorn-node "^1.2.0" -systeminformation@5.6.4: - version "5.6.4" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.6.4.tgz#5f32fcb05a5849e2a0e71b182c1f56ce32219310" - integrity sha512-b2tvW1R+qjNEoAGgh734EGLgqbDMghjsHRaWo36skAC6JM1tw1pitcGz/REt+qSIRSXbE4PKECojhaSrBRrEmw== +systeminformation@5.16.9: + version "5.16.9" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.16.9.tgz#8a5419c293afea142d93d98dde6850dceb4677b6" + integrity sha512-QTlv3GGSromPeLVW3pzM6uxU8RbkacW9e0+ZX23GAXaX+XE0UToSygAxCJDHSty6RB9lAFHCHg+FfiXFChi/+w== systemjs@^6.12.4: version "6.12.6" From 285e2b3b741def566db5620f8397053304588227 Mon Sep 17 00:00:00 2001 From: David Rowe <95636404+davidr-cy@users.noreply.github.com> Date: Thu, 29 Dec 2022 08:58:14 -0600 Subject: [PATCH 2/2] fix: updates to ci params sent to cloud for jenkins (#25036) * feat: updates to ci params sent to cloud for jenkins * fix: add more context around git commits via change trigger for jenkins pipelines * chore: test cleanup --- packages/server/lib/util/ci_provider.js | 34 +++- .../server/test/unit/util/ci_provider_spec.js | 163 ++++++++++++++---- 2 files changed, 155 insertions(+), 42 deletions(-) diff --git a/packages/server/lib/util/ci_provider.js b/packages/server/lib/util/ci_provider.js index 4942e2247730..b280e49ddb0d 100644 --- a/packages/server/lib/util/ci_provider.js +++ b/packages/server/lib/util/ci_provider.js @@ -132,6 +132,15 @@ const _detectProviderName = () => { }) } +// User provided environment variables are used to allow users to define their own +// values should the CI provider not have an existing or correct mapping from the list below. +const _userProvidedProviderCiParams = () => { + return extract([ + 'CYPRESS_PULL_REQUEST_ID', + 'CYPRESS_PULL_REQUEST_URL', + 'CYPRESS_CI_BUILD_URL', + ]) +} // TODO: don't forget about buildNumber! // look at the old commit that was removed to see how we did it const _providerCiParams = () => { @@ -299,11 +308,21 @@ const _providerCiParams = () => { 'SHORT_SHA', // https://cloud.google.com/cloud-build/docs/api/reference/rest/Shared.Types/Build ]), + /** + * References: + * https://ci.eclipse.org/webtools/env-vars.html/ + * https://www.jenkins.io/doc/book/pipeline/multibranch/#additional-environment-variables + */ jenkins: extract([ 'BUILD_ID', 'BUILD_URL', 'BUILD_NUMBER', 'ghprbPullId', + // Jenkins pipeline options change options + 'CHANGE_ID', + 'CHANGE_URL', + 'CHANGE_TARGET', + 'CHANGE_TITLE', ]), // https://semaphoreci.com/docs/available-environment-variables.html // some come from v1, some from v2 of semaphore @@ -539,10 +558,10 @@ const _providerCommitParams = () => { }, jenkins: { sha: env.GIT_COMMIT, - branch: env.GIT_BRANCH, - // message: ??? - // authorName: ??? - // authorEmail: ??? + branch: env.GIT_BRANCH || env.BRANCH_NAME || env.CHANGE_BRANCH, + // message: ??, + authorName: env.GIT_AUTHOR_NAME || env.CHANGE_AUTHOR_DISPLAY_NAME, + authorEmail: env.GIT_AUTHOR_EMAIL || env.CHANGE_AUTHOR_EMAIL, // remoteOrigin: ??? // defaultBranch: ??? }, @@ -617,7 +636,12 @@ const _get = (fn) => { } const ciParams = () => { - return _get(_providerCiParams) + const ciParams = { + ..._.chain(_userProvidedProviderCiParams()).thru(omitUndefined).defaultTo(null).value(), + ..._get(_providerCiParams), + } + + return Object.keys(ciParams).length > 0 ? ciParams : null } const commitParams = () => { diff --git a/packages/server/test/unit/util/ci_provider_spec.js b/packages/server/test/unit/util/ci_provider_spec.js index b98908aace65..139ffe2afab8 100644 --- a/packages/server/test/unit/util/ci_provider_spec.js +++ b/packages/server/test/unit/util/ci_provider_spec.js @@ -40,6 +40,23 @@ describe('lib/util/ci_provider', () => { return expectsCommitParams(null) }) + it('allows for user provided environment variables', () => { + resetEnv = mockedEnv({ + CYPRESS_PULL_REQUEST_ID: 'cypressPullRequestId', + CYPRESS_PULL_REQUEST_URL: 'cypressPullRequestUrl', + CYPRESS_CI_BUILD_URL: 'cypressCiBuildUrl', + }, { clear: true }) + + expectsName(null) + expectsCiParams({ + cypressPullRequestId: 'cypressPullRequestId', + cypressPullRequestUrl: 'cypressPullRequestUrl', + cypressCiBuildUrl: 'cypressCiBuildUrl', + }) + + return expectsCommitParams(null) + }) + it('does not extract from commit environment variables yet', () => { // see fallback environment variables // https://github.com/cypress-io/commit-info#fallback-environment-variables @@ -733,55 +750,127 @@ describe('lib/util/ci_provider', () => { return expectsName('googleCloud') }) - it('jenkins', () => { - resetEnv = mockedEnv({ - JENKINS_URL: 'true', + describe('jenkins', () => { + it('with legacy env', () => { + resetEnv = mockedEnv({ + JENKINS_URL: 'true', - BUILD_ID: 'buildId', - BUILD_URL: 'buildUrl', - BUILD_NUMBER: 'buildNumber', - ghprbPullId: 'gbprbPullId', + BUILD_ID: 'buildId', + BUILD_URL: 'buildUrl', + BUILD_NUMBER: 'buildNumber', + ghprbPullId: 'gbprbPullId', - GIT_COMMIT: 'gitCommit', - GIT_BRANCH: 'gitBranch', - }, { clear: true }) + GIT_COMMIT: 'gitCommit', + GIT_BRANCH: 'gitBranch', + GIT_AUTHOR_NAME: 'gitAuthorName', + GIT_AUTHOR_EMAIL: 'gitAuthorEmail', + }, { clear: true }) - expectsName('jenkins') - expectsCiParams({ - buildId: 'buildId', - buildUrl: 'buildUrl', - buildNumber: 'buildNumber', - ghprbPullId: 'gbprbPullId', - }) + expectsName('jenkins') + expectsCiParams({ + buildId: 'buildId', + buildUrl: 'buildUrl', + buildNumber: 'buildNumber', + ghprbPullId: 'gbprbPullId', + }) - expectsCommitParams({ - sha: 'gitCommit', - branch: 'gitBranch', - }) + expectsCommitParams({ + sha: 'gitCommit', + branch: 'gitBranch', + authorName: 'gitAuthorName', + authorEmail: 'gitAuthorEmail', + }) - resetEnv = mockedEnv({ - JENKINS_HOME: '/path/to/jenkins', - }, { clear: true }) + resetEnv = mockedEnv({ + JENKINS_HOME: '/path/to/jenkins', + }, { clear: true }) - expectsName('jenkins') + expectsName('jenkins') - resetEnv = mockedEnv({ - JENKINS_VERSION: '1.2.3', - }, { clear: true }) + resetEnv = mockedEnv({ + JENKINS_VERSION: '1.2.3', + }, { clear: true }) - expectsName('jenkins') + expectsName('jenkins') - resetEnv = mockedEnv({ - HUDSON_HOME: '/path/to/jenkins', - }, { clear: true }) + resetEnv = mockedEnv({ + HUDSON_HOME: '/path/to/jenkins', + }, { clear: true }) - expectsName('jenkins') + expectsName('jenkins') - resetEnv = mockedEnv({ - HUDSON_URL: 'true', - }, { clear: true }) + resetEnv = mockedEnv({ + HUDSON_URL: 'true', + }, { clear: true }) + + return expectsName('jenkins') + }) + + it('with change request params (PR Scenario)', () => { + resetEnv = mockedEnv({ + JENKINS_URL: 'true', + + BUILD_ID: 'buildId', + BUILD_NUMBER: 'buildNumber', + CHANGE_BRANCH: 'changeBranch', + CYPRESS_CI_BUILD_URL: 'cypressCiBuildUrl', + + GIT_COMMIT: 'gitCommit', + CHANGE_ID: 'changeId', + CHANGE_URL: 'changeUrl', + CHANGE_TITLE: 'changeTitle', + CHANGE_TARGET: 'changeTarget', + CHANGE_AUTHOR_DISPLAY_NAME: 'changeAuthorDisplayName', + CHANGE_AUTHOR_EMAIL: 'changeAuthorEmail', + }, { clear: true }) + + expectsName('jenkins') + expectsCiParams({ + buildId: 'buildId', + buildNumber: 'buildNumber', + cypressCiBuildUrl: 'cypressCiBuildUrl', + changeId: 'changeId', + changeTitle: 'changeTitle', + changeUrl: 'changeUrl', + changeTarget: 'changeTarget', + }) + + return expectsCommitParams({ + sha: 'gitCommit', + branch: 'changeBranch', + authorName: 'changeAuthorDisplayName', + authorEmail: 'changeAuthorEmail', + }) + }) - return expectsName('jenkins') + it('with userProvided', () => { + resetEnv = mockedEnv({ + JENKINS_URL: 'true', + + BUILD_ID: 'buildId', + BUILD_NUMBER: 'buildNumber', + CYPRESS_PULL_REQUEST_ID: 'cypressPullRequestId', + CYPRESS_PULL_REQUEST_URL: 'cypressPullRequestUrl', + CYPRESS_CI_BUILD_URL: 'cypressCiBuildUrl', + + GIT_COMMIT: 'gitCommit', + GIT_BRANCH: 'gitBranch', + }, { clear: true }) + + expectsName('jenkins') + expectsCiParams({ + buildId: 'buildId', + buildNumber: 'buildNumber', + cypressPullRequestId: 'cypressPullRequestId', + cypressPullRequestUrl: 'cypressPullRequestUrl', + cypressCiBuildUrl: 'cypressCiBuildUrl', + }) + + return expectsCommitParams({ + sha: 'gitCommit', + branch: 'gitBranch', + }) + }) }) it('semaphore', () => {