Skip to content

Commit

Permalink
SCANNPM-2 Detect Platform
Browse files Browse the repository at this point in the history
  • Loading branch information
lucas-paulger-sonarsource committed Apr 12, 2024
1 parent e055920 commit ff7b7e7
Show file tree
Hide file tree
Showing 9 changed files with 931 additions and 604 deletions.
6 changes: 4 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
*/

module.exports = {
collectCoverageFrom: ['src/**/*.js'],
preset: 'ts-jest',
testEnvironment: 'node',
collectCoverageFrom: ['src/**/*.{js,ts}'],
coverageReporters: ['lcov', 'text'],
coveragePathIgnorePatterns: ['.fixture.', '/fixtures/'],
moduleFileExtensions: ['js', 'ts', 'json'],
moduleDirectories: ['node_modules'],
testResultsProcessor: 'jest-sonar-reporter',
testMatch: ['<rootDir>/test/unit/**/*.test.js'],
testMatch: ['<rootDir>/test/unit/**/*.test.{js,ts}'],
testTimeout: 20000,
};
1,251 changes: 666 additions & 585 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@
"tar-stream": "3.1.7"
},
"devDependencies": {
"@types/adm-zip": "^0.5.5",
"@types/fs-extra": "^11.0.4",
"@types/adm-zip": "0.5.5",
"@types/fs-extra": "11.0.4",
"@types/jest": "29.5.12",
"@types/proxy-from-env": "^1.0.4",
"@types/semver": "^7.5.8",
"@types/tar-stream": "^3.1.3",
"@types/proxy-from-env": "1.0.4",
"@types/semver": "7.5.8",
"@types/sinon": "17.0.3",
"@types/tar-stream": "3.1.3",
"@typescript-eslint/parser": "7.4.0",
"chai": "4.4.1",
"eslint": "8.57.0",
Expand All @@ -51,6 +52,7 @@
"pretty-quick": "4.0.0",
"rimraf": "5.0.5",
"sinon": "17.0.1",
"ts-jest": "29.1.2",
"typescript": "5.4.3"
},
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion src/bin/sonar-scanner
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
const scan = require('../../build/src/index').scan;
const scan = require('../../build/index').scan;

const options = process.argv.length > 2 ? process.argv.slice(2) : [];

Expand Down
7 changes: 6 additions & 1 deletion src/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ let logLevel = DEFAULT_LOG_LEVEL;

export function log(level: LogLevel, ...message: unknown[]) {
if (logLevelValues[level] <= logLevelValues[logLevel]) {
console.log(`[${level}] Bootstrapper ${message}`);
const messageStr = message
.map(m => {
return typeof m === 'object' ? JSON.stringify(m) : m;
})
.join(' ');
console.log(`[${level}] Bootstrapper ${messageStr}`);
}
}

Expand Down
97 changes: 97 additions & 0 deletions src/platform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* sonar-scanner-npm
* Copyright (C) 2022-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import fs from 'fs';
import { LogLevel, log } from './logging';
import { PlatformInfo, SupportedOS } from './types';

export function getArch(): NodeJS.Architecture {
return process.arch;
}

function isLinux(): boolean {
return /^linux/.test(process.platform);
}

function isSupportedLinux(): SupportedOS | false {
const linuxMapping: { [nodePlatform: string]: SupportedOS } = {
linux: isAlpineLinux() ? 'alpine' : 'linux',
openbsd: 'linux',
sunos: 'linux',
freebsd: 'linux',
};

return linuxMapping[process.platform] || false;
}

function isWindows(): boolean {
return /^win/.test(process.platform);
}

function isMac(): boolean {
return /^darwin/.test(process.platform);
}

/**
* @see https://github.com/microsoft/vscode/blob/64874113ad3c59e8d045f75dc2ef9d33d13f3a03/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts#L171C1-L190C1
*/

function isAlpineLinux(): boolean {
if (!isLinux()) {
return false;
}
let content: string | undefined;
try {
const fileContent = fs.readFileSync('/etc/os-release');
content = fileContent.toString();
} catch (error) {
try {
const fileContent = fs.readFileSync('/usr/lib/os-release');
content = fileContent.toString();
} catch (error) {
log(LogLevel.ERROR, 'Failed to read /etc/os-release or /usr/lib/os-release');
}
}
return !!content && (content.match(/^ID=([^\u001b\r\n]*)/m) || [])[1] === 'alpine';
}

function getSupportedOS(): SupportedOS {
if (isWindows()) {
return 'windows';
}

if (isMac()) {
return 'macos';
}

const supportedLinux = isSupportedLinux();
if (supportedLinux) {
return supportedLinux;
}

throw new Error(`Unsupported platform: ${process.platform}`);
}

export function getPlatformInfo(): PlatformInfo {
return {
os: getSupportedOS(),
arch: getArch(),
};
}
22 changes: 12 additions & 10 deletions src/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

export type ScanOptions = {
serverUrl: string;
token: string;
jvmOptions: string[];
options?: { [key: string]: string };
caPath: string;
logLevel?: string;
verbose?: boolean;
};
import { log, LogLevel } from './logging';
import { getPlatformInfo } from './platform';
import { ScanOptions } from './types';

export async function scan(scanOptions: ScanOptions, cliArgs?: string[]) {
// TODO: NPMSCAN-2 new bootstrapper sequence
log(LogLevel.DEBUG, 'Finding platform info');
const platformInfo = getPlatformInfo();
log(LogLevel.INFO, 'Platform: ', platformInfo);

//TODO: verifyJRE based on platform
//TODO: fetchJRE
//TODO: verifyScannerEngine
//TODO: fetchScannerEngine
//TODO:
}
10 changes: 10 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ export type ScannerLogEntry = {
};

export type ScannerParams = { [key: string]: string };

export type ScanOptions = {
serverUrl: string;
token: string;
jvmOptions: string[];
options?: { [key: string]: string };
caPath: string;
logLevel?: string;
verbose?: boolean;
};
128 changes: 128 additions & 0 deletions test/unit/platform.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* sonar-scanner-npm
* Copyright (C) 2022-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import * as platform from '../../src/platform';
import * as logging from '../../src/logging';
import fs from 'fs';
import sinon from 'sinon';

// Mock the getPlatform function

describe('getPlatformInfo', () => {
it('detect macos', () => {
const platformStub = sinon.stub(process, 'platform').value('darwin');
const archStub = sinon.stub(process, 'arch').value('arm64');

expect(platform.getPlatformInfo()).toEqual({
os: 'macos',
arch: 'arm64',
});

platformStub.restore();
archStub.restore();
});

it('detect windows', () => {
const platformStub = sinon.stub(process, 'platform').value('win32');
const archStub = sinon.stub(process, 'arch').value('x64');

expect(platform.getPlatformInfo()).toEqual({
os: 'windows',
arch: 'x64',
});

platformStub.restore();
archStub.restore();
});

it('detect supported linux', () => {
const platformStub = sinon.stub(process, 'platform').value('openbsd');
const archStub = sinon.stub(process, 'arch').value('x64');

expect(platform.getPlatformInfo()).toEqual({
os: 'linux',
arch: 'x64',
});

platformStub.restore();
archStub.restore();
});

it('detect alpine', () => {
const platformStub = sinon.stub(process, 'platform').value('linux');
const archStub = sinon.stub(process, 'arch').value('x64');
const fsReadStub = sinon.stub(fs, 'readFileSync');
fsReadStub.withArgs('/etc/os-release').returns('NAME="Alpine Linux"\nID=alpine');

expect(platform.getPlatformInfo()).toEqual({
os: 'alpine',
arch: 'x64',
});

platformStub.restore();
archStub.restore();
fsReadStub.restore();
});

it('detect alpine with fallback', () => {
const platformStub = sinon.stub(process, 'platform').value('linux');
const archStub = sinon.stub(process, 'arch').value('x64');
const fsReadStub = sinon.stub(fs, 'readFileSync');
fsReadStub.withArgs('/usr/lib/os-release').returns('NAME="Alpine Linux"\nID=alpine');

expect(platform.getPlatformInfo()).toEqual({
os: 'alpine',
arch: 'x64',
});

platformStub.restore();
archStub.restore();
fsReadStub.restore();
});

it('failed to detect alpine', () => {
const logSpy = sinon.spy(logging, 'log');
const platformStub = sinon.stub(process, 'platform').value('linux');
const archStub = sinon.stub(process, 'arch').value('x64');

expect(platform.getPlatformInfo()).toEqual({
os: 'linux',
arch: 'x64',
});

expect(
logSpy.calledWith(
logging.LogLevel.ERROR,
'Failed to read /etc/os-release or /usr/lib/os-release',
),
).toBe(true);

platformStub.restore();
archStub.restore();
logSpy.restore();
});

it('throw if unsupported', () => {
const stub = sinon.stub(process, 'platform').value('android');

expect(() => platform.getPlatformInfo()).toThrow('Unsupported platform: android');
stub.restore();
});
});

0 comments on commit ff7b7e7

Please sign in to comment.