Skip to content
This repository was archived by the owner on May 4, 2023. It is now read-only.

Commit 0d30723

Browse files
feat: add interactive ruleset selection and multi inputs
1 parent 740312e commit 0d30723

File tree

4 files changed

+319
-51
lines changed

4 files changed

+319
-51
lines changed

src/addRuleset.js

Lines changed: 99 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import {
1212
printEmptyLine,
1313
printFailure,
1414
printInfo,
15+
printSubItem,
1516
printSuggestion,
1617
} from "../utils/print";
17-
import { convertRulesetsToString, getRuleset } from "../utils/ruleset";
18+
import { convertRulesetsToString } from "../utils/rulesets";
19+
import { getRulesetsByNames, getUserPromptedRulesets } from "../utils/rulesets";
1820

1921
/**
2022
* Creates a codiga.yml file's content with the rulesets given
@@ -44,39 +46,65 @@ export async function createCodigaYml(codigaFileLocation, rulesets) {
4446
* Handles adding a ruleset to a codiga.yml file
4547
* @param {string[]} rulesetNames
4648
*/
47-
export async function addRuleset(rulesetNames) {
48-
// TODO - change to a prompt when no rulesets are given
49-
const rulesetName = rulesetNames[0];
50-
if (!rulesetName) {
49+
export async function addRuleset(rulesetNamesParams) {
50+
let rulesetNames = rulesetNamesParams;
51+
52+
/**
53+
* if `codiga ruleset-add` was called with no rulesets, we'll
54+
* open an interactive menu for them to select suggested rulesets
55+
*/
56+
if (rulesetNamesParams.length === 0) {
57+
// prompt the user to choose some rulesets
58+
rulesetNames = await getUserPromptedRulesets();
59+
// if the user didn't choose any notify them and exit
60+
if (rulesetNames.length === 0) {
61+
printEmptyLine();
62+
printInfo("No rulesets were chosen.");
63+
printCommandSuggestion(
64+
" ↳ Run the command again to get started:",
65+
ACTION_RULESET_ADD
66+
);
67+
printSuggestion(
68+
" ↳ Find all publically available rulesets here:",
69+
"https://app.codiga.io/hub/rulesets"
70+
);
71+
printEmptyLine();
72+
process.exit(1);
73+
}
74+
}
75+
76+
/**
77+
* Here we take the ruleset names that were chosen through the interactive
78+
* mode or were received from the command parameters, and validate which
79+
* ones are valid and should be added to a `codiga.yml` file and which
80+
* we should inform the user won't be added
81+
*/
82+
83+
const { found: validRulesets, notFound } = await getRulesetsByNames(
84+
rulesetNames
85+
);
86+
87+
if (notFound.length > 0) {
5188
printEmptyLine();
52-
printFailure("You need to specify a ruleset to add");
53-
printSuggestion(
54-
" ↳ You can search for rulesets here:",
55-
"https://app.codiga.io/hub/rulesets"
89+
printFailure(
90+
"The following rulesets either don't exist or you lack the permissions to access it:"
5691
);
92+
notFound.forEach((notFoundRuleset) => {
93+
printSubItem(`- ${notFoundRuleset}`);
94+
});
5795
printCommandSuggestion(
58-
" ↳ Then follow this command structure:",
59-
`${ACTION_RULESET_ADD} <ruleset-name>`
96+
"Ensure you have a Codiga API token set with one of the following commands:",
97+
ACTION_TOKEN_ADD
6098
);
61-
printEmptyLine();
62-
process.exit(1);
6399
}
64100

65-
// Check if the ruleset exists before continuing onwards
66-
const ruleset = await getRuleset({ name: rulesetName });
67-
if (!ruleset) {
101+
if (validRulesets.length === 0) {
68102
printEmptyLine();
69-
printFailure(
70-
"That ruleset either doesn't exist or you lack the permissions to access it"
71-
);
103+
printInfo("No valid rulesets were found to continue");
72104
printCommandSuggestion(
73105
" ↳ Ensure you have a Codiga API token set with one of the following commands:",
74106
ACTION_TOKEN_ADD
75107
);
76-
printSuggestion(
77-
" ↳ You can find more rulesets here:",
78-
"https://app.codiga.io/hub/rulesets"
79-
);
80108
printEmptyLine();
81109
process.exit(1);
82110
}
@@ -92,46 +120,71 @@ export async function addRuleset(rulesetNames) {
92120
const codigaFileContent = readFile(codigaFileLocation);
93121

94122
/**
95-
* If we found a `codiga.yml` file, add the rule to it
96-
* If we don't find a `codiga.yml` file, create the file and add the rule to it
123+
* If we found a `codiga.yml` file, add the new rulesets to it
124+
* If we don't find a `codiga.yml` file, create the file and add the rulesets to it
97125
*/
98126
if (codigaFileContent) {
99127
const parsedFile = parseYamlFile(codigaFileContent, codigaFileLocation);
100128
const codigaRulesets = parsedFile.rulesets;
101-
if (codigaRulesets.includes(rulesetName)) {
129+
130+
// check which rulesets are new and which are already used in the `codiga.yml` file
131+
const { usedRulesets, newRulesets } = validRulesets.reduce(
132+
(acc, ruleset) => {
133+
if (codigaRulesets.includes(ruleset)) {
134+
acc.usedRulesets.push(ruleset);
135+
} else {
136+
acc.newRulesets.push(ruleset);
137+
}
138+
return acc;
139+
},
140+
{
141+
usedRulesets: [],
142+
newRulesets: [],
143+
}
144+
);
145+
146+
if (usedRulesets.length > 0) {
102147
printEmptyLine();
103148
printInfo(
104-
`The ruleset (${rulesetName}) already exists in your \`codiga.yml\``
105-
);
106-
printSuggestion;
107-
printEmptyLine();
108-
process.exit(1);
109-
} else {
110-
// adding the new ruleset to the file
111-
await createCodigaYml(codigaFileLocation, [
112-
...codigaRulesets,
113-
rulesetName,
114-
]);
115-
printSuggestion(
116-
`We added ${rulesetName} to your codiga.yml file:`,
117-
codigaFileLocation
118-
);
119-
printSuggestion(
120-
" ↳ Find more rulesets to add here:",
121-
"https://app.codiga.io/hub/rulesets"
149+
`The following rulesets already exists in your \`codiga.yml\` file:`
122150
);
151+
usedRulesets.forEach((ruleset) => {
152+
printSubItem(`- ${ruleset}`);
153+
});
123154
}
155+
156+
// adding the new ruleset to the file
157+
await createCodigaYml(codigaFileLocation, [
158+
...codigaRulesets,
159+
...newRulesets,
160+
]);
161+
printEmptyLine();
162+
printSuggestion(
163+
`We added ${newRulesets.length} ruleset${
164+
newRulesets.length === 1 ? "" : "s"
165+
} to your codiga.yml file:`,
166+
codigaFileLocation
167+
);
168+
printSuggestion(
169+
" ↳ Find more rulesets to add here:",
170+
"https://app.codiga.io/hub/rulesets"
171+
);
172+
printEmptyLine();
124173
} else {
125174
// creating a new codiga.yml with the ruleset here
126-
await createCodigaYml(codigaFileLocation, [rulesetName]);
175+
await createCodigaYml(codigaFileLocation, newRulesets);
176+
printEmptyLine();
127177
printSuggestion(
128-
`No codiga.yml file found, so we created one and added ${rulesetName} to it:`,
178+
`No codiga.yml file found, so we created one and added ${
179+
newRulesets.length
180+
} ruleset${newRulesets.length === 1 ? "" : "s"} to it:`,
129181
codigaFileLocation
130182
);
131183
printSuggestion(
132184
" ↳ Find more rulesets to add here:",
133185
"https://app.codiga.io/hub/rulesets"
134186
);
187+
printEmptyLine();
135188
}
136189

137190
process.exit(0);

tests/fixtures/rulesetsMock.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
export function getRulesetsWithRulesMock(rulesets) {
2-
return mockedRulesets.filter((ruleset) => rulesets.includes(ruleset.name));
3-
}
4-
51
export const mockedRulesets = [
62
{
73
id: 1,
@@ -39,3 +35,25 @@ export const mockedRulesets = [
3935
rules: [],
4036
},
4137
];
38+
39+
export function getRulesetsWithRulesMock(rulesets) {
40+
return mockedRulesets.filter((ruleset) => rulesets.includes(ruleset.name));
41+
}
42+
43+
export function getRulesetsByNamesMock(rulesetNames) {
44+
return rulesetNames.reduce(
45+
(acc, rulesetName) => {
46+
const isFound = mockedRulesets.find(({ name }) => name === rulesetName);
47+
if (isFound) {
48+
acc.found.push(rulesetName);
49+
} else {
50+
acc.notFound.push(rulesetName);
51+
}
52+
return acc;
53+
},
54+
{
55+
found: [],
56+
notFound: [],
57+
}
58+
);
59+
}

utils/constants.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,122 @@ export const API_TOKEN_HEADER = "X-Api-Token";
2323
export const USER_AGENT_HEADER = "User-Agent";
2424
export const USER_AGENT_CLI = `Cli/${version}`;
2525

26+
// `ruleset-add` PROMPTS
27+
// PYTHON
28+
export const CATEGORY_PYTHON_GENERAL_NAME = "Python (general)";
29+
export const CATEGORY_PYTHON_GENERAL_VALUE = "python-general";
30+
export const CATEGORY_PYTHON_FLASK_NAME = "Python (Flask)";
31+
export const CATEGORY_PYTHON_FLASK_VALUE = "python-flask";
32+
// JAVASCRIPT
33+
export const CATEGORY_JS_GENERAL_NAME = "Javascript (general)";
34+
export const CATEGORY_JS_GENERAL_VALUE = "javascript-general";
35+
export const CATEGORY_JS_REACT_NAME = "Javascript (React)";
36+
export const CATEGORY_JS_REACT_VALUE = "javascript-react";
37+
export const CATEGORY_JS_EXPRESS_NAME = "Javascript (Express)";
38+
export const CATEGORY_JS_EXPRESS_VALUE = "javascript-express";
39+
export const CATEGORY_JS_TESTING_NAME = "Javascript (testing)";
40+
export const CATEGORY_JS_TESTING_VALUE = "javascript-testing";
41+
export const CATEGORY_JS_APOLLO_GRAPHQL_NAME = "Javascript (Apollo GraphQL)";
42+
export const CATEGORY_JS_APOLLO_GRAPHQL_VALUE = "javascript-apollo-graphql";
43+
// TYPESCRIPT
44+
export const CATEGORY_TS_GENERAL_NAME = "Typescript (general)";
45+
export const CATEGORY_TS_GENERAL_VALUE = "typescript-general";
46+
export const CATEGORY_TS_TESTING_NAME = "Typescript (testing)";
47+
export const CATEGORY_TS_TESTING_VALUE = "typescript-testing";
48+
export const CATEGORY_TS_APOLLO_GRAPHQL_NAME = "Typescript (Apollo GraphQL)";
49+
export const CATEGORY_TS_APOLLO_GRAPHQL_VALUE = "typescript-apollo-graphql";
50+
51+
export const CATEGORY_CHOICES = [
52+
{
53+
name: CATEGORY_PYTHON_GENERAL_NAME,
54+
value: CATEGORY_PYTHON_GENERAL_VALUE,
55+
},
56+
{
57+
name: CATEGORY_PYTHON_FLASK_NAME,
58+
value: CATEGORY_PYTHON_FLASK_VALUE,
59+
},
60+
{
61+
name: CATEGORY_JS_GENERAL_NAME,
62+
value: CATEGORY_JS_GENERAL_VALUE,
63+
},
64+
{
65+
name: CATEGORY_JS_TESTING_NAME,
66+
value: CATEGORY_JS_TESTING_VALUE,
67+
},
68+
{
69+
name: CATEGORY_JS_REACT_NAME,
70+
value: CATEGORY_JS_REACT_VALUE,
71+
},
72+
{
73+
name: CATEGORY_JS_EXPRESS_NAME,
74+
value: CATEGORY_JS_EXPRESS_VALUE,
75+
},
76+
{
77+
name: CATEGORY_JS_APOLLO_GRAPHQL_NAME,
78+
value: CATEGORY_JS_APOLLO_GRAPHQL_VALUE,
79+
},
80+
{
81+
name: CATEGORY_TS_GENERAL_NAME,
82+
value: CATEGORY_TS_GENERAL_VALUE,
83+
},
84+
{
85+
name: CATEGORY_TS_TESTING_NAME,
86+
value: CATEGORY_TS_TESTING_VALUE,
87+
},
88+
{
89+
name: CATEGORY_TS_APOLLO_GRAPHQL_NAME,
90+
value: CATEGORY_TS_APOLLO_GRAPHQL_VALUE,
91+
},
92+
];
93+
94+
export const RULESET_CHOICES = {
95+
[CATEGORY_PYTHON_GENERAL_VALUE]: [
96+
"for-testing", // for testing
97+
"python-security", // https://app.codiga.io/hub/ruleset/python-security
98+
"python-best-practices", // https://app.codiga.io/hub/ruleset/python-best-practices
99+
"python-code-style", // https://app.codiga.io/hub/ruleset/python-code-style
100+
"python-inclusive", // https://app.codiga.io/hub/ruleset/python-inclusive
101+
],
102+
[CATEGORY_PYTHON_FLASK_VALUE]: [
103+
"python-flask", // https://app.codiga.io/hub/ruleset/python-flask
104+
],
105+
[CATEGORY_JS_GENERAL_VALUE]: [
106+
"javascript-client-security", // https://app.codiga.io/hub/ruleset/javascript-client-security
107+
"javascript-inclusive", // https://app.codiga.io/hub/ruleset/javascript-inclusive
108+
],
109+
[CATEGORY_JS_REACT_VALUE]: [
110+
"jsx-a11y", // https://app.codiga.io/hub/ruleset/jsx-a11y
111+
"jsx-react", // https://app.codiga.io/hub/ruleset/jsx-react
112+
"react-best-practices", // https://app.codiga.io/hub/ruleset/react-best-practices
113+
],
114+
[CATEGORY_JS_EXPRESS_VALUE]: [
115+
"javascript-expressjs", // https://app.codiga.io/hub/ruleset/javascript-expressjs
116+
"javascript-knex", // https://app.codiga.io/hub/ruleset/javascript-knex
117+
],
118+
[CATEGORY_JS_TESTING_VALUE]: [
119+
"playwright", // https://app.codiga.io/hub/ruleset/playwright
120+
"testing-library", // https://app.codiga.io/hub/ruleset/testing-library
121+
"jestjs", // https://app.codiga.io/hub/ruleset/jestjs
122+
],
123+
[CATEGORY_JS_APOLLO_GRAPHQL_VALUE]: [
124+
"apollo-graphql-client-javascript", // https://app.codiga.io/hub/ruleset/apollo-graphql-client-javascript
125+
],
126+
[CATEGORY_TS_GENERAL_VALUE]: [
127+
"typescript-best-practices", // https://app.codiga.io/hub/ruleset/typescript-best-practices
128+
"jsx-a11y", // https://app.codiga.io/hub/ruleset/jsx-a11y
129+
"jsx-react", // https://app.codiga.io/hub/ruleset/jsx-react
130+
"react-best-practices", // https://app.codiga.io/hub/ruleset/react-best-practices
131+
],
132+
[CATEGORY_TS_TESTING_VALUE]: [
133+
"playwright", // https://app.codiga.io/hub/ruleset/playwright
134+
"testing-library", // https://app.codiga.io/hub/ruleset/testing-library
135+
"jestjs", // https://app.codiga.io/hub/ruleset/jestjs
136+
],
137+
[CATEGORY_TS_APOLLO_GRAPHQL_VALUE]: [
138+
"apollo-graphql-client-javascript", // https://app.codiga.io/hub/ruleset/apollo-graphql-client-javascript
139+
],
140+
};
141+
26142
// EXTRA
27143
export const STORE_API_TOKEN = "codiga-token";
28144
export const BLANK_SHA = "0000000000000000000000000000000000000000";

0 commit comments

Comments
 (0)