@zohodesk/codestandard-validator 0.0.5 → 0.0.6-exp-2
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/bin/push.js +4 -0
- package/build/hooks/PrePush/pre-push.js +243 -0
- package/build/hooks/Precommit/pre-commit.js +19 -300
- package/build/setup/sample.config.js +9 -6
- package/build/utils/General/getGeneralInfo.js +14 -6
- package/build/utils/General/writeProjectDetailsToJson.js +7 -2
- package/build/utils/HuskySetup/configurePrecommitHook.js +11 -0
- package/build/utils/PluginsInstallation/installPlugins.js +0 -47
- package/changeLog.md +6 -3
- package/package.json +1 -1
package/bin/push.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
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/EslintConfigFileUtils/getEslintExecutablePath');
|
|
11
|
+
const {
|
|
12
|
+
getNodeModulesPath
|
|
13
|
+
} = require('../../utils/General/getNodeModulesPath');
|
|
14
|
+
const {
|
|
15
|
+
filterFiles
|
|
16
|
+
} = require('../../utils/FileAndFolderOperations/filterFiles');
|
|
17
|
+
const {
|
|
18
|
+
Logger
|
|
19
|
+
} = require('../../utils/Logger/Logger');
|
|
20
|
+
const {
|
|
21
|
+
checkIfPluginsAreInstalled
|
|
22
|
+
} = require('../../utils/PluginsInstallation/checkIfPluginsAreInstalled');
|
|
23
|
+
const {
|
|
24
|
+
getBranchName
|
|
25
|
+
} = require('../../utils/GitActions/gitActions');
|
|
26
|
+
const {
|
|
27
|
+
getConfigurationPrecommit,
|
|
28
|
+
getSupportedLanguage
|
|
29
|
+
} = require('../../utils/General/getGeneralInfo');
|
|
30
|
+
const {
|
|
31
|
+
getRootDirectory
|
|
32
|
+
} = require('../../utils/General/RootDirectoryUtils/getRootDirectory');
|
|
33
|
+
const {
|
|
34
|
+
impactBasedPrecommit,
|
|
35
|
+
shouldWarningsAbortCommit
|
|
36
|
+
} = getConfigurationPrecommit();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks if the current commit is a merge commit
|
|
40
|
+
* @returns {Promise<boolean>} True if it's a merge commit
|
|
41
|
+
*/
|
|
42
|
+
async function isMergeCommit() {
|
|
43
|
+
return new Promise(resolve => {
|
|
44
|
+
exec('git rev-parse -q --verify MERGE_HEAD', error => {
|
|
45
|
+
resolve(!error);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Gets staged files while filtering out deleted files
|
|
52
|
+
* @returns {Promise<string[]>} Array of staged file paths
|
|
53
|
+
*/
|
|
54
|
+
async function getStagedFiles() {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
exec('git diff --staged --name-only', (error, stdout) => {
|
|
57
|
+
if (error) {
|
|
58
|
+
return reject("Couldn't fetch staged files");
|
|
59
|
+
}
|
|
60
|
+
const files = stdout.trim().split('\n').filter(Boolean);
|
|
61
|
+
resolve(filterDeletedFiles(files));
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Filters out deleted files from the staged files list
|
|
68
|
+
* @param {string[]} files - Array of file paths
|
|
69
|
+
* @returns {string[]} Filtered array of existing files
|
|
70
|
+
*/
|
|
71
|
+
function filterDeletedFiles(files) {
|
|
72
|
+
return files.filter(file => {
|
|
73
|
+
const absolutePath = path.resolve(getRootDirectory(), file);
|
|
74
|
+
return fs.existsSync(absolutePath);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Runs ESLint on a specific file
|
|
80
|
+
* @param {string} file - File path to lint
|
|
81
|
+
* @returns {Promise<string[]>} ESLint output lines
|
|
82
|
+
*/
|
|
83
|
+
async function runEslintOnFile(file) {
|
|
84
|
+
const nodeModulesPath = getNodeModulesPath();
|
|
85
|
+
const eslintPath = getEslintExecutablePath();
|
|
86
|
+
const configPath = `${nodeModulesPath}/.eslintrc.js`;
|
|
87
|
+
if (!fs.existsSync(nodeModulesPath)) {
|
|
88
|
+
Logger.log(Logger.INFO_TYPE, 'node_modules not found');
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
if (!fs.existsSync(eslintPath)) {
|
|
92
|
+
Logger.log(Logger.INFO_TYPE, 'Eslint executable not found');
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
return new Promise(resolve => {
|
|
96
|
+
const command = `npx --ignore-existing "${eslintPath}" --config "${configPath}" --no-inline-config --resolve-plugins-relative-to="${nodeModulesPath}/node_modules" ${file}`;
|
|
97
|
+
exec(command, (error, stderr) => {
|
|
98
|
+
if (stderr) {
|
|
99
|
+
resolve(stderr.trim().split('\n'));
|
|
100
|
+
} else {
|
|
101
|
+
resolve([]);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Computes git diff for a file against current branch
|
|
109
|
+
* @param {string} branch - Current branch name
|
|
110
|
+
* @param {string} file - File path
|
|
111
|
+
* @returns {Promise<string[]>} Git diff output lines
|
|
112
|
+
*/
|
|
113
|
+
async function getGitDiff(branch, file) {
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
exec(`git diff -U0 ${branch.trim()} ${file}`, (error, stderr) => {
|
|
116
|
+
if (error) return reject(error);
|
|
117
|
+
resolve(stderr ? stderr.trim().split('\n') : []);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Extracts changed line ranges from git diff output
|
|
124
|
+
* @param {string[]} diffLines - Git diff output
|
|
125
|
+
* @returns {Array<{start: number, end: number}>} Array of changed line ranges
|
|
126
|
+
*/
|
|
127
|
+
function parseChangedLines(diffLines) {
|
|
128
|
+
return diffLines.filter(line => line.startsWith('@@')).map(line => {
|
|
129
|
+
const parts = line.split(' ')[2].split(',');
|
|
130
|
+
const start = parseInt(parts[0]);
|
|
131
|
+
const end = parts[1] ? start + parseInt(parts[1]) - 1 : start;
|
|
132
|
+
return {
|
|
133
|
+
start,
|
|
134
|
+
end
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Filters ESLint errors to only include those in changed lines
|
|
141
|
+
* @param {string[]} eslintErrors - ESLint output lines
|
|
142
|
+
* @param {Array<{start: number, end: number}>} changedRanges - Changed line ranges
|
|
143
|
+
* @returns {string[]} Filtered ESLint errors
|
|
144
|
+
*/
|
|
145
|
+
function filterErrorsToChangedLines(eslintErrors, changedRanges) {
|
|
146
|
+
return eslintErrors.filter(error => {
|
|
147
|
+
const match = error.match(/^(\d+):/);
|
|
148
|
+
if (!match) return false;
|
|
149
|
+
const line = parseInt(match[1]);
|
|
150
|
+
return changedRanges.some(({
|
|
151
|
+
start,
|
|
152
|
+
end
|
|
153
|
+
}) => line >= start && line <= end);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Checks if ESLint output contains only warnings
|
|
159
|
+
* @param {string[]} eslintErrors - ESLint output lines
|
|
160
|
+
* @returns {boolean} True if only warnings are present
|
|
161
|
+
*/
|
|
162
|
+
function hasOnlyWarnings(eslintErrors) {
|
|
163
|
+
return eslintErrors.every(error => error.includes('warning'));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Processes a file based on impact-based precommit configuration
|
|
168
|
+
* @param {string} file - File path to process
|
|
169
|
+
* @param {string} branch - Current branch name
|
|
170
|
+
* @returns {Promise<boolean>} True if errors should abort commit
|
|
171
|
+
*/
|
|
172
|
+
async function processFile(file, branch) {
|
|
173
|
+
if (!getSupportedLanguage().includes(path.extname(file))) return false;
|
|
174
|
+
try {
|
|
175
|
+
const eslintOutput = await runEslintOnFile(file);
|
|
176
|
+
if (eslintOutput.length < 3) return false; // No meaningful errors
|
|
177
|
+
|
|
178
|
+
const errors = eslintOutput.slice(1, -2);
|
|
179
|
+
if (!impactBasedPrecommit) {
|
|
180
|
+
Logger.log(Logger.FAILURE_TYPE, `\x1b[1m${file}\x1b[0m`);
|
|
181
|
+
errors.forEach(err => Logger.log(Logger.FAILURE_TYPE, `\x1b[37m${err.trimEnd()}\x1b[0m`));
|
|
182
|
+
if (!shouldWarningsAbortCommit && hasOnlyWarnings(errors)) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
const diffOutput = await getGitDiff(branch, file);
|
|
188
|
+
const changedRanges = parseChangedLines(diffOutput);
|
|
189
|
+
const changedLineErrors = filterErrorsToChangedLines(errors, changedRanges);
|
|
190
|
+
if (changedLineErrors.length > 0) {
|
|
191
|
+
Logger.log(Logger.FAILURE_TYPE, `\x1b[1m${file}\x1b[0m`);
|
|
192
|
+
changedLineErrors.forEach(err => Logger.log(Logger.FAILURE_TYPE, `\x1b[37m${err.trimEnd()}\x1b[0m`));
|
|
193
|
+
if (!shouldWarningsAbortCommit && hasOnlyWarnings(changedLineErrors)) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
Logger.log(Logger.FAILURE_TYPE, `Error processing ${file}: ${error}`);
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Main pre-push hook execution logic
|
|
206
|
+
*/
|
|
207
|
+
async function prePushCheck() {
|
|
208
|
+
Logger.log(Logger.INFO_TYPE, 'Executing pre commit hook...');
|
|
209
|
+
if (await isMergeCommit()) {
|
|
210
|
+
Logger.log(Logger.INFO_TYPE, 'Merge commit detected. Skipping checks.');
|
|
211
|
+
process.exit(0);
|
|
212
|
+
}
|
|
213
|
+
const pluginsStatus = checkIfPluginsAreInstalled();
|
|
214
|
+
if (pluginsStatus.uninstalledPlugins.length > 0) {
|
|
215
|
+
Logger.log(Logger.FAILURE_TYPE, 'Commit failed: Required plugins missing');
|
|
216
|
+
Logger.log(Logger.INFO_TYPE, 'Run: npx ZDPrecommit setupPlugins');
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const stagedFiles = await getStagedFiles();
|
|
221
|
+
if (stagedFiles.length === 0) {
|
|
222
|
+
Logger.log(Logger.INFO_TYPE, 'No staged files found');
|
|
223
|
+
process.exit(0);
|
|
224
|
+
}
|
|
225
|
+
const filteredFiles = filterFiles(stagedFiles, ['.eslintrc.js'], true);
|
|
226
|
+
const currentBranch = await getBranchName();
|
|
227
|
+
let shouldAbort = false;
|
|
228
|
+
for (const file of filteredFiles) {
|
|
229
|
+
const fileShouldAbort = await processFile(file, currentBranch);
|
|
230
|
+
if (fileShouldAbort) shouldAbort = true;
|
|
231
|
+
}
|
|
232
|
+
if (shouldAbort) {
|
|
233
|
+
Logger.log(Logger.FAILURE_TYPE, 'Commit aborted due to ESLint issues');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
Logger.log(Logger.SUCCESS_TYPE, 'Commit successful');
|
|
237
|
+
process.exit(0);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
Logger.log(Logger.FAILURE_TYPE, `Pre-commit hook failed: ${error}`);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
prePushCheck();
|
|
@@ -1,310 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
|
|
5
|
-
} = require(
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
4
|
+
cloneViaCdt
|
|
5
|
+
} = require("../../utils/CloneCommonLinterRepo/cloneViaCdt");
|
|
8
6
|
const {
|
|
9
|
-
|
|
10
|
-
} = require(
|
|
7
|
+
createEslintConfigFile
|
|
8
|
+
} = require("../../utils/EslintConfigFileUtils/createEslintConfigFile");
|
|
11
9
|
const {
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
getLastCommitHash,
|
|
11
|
+
getRequiredInfoPath
|
|
12
|
+
} = require("../../utils/General/getGeneralInfo");
|
|
14
13
|
const {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
* @function isMergeCommit - This method check whether it is merge or not
|
|
41
|
-
* @returns {Boolean} - return boolean based on latest merge changes
|
|
42
|
-
*/
|
|
43
|
-
async function isMergeCommit() {
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
45
|
-
exec('git rev-parse -q --verify MERGE_HEAD', (error, stderr, stdout) => {
|
|
46
|
-
if (error) {
|
|
47
|
-
reject(error.toString().trim());
|
|
48
|
-
} else if (stderr) {
|
|
49
|
-
resolve(stderr.trim());
|
|
50
|
-
} else if (stdout) {
|
|
51
|
-
resolve(stdout.trim());
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* @function {getStagedFiles} - methods return staged files
|
|
59
|
-
* @returns {Array<string>} - array of files
|
|
60
|
-
*/
|
|
61
|
-
|
|
62
|
-
async function getStagedFiles() {
|
|
63
|
-
return new Promise((resolve, reject) => {
|
|
64
|
-
exec("git diff --staged --name-only", (error, stderr, stdout) => {
|
|
65
|
-
if (error) {
|
|
66
|
-
if (error != null) reject("Couldn't fetch staged files");
|
|
67
|
-
} else if (stderr) {
|
|
68
|
-
resolve(filterDeltedFileFromStagedFiles(stderr.trim().split('\n')));
|
|
69
|
-
} else if (stdout.trim() === '') {
|
|
70
|
-
resolve(stdout.trim());
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* @function {filterDeltedFileFromStagedFiles} - filter deleted staged files
|
|
78
|
-
* @param {Array<string>} files - staged files
|
|
79
|
-
* @returns
|
|
80
|
-
*/
|
|
81
|
-
function filterDeltedFileFromStagedFiles(files) {
|
|
82
|
-
return files.filter(file => {
|
|
83
|
-
const absolutePath = path.resolve(getRootDirectory(), file);
|
|
84
|
-
if (fs.existsSync(absolutePath)) {
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
return false;
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* @function findEslintErrors - method Lint given file based on given configuration
|
|
93
|
-
* @param {*} file - path of file to lint
|
|
94
|
-
* @returns {Array<string>} - array of command line report as a string
|
|
95
|
-
*/
|
|
96
|
-
|
|
97
|
-
async function findEslintErrors(file) {
|
|
98
|
-
let nodeModulesPathOfProject = `${getNodeModulesPath()}`;
|
|
99
|
-
let eslintExecutablePath = getEslintExecutablePath();
|
|
100
|
-
let eslintConfigurationFilePath = `${nodeModulesPathOfProject}/.eslintrc.js`;
|
|
101
|
-
let isEslintExecutablePresent = fs.existsSync(eslintExecutablePath) ? true : false;
|
|
102
|
-
let isNodeModulesPresent = fs.existsSync(nodeModulesPathOfProject);
|
|
103
|
-
if (isNodeModulesPresent) {
|
|
104
|
-
if (isEslintExecutablePresent) {
|
|
105
|
-
return new Promise((resolve, reject) => {
|
|
106
|
-
exec(`npx --ignore-existing "${eslintExecutablePath}" --config "${eslintConfigurationFilePath}" --no-inline-config --resolve-plugins-relative-to="${nodeModulesPathOfProject}/node_modules" ${file}`, (error, stderr, stdout) => {
|
|
107
|
-
if (stderr) {
|
|
108
|
-
resolve(stderr.trim().split('\n'));
|
|
109
|
-
} else if (error) {
|
|
110
|
-
Logger.log(Logger.FAILURE_TYPE, error);
|
|
111
|
-
reject("Error executing eslint command");
|
|
112
|
-
} else {
|
|
113
|
-
resolve([]);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
} else {
|
|
118
|
-
Logger.log(Logger.INFO_TYPE, 'Eslint executable not found. make sure eslint plugin is installed');
|
|
119
|
-
}
|
|
120
|
-
} else {
|
|
121
|
-
Logger.log(Logger.INFO_TYPE, 'node_modules not found');
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* @function {calculateGitDiffForFile} - method calculate diff of file
|
|
126
|
-
* @param {*} branch_name - branch name
|
|
127
|
-
* @param {*} file - path of file
|
|
128
|
-
* @returns {Promise<Array<string>>} - array of command line report as a string
|
|
129
|
-
*/
|
|
130
|
-
async function calculateGitDiffForFile(branch_name, file) {
|
|
131
|
-
let gitDiffCommand = `git diff -U0 ${branch_name.trim()} ${file}`;
|
|
132
|
-
return new Promise((resolve, reject) => {
|
|
133
|
-
exec(gitDiffCommand, (error, stderr) => {
|
|
134
|
-
if (stderr) {
|
|
135
|
-
resolve(stderr.trim().split('\n'));
|
|
136
|
-
} else if (error) {
|
|
137
|
-
reject(error);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* @function {areAllPluginsInstalled} - method whether plugin is installed or not
|
|
144
|
-
* @returns {Boolean} - return boolean based on plugin installed or not
|
|
145
|
-
*/
|
|
146
|
-
|
|
147
|
-
function areAllPluginsInstalled() {
|
|
148
|
-
let unInstalledPlugins = checkIfPluginsAreInstalled().uninstalledPlugins;
|
|
149
|
-
return unInstalledPlugins.length === 0 ? true : false;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* @function {isOnlyWarningsPresentInFile} - method that checks if only eslint warnings are present in a file
|
|
154
|
-
* @returns {Boolean} - returns boolean based on only warnings present or not in file
|
|
155
|
-
*/
|
|
156
|
-
function isOnlyWarningsPresentInFile(eslintErrorsPresent) {
|
|
157
|
-
let severityOfEachErrorInFile = [];
|
|
158
|
-
eslintErrorsPresent.map(error => {
|
|
159
|
-
let partsInString = error.split(" ");
|
|
160
|
-
let severityOfError = partsInString.find(word => word === 'error' || word === 'warning');
|
|
161
|
-
severityOfEachErrorInFile.push(severityOfError);
|
|
162
|
-
});
|
|
163
|
-
return !severityOfEachErrorInFile.includes('error');
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @function {preCommitHook} - method execute pre commit hook
|
|
168
|
-
* @returns {void}
|
|
169
|
-
*/
|
|
170
|
-
|
|
171
|
-
async function preCommitHook() {
|
|
172
|
-
Logger.log(Logger.INFO_TYPE, 'Executing pre commit hook...');
|
|
173
|
-
Logger.log(Logger.INFO_TYPE, `working dir : ${process.cwd()}`);
|
|
14
|
+
arePluginsInstalled
|
|
15
|
+
} = require("../../utils/PluginsInstallation/arePluginsInstalled");
|
|
16
|
+
async function validateRemotePackages() {
|
|
174
17
|
try {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
18
|
+
const {
|
|
19
|
+
commitHash
|
|
20
|
+
} = JSON.parse(require(getRequiredInfoPath()));
|
|
21
|
+
if (!(commitHash == getLastCommitHash())) await executeMethodsThatReturnBooleanValue("Make sure zgit.csez.zohocorpin.com is accessible", cloneViaCdt, null);
|
|
22
|
+
await executeMethodsThatReturnBooleanValue("Some issue occurred in creating eslint config file.", createEslintConfigFile, null);
|
|
23
|
+
Logger.log(Logger.SUCCESS_TYPE, "Pre commit setup successfull");
|
|
24
|
+
arePluginsInstalled();
|
|
178
25
|
} catch (error) {
|
|
179
|
-
|
|
180
|
-
let staged_files = [];
|
|
181
|
-
let eslintConfigFiles = ['.eslintrc.js'];
|
|
182
|
-
let exemptionFiles = [];
|
|
183
|
-
let current_branch = '';
|
|
184
|
-
let hasEslintErrorsInChangedLines = false;
|
|
185
|
-
let hasEslintErrorsInFiles = false;
|
|
186
|
-
let areFilesStaged = false;
|
|
187
|
-
let shouldAbortCommit = false;
|
|
188
|
-
try {
|
|
189
|
-
current_branch = await getBranchName();
|
|
190
|
-
} catch {
|
|
191
|
-
Logger.log(Logger.INFO_TYPE, "Error fetching current branch");
|
|
192
|
-
}
|
|
193
|
-
try {
|
|
194
|
-
staged_files = await getStagedFiles();
|
|
195
|
-
if (!staged_files.length == 0) {
|
|
196
|
-
staged_files = filterFiles(staged_files, eslintConfigFiles, true);
|
|
197
|
-
|
|
198
|
-
// staged_files = filterFiles(staged_files,exemptionFiles) //this is the code for giving exemption to a file during pre commit
|
|
199
|
-
// if(staged_files.length === 0){
|
|
200
|
-
// Logger.log(Logger.SUCCESS_TYPE,`Commit Successful`)
|
|
201
|
-
// process.exit(0)
|
|
202
|
-
// }
|
|
203
|
-
areFilesStaged = true;
|
|
204
|
-
for (let file in staged_files) {
|
|
205
|
-
let currentFileName = staged_files[file];
|
|
206
|
-
let changedLinesArray = [];
|
|
207
|
-
let eslintErrorsInChangedLines = [];
|
|
208
|
-
let isOnlyEslintWarningsPresentInFile = false;
|
|
209
|
-
if (getSupportedLanguage().includes(path.extname(staged_files[file]))) {
|
|
210
|
-
try {
|
|
211
|
-
var eslintErrorsInFile = await findEslintErrors(staged_files[file]);
|
|
212
|
-
// eslintErrorsInFile = impactBasedPrecommit == false ? filterWarningInFile(eslintErrorsInFile) : eslintErrorsInFile
|
|
213
|
-
if (staged_files[file] && typeof staged_files[file] == 'string') {
|
|
214
|
-
if (!eslintErrorsInFile.length == 0) {
|
|
215
|
-
//Calculating changed lines in a file and storing them in respective arrays
|
|
216
|
-
if (impactBasedPrecommit) {
|
|
217
|
-
//git diff is computed and stored in an array
|
|
218
|
-
let git_diff = await calculateGitDiffForFile(current_branch, staged_files[file]);
|
|
219
|
-
changedLinesArray = git_diff.filter(line => line.startsWith('@@'));
|
|
220
|
-
let changedLinesStartArray = [];
|
|
221
|
-
let changedLinesEndArray = [];
|
|
222
|
-
for (let number of changedLinesArray) {
|
|
223
|
-
let changesStartLine = parseInt(number.split(' ')[2].split(',')[0]);
|
|
224
|
-
changedLinesStartArray.push(changesStartLine);
|
|
225
|
-
let changesEndLine = number.split(' ')[2].split(',')[1];
|
|
226
|
-
if (changesEndLine === undefined) {
|
|
227
|
-
changedLinesEndArray.push(changesStartLine);
|
|
228
|
-
} else {
|
|
229
|
-
changedLinesEndArray.push(changesStartLine + parseInt(changesEndLine) - 1);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
for (let error = 1; error < eslintErrorsInFile.length - 2; error++) {
|
|
233
|
-
//eslintErrorsInFile[error].trim() - 69:26 error => Do not hardcode content. Use I18N key instead no-hardcoding/no-hardcoding,
|
|
234
|
-
//eslintErrorsInFile[error].trim().split(' ')[0] => 69:26
|
|
235
|
-
//eslintErrorsInFile[error].trim().split(' ')[0].split(':')[0] => 69
|
|
236
|
-
|
|
237
|
-
let eslintErrorLineNumber = eslintErrorsInFile[error].trim().split(' ')[0].split(':')[0];
|
|
238
|
-
for (let lineNumber in changedLinesStartArray) {
|
|
239
|
-
if (eslintErrorLineNumber >= changedLinesStartArray[lineNumber] && eslintErrorLineNumber <= changedLinesEndArray[lineNumber]) {
|
|
240
|
-
eslintErrorsInChangedLines.push(eslintErrorsInFile[error]);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
if (eslintErrorsInChangedLines.length > 0) {
|
|
245
|
-
isOnlyEslintWarningsPresentInFile = isOnlyWarningsPresentInFile(eslintErrorsInChangedLines);
|
|
246
|
-
Logger.log(Logger.FAILURE_TYPE, `\x1b[1m${currentFileName}\x1b[0m`);
|
|
247
|
-
for (let eslintError of eslintErrorsInChangedLines) {
|
|
248
|
-
Logger.log(Logger.FAILURE_TYPE, `\x1b[37m${eslintError.trimEnd()}\x1b[0m`);
|
|
249
|
-
}
|
|
250
|
-
if (shouldWarningsAbortCommit) {
|
|
251
|
-
hasEslintErrorsInChangedLines = true;
|
|
252
|
-
shouldAbortCommit = true;
|
|
253
|
-
} else if (!shouldWarningsAbortCommit && isOnlyEslintWarningsPresentInFile) {
|
|
254
|
-
hasEslintErrorsInChangedLines = false;
|
|
255
|
-
} else if (!shouldWarningsAbortCommit && !isOnlyEslintWarningsPresentInFile) {
|
|
256
|
-
hasEslintErrorsInChangedLines = true;
|
|
257
|
-
shouldAbortCommit = true;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
} else {
|
|
261
|
-
if (eslintErrorsInFile.length > 0) {
|
|
262
|
-
let startIndex = 1;
|
|
263
|
-
let endIndex = eslintErrorsInFile.length - 2;
|
|
264
|
-
let listOsEslintErrors = eslintErrorsInFile.slice(startIndex, endIndex);
|
|
265
|
-
isOnlyEslintWarningsPresentInFile = isOnlyWarningsPresentInFile(listOsEslintErrors);
|
|
266
|
-
Logger.log(Logger.FAILURE_TYPE, `\x1b[1m${currentFileName}\x1b[0m`);
|
|
267
|
-
for (let eslintError of listOsEslintErrors) {
|
|
268
|
-
Logger.log(Logger.FAILURE_TYPE, `\x1b[37m${eslintError.trimEnd()}\x1b[0m`);
|
|
269
|
-
}
|
|
270
|
-
if (shouldWarningsAbortCommit) {
|
|
271
|
-
hasEslintErrorsInFiles = true;
|
|
272
|
-
shouldAbortCommit = true;
|
|
273
|
-
} else if (!shouldWarningsAbortCommit && isOnlyEslintWarningsPresentInFile) {
|
|
274
|
-
hasEslintErrorsInFiles = false;
|
|
275
|
-
} else if (!shouldWarningsAbortCommit && !isOnlyEslintWarningsPresentInFile) {
|
|
276
|
-
hasEslintErrorsInFiles = true;
|
|
277
|
-
shouldAbortCommit = true;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
} catch (err) {
|
|
284
|
-
Logger.log(Logger.FAILURE_TYPE, err);
|
|
285
|
-
Logger.log(Logger.FAILURE_TYPE, "Error in executing eslint command");
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
} else if (staged_files.length === 0) {
|
|
290
|
-
Logger.log(Logger.INFO_TYPE, 'No files have been staged. Stage your files before committing');
|
|
291
|
-
}
|
|
292
|
-
} catch {
|
|
293
|
-
Logger.log(Logger.INFO_TYPE, 'Error executing pre commit hook');
|
|
294
|
-
}
|
|
295
|
-
if (shouldAbortCommit) {
|
|
296
|
-
Logger.log(Logger.FAILURE_TYPE, `There are eslint errors/warnings present. So commit is aborted.`);
|
|
297
|
-
process.exit(1);
|
|
298
|
-
} else if (shouldAbortCommit === false && staged_files.length !== 0) {
|
|
299
|
-
Logger.log(Logger.SUCCESS_TYPE, `Commit Successful`);
|
|
300
|
-
process.exit(0);
|
|
301
|
-
}
|
|
302
|
-
} else {
|
|
303
|
-
Logger.log(Logger.FAILURE_TYPE, 'Commit failed since some eslint plugins are not installed');
|
|
304
|
-
Logger.log(Logger.INFO_TYPE, `Kindly execute the command \x1b[37mnpx ZDPrecommit setupPlugins \x1b[33mfrom the location where package.json is present to install the plugins`);
|
|
305
|
-
Logger.log(Logger.INFO_TYPE, 'Execute the command and kindly try committing again.');
|
|
306
|
-
process.exit(1);
|
|
307
|
-
}
|
|
26
|
+
Logger.log(Logger.FAILURE_TYPE, `Kindly retry npm install. & ${error.message}`);
|
|
308
27
|
}
|
|
309
28
|
}
|
|
310
|
-
|
|
29
|
+
validateRemotePackages();
|
|
@@ -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,17 @@ 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
|
-
compareBranch: 'release'
|
|
38
|
+
compareBranch: 'release',
|
|
39
|
+
supportedExtensions: ['.js', '.jsx', '.ts', '.tsx', '.properties']
|
|
37
40
|
};
|
|
@@ -54,11 +54,10 @@ function getConfigurationPrecommit() {
|
|
|
54
54
|
* @returns {Array<string>}
|
|
55
55
|
*/
|
|
56
56
|
function getSupportedLanguage() {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
_language
|
|
61
|
-
_language.push('.tsx');
|
|
57
|
+
const {
|
|
58
|
+
supportedExtensions
|
|
59
|
+
} = getConfigurationPrecommit();
|
|
60
|
+
const _language = supportedExtensions;
|
|
62
61
|
return _language;
|
|
63
62
|
}
|
|
64
63
|
function getRunningEnv() {
|
|
@@ -67,10 +66,19 @@ function getRunningEnv() {
|
|
|
67
66
|
shell: true
|
|
68
67
|
}).toString().trim();
|
|
69
68
|
}
|
|
69
|
+
function getLastCommitHash() {
|
|
70
|
+
const cmd = `curl --header "PRIVATE-TOKEN: 4aexpjRkZG1LBDDsHf3C" --url https://zgit.csez.zohocorpin.com/api/v4/projects/19251/repository/commits`;
|
|
71
|
+
return JSON.parse(execSync(cmd))[0]["id"];
|
|
72
|
+
}
|
|
73
|
+
function getRequiredInfoPath() {
|
|
74
|
+
return path.join(process.cwd(), 'jsonUtils', 'fsUtils.json');
|
|
75
|
+
}
|
|
70
76
|
module.exports = {
|
|
71
77
|
getSupportedLanguage,
|
|
72
78
|
getTimeStampInfo,
|
|
73
79
|
getEnv,
|
|
74
80
|
getConfigurationPrecommit,
|
|
75
|
-
getRunningEnv
|
|
81
|
+
getRunningEnv,
|
|
82
|
+
getLastCommitHash,
|
|
83
|
+
getRequiredInfoPath
|
|
76
84
|
};
|
|
@@ -7,11 +7,16 @@ const {
|
|
|
7
7
|
const {
|
|
8
8
|
executeSynchronizedCommands
|
|
9
9
|
} = require('./executeSyncCommands');
|
|
10
|
+
const {
|
|
11
|
+
getLastCommitHash,
|
|
12
|
+
getRequiredInfoPath
|
|
13
|
+
} = require('./getGeneralInfo');
|
|
10
14
|
function writeFsPaths() {
|
|
11
15
|
var fileContent = {
|
|
12
|
-
nodeModulesPath: path.resolve(process.cwd(), '..', '..', '..')
|
|
16
|
+
nodeModulesPath: path.resolve(process.cwd(), '..', '..', '..'),
|
|
17
|
+
commitHash: getLastCommitHash()
|
|
13
18
|
};
|
|
14
|
-
return executeSynchronizedCommands(writeFileSync, [
|
|
19
|
+
return executeSynchronizedCommands(writeFileSync, [getRequiredInfoPath(), JSON.stringify(fileContent), 'utf-8'], 'node_modules path updated in json file', 'Unable to write node_modules path to json file', false, true);
|
|
15
20
|
}
|
|
16
21
|
module.exports = {
|
|
17
22
|
writeFsPaths
|
|
@@ -22,16 +22,27 @@ const {
|
|
|
22
22
|
function configurePrecommitHook() {
|
|
23
23
|
let rootDirectory = getRootDirectory();
|
|
24
24
|
let customPrecomitHookPath = path.join(getLibraryInstalledLocation(), "bin", "execute.js");
|
|
25
|
+
let customPrePushHookPath = path.join(getLibraryInstalledLocation(), "bin", "push.js");
|
|
25
26
|
let huskyPrecommitPath = path.join(rootDirectory, ".husky", "pre-commit");
|
|
27
|
+
let huskyPrePushPath = path.join(rootDirectory, ".husky", "pre-push");
|
|
26
28
|
let huskyPrecommitHookContent = `#!/bin/sh
|
|
27
29
|
. "$(dirname "$0")/_/husky.sh"
|
|
28
30
|
|
|
29
31
|
"${customPrecomitHookPath}"
|
|
32
|
+
`;
|
|
33
|
+
let huskyPrePushHookContent = `#!/bin/sh
|
|
34
|
+
. "$(dirname "$0")/_/husky.sh"
|
|
35
|
+
|
|
36
|
+
"${customPrePushHookPath}"
|
|
30
37
|
`;
|
|
31
38
|
let isCustomPrecommitConfigurationSuccessful = executeSynchronizedCommands(writeFileSync, [huskyPrecommitPath, huskyPrecommitHookContent, 'utf-8'], '', 'Could not create and write pre-commit.sh inisde husky directory', false, true);
|
|
39
|
+
let isCustomPrePushConfigurationSuccessful = executeSynchronizedCommands(writeFileSync, [huskyPrePushPath, huskyPrePushHookContent, 'utf-8'], '', 'Could not create and write pre-commit.sh inisde husky directory', false, true);
|
|
32
40
|
if (isCustomPrecommitConfigurationSuccessful) {
|
|
33
41
|
let isExecutionPermissionGivenSuccessfully = grantExecutionPermission([huskyPrecommitPath, customPrecomitHookPath]);
|
|
34
42
|
return isExecutionPermissionGivenSuccessfully;
|
|
43
|
+
} else if (isCustomPrePushConfigurationSuccessful) {
|
|
44
|
+
let isExecutionPermissionGivenSuccessfully = grantExecutionPermission([huskyPrePushPath, customPrePushHookPath]);
|
|
45
|
+
return isExecutionPermissionGivenSuccessfully;
|
|
35
46
|
} else {
|
|
36
47
|
return false;
|
|
37
48
|
}
|
|
@@ -76,53 +76,6 @@ function restorePackageJsonContent(packageJsonContentBeforePluginsInstallation)
|
|
|
76
76
|
let isPackageJsonRestored = executeSynchronizedCommands(writeFileSync, [packageJsonFilePath, JSON.stringify(packageJsonContentBeforePluginsInstallation, null, 2)], 'Package.json content restored successfully', 'Unable to restore package.json content', false, true);
|
|
77
77
|
return isPackageJsonRestored;
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
// function appendInstalledPluginsToPackageJson(pluginsToBeAppendedInPackageJson){
|
|
81
|
-
// let packageJsonFilePath = `${nodeModulesPathOfProject}/package.json`
|
|
82
|
-
// let packageJsonContent = require(packageJsonFilePath)
|
|
83
|
-
// let devDependencies = packageJsonContent.devDependencies
|
|
84
|
-
// let pluginsToBeAddedInDevDependencies = {}
|
|
85
|
-
|
|
86
|
-
// executeSynchronizedCommands(process.chdir,[nodeModulesPathOfProject],'','Unable to navigate to node_modules path when trying to revert changes in package.json',false,false)
|
|
87
|
-
|
|
88
|
-
// let gitDiffOutput = executeSynchronizedCommands(execSync,['git diff --name-only'],'','Unable to execute git diff command while checking if package.json is changed',true,false).toString().trim()
|
|
89
|
-
// let isPackageJsonChanged = gitDiffOutput.includes('package.json') ? true : false
|
|
90
|
-
|
|
91
|
-
// if(isPackageJsonChanged){
|
|
92
|
-
// let packageJsonChangesRevertCommand = `git checkout "${packageJsonFilePath}"`
|
|
93
|
-
// isPackageJsonChangesRevertedSuccessfully = executeSynchronizedCommands(execSync,[packageJsonChangesRevertCommand,{stdio:'inherit'}],'Changes in package.json reverted successfully','Unable to revert the changes in package.json file',false,true)
|
|
94
|
-
|
|
95
|
-
// if(isPackageJsonChangesRevertedSuccessfully){
|
|
96
|
-
// pluginsToBeAppendedInPackageJson.map(uninstalledPlugin => {
|
|
97
|
-
// if(uninstalledPlugin.startsWith('@')){
|
|
98
|
-
// let indexOfAtCharacter = uninstalledPlugin.indexOf('@')
|
|
99
|
-
// let indexOfSecondOccurenceOfAtCharacter = uninstalledPlugin.indexOf('@',indexOfAtCharacter + 1)
|
|
100
|
-
// let unInstalledPluginName = uninstalledPlugin.slice(0,indexOfSecondOccurenceOfAtCharacter)
|
|
101
|
-
// let unInstalledPluginVersion = uninstalledPlugin.slice(indexOfSecondOccurenceOfAtCharacter + 1)
|
|
102
|
-
// pluginsToBeAddedInDevDependencies[unInstalledPluginName] = unInstalledPluginVersion
|
|
103
|
-
// }
|
|
104
|
-
// else{
|
|
105
|
-
// pluginsToBeAddedInDevDependencies[uninstalledPlugin.split('@')[0]] = uninstalledPlugin.split('@')[1]
|
|
106
|
-
// }
|
|
107
|
-
// })
|
|
108
|
-
// let updatedPluginsIndevDependencies = {
|
|
109
|
-
// ...devDependencies,
|
|
110
|
-
// ...pluginsToBeAddedInDevDependencies
|
|
111
|
-
// }
|
|
112
|
-
// packageJsonContent.devDependencies = updatedPluginsIndevDependencies
|
|
113
|
-
|
|
114
|
-
// let modifiedPackageJson = {
|
|
115
|
-
// ...packageJsonContent,
|
|
116
|
-
// }
|
|
117
|
-
// let isPluginsAddedInPackageJson = executeSynchronizedCommands(writeFileSync,[packageJsonFilePath,JSON.stringify(modifiedPackageJson,null,2)],'Newly installed plugins successfully added to package.json','Unable to append installed plugins to package.json',false,true)
|
|
118
|
-
// return isPluginsAddedInPackageJson
|
|
119
|
-
// }
|
|
120
|
-
// else{
|
|
121
|
-
// return true
|
|
122
|
-
// }
|
|
123
|
-
// }
|
|
124
|
-
// }
|
|
125
|
-
|
|
126
79
|
module.exports = {
|
|
127
80
|
installPlugins
|
|
128
81
|
};
|
package/changeLog.md
CHANGED
|
@@ -23,7 +23,10 @@
|
|
|
23
23
|
3. Added configuration to abort commits if there are warnings or issues.
|
|
24
24
|
4. Environment-specific clone configuration added.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
# 0.0.5 - Automatic eslint plugins installation
|
|
27
|
+
1. The required eslint plugins configured for a repository will be automatically installed during the installation of the library
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
# 0.0.6 - Support extended for .properties file
|
|
30
|
+
|
|
31
|
+
1. The file types that are supported by this library can now be configured using the configuration file "lint.config.js"
|
|
32
|
+
2. Support for .properties file is also extended in this version along with existing support for file types .js, .jsx, .ts, .tsx
|