Skip to content

Commit

Permalink
fix(launcher): support Firefox as a snap (#21328)
Browse files Browse the repository at this point in the history
  • Loading branch information
flotwig committed May 5, 2022
1 parent cb14ae6 commit 05ef83a
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 24 deletions.
46 changes: 44 additions & 2 deletions packages/launcher/lib/linux/index.ts
Expand Up @@ -3,8 +3,36 @@ import type { FoundBrowser, Browser, PathData } from '../types'
import { notInstalledErr } from '../errors'
import { utils } from '../utils'
import os from 'os'
import { promises as fs } from 'fs'
import path from 'path'
import Bluebird from 'bluebird'
import which from 'which'

async function isFirefoxSnap (binary: string): Promise<boolean> {
try {
return await Bluebird.resolve((async () => {
const binaryPath = await which(binary)

// if the bin path or what it's symlinked to start with `/snap/bin`, it's a snap
if (binaryPath.startsWith('/snap/bin/') || (await fs.realpath(binaryPath)).startsWith('/snap/bin')) return true

// read the first 16kb, don't read the entire file into memory in case it is a binary
const fd = await fs.open(binaryPath, 'r')
// @ts-ignore - needs @types/node at least 16
// https://github.com/cypress-io/cypress/issues/21329
const { buffer, bytesRead } = await fd.read<Buffer>({ length: 16384 })

await fd.close()

return buffer.slice(0, bytesRead).toString('utf8').includes('exec /snap/bin/firefox')
})())
.timeout(30000)
} catch (err) {
log('failed to check if Firefox is a snap, assuming it isn\'t %o', { err, binary })

return false
}
}

function getLinuxBrowser (
name: string,
Expand Down Expand Up @@ -43,11 +71,25 @@ function getLinuxBrowser (
throw notInstalledErr(binary)
}

const maybeSetSnapProfilePath = (versionString: string) => {
if (os.platform() === 'linux' && name === 'chromium' && versionString.endsWith('snap')) {
const maybeSetSnapProfilePath = async (versionString: string) => {
if (os.platform() !== 'linux') return

if (name === 'chromium' && versionString.endsWith('snap')) {
// when running as a snap, chromium can only write to certain directories
// @see https://github.com/cypress-io/cypress/issues/7020
log('chromium is running as a snap, changing profile path')
foundBrowser.profilePath = path.join(os.homedir(), 'snap', 'chromium', 'current')

return
}

if (name === 'firefox' && (await isFirefoxSnap(binary))) {
// if the binary in the path points to a script that calls the snap, set a snap-specific profile path
// @see https://github.com/cypress-io/cypress/issues/19793
log('firefox is running as a snap, changing profile path')
foundBrowser.profilePath = path.join(os.homedir(), 'snap', 'firefox', 'current')

return
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/launcher/package.json
Expand Up @@ -18,13 +18,15 @@
"fs-extra": "9.1.0",
"lodash": "^4.17.21",
"plist": "3.0.5",
"semver": "7.3.5"
"semver": "7.3.5",
"which": "2.0.2"
},
"devDependencies": {
"@packages/ts": "0.0.0-development",
"chai": "3.5.0",
"chai-as-promised": "7.1.1",
"mocha": "3.5.3",
"mock-fs": "5.1.2",
"shelljs": "0.8.5",
"sinon": "^10.0.0",
"sinon-chai": "3.4.0",
Expand Down
66 changes: 65 additions & 1 deletion packages/launcher/test/unit/linux_spec.ts
Expand Up @@ -10,12 +10,13 @@ import { expect } from 'chai'
import { utils } from '../../lib/utils'
import os from 'os'
import sinon, { SinonStub } from 'sinon'
import mockFs from 'mock-fs'

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

beforeEach(() => {
sinon.restore()
execa = sinon.stub(utils, 'getOutput')

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

afterEach(() => {
Object.assign(process.env, cachedEnv)
mockFs.restore()
sinon.restore()
})

it('detects browser by running --version', () => {
const goal = goalBrowsers[0]
const checkBrowser = (browser) => {
Expand Down Expand Up @@ -72,6 +79,63 @@ describe('linux browser detection', () => {
return detect().then(checkBrowser)
})

// https://github.com/cypress-io/cypress/issues/19793
context('sets profilePath on snapcraft firefox', () => {
const expectedSnapFirefox = {
channel: 'stable',
name: 'firefox',
family: 'firefox',
displayName: 'Firefox',
majorVersion: 99,
minSupportedVersion: 86,
path: 'firefox',
profilePath: '/home/foo/snap/firefox/current',
version: '99.2.3',
}

beforeEach(() => {
execa.withArgs('firefox', ['--version'])
.resolves({ stdout: 'Mozilla Firefox 99.2.3' })

sinon.stub(os, 'homedir').returns('/home/foo')
})

it('with shim script', async () => {
process.env.PATH = '/bin'
mockFs({
'/bin/firefox': mockFs.symlink({ path: '/usr/bin/firefox' }),
'/usr/bin/firefox': mockFs.file({ mode: 0o777, content: 'foo bar foo bar foo bar\nexec /snap/bin/firefox\n' }),
})

const [browser] = await detect()

expect(browser).to.deep.equal(expectedSnapFirefox)
})

it('with /snap/bin in path', async () => {
process.env.PATH = '/bin:/snap/bin'
mockFs({
'/snap/bin/firefox': mockFs.file({ mode: 0o777, content: 'binary' }),
})

const [browser] = await detect()

expect(browser).to.deep.equal(expectedSnapFirefox)
})

it('with symlink to /snap/bin in path', async () => {
process.env.PATH = '/bin'
mockFs({
'/bin/firefox': mockFs.symlink({ path: '/snap/bin/firefox' }),
'/snap/bin/firefox': mockFs.file({ mode: 0o777, content: 'binary' }),
})

const [browser] = await detect()

expect(browser).to.deep.equal(expectedSnapFirefox)
})
})

// https://github.com/cypress-io/cypress/issues/6669
it('detects browser if the --version stdout is multiline', () => {
execa.withArgs('multiline-foo', ['--version'])
Expand Down
53 changes: 33 additions & 20 deletions yarn.lock
Expand Up @@ -4140,7 +4140,7 @@

"@jest/types@^26.3.0", "@jest/types@^26.6.2":
version "26.6.2"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==
dependencies:
"@types/istanbul-lib-coverage" "^2.0.0"
Expand Down Expand Up @@ -8268,7 +8268,7 @@

"@types/cheerio@*", "@types/cheerio@0.22.21":
version "0.22.21"
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3"
resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3"
integrity sha512-aGI3DfswwqgKPiEOTaiHV2ZPC9KEhprpgEbJnv0fZl3SGX0cGgEva1126dGrMC6AJM6v/aihlUgJn9M5DbDZ/Q==
dependencies:
"@types/node" "*"
Expand Down Expand Up @@ -8370,7 +8370,7 @@

"@types/enzyme@*", "@types/enzyme@3.10.5":
version "3.10.5"
resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.5.tgz#fe7eeba3550369eed20e7fb565bfb74eec44f1f0"
resolved "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.5.tgz#fe7eeba3550369eed20e7fb565bfb74eec44f1f0"
integrity sha512-R+phe509UuUYy9Tk0YlSbipRpfVtIzb/9BHn5pTEtjJTF5LXvUjrIQcZvNyANNEyFrd2YGs196PniNT1fgvOQA==
dependencies:
"@types/cheerio" "*"
Expand Down Expand Up @@ -10818,9 +10818,9 @@ ansi-regex@^3.0.0:
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=

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

ansi-regex@^5.0.0, ansi-regex@^5.0.1:
version "5.0.1"
Expand Down Expand Up @@ -14123,7 +14123,7 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"

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

color-string@1.5.5, color-string@^1.5.4:
color-string@1.5.5:
version "1.5.5"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"

color-string@^1.6.0:
version "1.9.1"
resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"

color-support@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==

color@^3.0.0, color@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e"
integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==
version "3.2.1"
resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164"
integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==
dependencies:
color-convert "^1.9.1"
color-string "^1.5.4"
color-convert "^1.9.3"
color-string "^1.6.0"

colorette@^1.1.0, colorette@^1.2.1, colorette@^1.2.2:
version "1.2.2"
Expand Down Expand Up @@ -27246,6 +27254,11 @@ mock-fs@5.1.1:
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.1.1.tgz#d4c95e916abf400664197079d7e399d133bb6048"
integrity sha512-p/8oZ3qvfKGPw+4wdVCyjDxa6wn2tP0TCf3WXC1UyUBAevezPn1TtOoxtMYVbZu/S/iExg+Ghed1busItj2CEw==

mock-fs@5.1.2:
version "5.1.2"
resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-5.1.2.tgz#6fa486e06d00f8793a8d2228de980eff93ce6db7"
integrity sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A==

mock-require@3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/mock-require/-/mock-require-3.0.3.tgz#ccd544d9eae81dd576b3f219f69ec867318a1946"
Expand Down Expand Up @@ -27755,9 +27768,9 @@ node-addon-api@^1.6.3:
integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==

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

node-dir@^0.1.10:
version "0.1.17"
Expand Down Expand Up @@ -35316,7 +35329,7 @@ socket.io-client@4.0.1:

socket.io-parser@4.0.4, socket.io-parser@~3.3.0, socket.io-parser@~3.4.0, socket.io-parser@~4.0.3, socket.io-parser@~4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0"
resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0"
integrity sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==
dependencies:
"@types/component-emitter" "^1.2.10"
Expand Down Expand Up @@ -37950,7 +37963,7 @@ typescript@^4.2.3, typescript@^4.4.4:

ua-parser-js@0.7.24, ua-parser-js@^0.7.18:
version "0.7.24"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c"
resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c"
integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw==

uc.micro@^1.0.1, uc.micro@^1.0.5:
Expand Down Expand Up @@ -39398,7 +39411,7 @@ vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:

vue-template-compiler@2.6.12, vue-template-compiler@^2.6.11:
version "2.6.12"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e"
resolved "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e"
integrity sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg==
dependencies:
de-indent "^1.0.2"
Expand Down Expand Up @@ -40324,7 +40337,7 @@ which@1.3.1, which@^1.1.1, which@^1.2.12, which@^1.2.14, which@^1.2.4, which@^1.

which@2.0.2, which@^2.0.1, which@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
Expand Down

2 comments on commit 05ef83a

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 05ef83a May 5, 2022

Choose a reason for hiding this comment

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

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

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/9.6.1/linux-x64/develop-05ef83a87e8b17dee9e343aa88ae4f756979ac43/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 05ef83a May 5, 2022

Choose a reason for hiding this comment

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

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

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/9.6.1/darwin-x64/develop-05ef83a87e8b17dee9e343aa88ae4f756979ac43/cypress.tgz

Please sign in to comment.