Skip to content

Commit

Permalink
Merge pull request #410 from marp-team/improve-wsl2-browser-detection
Browse files Browse the repository at this point in the history
Improved WSL 2 detection and browser resolution
  • Loading branch information
yhatt authored Jan 16, 2022
2 parents baa5c83 + 44d0899 commit c3d5c32
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 20 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

- Allow to set timeout for Puppeteer actions by `PUPPETEER_TIMEOUT` env ([#409](https://github.com/marp-team/marp-cli/pull/409))

### Fixed

- Improved WSL 2 detection and browser resolution ([#410](https://github.com/marp-team/marp-cli/pull/410))

### Changed

- Upgrade Marpit to [v2.2.1](https://github.com/marp-team/marpit/releases/tag/v2.2.1) ([#408](https://github.com/marp-team/marp-cli/pull/408))
Expand Down
8 changes: 6 additions & 2 deletions src/utils/chrome-finder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ import {
import { isWSL } from './wsl'

// A lightweight version of Launcher.getFirstInstallation()
// https://github.com/GoogleChrome/chrome-launcher/blob/master/src/chrome-launcher.ts#L175
// https://github.com/GoogleChrome/chrome-launcher/blob/30755cde8627b7aad6caff1594c9752f80a39a4d/src/chrome-launcher.ts#L189-L192
export const findChromeInstallation = () => {
const platform = isWSL() ? 'wsl' : process.platform
// 'wsl' platform will resolve Chrome from Windows. In WSL 2, Puppeteer cannot
// communicate with Chrome spawned in the host OS so should follow the
// original platform ('linux') if CLI was executed in WSL 2.
const platform = isWSL() === 1 ? 'wsl' : process.platform

const installations = (() => {
switch (platform) {
/* istanbul ignore next: CI is not testing against darwin */
Expand Down
3 changes: 2 additions & 1 deletion src/utils/edge-finder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export const findAccessiblePath = (paths: string[]): string | undefined =>
})

const linux = (): string | undefined => {
if (isWSL()) {
// WSL 1 should find Edge executable from host OS
if (isWSL() === 1) {
const localAppData = resolveWindowsEnvSync('LOCALAPPDATA')

return win32({
Expand Down
4 changes: 2 additions & 2 deletions src/utils/puppeteer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ export const generatePuppeteerDataDirPath = async (
): Promise<string> => {
const dataDir = await (async () => {
if (isWSL() && wslHost) {
// In WSL environment, Marp CLI will use Chrome on Windows. Thus, we have to
// specify Windows path when converting within WSL.
// In WSL environment, Marp CLI may use Chrome on Windows. If Chrome has
// located in host OS (Windows), we have to specify Windows path.
if (wslTmp === undefined) wslTmp = await resolveWindowsEnv('TMP')
if (wslTmp !== undefined) return path.win32.resolve(wslTmp, name)
}
Expand Down
26 changes: 17 additions & 9 deletions src/utils/wsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,23 @@ export const resolveWindowsEnvSync = (key: string): string | undefined => {
export const isWSL = (): number => {
if (isWsl === undefined) {
if (require('is-wsl')) {
isWsl = 1

try {
// https://github.com/microsoft/WSL/issues/423#issuecomment-611086412
const release = readFileSync('/proc/sys/kernel/osrelease').toString()
if (release.includes('WSL2')) isWsl = 2
} catch (e: unknown) {
// no ops
}
// Detect whether WSL version is 2
// https://github.com/microsoft/WSL/issues/4555#issuecomment-700213318
const isWSL2 = (() => {
if (process.env.WSL_DISTRO_NAME && process.env.WSL_INTEROP) return true

try {
const verStr = readFileSync('/proc/version', 'utf8').toLowerCase()
if (verStr.includes('microsoft-standard-wsl2')) return true

const gccMatched = verStr.match(/gcc[^,]+?(\d+)\.\d+\.\d+/)
if (gccMatched && Number.parseInt(gccMatched[1], 10) >= 8) return true
} catch (e: unknown) {
// no ops
}
})()

isWsl = isWSL2 ? 2 : 1
} else {
isWsl = 0
}
Expand Down
70 changes: 64 additions & 6 deletions test/utils/wsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,23 @@ describe('#resolveWindowsEnvSync', () => {
})

describe('#isWSL', () => {
afterEach(() => jest.dontMock('is-wsl'))
let originalWSLDistroName: string | undefined
let originalWSLInterop: string | undefined

beforeEach(() => {
originalWSLDistroName = process.env.WSL_DISTRO_NAME
originalWSLInterop = process.env.WSL_INTEROP

process.env.WSL_DISTRO_NAME = ''
process.env.WSL_INTEROP = ''
})

afterEach(() => {
process.env.WSL_DISTRO_NAME = originalWSLDistroName
process.env.WSL_INTEROP = originalWSLInterop

jest.dontMock('is-wsl')
})

it('returns 0 if is-wsl module returned false', () => {
jest.doMock('is-wsl', () => false)
Expand All @@ -94,7 +110,9 @@ describe('#isWSL', () => {

const readFileSync = jest
.spyOn(fs, 'readFileSync')
.mockImplementation(() => '4.19.128-microsoft-standard')
.mockImplementation(
() => 'Linux version 4.5.6-12345-Microsoft (gcc version 5.4.0 (GCC) )'
)

expect(wsl().isWSL()).toBe(1)
expect(readFileSync).toHaveBeenCalledTimes(1)
Expand All @@ -104,15 +122,55 @@ describe('#isWSL', () => {
expect(readFileSync).toHaveBeenCalledTimes(1)
})

it('returns 2 if running on WSL 2', () => {
it('returns 2 if running on WSL 2 (Fast check by environment values)', () => {
jest.doMock('is-wsl', () => true)

// https://github.com/microsoft/WSL/issues/423#issuecomment-611086412
jest
const readFileSync = jest.spyOn(fs, 'readFileSync')

// WSL 2 has WSL_DISTRO_NAME and WSL_INTEROP
process.env.WSL_DISTRO_NAME = 'Ubuntu'
process.env.WSL_INTEROP = '/run/WSL/11_interop'

expect(wsl().isWSL()).toBe(2)
expect(readFileSync).not.toHaveBeenCalled()
})

it('returns 2 if running on WSL 2 (Check WSL2 annotation in /proc/version string)', () => {
jest.doMock('is-wsl', () => true)

const readFileSync = jest
.spyOn(fs, 'readFileSync')
.mockImplementation(() => 'Linux version 4.5.6-Microsoft-Standard-WSL2')

expect(wsl().isWSL()).toBe(2)
expect(readFileSync).toHaveBeenCalledTimes(1)
})

it('returns 2 if running on WSL 2 (Check gcc version in /proc/version string)', () => {
jest.doMock('is-wsl', () => true)

const readFileSync = jest
.spyOn(fs, 'readFileSync')
.mockImplementation(() => '4.19.128-microsoft-WSL2-standard')
.mockImplementation(
() => 'Linux version 4.5.6-12345-Microsoft (gcc version 8.4.0 (GCC) )'
)

expect(wsl().isWSL()).toBe(2)
expect(readFileSync).toHaveBeenCalledTimes(1)
})

it('returns 2 if running on WSL 2 (The latest format of /proc/version string)', () => {
jest.doMock('is-wsl', () => true)

const readFileSync = jest
.spyOn(fs, 'readFileSync')
.mockImplementation(
() =>
'Linux version 5.10.74.3 (x86_64-msft-linux-gcc (GCC) 9.3.0, GNU ld (GNU Binutils) 2.34.0.20200220)'
)

expect(wsl().isWSL()).toBe(2)
expect(readFileSync).toHaveBeenCalledTimes(1)
})
})

Expand Down

0 comments on commit c3d5c32

Please sign in to comment.