diff --git a/packages/playwright-core/src/common/userAgent.ts b/packages/playwright-core/src/common/userAgent.ts index c2e7dd825b..604a85f082 100644 --- a/packages/playwright-core/src/common/userAgent.ts +++ b/packages/playwright-core/src/common/userAgent.ts @@ -15,9 +15,8 @@ */ import { execSync } from 'child_process'; -import fs from 'fs'; import os from 'os'; -import { parseOSReleaseText } from '../utils/ubuntuVersion'; +import { getLinuxDistributionInfoSync } from '../utils/linuxUtils'; let cachedUserAgent: string | undefined; @@ -44,14 +43,11 @@ function determineUserAgent(): string { osIdentifier = 'macOS'; osVersion = `${version[0]}.${version[1]}`; } else if (process.platform === 'linux') { - try { - // List of /etc/os-release values for different distributions could be - // found here: https://gist.github.com/aslushnikov/8ceddb8288e4cf9db3039c02e0f4fb75 - const osReleaseText = fs.readFileSync('/etc/os-release', 'utf8'); - const fields = parseOSReleaseText(osReleaseText); - osIdentifier = fields.get('id') || 'unknown'; - osVersion = fields.get('version_id') || 'unknown'; - } catch (e) { + const distroInfo = getLinuxDistributionInfoSync(); + if (distroInfo) { + osIdentifier = distroInfo.id || 'linux'; + osVersion = distroInfo.version || 'unknown'; + } else { // Linux distribution without /etc/os-release. // Default to linux/unknown. osIdentifier = 'linux'; diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index 8f07acd56d..6827777dcf 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -20,7 +20,7 @@ import path from 'path'; import * as util from 'util'; import * as fs from 'fs'; import { lockfile } from '../../utilsBundle'; -import { getUbuntuVersion } from '../../utils/ubuntuVersion'; +import { getLinuxDistributionInfo } from '../../utils/linuxUtils'; import { fetchData } from '../../common/netUtils'; import { getClientLanguage } from '../../common/userAgent'; import { getFromENV, getAsBooleanFromENV, calculateSha1, wrapInASCIIBox } from '../../utils'; @@ -84,6 +84,7 @@ const DOWNLOAD_PATHS = { 'ubuntu18.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', 'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', + 'debian11': 'builds/chromium/%s/chromium-linux.zip', 'mac10.13': 'builds/chromium/%s/chromium-mac.zip', 'mac10.14': 'builds/chromium/%s/chromium-mac.zip', 'mac10.15': 'builds/chromium/%s/chromium-mac.zip', @@ -103,6 +104,7 @@ const DOWNLOAD_PATHS = { 'ubuntu18.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', 'ubuntu20.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', + 'debian11': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', 'mac10.13': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', 'mac10.14': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', 'mac10.15': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', @@ -122,6 +124,7 @@ const DOWNLOAD_PATHS = { 'ubuntu18.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip', 'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip', + 'debian11': 'builds/chromium/%s/chromium-with-symbols-linux.zip', 'mac10.13': 'builds/chromium/%s/chromium-with-symbols-mac.zip', 'mac10.14': 'builds/chromium/%s/chromium-with-symbols-mac.zip', 'mac10.15': 'builds/chromium/%s/chromium-with-symbols-mac.zip', @@ -141,6 +144,7 @@ const DOWNLOAD_PATHS = { 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': 'builds/firefox/%s/firefox-ubuntu-20.04-arm64.zip', 'ubuntu22.04-arm64': 'builds/firefox/%s/firefox-ubuntu-22.04-arm64.zip', + 'debian11': 'builds/firefox/%s/firefox-debian-11.zip', 'mac10.13': 'builds/firefox/%s/firefox-mac-11.zip', 'mac10.14': 'builds/firefox/%s/firefox-mac-11.zip', 'mac10.15': 'builds/firefox/%s/firefox-mac-11.zip', @@ -160,6 +164,7 @@ const DOWNLOAD_PATHS = { 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': undefined, 'ubuntu22.04-arm64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04-arm64.zip', + 'debian11': 'builds/firefox-beta/%s/firefox-beta-debian-11.zip', 'mac10.13': 'builds/firefox-beta/%s/firefox-beta-mac-11.zip', 'mac10.14': 'builds/firefox-beta/%s/firefox-beta-mac-11.zip', 'mac10.15': 'builds/firefox-beta/%s/firefox-beta-mac-11.zip', @@ -179,6 +184,7 @@ const DOWNLOAD_PATHS = { 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': 'builds/webkit/%s/webkit-ubuntu-20.04-arm64.zip', 'ubuntu22.04-arm64': 'builds/webkit/%s/webkit-ubuntu-22.04-arm64.zip', + 'debian11': 'builds/webkit/%s/webkit-linux-universal.zip', 'mac10.13': undefined, 'mac10.14': 'builds/deprecated-webkit-mac-10.14/%s/deprecated-webkit-mac-10.14.zip', 'mac10.15': 'builds/webkit/%s/webkit-mac-10.15.zip', @@ -198,6 +204,7 @@ const DOWNLOAD_PATHS = { 'ubuntu18.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip', 'ubuntu20.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip', + 'debian11': 'builds/ffmpeg/%s/ffmpeg-linux.zip', 'mac10.13': 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac10.14': 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac10.15': 'builds/ffmpeg/%s/ffmpeg-mac.zip', @@ -321,11 +328,11 @@ export class Registry { const descriptors = readDescriptors(browsersJSON); const findExecutablePath = (dir: string, name: keyof typeof EXECUTABLE_PATHS) => { let tokens = undefined; - if (hostPlatform.startsWith('ubuntu') || hostPlatform.startsWith('generic-linux')) + if (process.platform === 'linux') tokens = EXECUTABLE_PATHS[name]['linux']; - else if (hostPlatform.startsWith('mac')) + else if (process.platform === 'darwin') tokens = EXECUTABLE_PATHS[name]['mac']; - else if (hostPlatform.startsWith('win')) + else if (process.platform === 'win32') tokens = EXECUTABLE_PATHS[name]['win']; return tokens ? path.join(dir, ...tokens) : undefined; }; @@ -607,10 +614,15 @@ export class Registry { process.stdout.write('Skipping host requirements validation logic because `PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS` env variable is set.\n'); return; } - const ubuntuVersion = await getUbuntuVersion(); - if (browserName === 'firefox' && ubuntuVersion === '16.04') + const distributionInfo = await getLinuxDistributionInfo(); + if (browserName === 'firefox' && distributionInfo?.id === 'ubuntu' && distributionInfo?.version === '16.04') throw new Error(`Cannot launch Firefox on Ubuntu 16.04! Minimum required Ubuntu version for Firefox browser is 18.04`); + // Skip dependency validation for WebKit on non-ubuntu distributions since it takes + // forever and is not needed due to universal build. + if (os.platform() === 'linux' && browserName === 'webkit' && distributionInfo?.id !== 'ubuntu') + return; + if (os.platform() === 'linux') return await validateDependenciesLinux(sdkLanguage, linuxLddDirectories.map(d => path.join(browserDirectory, d)), dlOpenLibraries); if (os.platform() === 'win32' && os.arch() === 'x64') @@ -714,7 +726,7 @@ export class Registry { if (!downloadPathTemplate || !executablePath) throw new Error(`ERROR: Playwright does not support ${descriptor.name} on ${hostPlatform}`); if (hostPlatform === 'generic-linux' || hostPlatform === 'generic-linux-arm64') - logPolitely('BEWARE: your OS is not officially supported by Playwright; downloading Ubuntu build as a fallback.'); + logPolitely('BEWARE: your OS is not officially supported by Playwright; downloading fallback build.'); const downloadPath = util.format(downloadPathTemplate, descriptor.revision); let downloadURLs = PLAYWRIGHT_CDN_MIRRORS.map(mirror => `${mirror}/${downloadPath}`) ; diff --git a/packages/playwright-core/src/server/registry/nativeDeps.ts b/packages/playwright-core/src/server/registry/nativeDeps.ts index 2fed4d1432..a02483cf0b 100644 --- a/packages/playwright-core/src/server/registry/nativeDeps.ts +++ b/packages/playwright-core/src/server/registry/nativeDeps.ts @@ -647,6 +647,176 @@ export const deps: any = { 'libx264.so': 'libx264-163', 'libvpx.so.7': 'libvpx7' }, + }, + 'debian11': { + tools: [ + 'xvfb', + 'fonts-noto-color-emoji', + 'fonts-unifont', + 'libfontconfig1', + 'libfreetype6', + 'xfonts-cyrillic', + 'xfonts-scalable', + 'fonts-liberation', + 'fonts-ipafont-gothic', + 'fonts-wqy-zenhei', + 'fonts-tlwg-loma-otf', + 'fonts-freefont-ttf', + ], + chromium: [ + 'libasound2', + 'libatk-bridge2.0-0', + 'libatk1.0-0', + 'libatspi2.0-0', + 'libcairo2', + 'libcups2', + 'libdbus-1-3', + 'libdrm2', + 'libgbm1', + 'libglib2.0-0', + 'libnspr4', + 'libnss3', + 'libpango-1.0-0', + 'libwayland-client0', + 'libx11-6', + 'libxcb1', + 'libxcomposite1', + 'libxdamage1', + 'libxext6', + 'libxfixes3', + 'libxkbcommon0', + 'libxrandr2' + ], + firefox: [ + 'libasound2', + 'libatk1.0-0', + 'libcairo-gobject2', + 'libcairo2', + 'libdbus-1-3', + 'libdbus-glib-1-2', + 'libfontconfig1', + 'libfreetype6', + 'libgdk-pixbuf-2.0-0', + 'libglib2.0-0', + 'libgtk-3-0', + 'libharfbuzz0b', + 'libpango-1.0-0', + 'libpangocairo-1.0-0', + 'libx11-6', + 'libx11-xcb1', + 'libxcb-shm0', + 'libxcb1', + 'libxcomposite1', + 'libxcursor1', + 'libxdamage1', + 'libxext6', + 'libxfixes3', + 'libxi6', + 'libxrandr2', + 'libxrender1', + 'libxtst6' + ], + webkit: [ + // We use universal build on debian so webkit does not require any dependencies. + ], + lib2package: { + 'libasound.so.2': 'libasound2', + 'libatk-1.0.so.0': 'libatk1.0-0', + 'libatk-bridge-2.0.so.0': 'libatk-bridge2.0-0', + 'libatomic.so.1': 'libatomic1', + 'libatspi.so.0': 'libatspi2.0-0', + 'libc.so.6': 'libc6', + 'libcairo-gobject.so.2': 'libcairo-gobject2', + 'libcairo.so.2': 'libcairo2', + 'libcups.so.2': 'libcups2', + 'libdbus-1.so.3': 'libdbus-1-3', + 'libdbus-glib-1.so.2': 'libdbus-glib-1-2', + 'libdl.so.2': 'libc6', + 'libdrm.so.2': 'libdrm2', + 'libEGL.so.1': 'libegl1', + 'libenchant-2.so.2': 'libenchant-2-2', + 'libepoxy.so.0': 'libepoxy0', + 'libfontconfig.so.1': 'libfontconfig1', + 'libfreetype.so.6': 'libfreetype6', + 'libgbm.so.1': 'libgbm1', + 'libgcc_s.so.1': 'libgcc-s1', + 'libgcrypt.so.20': 'libgcrypt20', + 'libgdk_pixbuf-2.0.so.0': 'libgdk-pixbuf-2.0-0', + 'libgdk-3.so.0': 'libgtk-3-0', + 'libgio-2.0.so.0': 'libglib2.0-0', + 'libGLESv2.so.2': 'libgles2', + 'libglib-2.0.so.0': 'libglib2.0-0', + 'libGLX.so.0': 'libglx0', + 'libgmodule-2.0.so.0': 'libglib2.0-0', + 'libgobject-2.0.so.0': 'libglib2.0-0', + 'libgpg-error.so.0': 'libgpg-error0', + 'libgstallocators-1.0.so.0': 'libgstreamer-plugins-base1.0-0', + 'libgstapp-1.0.so.0': 'libgstreamer-plugins-base1.0-0', + 'libgstaudio-1.0.so.0': 'libgstreamer-plugins-base1.0-0', + 'libgstbase-1.0.so.0': 'libgstreamer1.0-0', + 'libgstcodecparsers-1.0.so.0': 'libgstreamer-plugins-bad1.0-0', + 'libgstfft-1.0.so.0': 'libgstreamer-plugins-base1.0-0', + 'libgstgl-1.0.so.0': 'libgstreamer-gl1.0-0', + 'libgstpbutils-1.0.so.0': 'libgstreamer-plugins-base1.0-0', + 'libgstreamer-1.0.so.0': 'libgstreamer1.0-0', + 'libgsttag-1.0.so.0': 'libgstreamer-plugins-base1.0-0', + 'libgstvideo-1.0.so.0': 'libgstreamer-plugins-base1.0-0', + 'libgtk-3.so.0': 'libgtk-3-0', + 'libharfbuzz-icu.so.0': 'libharfbuzz-icu0', + 'libharfbuzz.so.0': 'libharfbuzz0b', + 'libhyphen.so.0': 'libhyphen0', + 'libjavascriptcoregtk-4.0.so.18': 'libjavascriptcoregtk-4.0-18', + 'libjpeg.so.62': 'libjpeg62-turbo', + 'liblcms2.so.2': 'liblcms2-2', + 'libm.so.6': 'libc6', + 'libmanette-0.2.so.0': 'libmanette-0.2-0', + 'libnotify.so.4': 'libnotify4', + 'libnspr4.so': 'libnspr4', + 'libnss3.so': 'libnss3', + 'libnssutil3.so': 'libnss3', + 'libOpenGL.so.0': 'libopengl0', + 'libopenjp2.so.7': 'libopenjp2-7', + 'libopus.so.0': 'libopus0', + 'libpango-1.0.so.0': 'libpango-1.0-0', + 'libpangocairo-1.0.so.0': 'libpangocairo-1.0-0', + 'libpng16.so.16': 'libpng16-16', + 'libpthread.so.0': 'libc6', + 'libsecret-1.so.0': 'libsecret-1-0', + 'libsmime3.so': 'libnss3', + 'libsoup-2.4.so.1': 'libsoup2.4-1', + 'libsqlite3.so.0': 'libsqlite3-0', + 'libstdc++.so.6': 'libstdc++6', + 'libsystemd.so.0': 'libsystemd0', + 'libtasn1.so.6': 'libtasn1-6', + 'libvpx.so.6': 'libvpx6', + 'libwayland-client.so.0': 'libwayland-client0', + 'libwayland-egl.so.1': 'libwayland-egl1', + 'libwayland-server.so.0': 'libwayland-server0', + 'libwebkit2gtk-4.0.so.37': 'libwebkit2gtk-4.0-37', + 'libwebpdemux.so.2': 'libwebpdemux2', + 'libwoff2dec.so.1.0.2': 'libwoff1', + 'libwpe-1.0.so.1': 'libwpe-1.0-1', + 'libWPEBackend-fdo-1.0.so.1': 'libwpebackend-fdo-1.0-1', + 'libWPEWebKit-1.0.so.3': 'libwpewebkit-1.0-3', + 'libX11-xcb.so.1': 'libx11-xcb1', + 'libX11.so.6': 'libx11-6', + 'libxcb-shm.so.0': 'libxcb-shm0', + 'libxcb.so.1': 'libxcb1', + 'libXcomposite.so.1': 'libxcomposite1', + 'libXcursor.so.1': 'libxcursor1', + 'libXdamage.so.1': 'libxdamage1', + 'libXext.so.6': 'libxext6', + 'libXfixes.so.3': 'libxfixes3', + 'libXi.so.6': 'libxi6', + 'libxkbcommon.so.0': 'libxkbcommon0', + 'libxml2.so.2': 'libxml2', + 'libXrandr.so.2': 'libxrandr2', + 'libXrender.so.1': 'libxrender1', + 'libxslt.so.1': 'libxslt1.1', + 'libXt.so.6': 'libxt6', + 'libXtst.so.6': 'libxtst6', + 'libz.so.1': 'libzadc4', + }, } }; diff --git a/packages/playwright-core/src/utils/hostPlatform.ts b/packages/playwright-core/src/utils/hostPlatform.ts index cb2fe1cb6d..4f343b186d 100644 --- a/packages/playwright-core/src/utils/hostPlatform.ts +++ b/packages/playwright-core/src/utils/hostPlatform.ts @@ -15,7 +15,7 @@ */ import os from 'os'; -import { getUbuntuVersionSync } from './ubuntuVersion'; +import { getLinuxDistributionInfoSync } from './linuxUtils'; export type HostPlatform = 'win64' | 'mac10.13' | @@ -25,6 +25,7 @@ export type HostPlatform = 'win64' | 'mac12' | 'mac12-arm64' | 'ubuntu18.04' | 'ubuntu18.04-arm64' | 'ubuntu20.04' | 'ubuntu20.04-arm64' | + 'debian11' | 'generic-linux' | 'generic-linux-arm64' | ''; @@ -53,14 +54,19 @@ export const hostPlatform = ((): HostPlatform => { } if (platform === 'linux') { const archSuffix = os.arch() === 'arm64' ? '-arm64' : ''; - const ubuntuVersion = getUbuntuVersionSync(); - if (!ubuntuVersion) - return ('generic-linux' + archSuffix) as HostPlatform; - if (parseInt(ubuntuVersion, 10) <= 19) - return ('ubuntu18.04' + archSuffix) as HostPlatform; - if (parseInt(ubuntuVersion, 10) <= 21) - return ('ubuntu20.04' + archSuffix) as HostPlatform; - return ('ubuntu22.04' + archSuffix) as HostPlatform; + const distroInfo = getLinuxDistributionInfoSync(); + + // Pop!_OS is ubuntu-based and has the same versions. + if (distroInfo?.id === 'ubuntu' || distroInfo?.id === 'pop') { + if (parseInt(distroInfo.version, 10) <= 19) + return ('ubuntu18.04' + archSuffix) as HostPlatform; + if (parseInt(distroInfo.version, 10) <= 21) + return ('ubuntu20.04' + archSuffix) as HostPlatform; + return ('ubuntu22.04' + archSuffix) as HostPlatform; + } + if (distroInfo?.id === 'debian' && distroInfo?.version === '11' && !archSuffix) + return 'debian11'; + return ('generic-linux' + archSuffix) as HostPlatform; } if (platform === 'win32') return 'win64'; diff --git a/packages/playwright-core/src/utils/linuxUtils.ts b/packages/playwright-core/src/utils/linuxUtils.ts new file mode 100644 index 0000000000..d8e227f107 --- /dev/null +++ b/packages/playwright-core/src/utils/linuxUtils.ts @@ -0,0 +1,80 @@ +/** + * Copyright 2017 Google Inc. All rights reserved. + * Modifications copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; + +let didFailToReadOSRelease = false; +let osRelease: { + id: string, + version: string, +} | undefined; + +export async function getLinuxDistributionInfo(): Promise<{ id: string, version: string } | undefined> { + if (process.platform !== 'linux') + return undefined; + if (!osRelease && !didFailToReadOSRelease) { + try { + // List of /etc/os-release values for different distributions could be + // found here: https://gist.github.com/aslushnikov/8ceddb8288e4cf9db3039c02e0f4fb75 + const osReleaseText = await fs.promises.readFile('/etc/os-release', 'utf8'); + const fields = parseOSReleaseText(osReleaseText); + osRelease = { + id: fields.get('id') ?? '', + version: fields.get('version_id') ?? '', + }; + } catch (e) { + didFailToReadOSRelease = true; + } + } + return osRelease; +} + +export function getLinuxDistributionInfoSync(): { id: string, version: string } | undefined { + if (process.platform !== 'linux') + return undefined; + if (!osRelease && !didFailToReadOSRelease) { + try { + // List of /etc/os-release values for different distributions could be + // found here: https://gist.github.com/aslushnikov/8ceddb8288e4cf9db3039c02e0f4fb75 + const osReleaseText = fs.readFileSync('/etc/os-release', 'utf8'); + const fields = parseOSReleaseText(osReleaseText); + osRelease = { + id: fields.get('id') ?? '', + version: fields.get('version_id') ?? '', + }; + } catch (e) { + didFailToReadOSRelease = true; + } + } + return osRelease; +} + +function parseOSReleaseText(osReleaseText: string): Map { + const fields = new Map(); + for (const line of osReleaseText.split('\n')) { + const tokens = line.split('='); + const name = tokens.shift(); + let value = tokens.join('=').trim(); + if (value.startsWith('"') && value.endsWith('"')) + value = value.substring(1, value.length - 1); + if (!name) + continue; + fields.set(name.toLowerCase(), value); + } + return fields; +} + diff --git a/packages/playwright-core/src/utils/ubuntuVersion.ts b/packages/playwright-core/src/utils/ubuntuVersion.ts deleted file mode 100644 index 674938c293..0000000000 --- a/packages/playwright-core/src/utils/ubuntuVersion.ts +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2017 Google Inc. All rights reserved. - * Modifications copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs'; -import * as os from 'os'; - -let ubuntuVersionCached: string | undefined; - -export async function getUbuntuVersion(): Promise { - if (ubuntuVersionCached === undefined) - ubuntuVersionCached = await getUbuntuVersionAsyncInternal(); - return ubuntuVersionCached; -} - -export function getUbuntuVersionSync(): string { - if (ubuntuVersionCached === undefined) - ubuntuVersionCached = getUbuntuVersionSyncInternal(); - return ubuntuVersionCached; -} - -async function getUbuntuVersionAsyncInternal(): Promise { - if (os.platform() !== 'linux') - return ''; - let osReleaseText = await fs.promises.readFile('/etc/upstream-release/lsb-release', 'utf8').catch(e => ''); - if (!osReleaseText) - osReleaseText = await fs.promises.readFile('/etc/os-release', 'utf8').catch(e => ''); - if (!osReleaseText) - return ''; - return parseUbuntuVersion(osReleaseText); -} - -function getUbuntuVersionSyncInternal(): string { - if (os.platform() !== 'linux') - return ''; - try { - let osReleaseText: string; - if (fs.existsSync('/etc/upstream-release/lsb-release')) - osReleaseText = fs.readFileSync('/etc/upstream-release/lsb-release', 'utf8'); - else - osReleaseText = fs.readFileSync('/etc/os-release', 'utf8'); - if (!osReleaseText) - return ''; - return parseUbuntuVersion(osReleaseText); - } catch (e) { - return ''; - } -} - -export function parseOSReleaseText(osReleaseText: string): Map { - const fields = new Map(); - for (const line of osReleaseText.split('\n')) { - const tokens = line.split('='); - const name = tokens.shift(); - let value = tokens.join('=').trim(); - if (value.startsWith('"') && value.endsWith('"')) - value = value.substring(1, value.length - 1); - if (!name) - continue; - fields.set(name.toLowerCase(), value); - } - return fields; -} - -function parseUbuntuVersion(osReleaseText: string): string { - const fields = parseOSReleaseText(osReleaseText); - // For Linux mint - if (fields.get('distrib_id') && fields.get('distrib_id')?.toLowerCase() === 'ubuntu') - return fields.get('distrib_release') || ''; - - // For Pop!_OS - if (fields.get('id') && fields.get('id')?.toLowerCase() === 'pop') - return fields.get('version_id') || ''; - - if (!fields.get('name') || fields.get('name')?.toLowerCase() !== 'ubuntu') - return ''; - return fields.get('version_id') || ''; -}