diff --git a/packages/get-os-info/src/get-os-info.spec.ts b/packages/get-os-info/src/get-os-info.spec.ts
index f0b6ad6e..00beb2dc 100644
--- a/packages/get-os-info/src/get-os-info.spec.ts
+++ b/packages/get-os-info/src/get-os-info.spec.ts
@@ -2,16 +2,11 @@ import { expect } from 'chai';
import os from 'os';
import { promises as fs } from 'fs';
-import { getOsInfo } from './get-os-info';
+import { getOsInfo, parseDarwinInfo, parseLinuxInfo } from './get-os-info';
describe('get-os-info', function () {
- let osInfo;
- beforeEach(async function () {
- osInfo = await getOsInfo();
- });
-
- it('returns info from "os" module', function () {
- const { os_arch, os_type, os_version, os_release } = osInfo;
+ it('returns info from "os" module', async function () {
+ const { os_arch, os_type, os_version, os_release } = await getOsInfo();
expect({ os_arch, os_type, os_version, os_release }).to.deep.equal({
os_arch: os.arch(),
os_type: os.type(),
@@ -21,13 +16,31 @@ describe('get-os-info', function () {
});
describe('on linux', function () {
- beforeEach(function () {
+ it('parses os-release file', function () {
+ // Copied from https://manpages.ubuntu.com/manpages/focal/man5/os-release.5.html#example
+ const fixture = `
+ NAME=Fedora
+ VERSION="17 (Beefy Miracle)"
+ ID=fedora
+ VERSION_ID=17
+ PRETTY_NAME="Fedora 17 (Beefy Miracle)"
+ ANSI_COLOR="0;34"
+ CPE_NAME="cpe:/o:fedoraproject:fedora:17"
+ HOME_URL="https://fedoraproject.org/"
+ BUG_REPORT_URL="https://bugzilla.redhat.com/"
+ `;
+
+ expect(parseLinuxInfo(fixture)).to.deep.equal({
+ os_linux_dist: 'fedora',
+ os_linux_release: '17',
+ });
+ });
+
+ it('returns info from /etc/releases', async function () {
if (process.platform !== 'linux') {
this.skip();
}
- });
- it('returns info from /etc/releases', async function () {
const etcRelease = await fs.readFile('/etc/os-release', 'utf-8');
const releaseKv = etcRelease
@@ -47,11 +60,66 @@ describe('get-os-info', function () {
expect(distroId).to.match(/^(rhel|ubuntu|debian)$/);
expect(distroVer).to.match(/^\d+/);
- const { os_linux_dist, os_linux_release } = osInfo;
+ const { os_linux_dist, os_linux_release } = await getOsInfo();
expect({ os_linux_dist, os_linux_release }).to.deep.equal({
os_linux_dist: distroId,
os_linux_release: distroVer,
});
});
});
+
+ describe('on darwin', function () {
+ it('parses the SystemVersion.plist file', function () {
+ const fixture = `
+
+
+
+
+ BuildID
+ 2B3829A8-E319-11EF-8892-025514DE0AB1
+ ProductBuildVersion
+ 24D70
+ ProductCopyright
+ 1983-2025 Apple Inc.
+ ProductName
+ macOS
+ ProductUserVisibleVersion
+ 15.3.1
+ ProductVersion
+ 15.3.1
+ iOSSupportVersion
+ 18.3
+
+
+ `;
+
+ expect(parseDarwinInfo(fixture)).to.deep.equal({
+ os_darwin_product_name: 'macOS',
+ os_darwin_product_version: '15.3.1',
+ os_darwin_product_build_version: '24D70',
+ });
+ });
+
+ it('returns info from /System/Library/CoreServices/SystemVersion.plist', async function () {
+ if (process.platform !== 'darwin') {
+ this.skip();
+ }
+
+ const systemVersionPlist = await fs.readFile(
+ '/System/Library/CoreServices/SystemVersion.plist',
+ 'utf-8'
+ );
+
+ const {
+ os_darwin_product_name,
+ os_darwin_product_version,
+ os_darwin_product_build_version,
+ } = await getOsInfo();
+
+ // Instead of reimplementing the parser, we simply check that the values are present in the original file
+ expect(systemVersionPlist).contains(os_darwin_product_name);
+ expect(systemVersionPlist).contains(os_darwin_product_version);
+ expect(systemVersionPlist).contains(os_darwin_product_build_version);
+ });
+ });
});
diff --git a/packages/get-os-info/src/get-os-info.ts b/packages/get-os-info/src/get-os-info.ts
index 67585f50..818f05b9 100644
--- a/packages/get-os-info/src/get-os-info.ts
+++ b/packages/get-os-info/src/get-os-info.ts
@@ -1,46 +1,89 @@
import os from 'os';
import { promises as fs } from 'fs';
+type LinuxInfo = {
+ os_linux_dist: string;
+ os_linux_release: string;
+};
+
+type DarwinInfo = {
+ os_darwin_product_name: string;
+ os_darwin_product_version: string;
+ os_darwin_product_build_version: string;
+};
+
type OsInfo = {
os_type: string;
os_version: string;
os_arch: string;
os_release: string;
- os_linux_dist?: string;
- os_linux_release?: string;
-};
+} & Partial &
+ Partial;
-async function getLinuxInfo(): Promise<{
- os_linux_dist: string;
- os_linux_release: string;
-}> {
+async function getLinuxInfo(): Promise {
try {
const releaseFile = '/etc/os-release';
const etcRelease = await fs.readFile(releaseFile, 'utf-8');
+ return parseLinuxInfo(etcRelease);
+ } catch (e) {
+ return {
+ os_linux_dist: 'unknown',
+ os_linux_release: 'unknown',
+ };
+ }
+}
+
+export function parseLinuxInfo(etcRelease: string): LinuxInfo {
+ const osReleaseEntries = etcRelease
+ .split('\n')
+ .map((l) => l.trim())
+ .filter(Boolean)
+ .map((l) => l.split('='))
+ .map(([k, v]) => [k, (v || '').replace(/^["']/, '').replace(/["']$/, '')]);
- const osReleaseEntries = etcRelease
- .split('\n')
- .map((l) => l.trim())
- .filter(Boolean)
- .map((l) => l.split('='))
- .map(([k, v]) => [
- k,
- (v || '').replace(/^["']/, '').replace(/["']$/, ''),
- ]);
+ const osReleaseKv = Object.fromEntries(osReleaseEntries);
- const osReleaseKv = Object.fromEntries(osReleaseEntries);
+ return {
+ os_linux_dist: osReleaseKv.ID || 'unknown',
+ os_linux_release: osReleaseKv.VERSION_ID || 'unknown',
+ };
+}
+async function getDarwinInfo(): Promise {
+ try {
+ const systemVersionPlistPath =
+ '/System/Library/CoreServices/SystemVersion.plist';
+ const systemVersionPlist = await fs.readFile(
+ systemVersionPlistPath,
+ 'utf-8'
+ );
+ return parseDarwinInfo(systemVersionPlist);
+ } catch (e) {
return {
- os_linux_dist: osReleaseKv.ID || 'unknown',
- os_linux_release: osReleaseKv.VERSION_ID || 'unknown',
+ os_darwin_product_name: 'unknown',
+ os_darwin_product_version: 'unknown',
+ os_darwin_product_build_version: 'unknown',
};
- } catch (e) {
- // couldn't read /etc/os-release
}
+}
+
+export function parseDarwinInfo(systemVersionPlist: string): DarwinInfo {
+ const match = systemVersionPlist.matchAll(
+ /(?[^<]+)<\/key>\s*(?[^<]+)<\/string>/gm
+ );
+
+ const {
+ ProductName: os_darwin_product_name = 'unknown',
+ ProductVersion: os_darwin_product_version = 'unknown',
+ ProductBuildVersion: os_darwin_product_build_version = 'unknown',
+ } = Object.fromEntries(
+ Array.from(match).map((m) => [m.groups?.key, m.groups?.value])
+ );
return {
- os_linux_dist: 'unknown',
- os_linux_release: 'unknown',
+ os_darwin_product_name,
+ os_darwin_product_version,
+ os_darwin_product_build_version,
};
}
@@ -51,5 +94,6 @@ export async function getOsInfo(): Promise {
os_arch: os.arch(),
os_release: os.release(),
...(process.platform === 'linux' ? await getLinuxInfo() : {}),
+ ...(process.platform === 'darwin' ? await getDarwinInfo() : {}),
};
}