Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extra options #27

Merged
merged 9 commits into from
Apr 18, 2023
33 changes: 32 additions & 1 deletion packages/nx-sonarqube/src/executors/scan/executor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { DependencyType, ExecutorContext, ProjectGraph } from '@nrwl/devkit';
import * as fs from 'fs';
import * as sonarQubeScanner from 'sonarqube-scanner';
import * as childProcess from 'child_process';

import { getScannerOptions } from './utils/utils';
process.env['NODE_TEST'] = 'true';
let projectGraph: ProjectGraph;
let context: ExecutorContext;

Expand Down Expand Up @@ -307,4 +308,34 @@ describe('Scan Executor', () => {
);
expect(output.success).toBeFalsy();
});

it('should override environment variable over options over extra ', async () => {
jest.spyOn(fs, 'readFileSync').mockReturnValue(jestConfig);
sonarQubeScanner.async.mockResolvedValue(true);
process.env['SONAR_BRANCH'] = 'main';
process.env['SONAR_VERBOSE'] = 'true';

const output = getScannerOptions(
{
hostUrl: 'url',
verbose: false,
projectKey: 'key',
qualityGate: true,
organization: 'org',
testInclusions: 'include',
extra: {
'sonar.test.inclusions': 'dontInclude',
'sonar.log.level': 'DEBUG',
},
},
'src/',
'coverage/apps',
''
);

expect(output['sonar.branch']).toBe('main');
expect(output['sonar.verbose']).toBe('true');
expect(output['sonar.log.level']).toBe('DEBUG');
expect(output['sonar.test.inclusions']).toBe('include');
});
});
1 change: 1 addition & 0 deletions packages/nx-sonarqube/src/executors/scan/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export interface ScanExecutorSchema {
skipImplicitDeps?: boolean;
testInclusions?: string;
verbose?: boolean;
extra?: { [option: string]: string };
}
89 changes: 71 additions & 18 deletions packages/nx-sonarqube/src/executors/scan/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ScanExecutorSchema } from '../schema';

import {
createProjectGraphAsync,
DependencyType,
Expand All @@ -13,13 +14,37 @@ import { tsquery } from '@phenomnomnominal/tsquery';
import { execSync } from 'child_process';
import * as sonarQubeScanner from 'sonarqube-scanner';
import { TargetConfiguration } from 'nx/src/config/workspace-json-project-json';
interface OptionMarshaller {
Options(): { [option: string]: string };
}

export declare type WorkspaceLibrary = {
name: string;
type: DependencyType | string;
sourceRoot: string;
testTarget?: TargetConfiguration;
};
class ExtraMarshaller implements OptionMarshaller {
private readonly options: { [option: string]: string };
constructor(options: { [option: string]: string }) {
this.options = options;
}
Options(): { [p: string]: string } {
return this.options;
}
}
class EnvMarshaller implements OptionMarshaller {
Options(): { [p: string]: string } {
return Object.keys(process.env)
.filter((e) => e.startsWith('SONAR'))
.reduce((option, env) => {
let sonarEnv = env.toLowerCase();
sonarEnv = sonarEnv.replace('_', '.');
option[sonarEnv] = process.env[env];
return option;
}, {});
}
}

async function determinePaths(
options: ScanExecutorSchema,
Expand Down Expand Up @@ -92,15 +117,39 @@ async function determinePaths(
export async function scanner(
options: ScanExecutorSchema,
context: ExecutorContext
) {
): Promise<{ success: boolean }> {
const paths = await determinePaths(options, context);

logger.log(`Included sources: ${paths.sources}`);
if (!options.qualityGate) logger.warn(`Skipping quality gate check`);

let branch = '';
if (options.branches) {
branch = execSync('git rev-parse --abbrev-ref HEAD').toString();
}
const scannerOptions = getScannerOptions(
options,
paths.sources,
paths.lcovPaths,
branch
);
const success = await sonarQubeScanner.async({
serverUrl: options.hostUrl,
options: scannerOptions,
});
return {
success: success,
};
}
export function getScannerOptions(
options: ScanExecutorSchema,
sources: string,
lcovPaths: string,
branch: string
): { [option: string]: string } {
let scannerOptions: { [option: string]: string } = {
'sonar.exclusions': options.exclusions,
'sonar.javascript.lcov.reportPaths': paths.lcovPaths,
'sonar.javascript.lcov.reportPaths': lcovPaths,
'sonar.language': 'ts',
'sonar.login': process.env.SONAR_LOGIN,
'sonar.organization': options.organization,
Expand All @@ -111,29 +160,23 @@ export async function scanner(
'sonar.qualitygate.timeout': options.qualityGateTimeout,
'sonar.qualitygate.wait': String(options.qualityGate),
'sonar.scm.provider': 'git',
'sonar.sources': paths.sources,
'sonar.sources': sources,
'sonar.sourceEncoding': 'UTF-8',
'sonar.tests': paths.sources,
'sonar.tests': sources,
'sonar.test.inclusions': options.testInclusions,
'sonar.typescript.tsconfigPath': 'tsconfig.base.json',
'sonar.verbose': String(options.verbose),
};

if (options.branches) {
scannerOptions = {
'sonar.branch.name': execSync(
'git rev-parse --abbrev-ref HEAD'
).toString(),
...scannerOptions,
};
scannerOptions['sonar.branch.name'] = branch;
}

await sonarQubeScanner.async({
serverUrl: options.hostUrl,
options: scannerOptions,
});
scannerOptions = combineOptions(
new ExtraMarshaller(options.extra),
new EnvMarshaller(),
scannerOptions
);
return scannerOptions;
}

async function getDependentPackagesForProject(name: string): Promise<{
workspaceLibraries: WorkspaceLibrary[];
}> {
Expand All @@ -151,7 +194,17 @@ async function getDependentPackagesForProject(name: string): Promise<{
workspaceLibraries: [...workspaceLibraries.values()],
});
}

function combineOptions(
extraOptions: ExtraMarshaller,
envOptions: EnvMarshaller,
scannerOptions: { [option: string]: string }
): { [option: string]: string } {
return {
...extraOptions.Options(),
...scannerOptions,
...envOptions.Options(),
};
}
function collectDependencies(
projectGraph: ProjectGraph,
name: string,
Expand Down