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

docs: add list of all practices to README.md #199

Merged
merged 29 commits into from
Jan 28, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fa1345a
docs: add list of all practices to README.md
adelkahomolova Jan 7, 2020
06fedeb
WIP: implement possibility to get list of practices in CLI
adelkahomolova Jan 8, 2020
673148e
Merge branch 'master' of https://github.com/DXHeroes/dx-scanner into …
adelkahomolova Jan 9, 2020
e604238
fix: remove comments a comment ideas
adelkahomolova Jan 9, 2020
796e30e
Merge branch 'master' of https://github.com/DXHeroes/dx-scanner into …
adelkahomolova Jan 19, 2020
6c33fc4
feat: wip - add subcommand
adelkahomolova Jan 20, 2020
1edb872
Merge branch 'master' of https://github.com/DXHeroes/dx-scanner into …
adelkahomolova Jan 20, 2020
457eb72
fix: allow fail and recursive to be optional
adelkahomolova Jan 21, 2020
b4b6497
fix: revert change
adelkahomolova Jan 21, 2020
871f7f2
feat: implement method to get practices
adelkahomolova Jan 21, 2020
9661d6b
feat: add two subcommands
adelkahomolova Jan 21, 2020
4754c45
Merge branch 'master' of https://github.com/DXHeroes/dx-scanner into …
adelkahomolova Jan 21, 2020
5ad591a
fix: remove unused commented code and import reflect-metadata
adelkahomolova Jan 21, 2020
e939b9e
fix: rename subcommand
adelkahomolova Jan 21, 2020
19e8bd1
fix: typo
adelkahomolova Jan 21, 2020
f44d4bf
Merge branch 'master' of https://github.com/DXHeroes/dx-scanner into …
adelkahomolova Jan 21, 2020
dc8edd6
fix: set arguments back to be required
adelkahomolova Jan 22, 2020
bb88dd4
fix: remove unnecessary code
adelkahomolova Jan 22, 2020
cccdf57
fix: remove unused flag, aliases and set new examples
adelkahomolova Jan 22, 2020
d8a88cf
fix: rename method
adelkahomolova Jan 22, 2020
569725a
fix: inform user if the config file already exists
adelkahomolova Jan 22, 2020
9594d3a
feat: convert init flag to subcommand
adelkahomolova Jan 22, 2020
3ede8e0
fix: parse flags with init subcommand
adelkahomolova Jan 22, 2020
b903bb9
refactor: sort practices in a function
adelkahomolova Jan 22, 2020
1463309
test: add test for sortAlphabetically()
adelkahomolova Jan 22, 2020
9d8a8f6
feat: list practices in a table
adelkahomolova Jan 22, 2020
cbf03b0
feat(core): CLI changed to multi-command
prokopsimek Jan 28, 2020
d0bfc1c
Merge branch 'master' into feature/list-practices
prokopsimek Jan 28, 2020
1b7586b
chore(release): 2.0.0-pre-list-practices.1 [skip ci]
semantic-release-bot Jan 28, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,37 @@ EXAMPLES
dx-scanner github.com/DXHeroes/dx-scanner
```

<details open>
<summary>List of All Practices 🔍</summary>

Practice | Impact | Language Independent | JavaScript/TypeScript | Java
------------- | ------------- | ------------- | ------------- | -------------
Create a Readme File | <span style="color:red">high</span> | ✅ | ✅ | ✅
Create a License File | <span style="color:yellow">medium</span> | ✅ | ✅ | ✅
Create a Lockfile | <span style="color:red">high</span> | ✅ | ✅ | ✅
Create a .gitignore | <span style="color:red">high</span> | ✅ | ✅ | ✅
Write in Typescript | <span style="color:yellow">medium</span> | ❌ | ✅ | ❌
Set .gitignore Correctly | <span style="color:red">high</span> | ❌ | ✅ | ✅
Use Continuous Integration | <span style="color:red">high</span> | ✅ | ✅ | ✅
Use Docker | <span style="color:green">small</span> | ✅ | ✅ | ✅
Use .editorconfig | <span style="color:green">small</span> | ✅ | ✅ | ✅
Format your code automatically | <span style="color:green">small</span> | ❌ | ✅ | ❌
Use ESLint | <span style="color:yellow">medium</span> | ❌ | ✅ | ❌
ESLint Without Errors | <span style="color:yellow">medium</span> | ❌ | ✅ | ❌
Use a different linter | <span style="color:yellow">medium</span> | ❌ | ✅ | ❌
Use JS Frontend Testing Framework | <span style="color:yellow">medium</span> | ❌ | ✅ | ❌
Use JS Frontend Build Tools | <span style="color:yellow">medium</span> | ❌ | ✅ | ❌
Use JS Backend Testing Frameworks | <span style="color:red">high</span> | ❌ | ✅ | ❌
Use a JS Logging Library | <span style="color:green">small</span> | ❌ | ✅ | ❌
Use JS Package Management | <span style="color:red">high</span> | ❌ | ✅ | ❌
Configure Scripts in package.json | <span style="color:yellow">medium</span> | ❌ | ✅ | ❌
Update Dependencies of Major Level | <span style="color:green">small</span> | ❌ | ✅ | ❌
Update Dependencies of Minor and Patch Level | <span style="color:red">high</span> | ❌ | ✅ | ❌
Do PullRequests | <span style="color:yellow">medium</span> | ✅ | ✅ | ✅
Solve Pull Requests Continuously | <span style="color:yellow">medium</span> | ✅ | ✅ | ✅
Write Commit Messages by Convention | <span style="color:green">small</span> | ✅ | ✅ | ✅
</details>

## Configuration ⚙️
Add ```dxscannerrc.*``` config file to change default configuration settings. It can be a ```.json```, ```.yml```, and even a dotfile!

Expand Down Expand Up @@ -165,3 +196,9 @@ Many thanks to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Any kind of contributions are welcome!






3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@
"/lib"
],
"oclif": {
"bin": "dx-scanner"
"bin": "dx-scanner",
"commands": "./src/commands"
},
"keywords": [
"oclif",
Expand Down
55 changes: 55 additions & 0 deletions src/commands/practices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Command, flags } from '@oclif/command';
import { createRootContainer } from '../inversify.config';
import { Scanner } from '../scanner';
import { PracticeImpact } from '../model';

export default class Practices extends Command {
static description = 'List all practices id with name and impact.';

static examples = [`$ dx-scanner practices`];

static flags = {
help: flags.help({ char: 'h' }),
json: flags.boolean({ char: 'j', description: 'Print practices in JSON' }),
};

static args = [{ name: 'path', default: process.cwd() }];
prokopsimek marked this conversation as resolved.
Show resolved Hide resolved

async run() {
const { args, flags } = this.parse(Practices);

const scanPath = args.path;
const json = flags.json;

const container = createRootContainer({
uri: scanPath,
auth: undefined,
json,
ci: false,
});
const scanner = container.get(Scanner);

const practices = await scanner.getPractices();
const practicesToReport: PracticeToReport[] = [];
practices.forEach((practice) => {
const practiceId: string = practice.getMetadata().id;
practicesToReport.push({
[practiceId]: { name: practice.getMetadata().name, impact: practice.getMetadata().impact },
});
});
if (flags.json) {
// print practices in JSON format
console.log(JSON.stringify(practicesToReport, null, 2));
} else {
console.log(practicesToReport);
prokopsimek marked this conversation as resolved.
Show resolved Hide resolved
}
process.exit(0);
}
}

interface PracticeToReport {
[id: string]: {
name: string;
impact: PracticeImpact;
};
}
121 changes: 121 additions & 0 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* eslint-disable no-process-env */
import { Command, flags } from '@oclif/command';
import cli from 'cli-ux';
import debug from 'debug';
import 'reflect-metadata';
import updateNotifier from 'update-notifier';
import { PracticeImpact } from '../model';
import { createRootContainer } from '../inversify.config';
import { Scanner } from '../scanner';
import { ScanningStrategyDetectorUtils } from '../detectors/utils/ScanningStrategyDetectorUtils';
import { ServiceType } from '../detectors';

class DXScannerCommand extends Command {
static description = 'Scan your project for possible DX recommendations.';
static usage = ['[PATH] [OPTIONS]'];

static flags = {
// add --version flag to show CLI version
version: flags.version({ char: 'v', description: 'Output the version number' }),
help: flags.help({ char: 'h', description: 'Help' }),
// flag with a value (-n, --name=VALUE)
authorization: flags.string({
char: 'a',
description:
'Credentials to the repository. (in format "token" or "username:token"; can be set as ENV variable DX_GIT_SERVICE_TOKEN)',
}),
json: flags.boolean({ char: 'j', description: 'Print report in JSON' }),
recursive: flags.boolean({ char: 'r', description: 'Scan all components recursively in all sub folders' }),
init: flags.boolean({ char: 'i', description: 'Initialize DX Scanner configuration' }),
prokopsimek marked this conversation as resolved.
Show resolved Hide resolved
ci: flags.boolean({
description: 'CI mode',
default: () => process.env.CI === 'true',
}),
fail: flags.string({
options: ['high', 'medium', 'small', 'off', 'all'],
description: 'Run scanner in failure mode. Exits process with code 1 for any non-practicing condition of given level.',
default: PracticeImpact.high,
}),
practices: flags.boolean({
prokopsimek marked this conversation as resolved.
Show resolved Hide resolved
char: 'p',
description: 'List all practices DX Scanner checks if they are applicable for your code.',
}),
};

static args = [{ name: 'path', default: process.cwd() }];

static aliases = ['dxs', 'dxscanner', 'run'];
prokopsimek marked this conversation as resolved.
Show resolved Hide resolved
static examples = ['dx-scanner', 'dx-scanner ./ --fail=high', 'dx-scanner github.com/DXHeroes/dx-scanner'];

async run() {
const { args, flags } = this.parse(DXScannerCommand);
debug('cli args')(args);
debug('cli flags')(flags);
const scanPath = args.path;

let authorization = flags.authorization ? flags.authorization : this.loadAuthTokenFromEnvs();
const json = flags.json;
const fail = <PracticeImpact | 'all'>flags.fail;

const notifier = updateNotifier({ pkg: this.config.pjson });
const hrstart = process.hrtime();

cli.action.start(`Scanning URI: ${scanPath}`);

const container = createRootContainer({
uri: scanPath,
auth: authorization,
json,
fail,
recursive: flags.recursive,
ci: flags.ci,
});
const scanner = container.get(Scanner);

if (flags.init) {
await scanner.init(scanPath);
process.exit(0);
}

let scanResult = await scanner.scan();

if (scanResult.needsAuth && !flags.ci) {
if (ScanningStrategyDetectorUtils.isGitHubPath(scanPath) || scanResult.serviceType === ServiceType.github) {
authorization = await cli.prompt('Insert your GitHub personal access token. https://github.com/settings/tokens\n', {
type: 'hide',
});
} else if (ScanningStrategyDetectorUtils.isBitbucketPath(scanPath) || scanResult.serviceType === ServiceType.bitbucket) {
authorization = await cli.prompt(
'Insert your Bitbucket credentials (in format "appPassword" or "username:appPasword"). https://confluence.atlassian.com/bitbucket/app-passwords-828781300.html\n',
{ type: 'hide' },
);
}

const container = createRootContainer({ uri: scanPath, auth: authorization, json, fail, recursive: flags.recursive, ci: flags.ci });
const scanner = container.get(Scanner);

scanResult = await scanner.scan({ determineRemote: false });
}
cli.action.stop();
notifier.notify({ isGlobal: true });

const hrend = process.hrtime(hrstart);

console.info('Scan duration %ds.', hrend[0]);

if (scanResult.shouldExitOnEnd) {
process.exit(1);
}
}

/**
* Loads API token from environment variables
*/
private loadAuthTokenFromEnvs = (): string | undefined => {
// eslint-disable-next-line no-process-env
const ev = process.env;
return ev.DX_GIT_SERVICE_TOKEN || ev.GITHUB_TOKEN;
};
}

export = DXScannerCommand;
110 changes: 1 addition & 109 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,2 @@
/* eslint-disable no-process-env */
import { Command, flags } from '@oclif/command';
import cli from 'cli-ux';
import debug from 'debug';
export { run } from '@oclif/command';
import 'reflect-metadata';
import updateNotifier from 'update-notifier';
import { ServiceType } from './detectors/ScanningStrategyDetector';
import { ScanningStrategyDetectorUtils } from './detectors/utils/ScanningStrategyDetectorUtils';
import { createRootContainer } from './inversify.config';
import { PracticeImpact } from './model';
import { Scanner } from './scanner/Scanner';

class DXScannerCommand extends Command {
static description = 'Scan your project for possible DX recommendations.';
static usage = ['[PATH] [OPTIONS]'];

static flags = {
// add --version flag to show CLI version
version: flags.version({ char: 'v', description: 'Output the version number' }),
help: flags.help({ char: 'h', description: 'Help' }),
// flag with a value (-n, --name=VALUE)
authorization: flags.string({
char: 'a',
description:
'Credentials to the repository. (in format "token" or "username:token"; can be set as ENV variable DX_GIT_SERVICE_TOKEN)',
}),
json: flags.boolean({ char: 'j', description: 'Print report in JSON' }),
recursive: flags.boolean({ char: 'r', description: 'Scan all components recursively in all sub folders' }),
init: flags.boolean({ char: 'i', description: 'Initialize DX Scanner configuration' }),
ci: flags.boolean({
description: 'CI mode',
default: () => process.env.CI === 'true',
}),
fail: flags.string({
options: ['high', 'medium', 'small', 'off', 'all'],
description: 'Run scanner in failure mode. Exits process with code 1 for any non-practicing condition of given level.',
default: PracticeImpact.high,
}),
};

static args = [{ name: 'path', default: process.cwd() }];

static aliases = ['dxs', 'dxscanner'];
static examples = ['dx-scanner', 'dx-scanner ./ --fail=high', 'dx-scanner github.com/DXHeroes/dx-scanner'];

async run() {
const { args, flags } = this.parse(DXScannerCommand);
debug('cli args')(args);
debug('cli flags')(flags);
const scanPath = args.path;

let authorization = flags.authorization ? flags.authorization : this.loadAuthTokenFromEnvs();
const json = flags.json;
const fail = <PracticeImpact | 'all'>flags.fail;

const notifier = updateNotifier({ pkg: this.config.pjson });
const hrstart = process.hrtime();

cli.action.start(`Scanning URI: ${scanPath}`);

const container = createRootContainer({ uri: scanPath, auth: authorization, json, fail, recursive: flags.recursive, ci: flags.ci });
const scanner = container.get(Scanner);

if (flags.init) {
await scanner.init(scanPath);
process.exit(0);
}

let scanResult = await scanner.scan();

if (scanResult.needsAuth && !flags.ci) {
if (ScanningStrategyDetectorUtils.isGitHubPath(scanPath) || scanResult.serviceType === ServiceType.github) {
authorization = await cli.prompt('Insert your GitHub personal access token. https://github.com/settings/tokens\n', {
type: 'hide',
});
} else if (ScanningStrategyDetectorUtils.isBitbucketPath(scanPath) || scanResult.serviceType === ServiceType.bitbucket) {
authorization = await cli.prompt(
'Insert your Bitbucket credentials (in format "appPassword" or "username:appPasword"). https://confluence.atlassian.com/bitbucket/app-passwords-828781300.html\n',
{ type: 'hide' },
);
}

const container = createRootContainer({ uri: scanPath, auth: authorization, json, fail, recursive: flags.recursive, ci: flags.ci });
const scanner = container.get(Scanner);

scanResult = await scanner.scan({ determineRemote: false });
}
cli.action.stop();
notifier.notify({ isGlobal: true });

const hrend = process.hrtime(hrstart);

console.info('Scan duration %ds.', hrend[0]);

if (scanResult.shouldExitOnEnd) {
process.exit(1);
}
}

/**
* Loads API token from environment variables
*/
private loadAuthTokenFromEnvs = (): string | undefined => {
// eslint-disable-next-line no-process-env
const ev = process.env;
return ev.DX_GIT_SERVICE_TOKEN || ev.GITHUB_TOKEN;
};
}

export = DXScannerCommand;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { PracticeContext } from '../../contexts/practice/PracticeContext';
@DxPractice({
id: 'Javascript.PackageManagementUsed',
name: 'Use JS Package Management',
impact: PracticeImpact.high, //which impact?
impact: PracticeImpact.high,
suggestion: 'Use Package.json to keep track of packages that are being used in your application.',
reportOnlyOnce: true,
url: 'https://docs.npmjs.com/files/package.json',
Expand Down
6 changes: 3 additions & 3 deletions src/scanner/ArgumentsProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { PracticeImpact } from '../model';
export type ArgumentsProvider = {
uri: string;
auth: string | undefined;
json: boolean | undefined;
fail: PracticeImpact | 'all';
recursive: boolean;
json: boolean;
fail?: PracticeImpact | 'all'; // optional as the AP is used also with command dxs practices
recursive?: boolean; // optional as the AP is used also with command dxs practices
prokopsimek marked this conversation as resolved.
Show resolved Hide resolved
ci: boolean;
};
4 changes: 4 additions & 0 deletions src/scanner/Scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,10 @@ export class Scanner {
throw ErrorFactory.newInternalError(`Error during configuration file initialization: ${err.message}`);
}
}

async getPractices(): Promise<IPracticeWithMetadata[]> {
prokopsimek marked this conversation as resolved.
Show resolved Hide resolved
return this.practices;
prokopsimek marked this conversation as resolved.
Show resolved Hide resolved
}
}

interface ProjectComponentAndLangContext {
Expand Down