Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/DXHeroes/dx-scanner into …
Browse files Browse the repository at this point in the history
…feature/extend-services-interface
  • Loading branch information
adelkahomolova committed Dec 11, 2019
2 parents 0648ce9 + 0296989 commit ce8b4dd
Show file tree
Hide file tree
Showing 21 changed files with 206 additions and 137 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@
"@octokit/rest": "16.35.0",
"@types/eslint": "6.1.3",
"@types/js-yaml": "3.12.1",
"@types/table": "^4.0.7",
"@types/qs": "^6.9.0",
"@types/yaml": "^1.2.0",
"axios": "^0.19.0",
"axios": "0.19.0",
"bitbucket": "1.15.2",
"colors": "1.4.0",
"cross-env": "6.0.3",
Expand All @@ -56,6 +57,7 @@
"reflect-metadata": "0.1.13",
"semver": "6.3.0",
"simple-git": "1.126.0",
"table": "^5.4.6",
"toposort": "2.0.2",
"ts-node": "8.5.4",
"tslib": "1.10.0",
Expand Down
19 changes: 0 additions & 19 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,6 @@ export interface Repository {
authString?: string;
}

export interface Project {
components: DeprecatedProjectComponent[];
}

/**
* @deprecated
*/
export interface DeprecatedProjectComponent {
githubUrl?: string;
path: string;
git?: GitInfo;
language: ProgrammingLanguage;
type: ProjectComponentType;
platform: ProjectComponentPlatform;
framework: ProjectComponentFramework;
packageManagement?: PackageManagement;
testing: TestingInfo;
}

export interface PracticeMetadata {
id: string;
name: string;
Expand Down
11 changes: 10 additions & 1 deletion src/practices/IPractice.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { PracticeEvaluationResult } from '../model';
import { PracticeContext } from '../contexts/practice/PracticeContext';
import { ReportTable, ReportText } from '../reporters/ReporterData';

export interface IPractice<T extends {} = {}> {
data?: Partial<T> & PracticeData;

export interface IPractice {
/**
* Returns true if this practice is applicable for the given project component
*
Expand All @@ -21,3 +24,9 @@ export interface IPractice {
*/
evaluate(ctx: PracticeContext): Promise<PracticeEvaluationResult>;
}

export type PracticeData = {
details?: PracticeDetail[];
};

export type PracticeDetail = ReportTable | ReportText;
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { PracticeEvaluationResult } from '../../model';
import { DependenciesVersionMajorLevel } from './DependenciesVersionMajorLevel';
import { DependenciesVersionMajorLevelPractice } from './DependenciesVersionMajorLevel';
import { createTestContainer, TestContainerContext } from '../../inversify.config';
import ncu from 'npm-check-updates';
import { JavaScriptPackageInspector } from '../../inspectors/package/JavaScriptPackageInspector';
import { mockPackage } from '../../test/helpers/mockPackage';
jest.mock('npm-check-updates');

describe('DependenciesVersionPractice of Major Level', () => {
let practice: DependenciesVersionMajorLevel;
let practice: DependenciesVersionMajorLevelPractice;
let containerCtx: TestContainerContext;
const mockedNcu = <jest.Mock>ncu.run;
const MockedJSPackageInspector = <jest.Mock<JavaScriptPackageInspector>>(<unknown>JavaScriptPackageInspector);
let mockJsPackageInspector: JavaScriptPackageInspector;

beforeAll(async () => {
containerCtx = createTestContainer();
containerCtx.container.bind('DependenciesVersionMajorLevel').to(DependenciesVersionMajorLevel);
containerCtx.container.bind('DependenciesVersionMajorLevel').to(DependenciesVersionMajorLevelPractice);
practice = containerCtx.container.get('DependenciesVersionMajorLevel');
mockJsPackageInspector = new MockedJSPackageInspector();
});
Expand All @@ -34,6 +34,7 @@ describe('DependenciesVersionPractice of Major Level', () => {

const evaluated = await practice.evaluate(containerCtx.practiceContext);
expect(evaluated).toEqual(PracticeEvaluationResult.notPracticing);
expect(practice.data.details).not.toBeUndefined();
});

it('practicing if newest package version dependency of major level', async () => {
Expand Down
55 changes: 30 additions & 25 deletions src/practices/JavaScript/DependenciesVersionMajorLevel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,71 +4,76 @@ import { Package } from '../../inspectors/IPackageInspector';
import { PackageInspectorBase, SemverLevel } from '../../inspectors/package/PackageInspectorBase';
import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '../../model';
import { DxPractice } from '../DxPracticeDecorator';
import { IPractice } from '../IPractice';
import { PracticeBase } from '../PracticeBase';
import { ReportDetailType } from '../../reporters/ReporterData';

@DxPractice({
id: 'JavaScript.DependenciesVersionMajorLevel',
name: 'Update Dependencies of Major Level',
impact: PracticeImpact.small,
suggestion: 'Keep the dependencies updated to have all possible features. Use, for example, Renovate Bot.',
suggestion: 'Keep the dependencies updated to have all possible features. Use, for example, npm-check-updates.',
reportOnlyOnce: true,
url: 'https://renovatebot.com/',
url: 'https://github.com/tjunnone/npm-check-updates',
})
export class DependenciesVersionMajorLevel implements IPractice {
export class DependenciesVersionMajorLevelPractice extends PracticeBase {
async isApplicable(ctx: PracticeContext): Promise<boolean> {
return (
ctx.projectComponent.language === ProgrammingLanguage.JavaScript || ctx.projectComponent.language === ProgrammingLanguage.TypeScript
);
}

async evaluate(ctx: PracticeContext): Promise<PracticeEvaluationResult> {
if (ctx.fileInspector === undefined || ctx.packageInspector === undefined) {
if (!ctx.fileInspector || !ctx.packageInspector || !ctx.packageInspector.packages) {
return PracticeEvaluationResult.unknown;
}

const pkgs = ctx.packageInspector.packages;
if (pkgs === undefined) {
return PracticeEvaluationResult.unknown;
}

const result = await DependenciesVersionMajorLevel.runNcu(pkgs);
const practiceEvaluationResult = DependenciesVersionMajorLevel.isPracticing(result, SemverLevel.major, pkgs);
const result = await this.runNcu(pkgs);
const pkgsToUpdate = this.packagesToBeUpdated(result, SemverLevel.major, pkgs);
this.setData(pkgsToUpdate);

return practiceEvaluationResult || PracticeEvaluationResult.practicing;
if (pkgsToUpdate.length > 0) return PracticeEvaluationResult.notPracticing;
return PracticeEvaluationResult.practicing;
}

static async runNcu(pkgs: Package[] | undefined) {
async runNcu(pkgs: Package[] | undefined) {
const fakePkgJson: { dependencies: { [key: string]: string } } = { dependencies: {} };

pkgs &&
pkgs.forEach((p) => {
fakePkgJson.dependencies[p.name] = p.requestedVersion.value;
});

const result = await ncu.run({
const pkgsToBeUpdated = await ncu.run({
packageData: JSON.stringify(fakePkgJson),
});

return result;
return pkgsToBeUpdated;
}

static isPracticing(
result: { [key: string]: string },
semverVersion: SemverLevel,
pkgs: Package[],
): PracticeEvaluationResult | undefined {
for (const packageName in result) {
const parsedVersion = PackageInspectorBase.semverToPackageVersion(result[packageName]);
packagesToBeUpdated(pkgsWithNewVersion: { [key: string]: string }, semverLevel: SemverLevel, pkgs: Package[]) {
// packages with Major level to be updated
const pkgsToUpdate: PkgToUpdate[] = [];

for (const packageName in pkgsWithNewVersion) {
const parsedVersion = PackageInspectorBase.semverToPackageVersion(pkgsWithNewVersion[packageName]);
if (parsedVersion) {
for (const pkg of pkgs) {
if (pkg.name === packageName) {
if (parsedVersion[semverVersion] > pkg.lockfileVersion[semverVersion]) {
return PracticeEvaluationResult.notPracticing;
if (parsedVersion[semverLevel] > pkg.lockfileVersion[semverLevel]) {
pkgsToUpdate.push({ name: pkg.name, newVersion: parsedVersion.value, currentVersion: pkg.lockfileVersion.value });
}
}
}
}
}
return undefined;

return pkgsToUpdate;
}

setData(pkgsToUpdate: PkgToUpdate[]): void {
this.data.details = [{ type: ReportDetailType.table, headers: ['Name', 'New', 'Current'], data: pkgsToUpdate }];
}
}

export type PkgToUpdate = { name: string; newVersion: string; currentVersion: string };
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import { mockPackage } from '../../test/helpers/mockPackage';
import { JavaScriptPackageInspector } from '../../inspectors/package/JavaScriptPackageInspector';
import { createTestContainer, TestContainerContext } from '../../inversify.config';
import { PracticeEvaluationResult } from '../../model';
import { DependenciesVersionMinorPatchLevel } from './DependenciesVersionMinorPatchLevel';
import { DependenciesVersionMinorPatchLevelPractice } from './DependenciesVersionMinorPatchLevel';
jest.mock('npm-check-updates');

describe('DependenciesVersionPractice of Minor and Patch Level', () => {
let practice: DependenciesVersionMinorPatchLevel;
let practice: DependenciesVersionMinorPatchLevelPractice;
let containerCtx: TestContainerContext;
const mockedNcu = <jest.Mock>ncu.run;
const MockedJSPackageInspector = <jest.Mock<JavaScriptPackageInspector>>(<unknown>JavaScriptPackageInspector);
let mockJsPackageInspector: JavaScriptPackageInspector;

beforeAll(async () => {
containerCtx = createTestContainer();
containerCtx.container.bind('DependenciesVersionMinorPatchLevel').to(DependenciesVersionMinorPatchLevel);
containerCtx.container.bind('DependenciesVersionMinorPatchLevel').to(DependenciesVersionMinorPatchLevelPractice);
practice = containerCtx.container.get('DependenciesVersionMinorPatchLevel');
mockJsPackageInspector = new MockedJSPackageInspector();
});
Expand All @@ -34,6 +34,7 @@ describe('DependenciesVersionPractice of Minor and Patch Level', () => {

const evaluated = await practice.evaluate(containerCtx.practiceContext);
expect(evaluated).toEqual(PracticeEvaluationResult.notPracticing);
expect(practice.data.details).not.toBeUndefined();
});

it('practicing if newest package version dependency of minor or patch level', async () => {
Expand Down
24 changes: 11 additions & 13 deletions src/practices/JavaScript/DependenciesVersionMinorPatchLevel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,41 @@ import { PracticeContext } from '../../contexts/practice/PracticeContext';
import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '../../model';
import { DxPractice } from '../DxPracticeDecorator';
import { IPractice } from '../IPractice';
import { DependenciesVersionMajorLevel } from './DependenciesVersionMajorLevel';
import { DependenciesVersionMajorLevelPractice } from './DependenciesVersionMajorLevel';
import { SemverLevel } from '../../inspectors/package/PackageInspectorBase';
import { flatten } from 'lodash';

@DxPractice({
id: 'JavaScript.DependenciesVersionMinorPatchLevel',
name: 'Update Dependencies of Minor and Patch Level',
impact: PracticeImpact.high,
suggestion: 'Keep the dependencies updated to eliminate security concerns and compatibility issues. Use, for example, Renovate Bot.',
suggestion: 'Keep the dependencies updated to eliminate security concerns and compatibility issues. Use, for example, npm-check-updates.',
reportOnlyOnce: true,
url: 'https://renovatebot.com/',
url: 'https://github.com/tjunnone/npm-check-updates',
})
export class DependenciesVersionMinorPatchLevel extends DependenciesVersionMajorLevel implements IPractice {
export class DependenciesVersionMinorPatchLevelPractice extends DependenciesVersionMajorLevelPractice implements IPractice {
async isApplicable(ctx: PracticeContext): Promise<boolean> {
return (
ctx.projectComponent.language === ProgrammingLanguage.JavaScript || ctx.projectComponent.language === ProgrammingLanguage.TypeScript
);
}

async evaluate(ctx: PracticeContext): Promise<PracticeEvaluationResult> {
if (ctx.fileInspector === undefined || ctx.packageInspector === undefined) {
if (!ctx.fileInspector || !ctx.packageInspector || !ctx.packageInspector.packages) {
return PracticeEvaluationResult.unknown;
}

const pkgs = ctx.packageInspector.packages;
if (pkgs === undefined) {
return PracticeEvaluationResult.unknown;
}

const result = await DependenciesVersionMajorLevel.runNcu(pkgs);
const result = await this.runNcu(pkgs);

const patchLevel = DependenciesVersionMajorLevel.isPracticing(result, SemverLevel.patch, pkgs);
const minorLevel = DependenciesVersionMajorLevel.isPracticing(result, SemverLevel.minor, pkgs);
const patchLevelPkgs = this.packagesToBeUpdated(result, SemverLevel.patch, pkgs);
const minorLevelPkgs = this.packagesToBeUpdated(result, SemverLevel.minor, pkgs);
this.setData(flatten([patchLevelPkgs, minorLevelPkgs]));

if (patchLevel === PracticeEvaluationResult.notPracticing || minorLevel === PracticeEvaluationResult.notPracticing) {
if (patchLevelPkgs.length > 0 || minorLevelPkgs.length > 0) {
return PracticeEvaluationResult.notPracticing;
}

return PracticeEvaluationResult.practicing;
}
}
46 changes: 0 additions & 46 deletions src/practices/JavaScript/DependenciesVersionPractice.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('JsGitignoreCorrectlySetPractice', () => {

const evaluated = await practice.evaluate(containerCtx.practiceContext);
expect(evaluated).toEqual(PracticeEvaluationResult.notPracticing);
expect(practice.data.details).not.toBeUndefined();
});

it('Returns unknown if there is no fileInspector', async () => {
Expand Down
15 changes: 14 additions & 1 deletion src/practices/JavaScript/JsGitignoreCorrectlySetPractice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { IPractice } from '../IPractice';
import { PracticeEvaluationResult, PracticeImpact, ProgrammingLanguage } from '../../model';
import { DxPractice } from '../DxPracticeDecorator';
import { PracticeContext } from '../../contexts/practice/PracticeContext';
import { PracticeBase } from '../PracticeBase';
import { ReportDetailType } from '../../reporters/ReporterData';

@DxPractice({
id: 'JavaScript.GitignoreCorrectlySet',
Expand All @@ -12,7 +14,7 @@ import { PracticeContext } from '../../contexts/practice/PracticeContext';
url: 'https://github.com/github/gitignore/blob/master/Node.gitignore',
dependsOn: { practicing: ['LanguageIndependent.GitignoreIsPresent'] },
})
export class JsGitignoreCorrectlySetPractice implements IPractice {
export class JsGitignoreCorrectlySetPractice extends PracticeBase {
async isApplicable(ctx: PracticeContext): Promise<boolean> {
return ctx.projectComponent.language === ProgrammingLanguage.JavaScript;
}
Expand Down Expand Up @@ -44,6 +46,17 @@ export class JsGitignoreCorrectlySetPractice implements IPractice {
return PracticeEvaluationResult.practicing;
}

this.setData();
return PracticeEvaluationResult.notPracticing;
}

private setData() {
this.data.details = [
{
type: ReportDetailType.text,
text:
'You should ignore one of the lock files (package-lock.json or yarn.lock), node_modules folder, coverage folder and log files (*.log)',
},
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe('JsPackageJsonConfigurationSetCorrectlyPractice', () => {

const evaluated = await practice.evaluate(containerCtx.practiceContext);
expect(evaluated).toEqual(PracticeEvaluationResult.notPracticing);
expect(practice.data.details).not.toBeUndefined();
});

it('Returns unknown if there are no file inspector', async () => {
Expand Down
Loading

0 comments on commit ce8b4dd

Please sign in to comment.