@zohodesk/codestandard-validator 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/hooks/PrePush/pre-push.js +247 -0
- package/build/hooks/Precommit/lint.js +211 -0
- package/build/setup/sample.config.js +7 -5
- package/build/utils/CloneCommonLinterRepo/cloneViaCdt.js +2 -2
- package/build/utils/ConfigFileUtils/createConfigFiles.js +68 -0
- package/build/utils/ConfigFileUtils/getEslintExecutablePath.js +24 -0
- package/build/utils/ConfigFileUtils/getLintConfiguration.js +54 -0
- package/build/utils/PluginsInstallation/Worker/installPluginsByWoker.js +38 -0
- package/build/utils/PluginsInstallation/Worker/worker.js +33 -0
- package/jsonUtils/commonLinterRepoDetails.js +7 -2
- package/package.json +1 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
const {
|
|
8
|
+
exec
|
|
9
|
+
} = require('child_process');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const {
|
|
13
|
+
getEslintExecutablePath
|
|
14
|
+
} = require('../../utils/EslintConfigFileUtils/getEslintExecutablePath');
|
|
15
|
+
const {
|
|
16
|
+
getNodeModulesPath
|
|
17
|
+
} = require('../../utils/General/getNodeModulesPath');
|
|
18
|
+
const {
|
|
19
|
+
filterFiles
|
|
20
|
+
} = require('../../utils/FileAndFolderOperations/filterFiles');
|
|
21
|
+
const {
|
|
22
|
+
Logger
|
|
23
|
+
} = require('../../utils/Logger/Logger');
|
|
24
|
+
const {
|
|
25
|
+
checkIfPluginsAreInstalled
|
|
26
|
+
} = require('../../utils/PluginsInstallation/checkIfPluginsAreInstalled');
|
|
27
|
+
const {
|
|
28
|
+
getBranchName
|
|
29
|
+
} = require('../../utils/GitActions/gitActions');
|
|
30
|
+
const {
|
|
31
|
+
getConfigurationPrecommit,
|
|
32
|
+
getSupportedLanguage
|
|
33
|
+
} = require('../../utils/General/getGeneralInfo');
|
|
34
|
+
const {
|
|
35
|
+
getRootDirectory
|
|
36
|
+
} = require('../../utils/General/RootDirectoryUtils/getRootDirectory');
|
|
37
|
+
const {
|
|
38
|
+
impactBasedPrecommit,
|
|
39
|
+
shouldWarningsAbortCommit
|
|
40
|
+
} = getConfigurationPrecommit();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Checks if the current commit is a merge commit
|
|
44
|
+
* @returns {Promise<boolean>} True if it's a merge commit
|
|
45
|
+
*/
|
|
46
|
+
async function isMergeCommit() {
|
|
47
|
+
return new Promise(resolve => {
|
|
48
|
+
exec('git rev-parse -q --verify MERGE_HEAD', error => {
|
|
49
|
+
resolve(!error);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Gets staged files while filtering out deleted files
|
|
56
|
+
* @returns {Promise<string[]>} Array of staged file paths
|
|
57
|
+
*/
|
|
58
|
+
async function getStagedFiles() {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
exec('git diff --staged --name-only', (error, stdout) => {
|
|
61
|
+
if (error) {
|
|
62
|
+
return reject("Couldn't fetch staged files");
|
|
63
|
+
}
|
|
64
|
+
const files = stdout.trim().split('\n').filter(Boolean);
|
|
65
|
+
resolve(filterDeletedFiles(files));
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Filters out deleted files from the staged files list
|
|
72
|
+
* @param {string[]} files - Array of file paths
|
|
73
|
+
* @returns {string[]} Filtered array of existing files
|
|
74
|
+
*/
|
|
75
|
+
function filterDeletedFiles(files) {
|
|
76
|
+
return files.filter(file => {
|
|
77
|
+
const absolutePath = path.resolve(getRootDirectory(), file);
|
|
78
|
+
return fs.existsSync(absolutePath);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Runs ESLint on a specific file
|
|
84
|
+
* @param {string} file - File path to lint
|
|
85
|
+
* @returns {Promise<string[]>} ESLint output lines
|
|
86
|
+
*/
|
|
87
|
+
async function runEslintOnFile(file) {
|
|
88
|
+
const nodeModulesPath = getNodeModulesPath();
|
|
89
|
+
const eslintPath = getEslintExecutablePath();
|
|
90
|
+
const configPath = `${nodeModulesPath}/.eslintrc.js`;
|
|
91
|
+
if (!fs.existsSync(nodeModulesPath)) {
|
|
92
|
+
Logger.log(Logger.INFO_TYPE, 'node_modules not found');
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
if (!fs.existsSync(eslintPath)) {
|
|
96
|
+
Logger.log(Logger.INFO_TYPE, 'Eslint executable not found');
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
return new Promise(resolve => {
|
|
100
|
+
const command = `npx --ignore-existing "${eslintPath}" --config "${configPath}" --no-inline-config --resolve-plugins-relative-to="${nodeModulesPath}/node_modules" ${file}`;
|
|
101
|
+
exec(command, (error, stderr) => {
|
|
102
|
+
if (stderr) {
|
|
103
|
+
resolve(stderr.trim().split('\n'));
|
|
104
|
+
} else {
|
|
105
|
+
resolve([]);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Computes git diff for a file against current branch
|
|
113
|
+
* @param {string} branch - Current branch name
|
|
114
|
+
* @param {string} file - File path
|
|
115
|
+
* @returns {Promise<string[]>} Git diff output lines
|
|
116
|
+
*/
|
|
117
|
+
async function getGitDiff(branch, file) {
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
exec(`git diff -U0 ${branch.trim()} ${file}`, (error, stderr) => {
|
|
120
|
+
if (error) return reject(error);
|
|
121
|
+
resolve(stderr ? stderr.trim().split('\n') : []);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Extracts changed line ranges from git diff output
|
|
128
|
+
* @param {string[]} diffLines - Git diff output
|
|
129
|
+
* @returns {Array<{start: number, end: number}>} Array of changed line ranges
|
|
130
|
+
*/
|
|
131
|
+
function parseChangedLines(diffLines) {
|
|
132
|
+
return diffLines.filter(line => line.startsWith('@@')).map(line => {
|
|
133
|
+
const parts = line.split(' ')[2].split(',');
|
|
134
|
+
const start = parseInt(parts[0]);
|
|
135
|
+
const end = parts[1] ? start + parseInt(parts[1]) - 1 : start;
|
|
136
|
+
return {
|
|
137
|
+
start,
|
|
138
|
+
end
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Filters ESLint errors to only include those in changed lines
|
|
145
|
+
* @param {string[]} eslintErrors - ESLint output lines
|
|
146
|
+
* @param {Array<{start: number, end: number}>} changedRanges - Changed line ranges
|
|
147
|
+
* @returns {string[]} Filtered ESLint errors
|
|
148
|
+
*/
|
|
149
|
+
function filterErrorsToChangedLines(eslintErrors, changedRanges) {
|
|
150
|
+
return eslintErrors.filter(error => {
|
|
151
|
+
const match = error.match(/^(\d+):/);
|
|
152
|
+
if (!match) return false;
|
|
153
|
+
const line = parseInt(match[1]);
|
|
154
|
+
return changedRanges.some(({
|
|
155
|
+
start,
|
|
156
|
+
end
|
|
157
|
+
}) => line >= start && line <= end);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Checks if ESLint output contains only warnings
|
|
163
|
+
* @param {string[]} eslintErrors - ESLint output lines
|
|
164
|
+
* @returns {boolean} True if only warnings are present
|
|
165
|
+
*/
|
|
166
|
+
function hasOnlyWarnings(eslintErrors) {
|
|
167
|
+
return eslintErrors.every(error => error.includes('warning'));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Processes a file based on impact-based precommit configuration
|
|
172
|
+
* @param {string} file - File path to process
|
|
173
|
+
* @param {string} branch - Current branch name
|
|
174
|
+
* @returns {Promise<boolean>} True if errors should abort commit
|
|
175
|
+
*/
|
|
176
|
+
async function processFile(file, branch) {
|
|
177
|
+
if (!getSupportedLanguage().includes(path.extname(file))) return false;
|
|
178
|
+
try {
|
|
179
|
+
const eslintOutput = await runEslintOnFile(file);
|
|
180
|
+
if (eslintOutput.length < 3) return false; // No meaningful errors
|
|
181
|
+
|
|
182
|
+
const errors = eslintOutput.slice(1, -2);
|
|
183
|
+
if (!impactBasedPrecommit) {
|
|
184
|
+
Logger.log(Logger.FAILURE_TYPE, `\x1b[1m${file}\x1b[0m`);
|
|
185
|
+
errors.forEach(err => Logger.log(Logger.FAILURE_TYPE, `\x1b[37m${err.trimEnd()}\x1b[0m`));
|
|
186
|
+
if (!shouldWarningsAbortCommit && hasOnlyWarnings(errors)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
const diffOutput = await getGitDiff(branch, file);
|
|
192
|
+
const changedRanges = parseChangedLines(diffOutput);
|
|
193
|
+
const changedLineErrors = filterErrorsToChangedLines(errors, changedRanges);
|
|
194
|
+
if (changedLineErrors.length > 0) {
|
|
195
|
+
Logger.log(Logger.FAILURE_TYPE, `\x1b[1m${file}\x1b[0m`);
|
|
196
|
+
changedLineErrors.forEach(err => Logger.log(Logger.FAILURE_TYPE, `\x1b[37m${err.trimEnd()}\x1b[0m`));
|
|
197
|
+
if (!shouldWarningsAbortCommit && hasOnlyWarnings(changedLineErrors)) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
Logger.log(Logger.FAILURE_TYPE, `Error processing ${file}: ${error}`);
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Main pre-push hook execution logic
|
|
210
|
+
*/
|
|
211
|
+
async function prePushCheck() {
|
|
212
|
+
Logger.log(Logger.INFO_TYPE, 'Executing pre commit hook...');
|
|
213
|
+
if (await isMergeCommit()) {
|
|
214
|
+
Logger.log(Logger.INFO_TYPE, 'Merge commit detected. Skipping checks.');
|
|
215
|
+
process.exit(0);
|
|
216
|
+
}
|
|
217
|
+
const pluginsStatus = checkIfPluginsAreInstalled();
|
|
218
|
+
if (pluginsStatus.uninstalledPlugins.length > 0) {
|
|
219
|
+
Logger.log(Logger.FAILURE_TYPE, 'Commit failed: Required plugins missing');
|
|
220
|
+
Logger.log(Logger.INFO_TYPE, 'Run: npx ZDPrecommit setupPlugins');
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const stagedFiles = await getStagedFiles();
|
|
225
|
+
if (stagedFiles.length === 0) {
|
|
226
|
+
Logger.log(Logger.INFO_TYPE, 'No staged files found');
|
|
227
|
+
process.exit(0);
|
|
228
|
+
}
|
|
229
|
+
const filteredFiles = filterFiles(stagedFiles, ['.eslintrc.js'], true);
|
|
230
|
+
const currentBranch = await getBranchName();
|
|
231
|
+
let shouldAbort = false;
|
|
232
|
+
for (const file of filteredFiles) {
|
|
233
|
+
const fileShouldAbort = await processFile(file, currentBranch);
|
|
234
|
+
if (fileShouldAbort) shouldAbort = true;
|
|
235
|
+
}
|
|
236
|
+
if (shouldAbort) {
|
|
237
|
+
Logger.log(Logger.FAILURE_TYPE, 'Commit aborted due to ESLint issues');
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
Logger.log(Logger.SUCCESS_TYPE, 'Commit successful');
|
|
241
|
+
process.exit(0);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
Logger.log(Logger.FAILURE_TYPE, `Pre-commit hook failed: ${error}`);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
var _default = exports.default = prePushCheck;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
exec
|
|
5
|
+
} = require('child_process');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const {
|
|
9
|
+
getEslintExecutablePath
|
|
10
|
+
} = require('../../utils/ConfigFileUtils/getEslintExecutablePath');
|
|
11
|
+
const {
|
|
12
|
+
getNodeModulesPath
|
|
13
|
+
} = require('../../utils/General/getNodeModulesPath');
|
|
14
|
+
const {
|
|
15
|
+
filterFiles,
|
|
16
|
+
filterWarningInFile
|
|
17
|
+
} = require('../../utils/FileAndFolderOperations/filterFiles');
|
|
18
|
+
const {
|
|
19
|
+
Logger
|
|
20
|
+
} = require('../../utils/Logger/Logger');
|
|
21
|
+
const {
|
|
22
|
+
checkIfPluginsAreInstalled
|
|
23
|
+
} = require('../../utils/PluginsInstallation/checkIfPluginsAreInstalled');
|
|
24
|
+
const {
|
|
25
|
+
getBranchName
|
|
26
|
+
} = require('../../utils/GitActions/gitActions');
|
|
27
|
+
const {
|
|
28
|
+
getConfigurationPrecommit,
|
|
29
|
+
getSupportedLanguage
|
|
30
|
+
} = require('../../utils/General/getGeneralInfo');
|
|
31
|
+
const {
|
|
32
|
+
getRootDirectory
|
|
33
|
+
} = require('../../utils/General/RootDirectoryUtils/getRootDirectory');
|
|
34
|
+
const {
|
|
35
|
+
impactBasedPrecommit,
|
|
36
|
+
shouldWarningsAbortCommit
|
|
37
|
+
} = getConfigurationPrecommit();
|
|
38
|
+
|
|
39
|
+
/* ------------------------------------------
|
|
40
|
+
Git Helpers
|
|
41
|
+
------------------------------------------ */
|
|
42
|
+
|
|
43
|
+
function isMergeCommit() {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
exec('git rev-parse -q --verify MERGE_HEAD', (err, stderr, stdout) => {
|
|
46
|
+
if (err) return reject(err.toString().trim());
|
|
47
|
+
resolve((stdout || stderr).trim());
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function getStagedFiles() {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
exec('git diff --staged --name-only', (err, stderr, stdout) => {
|
|
54
|
+
if (err) return reject("Couldn't fetch staged files");
|
|
55
|
+
const output = stderr || stdout;
|
|
56
|
+
if (!output.trim()) return resolve([]);
|
|
57
|
+
resolve(filterDeletedFiles(output.trim().split('\n')));
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function filterDeletedFiles(files) {
|
|
62
|
+
return files.filter(file => fs.existsSync(path.resolve(getRootDirectory(), file)));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ------------------------------------------
|
|
66
|
+
Lint Helpers
|
|
67
|
+
------------------------------------------ */
|
|
68
|
+
|
|
69
|
+
function lintFiles(filePath) {
|
|
70
|
+
const ext = path.extname(filePath);
|
|
71
|
+
if (['.js', '.ts', '.tsx', '.jsx'].includes(ext)) {
|
|
72
|
+
return findEslintErrors(filePath);
|
|
73
|
+
}
|
|
74
|
+
if (['.css', '.scss'].includes(ext)) {
|
|
75
|
+
return findStyleLintErrors(filePath);
|
|
76
|
+
}
|
|
77
|
+
return Promise.resolve([]);
|
|
78
|
+
}
|
|
79
|
+
function findStyleLintErrors(filePath) {
|
|
80
|
+
const config = path.resolve(getNodeModulesPath(), '.stylelintrc.js');
|
|
81
|
+
const absolutePath = path.join(getRootDirectory(), filePath);
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
exec(`npx stylelint ${absolutePath} --config ${config}`, {
|
|
84
|
+
cwd: getNodeModulesPath()
|
|
85
|
+
}, (err, stderr) => {
|
|
86
|
+
if (stderr) return resolve(stderr.trim().split('\n'));
|
|
87
|
+
if (err) {
|
|
88
|
+
Logger.log(Logger.FAILURE_TYPE, err);
|
|
89
|
+
return reject('Error executing stylelint command');
|
|
90
|
+
}
|
|
91
|
+
resolve([]);
|
|
92
|
+
});
|
|
93
|
+
}).catch(err => {
|
|
94
|
+
Logger.log(Logger.FAILURE_TYPE, 'Issue linting CSS files');
|
|
95
|
+
return [];
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function findEslintErrors(filePath) {
|
|
99
|
+
const eslintPath = getEslintExecutablePath();
|
|
100
|
+
const nodeModulesPath = getNodeModulesPath();
|
|
101
|
+
const eslintConfig = `${nodeModulesPath}/.eslintrc.js`;
|
|
102
|
+
if (!fs.existsSync(eslintPath)) {
|
|
103
|
+
Logger.log(Logger.INFO_TYPE, 'Eslint executable not found. Ensure eslint plugin is installed.');
|
|
104
|
+
return Promise.resolve([]);
|
|
105
|
+
}
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
const cmd = `npx --ignore-existing "${eslintPath}" --config "${eslintConfig}" --no-inline-config --resolve-plugins-relative-to="${nodeModulesPath}/node_modules" ${filePath}`;
|
|
108
|
+
exec(cmd, (err, stderr) => {
|
|
109
|
+
if (stderr) return resolve(stderr.trim().split('\n'));
|
|
110
|
+
if (err) {
|
|
111
|
+
Logger.log(Logger.FAILURE_TYPE, err);
|
|
112
|
+
return reject('Error executing eslint command');
|
|
113
|
+
}
|
|
114
|
+
resolve([]);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function calculateGitDiffForFile(branch, file) {
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
exec(`git diff -U0 ${branch.trim()} ${file}`, (err, stderr) => {
|
|
121
|
+
if (stderr) return resolve(stderr.trim().split('\n'));
|
|
122
|
+
if (err) return reject(err);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* ------------------------------------------
|
|
128
|
+
Utility
|
|
129
|
+
------------------------------------------ */
|
|
130
|
+
|
|
131
|
+
function areAllPluginsInstalled() {
|
|
132
|
+
const {
|
|
133
|
+
uninstalledPlugins
|
|
134
|
+
} = checkIfPluginsAreInstalled();
|
|
135
|
+
console.log('unInstalledPlugins', uninstalledPlugins);
|
|
136
|
+
return uninstalledPlugins.length === 0;
|
|
137
|
+
}
|
|
138
|
+
function isOnlyWarningsPresent(errors) {
|
|
139
|
+
const severities = errors.map(e => e.split(' ').find(w => w === 'error' || w === 'warning'));
|
|
140
|
+
return !severities.includes('error');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* ------------------------------------------
|
|
144
|
+
Main Execution
|
|
145
|
+
------------------------------------------ */
|
|
146
|
+
|
|
147
|
+
async function preCommitHook() {
|
|
148
|
+
Logger.log(Logger.INFO_TYPE, 'Executing pre commit hook...');
|
|
149
|
+
Logger.log(Logger.INFO_TYPE, `Working directory: ${process.cwd()}`);
|
|
150
|
+
try {
|
|
151
|
+
await isMergeCommit();
|
|
152
|
+
Logger.log(Logger.INFO_TYPE, 'Looks like you have merged. So skipping pre-commit check');
|
|
153
|
+
process.exit(0);
|
|
154
|
+
} catch {
|
|
155
|
+
if (!areAllPluginsInstalled()) {
|
|
156
|
+
Logger.log(Logger.FAILURE_TYPE, 'Commit failed due to missing eslint plugins');
|
|
157
|
+
Logger.log(Logger.INFO_TYPE, `Run \x1b[37mnpx ZDPrecommit setupPlugins\x1b[33m in the project root`);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
let files = await getStagedFiles();
|
|
161
|
+
if (files.length === 0) {
|
|
162
|
+
Logger.log(Logger.INFO_TYPE, 'No files staged. Stage files before committing.');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const currentBranch = await getBranchName().catch(() => '');
|
|
166
|
+
const filteredFiles = filterFiles(files, ['.eslintrc.js'], true);
|
|
167
|
+
let shouldAbort = false;
|
|
168
|
+
for (const file of filteredFiles) {
|
|
169
|
+
if (!getSupportedLanguage().includes(path.extname(file))) continue;
|
|
170
|
+
try {
|
|
171
|
+
const lintResults = await lintFiles(file);
|
|
172
|
+
if (lintResults.length === 0) continue;
|
|
173
|
+
let relevantErrors = [];
|
|
174
|
+
if (impactBasedPrecommit) {
|
|
175
|
+
const gitDiff = await calculateGitDiffForFile(currentBranch, file);
|
|
176
|
+
const changeHunks = gitDiff.filter(line => line.startsWith('@@'));
|
|
177
|
+
const ranges = changeHunks.map(h => {
|
|
178
|
+
const start = parseInt(h.split(' ')[2].split(',')[0]);
|
|
179
|
+
const len = parseInt(h.split(' ')[2].split(',')[1]) || 1;
|
|
180
|
+
return [start, start + len - 1];
|
|
181
|
+
});
|
|
182
|
+
for (const error of lintResults.slice(1, -2)) {
|
|
183
|
+
const lineNum = parseInt(error.trim().split(' ')[0].split(':')[0]);
|
|
184
|
+
if (ranges.some(([start, end]) => lineNum >= start && lineNum <= end)) {
|
|
185
|
+
relevantErrors.push(error);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
relevantErrors = lintResults.slice(1, -2);
|
|
190
|
+
}
|
|
191
|
+
if (relevantErrors.length) {
|
|
192
|
+
const onlyWarnings = isOnlyWarningsPresent(relevantErrors);
|
|
193
|
+
Logger.log(Logger.FAILURE_TYPE, `\x1b[1m${file}\x1b[0m`);
|
|
194
|
+
relevantErrors.forEach(e => Logger.log(Logger.FAILURE_TYPE, `\x1b[37m${e.trimEnd()}\x1b[0m`));
|
|
195
|
+
if (shouldWarningsAbortCommit || !onlyWarnings) {
|
|
196
|
+
shouldAbort = true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch (err) {
|
|
200
|
+
Logger.log(Logger.FAILURE_TYPE, err);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (shouldAbort) {
|
|
204
|
+
Logger.log(Logger.FAILURE_TYPE, 'There are eslint errors/warnings. Aborting commit.');
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
Logger.log(Logger.SUCCESS_TYPE, 'Commit Successful');
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
preCommitHook();
|
|
@@ -8,13 +8,14 @@ const path = require("path");
|
|
|
8
8
|
* @property {boolean} impactBased - Indicates if the linting is impact-based.
|
|
9
9
|
* @property {string} lintReportPath - The path to the lint report JSON file.
|
|
10
10
|
* @property {string} metricServerHost - The URL of the SonarQube server.
|
|
11
|
-
* @property {string} meticHandler_api_token - The token
|
|
12
|
-
* @property {string} metric_token - The token
|
|
11
|
+
* @property {string} meticHandler_api_token - The token to authenticate sonarQube apis.
|
|
12
|
+
* @property {string} metric_token - The token to authenticate sonarQube.
|
|
13
13
|
* @property {string} gitEndPoint - API EndPoint for Git Actions
|
|
14
14
|
* @property {string} tsConfigurationPath - The path of the ts configuration Path
|
|
15
15
|
* @property {number} projectId - project id of repository
|
|
16
16
|
* @property {boolean} impactBasedPrecommit - Indicates if the linting is impact-based in pre commit
|
|
17
17
|
* @property {boolean} shouldWarningsAbortCommit - Indicates if eslint warnings should abort the commit
|
|
18
|
+
* @property {string} pushMetricsOnPreCommit - Indicates to push metrics to sonarQube or not
|
|
18
19
|
* @property {string} token - Encrypted Authentication Token
|
|
19
20
|
* @property {string} compareBranch - Branch to compare diff
|
|
20
21
|
*/
|
|
@@ -23,15 +24,16 @@ module.exports = {
|
|
|
23
24
|
ruleConfigurationPath: path.resolve(process.cwd(), ".eslintrc.js"),
|
|
24
25
|
impactBased: true,
|
|
25
26
|
lintReportPath: path.resolve(process.cwd(), "lint-report", "lintReport.json"),
|
|
26
|
-
metricServerHost: "https://client-linters.
|
|
27
|
-
meticHandler_api_token: "
|
|
28
|
-
metric_token: "
|
|
27
|
+
metricServerHost: "https://client-linters.zdesk.csez.zohocorpin.com",
|
|
28
|
+
meticHandler_api_token: "KIDfmI46KIDfmI4kYPU1",
|
|
29
|
+
metric_token: "zxh_371336m636584i54m662j6495h46228mkj6hihh8",
|
|
29
30
|
branchDiffPath: path.resolve(process.cwd(), "diffBranch.json"),
|
|
30
31
|
gitEndPoint: "https://zgit.csez.zohocorpin.com",
|
|
31
32
|
tsConfigurationPath: path.resolve(process.cwd(), 'tsconfig.json'),
|
|
32
33
|
projectId: `project-id`,
|
|
33
34
|
impactBasedPrecommit: true,
|
|
34
35
|
shouldWarningsAbortCommit: false,
|
|
36
|
+
pushMetricsOnPreCommit: true,
|
|
35
37
|
token: "w-OkG3f5OOM1Rkly8phZ",
|
|
36
38
|
compareBranch: 'release',
|
|
37
39
|
supportedExtensions: ['.js', '.jsx', '.ts', '.tsx', '.properties']
|
|
@@ -49,9 +49,9 @@ function cloneViaCdt() {
|
|
|
49
49
|
const runningEnv = getRunningEnv();
|
|
50
50
|
if (runningEnv === "CI" || runningEnv === "DEVAUTOMATION") {
|
|
51
51
|
Logger.log(Logger.INFO_TYPE, `Running in ${runningEnv}`);
|
|
52
|
-
absoluteEndPoint = `https://${userName}:${decrypt(token,
|
|
52
|
+
absoluteEndPoint = `https://${userName}:${decrypt(token, process.env.DECRYPT_TOKEN)}@${endPoint}`;
|
|
53
53
|
}
|
|
54
|
-
var commandToCloneCommonConfigRepo = `npx cdt clone --clone:type=${type} --clone:url=${absoluteEndPoint} --clone:branch=${branch} --clone:cacheDir=${cacheDirectory} --clone:proj:name=${commonLinterRepoName}`;
|
|
54
|
+
var commandToCloneCommonConfigRepo = `npx cdt clone --clone:type=${type} --clone:url=${absoluteEndPoint} --clone:branch=${process.env.CONFIGURATION_BRANCH || branch} --clone:cacheDir=${cacheDirectory} --clone:proj:name=${commonLinterRepoName}`;
|
|
55
55
|
let isCommonConfigurationClonedSuccessfully = executeSynchronizedCommands(execSync, [commandToCloneCommonConfigRepo, {
|
|
56
56
|
cwd: getClonedDirPath()
|
|
57
57
|
}], `Lint Configuration Cloned Successfully - ${getRepoName() || 'common'}`, 'Could not clone the linters common repo', false, true);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const {
|
|
5
|
+
writeFileSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
unlinkSync,
|
|
8
|
+
readFileSync
|
|
9
|
+
} = require('fs');
|
|
10
|
+
const {
|
|
11
|
+
getNodeModulesPath
|
|
12
|
+
} = require('../General/getNodeModulesPath.js');
|
|
13
|
+
const {
|
|
14
|
+
Logger
|
|
15
|
+
} = require('../Logger/Logger.js');
|
|
16
|
+
const {
|
|
17
|
+
executeSynchronizedCommands
|
|
18
|
+
} = require('../General/executeSyncCommands.js');
|
|
19
|
+
const {
|
|
20
|
+
getServicePathElseCommon
|
|
21
|
+
} = require('./getLintConfiguration.js');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates the ESLint configuration file from the service-specific or common template.
|
|
25
|
+
* Also creates the Stylelint configuration file if available.
|
|
26
|
+
*
|
|
27
|
+
* @returns {boolean} Success status of ESLint config creation
|
|
28
|
+
*/
|
|
29
|
+
function createConfigFiles() {
|
|
30
|
+
const eslintConfigFilePath = path.join(getNodeModulesPath(), '.eslintrc.js');
|
|
31
|
+
const stylelintConfigFilePath = path.join(getNodeModulesPath(), '.stylelintrc.js');
|
|
32
|
+
const {
|
|
33
|
+
pathOfServiceSpecificEslintConfigFile,
|
|
34
|
+
cssLintConfig
|
|
35
|
+
} = getServicePathElseCommon();
|
|
36
|
+
try {
|
|
37
|
+
const eslintContent = readConfig(pathOfServiceSpecificEslintConfigFile, 'eslint');
|
|
38
|
+
const stylelintContent = readConfig(cssLintConfig, 'stylelint');
|
|
39
|
+
if (stylelintContent) {
|
|
40
|
+
writeConfig(stylelintConfigFilePath, stylelintContent, 'stylelint');
|
|
41
|
+
}
|
|
42
|
+
if (existsSync(eslintConfigFilePath)) {
|
|
43
|
+
executeSynchronizedCommands(unlinkSync, [eslintConfigFilePath], 'ESLint configuration file removed successfully!', 'Unable to remove the ESLint config file.', false, false);
|
|
44
|
+
}
|
|
45
|
+
return writeConfig(eslintConfigFilePath, eslintContent, 'eslint');
|
|
46
|
+
} catch (error) {
|
|
47
|
+
Logger.log(Logger.FAILURE_TYPE, error);
|
|
48
|
+
Logger.log(Logger.FAILURE_TYPE, 'Error occurred while creating ESLint config file');
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Reads a configuration file with logging and error handling.
|
|
55
|
+
*/
|
|
56
|
+
function readConfig(filePath, type) {
|
|
57
|
+
return executeSynchronizedCommands(readFileSync, [filePath, 'utf-8'], '', `Unable to read content of ${type} config file.`, true, false);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Writes a configuration file with logging and error handling.
|
|
62
|
+
*/
|
|
63
|
+
function writeConfig(filePath, content, type) {
|
|
64
|
+
return executeSynchronizedCommands(writeFileSync, [filePath, content, 'utf-8'], `${type[0].toUpperCase() + type.slice(1)} configuration file created successfully!`, `Unable to create and write a ${type} configuration file.`, false, true);
|
|
65
|
+
}
|
|
66
|
+
module.exports = {
|
|
67
|
+
createConfigFiles
|
|
68
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const {
|
|
5
|
+
readdirSync
|
|
6
|
+
} = require('fs');
|
|
7
|
+
const {
|
|
8
|
+
getNodeModulesPath
|
|
9
|
+
} = require('../General/getNodeModulesPath');
|
|
10
|
+
const {
|
|
11
|
+
executeSynchronizedCommands
|
|
12
|
+
} = require('../General/executeSyncCommands');
|
|
13
|
+
const {
|
|
14
|
+
getLibraryInstalledLocation
|
|
15
|
+
} = require('../General/getLibraryInstalledLocation');
|
|
16
|
+
function getEslintExecutablePath() {
|
|
17
|
+
let eslintLibraryName = 'eslint';
|
|
18
|
+
let librariesInNodeModulesOfProject = executeSynchronizedCommands(readdirSync, [path.join(getNodeModulesPath(), 'node_modules')], '', 'Unable to get the plugins inside node_modules', true, false);
|
|
19
|
+
let isEslintInstalledinProject = librariesInNodeModulesOfProject.some(library => library === eslintLibraryName);
|
|
20
|
+
return isEslintInstalledinProject ? path.join(getNodeModulesPath(), 'node_modules', eslintLibraryName, 'bin', 'eslint.js') : path.join(getLibraryInstalledLocation(), 'node_modules', eslintLibraryName, 'bin', 'eslint.js');
|
|
21
|
+
}
|
|
22
|
+
module.exports = {
|
|
23
|
+
getEslintExecutablePath
|
|
24
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const {
|
|
5
|
+
existsSync
|
|
6
|
+
} = require('fs');
|
|
7
|
+
const {
|
|
8
|
+
endPoint,
|
|
9
|
+
commonLinterRepoName
|
|
10
|
+
} = require("../../../jsonUtils/commonLinterRepoDetails.js");
|
|
11
|
+
const {
|
|
12
|
+
getRepoName
|
|
13
|
+
} = require('../GitActions/gitActions.js');
|
|
14
|
+
const {
|
|
15
|
+
getLibraryInstalledLocation
|
|
16
|
+
} = require('../General/getLibraryInstalledLocation.js');
|
|
17
|
+
const {
|
|
18
|
+
executeMethodsThatMayThrowException
|
|
19
|
+
} = require("../General/wrapperFunctionToExecuteAFunction.js");
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @function getLintConfiguration - returns eslint configuration file from common linters repo
|
|
23
|
+
* @returns {object} - containing properties of the eslint configuration file
|
|
24
|
+
*/
|
|
25
|
+
function getServicePathElseCommon() {
|
|
26
|
+
const repoName = getRepoName();
|
|
27
|
+
const libraryInstalledLocation = getLibraryInstalledLocation();
|
|
28
|
+
let commonConfigPath = path.join(libraryInstalledLocation, commonLinterRepoName, 'common', 'index.js');
|
|
29
|
+
const _configurationPath = repoName && existsSync(path.join(libraryInstalledLocation, commonLinterRepoName, 'services', repoName, 'index.js')) ? path.join(libraryInstalledLocation, commonLinterRepoName, 'services', repoName, 'index.js') : commonConfigPath;
|
|
30
|
+
let _pathOfServiceSpecificEslintConfigFile = repoName && existsSync(path.join(libraryInstalledLocation, commonLinterRepoName, 'services', repoName, '.eslintrc.js')) ? path.join(libraryInstalledLocation, commonLinterRepoName, 'services', repoName, '.eslintrc.js') : path.join(libraryInstalledLocation, commonLinterRepoName, 'common', '.eslintrc.js');
|
|
31
|
+
let _pluginVersionPath = repoName && existsSync(path.join(libraryInstalledLocation, commonLinterRepoName, 'services', repoName, 'pluginVersion.js')) ? path.join(libraryInstalledLocation, commonLinterRepoName, 'services', repoName, 'pluginVersion.js') : undefined;
|
|
32
|
+
let _stylelintPath = repoName && existsSync(path.join(libraryInstalledLocation, commonLinterRepoName, 'services', repoName, '.stylelintrc.js')) ? path.join(libraryInstalledLocation, commonLinterRepoName, 'services', repoName, '.stylelintrc.js') : path.join(libraryInstalledLocation, commonLinterRepoName, 'common', '.stylelintrc.js');
|
|
33
|
+
return {
|
|
34
|
+
configurationPath: _configurationPath,
|
|
35
|
+
pathOfServiceSpecificEslintConfigFile: _pathOfServiceSpecificEslintConfigFile,
|
|
36
|
+
pluginVersionPath: _pluginVersionPath,
|
|
37
|
+
cssLintConfig: _stylelintPath
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function getLintConfigurationUtil() {
|
|
41
|
+
const {
|
|
42
|
+
configurationPath
|
|
43
|
+
} = getServicePathElseCommon();
|
|
44
|
+
const exceptionHandleObject = {
|
|
45
|
+
rulesConfig: [{}, {}],
|
|
46
|
+
plugins: [],
|
|
47
|
+
extendPlugins: []
|
|
48
|
+
};
|
|
49
|
+
return executeMethodsThatMayThrowException(exceptionHandleObject, `Kindly Ensure that Configuration is setup in Configuration repo \n ${endPoint}`, require, configurationPath);
|
|
50
|
+
}
|
|
51
|
+
module.exports = {
|
|
52
|
+
getLintConfigurationUtil,
|
|
53
|
+
getServicePathElseCommon
|
|
54
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
Worker
|
|
5
|
+
} = require('worker_threads');
|
|
6
|
+
const {
|
|
7
|
+
Logger
|
|
8
|
+
} = require('../../Logger/Logger');
|
|
9
|
+
async function installPluginsByWorker(plugins) {
|
|
10
|
+
try {
|
|
11
|
+
const results = await Promise.all(plugins.map(pkg => runWorker({
|
|
12
|
+
packageName: pkg.packageName,
|
|
13
|
+
version: pkg.version
|
|
14
|
+
})));
|
|
15
|
+
Logger.log(Logger.SUCCESS_TYPE, 'Package are installed By from workers:', results.join('\n'));
|
|
16
|
+
} catch (error) {
|
|
17
|
+
Logger.log(Logger.FAILURE_TYPE, 'Error:', error);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function runWorker(data) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const worker = new Worker(`${__dirname}/worker.js`, {
|
|
23
|
+
workerData: data
|
|
24
|
+
});
|
|
25
|
+
worker.on('message', result => {
|
|
26
|
+
resolve(result);
|
|
27
|
+
});
|
|
28
|
+
worker.on('error', error => {
|
|
29
|
+
reject(error);
|
|
30
|
+
});
|
|
31
|
+
worker.on('exit', code => {
|
|
32
|
+
if (code !== 0) {
|
|
33
|
+
reject(new Error(`Worker stopped with exit code ${code}`));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
module.exports = installPluginsByWorker;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
workerData,
|
|
5
|
+
parentPort
|
|
6
|
+
} = require('worker_threads');
|
|
7
|
+
const {
|
|
8
|
+
spawnSync
|
|
9
|
+
} = require('child_process');
|
|
10
|
+
const {
|
|
11
|
+
getNodeModulesPath
|
|
12
|
+
} = require('../../General/getNodeModulesPath');
|
|
13
|
+
function performanceInstalllation({
|
|
14
|
+
packageName,
|
|
15
|
+
version
|
|
16
|
+
}) {
|
|
17
|
+
const start = performance.now();
|
|
18
|
+
try {
|
|
19
|
+
require.resolve(packageName);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
spawnSync('npm', ['install', `${packageName}@${version}`, '--no-save'], {
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
cwd: getNodeModulesPath()
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const end = performance.now();
|
|
27
|
+
return `Package ${packageName}@${version} installed successfully and installed in ${end - start} ms`;
|
|
28
|
+
}
|
|
29
|
+
const result = performanceInstalllation({
|
|
30
|
+
packageName: workerData.packageName,
|
|
31
|
+
version: workerData.version
|
|
32
|
+
});
|
|
33
|
+
parentPort.postMessage(result);
|
|
@@ -14,5 +14,10 @@ module.exports = {
|
|
|
14
14
|
cacheDirectory: "./",
|
|
15
15
|
commonLinterRepoName: "configuration",
|
|
16
16
|
type: "git",
|
|
17
|
-
user:"rajasekar.hm"
|
|
18
|
-
|
|
17
|
+
user:"rajasekar.hm",
|
|
18
|
+
commitHashEndPoint:"https://zgit.csez.zohocorpin.com/api/v4/projects/19251/repository/commits",
|
|
19
|
+
readOnlyToken:"k-CyU3t5CCA1Fyzm8dvN"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|