Skip to content

Commit 61f19c0

Browse files
authored
feat: build Cypress for linux-arm64 (#22252)
1 parent e18b0d5 commit 61f19c0

14 files changed

+195
-68
lines changed

__snapshots__/upload-spec.js

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ exports['test runner manifest'] = {
2323
"linux-x64": {
2424
"url": "https://cdn.cypress.io/desktop/3.3.0/linux-x64/cypress.zip"
2525
},
26+
"linux-arm64": {
27+
"url": "https://cdn.cypress.io/desktop/3.3.0/linux-arm64/cypress.zip"
28+
},
2629
"win32-x64": {
2730
"url": "https://cdn.cypress.io/desktop/3.3.0/win32-x64/cypress.zip"
2831
}

__snapshots__/util-upload-spec.js

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ exports['upload util isValidPlatformArch checks given strings second 1'] = {
1313
"given": "linux-x64",
1414
"expect": true
1515
},
16+
{
17+
"given": "linux-arm64",
18+
"expect": true
19+
},
1620
{
1721
"given": "win32-x64",
1822
"expect": true

circle.yml

+66-11
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,13 @@ defaults: &defaults
2222
COLUMNS: 100
2323
LINES: 24
2424

25-
# filters and requires for testing binary with Firefox
2625
mainBuildFilters: &mainBuildFilters
2726
filters:
2827
branches:
2928
only:
3029
- develop
3130
- 10.0-release
32-
- use-m1-runners
31+
- linux-arm64
3332

3433
# usually we don't build Mac app - it takes a long time
3534
# but sometimes we want to really confirm we are doing the right thing
@@ -38,14 +37,23 @@ macWorkflowFilters: &darwin-workflow-filters
3837
when:
3938
or:
4039
- equal: [ develop, << pipeline.git.branch >> ]
41-
- equal: [ use-m1-runners, << pipeline.git.branch >> ]
40+
- equal: [ linux-arm64, << pipeline.git.branch >> ]
41+
- matches:
42+
pattern: "-release$"
43+
value: << pipeline.git.branch >>
44+
45+
linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
46+
when:
47+
or:
48+
- equal: [ develop, << pipeline.git.branch >> ]
49+
- equal: [ linux-arm64, << pipeline.git.branch >> ]
4250
- matches:
4351
pattern: "-release$"
4452
value: << pipeline.git.branch >>
4553

4654
# uncomment & add to the branch conditions below to disable the main linux
4755
# flow if we don't want to test it for a certain branch
48-
linuxWorkflowExcludeFilters: &linux-workflow-exclude-filters
56+
linuxWorkflowExcludeFilters: &linux-x64-workflow-exclude-filters
4957
unless:
5058
or:
5159
- false
@@ -58,7 +66,7 @@ fullWindowsWorkflowFilters: &full-windows-workflow-filters
5866
branches:
5967
only:
6068
- develop
61-
- 'use-m1-runners'
69+
- 'linux-arm64'
6270
- '*-release'
6371
- 'win*'
6472

@@ -108,13 +116,20 @@ executors:
108116
environment:
109117
PLATFORM: darwin
110118

119+
linux-arm64:
120+
machine:
121+
image: ubuntu-2004:2022.04.1
122+
resource_class: arm.medium
123+
environment:
124+
PLATFORM: linux
125+
111126
commands:
112127
verify_should_persist_artifacts:
113128
steps:
114129
- run:
115130
name: Check current branch to persist artifacts
116131
command: |
117-
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "use-m1-runners" ]]; then
132+
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "linux-arm64" ]]; then
118133
echo "Not uploading artifacts or posting install comment for this branch."
119134
circleci-agent step halt
120135
fi
@@ -955,12 +970,24 @@ commands:
955970
# notarization on Mac can take a while
956971
no_output_timeout: "45m"
957972
command: |
973+
if [[ `node ./scripts/get-platform-key.js` == 'linux-arm64' ]]; then
974+
# these are missing on Circle and there is no way to pre-install them on Arm
975+
sudo apt-get update
976+
sudo apt-get install -y libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
977+
fi
958978
node --version
959979
yarn binary-build --version $(node ./scripts/get-next-version.js)
960980
- run:
961981
name: Zip the binary
962982
command: |
963-
[[ $PLATFORM == 'linux' ]] && apt-get update && apt-get install -y zip || [[ $PLATFORM != 'linux' ]]
983+
if [[ $PLATFORM == 'linux' ]]; then
984+
# on Arm, CI runs as non-root, on x64 CI runs as root but there is no sudo binary
985+
if [[ `whoami` == 'root' ]]; then
986+
apt-get update && apt-get install -y zip
987+
else
988+
sudo apt-get update && sudo apt-get install -y zip
989+
fi
990+
fi
964991
yarn binary-zip
965992
- store-npm-logs
966993
- persist_to_workspace:
@@ -2236,7 +2263,7 @@ jobs:
22362263
command: DEBUG=cypress:cli $(yarn bin cypress) run
22372264
- store-npm-logs
22382265

2239-
linux-workflow: &linux-workflow
2266+
linux-x64-workflow: &linux-x64-workflow
22402267
jobs:
22412268
- node_modules_install
22422269
- build:
@@ -2545,6 +2572,31 @@ linux-workflow: &linux-workflow
25452572
- create-build-artifacts
25462573
- system-tests-node-modules-install
25472574

2575+
linux-arm64-workflow: &linux-arm64-workflow
2576+
jobs:
2577+
- node_modules_install:
2578+
name: linux-arm64-node-modules-install
2579+
executor: linux-arm64
2580+
resource_class: arm.medium
2581+
only-cache-for-root-user: true
2582+
2583+
- build:
2584+
name: linux-arm64-build
2585+
executor: linux-arm64
2586+
resource_class: arm.medium
2587+
requires:
2588+
- linux-arm64-node-modules-install
2589+
2590+
- create-build-artifacts:
2591+
name: linux-arm64-create-build-artifacts
2592+
context:
2593+
- test-runner:upload
2594+
- test-runner:commit-status-checks
2595+
executor: linux-arm64
2596+
resource_class: arm.medium
2597+
requires:
2598+
- linux-arm64-build
2599+
25482600
darwin-x64-workflow: &darwin-x64-workflow
25492601
jobs:
25502602
- node_modules_install:
@@ -2675,9 +2727,12 @@ windows-workflow: &windows-workflow
26752727
- windows-create-build-artifacts
26762728

26772729
workflows:
2678-
linux:
2679-
<<: *linux-workflow
2680-
<<: *linux-workflow-exclude-filters
2730+
linux-x64:
2731+
<<: *linux-x64-workflow
2732+
<<: *linux-x64-workflow-exclude-filters
2733+
linux-arm64:
2734+
<<: *linux-arm64-workflow
2735+
<<: *linux-arm64-workflow-filters
26812736
darwin-x64:
26822737
<<: *darwin-x64-workflow
26832738
<<: *darwin-workflow-filters

cli/.eslintrc.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"extends": "../.eslintrc.js",
3+
"rules": {
4+
"no-restricted-syntax": [
5+
"error",
6+
{
7+
"selector": "CallExpression[callee.name='arch']",
8+
"message": "Do not use `arch()` to detect the user's machine architecture. Use util.getRealArch() instead."
9+
},
10+
{
11+
"selector": "CallExpression[callee.object.name='os'][callee.property.name='arch']",
12+
"message": "Do not use `os.arch()` to detect the user's machine architecture. Use util.getRealArch() instead."
13+
},
14+
{
15+
"selector": "MemberExpression[object.name='process'][property.name='arch']",
16+
"message": "Do not use `process.arch` to detect the user's machine architecture. Use util.getRealArch() instead."
17+
}
18+
]
19+
}
20+
}

cli/lib/tasks/install.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ const downloadAndUnzip = ({ version, installDir, downloadDir }) => {
134134

135135
const validateOS = () => {
136136
return util.getPlatformInfo().then((platformInfo) => {
137-
return platformInfo.match(/(win32-x64|linux-x64|darwin-x64|darwin-arm64)/)
137+
return platformInfo.match(/(win32-x64|linux-x64|linux-arm64|darwin-x64|darwin-arm64)/)
138138
})
139139
}
140140

cli/lib/util.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -481,22 +481,32 @@ const util = {
481481

482482
async function _getRealArch () {
483483
const osPlatform = os.platform()
484+
// eslint-disable-next-line no-restricted-syntax
484485
const osArch = os.arch()
485486

486487
debug('detecting arch %o', { osPlatform, osArch })
487488

488-
if (osPlatform === 'darwin') {
489-
if (osArch === 'arm64') return 'arm64'
489+
if (osArch === 'arm64') return 'arm64'
490490

491+
if (osPlatform === 'darwin') {
491492
// could possibly be x64 node on arm64 darwin, check if we are being translated by Rosetta
492493
// https://stackoverflow.com/a/65347893/3474615
493494
const { stdout } = await execa('sysctl', ['-n', 'sysctl.proc_translated']).catch(() => '')
494495

495496
debug('rosetta check result: %o', { stdout })
496-
497497
if (stdout === '1') return 'arm64'
498498
}
499499

500+
if (osPlatform === 'linux') {
501+
// could possibly be x64 node on arm64 linux, check the "machine hardware name"
502+
// list of names for reference: https://stackoverflow.com/a/45125525/3474615
503+
const { stdout } = await execa('uname', ['-m']).catch(() => '')
504+
505+
debug('arm uname -m result: %o ', { stdout })
506+
if (['aarch64_be', 'aarch64', 'armv8b', 'armv8l'].includes(stdout)) return 'arm64'
507+
}
508+
509+
// eslint-disable-next-line no-restricted-syntax
500510
const pkgArch = arch()
501511

502512
if (pkgArch === 'x86') return 'ia32'

cli/test/lib/tasks/download_spec.js

+74-27
Original file line numberDiff line numberDiff line change
@@ -478,44 +478,91 @@ describe('lib/tasks/download', function () {
478478
sinon.stub(os, 'arch')
479479
})
480480

481-
function nockDarwinArm64 () {
482-
return nock('https://download.cypress.io')
483-
.get('/desktop/1.2.3')
484-
.query({ arch: 'arm64', platform: 'darwin' })
485-
.reply(200, undefined, {
486-
'x-version': '1.2.3',
481+
context('Apple Silicon/M1', () => {
482+
function nockDarwinArm64 () {
483+
return nock('https://download.cypress.io')
484+
.get('/desktop/1.2.3')
485+
.query({ arch: 'arm64', platform: 'darwin' })
486+
.reply(200, undefined, {
487+
'x-version': '1.2.3',
488+
})
489+
}
490+
491+
it('downloads darwin-arm64 on M1', async function () {
492+
os.platform.returns('darwin')
493+
os.arch.returns('arm64')
494+
nockDarwinArm64()
495+
496+
const responseVersion = await download.start(this.options)
497+
498+
expect(responseVersion).to.eq('1.2.3')
499+
500+
await fs.statAsync(downloadDestination)
487501
})
488-
}
489502

490-
it('downloads darwin-arm64 on M1', async function () {
491-
os.platform.returns('darwin')
492-
os.arch.returns('arm64')
493-
nockDarwinArm64()
503+
it('downloads darwin-arm64 on M1 translated by Rosetta', async function () {
504+
os.platform.returns('darwin')
505+
os.arch.returns('x64')
506+
nockDarwinArm64()
507+
508+
sinon.stub(cp, 'spawn').withArgs('sysctl', ['-n', 'sysctl.proc_translated'])
509+
.callsFake(mockSpawn(((cp) => {
510+
cp.stdout.write('1')
511+
cp.emit('exit', 0, null)
512+
cp.end()
513+
})))
494514

495-
const responseVersion = await download.start(this.options)
515+
const responseVersion = await download.start(this.options)
496516

497-
expect(responseVersion).to.eq('1.2.3')
517+
expect(responseVersion).to.eq('1.2.3')
498518

499-
await fs.statAsync(downloadDestination)
519+
await fs.statAsync(downloadDestination)
520+
})
500521
})
501522

502-
it('downloads darwin-arm64 on M1 translated by Rosetta', async function () {
503-
os.platform.returns('darwin')
504-
os.arch.returns('x64')
505-
nockDarwinArm64()
523+
context('Linux arm64/aarch64', () => {
524+
function nockLinuxArm64 () {
525+
return nock('https://download.cypress.io')
526+
.get('/desktop/1.2.3')
527+
.query({ arch: 'arm64', platform: 'linux' })
528+
.reply(200, undefined, {
529+
'x-version': '1.2.3',
530+
})
531+
}
532+
533+
it('downloads linux-arm64 on arm64 processor', async function () {
534+
os.platform.returns('linux')
535+
os.arch.returns('arm64')
536+
nockLinuxArm64()
506537

507-
sinon.stub(cp, 'spawn').withArgs('sysctl', ['-n', 'sysctl.proc_translated'])
508-
.callsFake(mockSpawn(((cp) => {
509-
cp.stdout.write('1')
510-
cp.emit('exit', 0, null)
511-
cp.end()
512-
})))
538+
const responseVersion = await download.start(this.options)
513539

514-
const responseVersion = await download.start(this.options)
540+
expect(responseVersion).to.eq('1.2.3')
515541

516-
expect(responseVersion).to.eq('1.2.3')
542+
await fs.statAsync(downloadDestination)
543+
})
517544

518-
await fs.statAsync(downloadDestination)
545+
it('downloads linux-arm64 on non-arm64 node running on arm machine', async function () {
546+
os.platform.returns('linux')
547+
os.arch.returns('x64')
548+
sinon.stub(cp, 'spawn')
549+
550+
for (const arch of ['aarch64_be', 'aarch64', 'armv8b', 'armv8l']) {
551+
nockLinuxArm64()
552+
cp.spawn.withArgs('uname', ['-m'])
553+
.callsFake(mockSpawn(((cp) => {
554+
cp.stdout.write(arch)
555+
cp.emit('exit', 0, null)
556+
cp.end()
557+
})))
558+
559+
const responseVersion = await download.start(this.options)
560+
561+
expect(responseVersion).to.eq('1.2.3')
562+
563+
await fs.statAsync(downloadDestination)
564+
}
565+
})
519566
})
520567
})
521568

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
"dedent": "^0.7.0",
142142
"del": "3.0.0",
143143
"detect-port": "^1.3.0",
144-
"electron": "18.0.4",
144+
"electron": "18.3.0",
145145
"electron-builder": "^22.13.1",
146146
"electron-notarize": "^1.1.1",
147147
"enzyme-adapter-react-16": "1.12.1",

scripts/binary/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ const deploy = {
113113
checkDownloads ({ version }) {
114114
const systems = [
115115
{ platform: 'linux', arch: 'x64' },
116+
{ platform: 'linux', arch: 'arm64' },
116117
{ platform: 'darwin', arch: 'x64' },
117118
{ platform: 'darwin', arch: 'arm64' },
118119
{ platform: 'win32', arch: 'x64' },

scripts/binary/move-binaries.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type semver = string
3434
/**
3535
* Platform plus architecture string like "darwin-x64"
3636
*/
37-
type platformArch = 'darwin-x64' | 'darwin-arm64' | 'linux-x64' | 'win32-x64'
37+
type platformArch = 'darwin-x64' | 'darwin-arm64' | 'linux-x64' | 'linux-arm64' | 'win32-x64'
3838

3939
interface ReleaseInformation {
4040
commit: commit

scripts/binary/upload.js

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ module.exports = {
8787
'darwin-x64': getUrl('darwin-x64'),
8888
'darwin-arm64': getUrl('darwin-arm64'),
8989
'linux-x64': getUrl('linux-x64'),
90+
'linux-arm64': getUrl('linux-arm64'),
9091
'win32-x64': getUrl('win32-x64'),
9192
},
9293
}

0 commit comments

Comments
 (0)