Skip to content

Commit 05ef83a

Browse files
authored
fix(launcher): support Firefox as a snap (#21328)
1 parent cb14ae6 commit 05ef83a

File tree

4 files changed

+145
-24
lines changed

4 files changed

+145
-24
lines changed

packages/launcher/lib/linux/index.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,36 @@ import type { FoundBrowser, Browser, PathData } from '../types'
33
import { notInstalledErr } from '../errors'
44
import { utils } from '../utils'
55
import os from 'os'
6+
import { promises as fs } from 'fs'
67
import path from 'path'
78
import Bluebird from 'bluebird'
9+
import which from 'which'
10+
11+
async function isFirefoxSnap (binary: string): Promise<boolean> {
12+
try {
13+
return await Bluebird.resolve((async () => {
14+
const binaryPath = await which(binary)
15+
16+
// if the bin path or what it's symlinked to start with `/snap/bin`, it's a snap
17+
if (binaryPath.startsWith('/snap/bin/') || (await fs.realpath(binaryPath)).startsWith('/snap/bin')) return true
18+
19+
// read the first 16kb, don't read the entire file into memory in case it is a binary
20+
const fd = await fs.open(binaryPath, 'r')
21+
// @ts-ignore - needs @types/node at least 16
22+
// https://github.com/cypress-io/cypress/issues/21329
23+
const { buffer, bytesRead } = await fd.read<Buffer>({ length: 16384 })
24+
25+
await fd.close()
26+
27+
return buffer.slice(0, bytesRead).toString('utf8').includes('exec /snap/bin/firefox')
28+
})())
29+
.timeout(30000)
30+
} catch (err) {
31+
log('failed to check if Firefox is a snap, assuming it isn\'t %o', { err, binary })
32+
33+
return false
34+
}
35+
}
836

937
function getLinuxBrowser (
1038
name: string,
@@ -43,11 +71,25 @@ function getLinuxBrowser (
4371
throw notInstalledErr(binary)
4472
}
4573

46-
const maybeSetSnapProfilePath = (versionString: string) => {
47-
if (os.platform() === 'linux' && name === 'chromium' && versionString.endsWith('snap')) {
74+
const maybeSetSnapProfilePath = async (versionString: string) => {
75+
if (os.platform() !== 'linux') return
76+
77+
if (name === 'chromium' && versionString.endsWith('snap')) {
4878
// when running as a snap, chromium can only write to certain directories
4979
// @see https://github.com/cypress-io/cypress/issues/7020
80+
log('chromium is running as a snap, changing profile path')
5081
foundBrowser.profilePath = path.join(os.homedir(), 'snap', 'chromium', 'current')
82+
83+
return
84+
}
85+
86+
if (name === 'firefox' && (await isFirefoxSnap(binary))) {
87+
// if the binary in the path points to a script that calls the snap, set a snap-specific profile path
88+
// @see https://github.com/cypress-io/cypress/issues/19793
89+
log('firefox is running as a snap, changing profile path')
90+
foundBrowser.profilePath = path.join(os.homedir(), 'snap', 'firefox', 'current')
91+
92+
return
5193
}
5294
}
5395

packages/launcher/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
"fs-extra": "9.1.0",
1919
"lodash": "^4.17.21",
2020
"plist": "3.0.5",
21-
"semver": "7.3.5"
21+
"semver": "7.3.5",
22+
"which": "2.0.2"
2223
},
2324
"devDependencies": {
2425
"@packages/ts": "0.0.0-development",
2526
"chai": "3.5.0",
2627
"chai-as-promised": "7.1.1",
2728
"mocha": "3.5.3",
29+
"mock-fs": "5.1.2",
2830
"shelljs": "0.8.5",
2931
"sinon": "^10.0.0",
3032
"sinon-chai": "3.4.0",

packages/launcher/test/unit/linux_spec.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ import { expect } from 'chai'
1010
import { utils } from '../../lib/utils'
1111
import os from 'os'
1212
import sinon, { SinonStub } from 'sinon'
13+
import mockFs from 'mock-fs'
1314

1415
describe('linux browser detection', () => {
1516
let execa: SinonStub
17+
let cachedEnv = { ...process.env }
1618

1719
beforeEach(() => {
18-
sinon.restore()
1920
execa = sinon.stub(utils, 'getOutput')
2021

2122
sinon.stub(os, 'platform').returns('linux')
@@ -34,6 +35,12 @@ describe('linux browser detection', () => {
3435
.resolves({ stdout: 'foo-browser v9001.1.2.3' })
3536
})
3637

38+
afterEach(() => {
39+
Object.assign(process.env, cachedEnv)
40+
mockFs.restore()
41+
sinon.restore()
42+
})
43+
3744
it('detects browser by running --version', () => {
3845
const goal = goalBrowsers[0]
3946
const checkBrowser = (browser) => {
@@ -72,6 +79,63 @@ describe('linux browser detection', () => {
7279
return detect().then(checkBrowser)
7380
})
7481

82+
// https://github.com/cypress-io/cypress/issues/19793
83+
context('sets profilePath on snapcraft firefox', () => {
84+
const expectedSnapFirefox = {
85+
channel: 'stable',
86+
name: 'firefox',
87+
family: 'firefox',
88+
displayName: 'Firefox',
89+
majorVersion: 99,
90+
minSupportedVersion: 86,
91+
path: 'firefox',
92+
profilePath: '/home/foo/snap/firefox/current',
93+
version: '99.2.3',
94+
}
95+
96+
beforeEach(() => {
97+
execa.withArgs('firefox', ['--version'])
98+
.resolves({ stdout: 'Mozilla Firefox 99.2.3' })
99+
100+
sinon.stub(os, 'homedir').returns('/home/foo')
101+
})
102+
103+
it('with shim script', async () => {
104+
process.env.PATH = '/bin'
105+
mockFs({
106+
'/bin/firefox': mockFs.symlink({ path: '/usr/bin/firefox' }),
107+
'/usr/bin/firefox': mockFs.file({ mode: 0o777, content: 'foo bar foo bar foo bar\nexec /snap/bin/firefox\n' }),
108+
})
109+
110+
const [browser] = await detect()
111+
112+
expect(browser).to.deep.equal(expectedSnapFirefox)
113+
})
114+
115+
it('with /snap/bin in path', async () => {
116+
process.env.PATH = '/bin:/snap/bin'
117+
mockFs({
118+
'/snap/bin/firefox': mockFs.file({ mode: 0o777, content: 'binary' }),
119+
})
120+
121+
const [browser] = await detect()
122+
123+
expect(browser).to.deep.equal(expectedSnapFirefox)
124+
})
125+
126+
it('with symlink to /snap/bin in path', async () => {
127+
process.env.PATH = '/bin'
128+
mockFs({
129+
'/bin/firefox': mockFs.symlink({ path: '/snap/bin/firefox' }),
130+
'/snap/bin/firefox': mockFs.file({ mode: 0o777, content: 'binary' }),
131+
})
132+
133+
const [browser] = await detect()
134+
135+
expect(browser).to.deep.equal(expectedSnapFirefox)
136+
})
137+
})
138+
75139
// https://github.com/cypress-io/cypress/issues/6669
76140
it('detects browser if the --version stdout is multiline', () => {
77141
execa.withArgs('multiline-foo', ['--version'])

yarn.lock

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4140,7 +4140,7 @@
41404140

41414141
"@jest/types@^26.3.0", "@jest/types@^26.6.2":
41424142
version "26.6.2"
4143-
resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
4143+
resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
41444144
integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==
41454145
dependencies:
41464146
"@types/istanbul-lib-coverage" "^2.0.0"
@@ -8268,7 +8268,7 @@
82688268

82698269
"@types/cheerio@*", "@types/[email protected]":
82708270
version "0.22.21"
8271-
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3"
8271+
resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3"
82728272
integrity sha512-aGI3DfswwqgKPiEOTaiHV2ZPC9KEhprpgEbJnv0fZl3SGX0cGgEva1126dGrMC6AJM6v/aihlUgJn9M5DbDZ/Q==
82738273
dependencies:
82748274
"@types/node" "*"
@@ -8370,7 +8370,7 @@
83708370

83718371
"@types/enzyme@*", "@types/[email protected]":
83728372
version "3.10.5"
8373-
resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.5.tgz#fe7eeba3550369eed20e7fb565bfb74eec44f1f0"
8373+
resolved "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.5.tgz#fe7eeba3550369eed20e7fb565bfb74eec44f1f0"
83748374
integrity sha512-R+phe509UuUYy9Tk0YlSbipRpfVtIzb/9BHn5pTEtjJTF5LXvUjrIQcZvNyANNEyFrd2YGs196PniNT1fgvOQA==
83758375
dependencies:
83768376
"@types/cheerio" "*"
@@ -10818,9 +10818,9 @@ ansi-regex@^3.0.0:
1081810818
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
1081910819

1082010820
ansi-regex@^4.1.0:
10821-
version "4.1.0"
10822-
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
10823-
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
10821+
version "4.1.1"
10822+
resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
10823+
integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
1082410824

1082510825
ansi-regex@^5.0.0, ansi-regex@^5.0.1:
1082610826
version "5.0.1"
@@ -14123,7 +14123,7 @@ collection-visit@^1.0.0:
1412314123
map-visit "^1.0.0"
1412414124
object-visit "^1.0.0"
1412514125

14126-
color-convert@^1.9.0, color-convert@^1.9.1:
14126+
color-convert@^1.9.0, color-convert@^1.9.3:
1412714127
version "1.9.3"
1412814128
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
1412914129
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
@@ -14147,26 +14147,34 @@ color-name@^1.0.0, color-name@~1.1.4:
1414714147
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
1414814148
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
1414914149

14150-
[email protected], color-string@^1.5.4:
14150+
1415114151
version "1.5.5"
1415214152
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
1415314153
integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
1415414154
dependencies:
1415514155
color-name "^1.0.0"
1415614156
simple-swizzle "^0.2.2"
1415714157

14158+
color-string@^1.6.0:
14159+
version "1.9.1"
14160+
resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
14161+
integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
14162+
dependencies:
14163+
color-name "^1.0.0"
14164+
simple-swizzle "^0.2.2"
14165+
1415814166
color-support@^1.1.3:
1415914167
version "1.1.3"
1416014168
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
1416114169
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
1416214170

1416314171
color@^3.0.0, color@^3.1.1:
14164-
version "3.1.3"
14165-
resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e"
14166-
integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==
14172+
version "3.2.1"
14173+
resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164"
14174+
integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==
1416714175
dependencies:
14168-
color-convert "^1.9.1"
14169-
color-string "^1.5.4"
14176+
color-convert "^1.9.3"
14177+
color-string "^1.6.0"
1417014178

1417114179
colorette@^1.1.0, colorette@^1.2.1, colorette@^1.2.2:
1417214180
version "1.2.2"
@@ -27246,6 +27254,11 @@ [email protected]:
2724627254
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.1.1.tgz#d4c95e916abf400664197079d7e399d133bb6048"
2724727255
integrity sha512-p/8oZ3qvfKGPw+4wdVCyjDxa6wn2tP0TCf3WXC1UyUBAevezPn1TtOoxtMYVbZu/S/iExg+Ghed1busItj2CEw==
2724827256

27257+
27258+
version "5.1.2"
27259+
resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-5.1.2.tgz#6fa486e06d00f8793a8d2228de980eff93ce6db7"
27260+
integrity sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A==
27261+
2724927262
2725027263
version "3.0.3"
2725127264
resolved "https://registry.yarnpkg.com/mock-require/-/mock-require-3.0.3.tgz#ccd544d9eae81dd576b3f219f69ec867318a1946"
@@ -27755,9 +27768,9 @@ node-addon-api@^1.6.3:
2775527768
integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==
2775627769

2775727770
node-addon-api@^3.1.0:
27758-
version "3.1.0"
27759-
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239"
27760-
integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
27771+
version "3.2.1"
27772+
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
27773+
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
2776127774

2776227775
node-dir@^0.1.10:
2776327776
version "0.1.17"
@@ -35316,7 +35329,7 @@ [email protected]:
3531635329

3531735330
[email protected], socket.io-parser@~3.3.0, socket.io-parser@~3.4.0, socket.io-parser@~4.0.3, socket.io-parser@~4.0.4:
3531835331
version "4.0.4"
35319-
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0"
35332+
resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0"
3532035333
integrity sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==
3532135334
dependencies:
3532235335
"@types/component-emitter" "^1.2.10"
@@ -37950,7 +37963,7 @@ typescript@^4.2.3, typescript@^4.4.4:
3795037963

3795137964
[email protected], ua-parser-js@^0.7.18:
3795237965
version "0.7.24"
37953-
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c"
37966+
resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c"
3795437967
integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw==
3795537968

3795637969
uc.micro@^1.0.1, uc.micro@^1.0.5:
@@ -39398,7 +39411,7 @@ vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
3939839411

3939939412
[email protected], vue-template-compiler@^2.6.11:
3940039413
version "2.6.12"
39401-
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e"
39414+
resolved "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e"
3940239415
integrity sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg==
3940339416
dependencies:
3940439417
de-indent "^1.0.2"
@@ -40324,7 +40337,7 @@ [email protected], which@^1.1.1, which@^1.2.12, which@^1.2.14, which@^1.2.4, which@^1.
4032440337

4032540338
[email protected], which@^2.0.1, which@^2.0.2:
4032640339
version "2.0.2"
40327-
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
40340+
resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
4032840341
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
4032940342
dependencies:
4033040343
isexe "^2.0.0"

0 commit comments

Comments
 (0)