npm-groovy-lint 14.3.0 → 14.4.0

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.
@@ -12,7 +12,6 @@ const { prepareCodeNarcCall, parseCodeNarcResult } = require("./codenarc-factory
12
12
  const { NPM_GROOVY_LINT_CONSTANTS, loadConfig, getConfigFileName } = require("./config.js");
13
13
  const optionsDefinition = require("./options");
14
14
  const { computeStats, processOutput } = require("./output.js");
15
- const { recordAnonymousEvent } = require("./analytics.js");
16
15
  const { getNpmGroovyLintVersion, getSourceLines, isErrorInLogLevelScope } = require("./utils");
17
16
 
18
17
  class NpmGroovyLint {
@@ -336,25 +335,6 @@ class NpmGroovyLint {
336
335
  this.outputString = await processOutput(this.outputType, this.output, this.lintResult, this.options, this.fixer);
337
336
  }
338
337
 
339
- // Manage anonymous usage stats (except if current lint has been cancelled by a duplicate call)
340
- if (this.startElapse && this.options.insight === true && this.status !== 9) {
341
- const elapsedTimeMs = parseInt(performance.now() - this.startElapse);
342
- const callerKey = this.origin === "index" ? "cli" : "module";
343
- const actionKey = this.options.format ? "format" : this.options.fix ? "fix" : "lint";
344
- const data = {
345
- status: this.status,
346
- fileList: this.fileList,
347
- result: this.lintResult,
348
- elapsed: elapsedTimeMs,
349
- options: this.options,
350
- error: this.error
351
- };
352
- const eventSentPromise = recordAnonymousEvent(callerKey + "-" + actionKey, data);
353
- if (callerKey === "cli") {
354
- await eventSentPromise;
355
- }
356
- }
357
-
358
338
  await this.manageDeleteTmpFiles();
359
339
 
360
340
  // Manage return code in case failonerror, failonwarning or failoninfo is called
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-groovy-lint",
3
- "version": "14.3.0",
3
+ "version": "14.4.0",
4
4
  "description": "Lint, format and auto-fix your Groovy / Jenkinsfile / Gradle files",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -46,7 +46,6 @@
46
46
  "npm-groovy-lint": "lib/index.js"
47
47
  },
48
48
  "dependencies": {
49
- "amplitude": "^6.0.0",
50
49
  "ansi-colors": "^4.1.1",
51
50
  "axios": "^1.6.2",
52
51
  "chalk": "^4.1.2",
package/lib/analytics.js DELETED
@@ -1,353 +0,0 @@
1
- const Amplitude = require("amplitude");
2
- const crypto = require("crypto");
3
- const debug = require("debug")("npm-groovy-lint");
4
- const fse = require("fs-extra");
5
- const os = require("os");
6
- const path = require("path");
7
- const { getSourceLines } = require("./utils");
8
-
9
- const AMPLITUDE_TOKEN = "2e52ce300e4bd3a76e97e27fe1bf31ad";
10
- const STATS_VERSION = globalThis.NPM_GROOVY_LINT_TEST === true ? -1 : 2;
11
-
12
- let amplitudeClient;
13
- let pkgJson;
14
- let anonymousUserId;
15
-
16
- // Record anonymous statistics for better use. Returns a promise that can be awaited by the caller or not
17
- async function recordAnonymousEvent(eventType, data) {
18
- debug("Analytics init: " + eventType);
19
- if (amplitudeClient == null) {
20
- amplitudeClient = new Amplitude(AMPLITUDE_TOKEN);
21
- }
22
- if (pkgJson == null) {
23
- pkgJson = getPackageJson();
24
- }
25
- if (anonymousUserId == null) {
26
- anonymousUserId = getUuidV4();
27
- }
28
- const events = [];
29
- const linterEvent = buildLinterEvent(eventType, data);
30
- events.push(linterEvent);
31
- events.push(...(await buildFileStatsEvents(linterEvent, data)));
32
- try {
33
- return (async resolve => {
34
- // Failing to send analytics isn't fatal.
35
- try {
36
- await amplitudeClient.track(events);
37
- debug(`Analytics sent type: ${eventType} ${JSON.stringify(events)}`);
38
- } catch (err) {
39
- debug(`Analytics send failed type: ${eventType} ${JSON.stringify(events)} ${err}`);
40
- } finally {
41
- resolve();
42
- }
43
- })();
44
- } catch (e) {
45
- debug(`Analytics send failed type: ${eventType} ${JSON.stringify(events)} ${e}`);
46
- return Promise.resolve();
47
- }
48
- }
49
-
50
- // Build payload for main linter event
51
- function buildLinterEvent(eventType, data) {
52
- const payloadFiltered = {
53
- app: pkgJson.name,
54
- appVersion: pkgJson.version,
55
- osPlatform: os.platform(),
56
- osRelease: os.release(),
57
- ci: process.env.CI ? true : false,
58
- statsVersion: STATS_VERSION
59
- };
60
- // Status
61
- if (data.status || data.status === 0) {
62
- payloadFiltered.status = data.status === 0 ? 69 : data.status;
63
- }
64
- // Error
65
- if (data.error) {
66
- payloadFiltered.error = data.error;
67
- }
68
- // Elapsed time
69
- if (data.elapsed) {
70
- payloadFiltered.elapsedTimeMs = data.elapsed;
71
- }
72
- // Options
73
- if (data.options) {
74
- if (data.options.rulesets) {
75
- payloadFiltered.rulesets = data.options.rulesets;
76
- }
77
- if (data.options.overriddenRules) {
78
- payloadFiltered.overriddenRules = data.options.overriddenRules;
79
- }
80
- if (data.options.path) {
81
- payloadFiltered.optionPath = data.options.path;
82
- }
83
- if (data.options.files) {
84
- payloadFiltered.optionFiles = data.options.files.replace(/\*/g, "#");
85
- } else if (data.options.sourcefilepath) {
86
- payloadFiltered.optionFiles = data.options.sourcefilepath;
87
- }
88
- if (data.options.parse) {
89
- payloadFiltered.optionParse = data.options.parse;
90
- }
91
- if (data.options.output) {
92
- payloadFiltered.optionOutput = data.options.output;
93
- }
94
- if (data.options.failonerror || data.options.failonwarning || data.options.failoninfo) {
95
- payloadFiltered.optionFailOn = data.options.failonerror ? "error" : data.options.failonwarning ? "warning" : "info";
96
- }
97
- if (data.options.codenarcargs) {
98
- payloadFiltered.optionCodeNarcArgs = data.options.codenarcargs;
99
- }
100
- if (data.options.ignorepattern) {
101
- payloadFiltered.optionIgnorePattern = data.options.ignorepattern;
102
- }
103
- if (data.options.config) {
104
- payloadFiltered.optionConfig = data.options.config;
105
- }
106
- }
107
-
108
- // *Summary
109
- if (data.result && data.result.summary) {
110
- // Counters
111
- if (data.result.summary.totalFoundNumber) {
112
- payloadFiltered.totalFoundNumber = data.result.summary.totalFoundNumber;
113
- }
114
- if (data.options && [data.options.format, data.options.fix].includes(true) && data.result.summary.totalFixedNumber) {
115
- payloadFiltered.totalFixedNumber = data.result.summary.totalFixedNumber;
116
- }
117
- if (data.options && [data.options.format, data.options.fix].includes(true) && data.result.summary.totalRemainingNumber) {
118
- payloadFiltered.totalRemainingNumber = data.result.summary.totalRemainingNumber;
119
- }
120
- // Stats of rules
121
- if (data.result.summary.detectedRules) {
122
- payloadFiltered.detectedRules = data.result.summary.detectedRules;
123
- }
124
- if (data.result.summary.fixedRules) {
125
- payloadFiltered.fixedRules = data.result.summary.fixedRules;
126
- }
127
- // Number of lines of the first linted file
128
- if (data.result.linesNumber) {
129
- payloadFiltered.fileLinesNumber = data.result.linesNumber;
130
- }
131
- }
132
-
133
- const linterEvent = {
134
- app_version: payloadFiltered.appVersion,
135
- os_name: payloadFiltered.osPlatform,
136
- os_version: payloadFiltered.osRelease,
137
- language: process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || process.env.LC_MESSAGES,
138
- event_type: eventType,
139
- event_properties: payloadFiltered,
140
- user_id: anonymousUserId,
141
- ip: "127.0.0.1"
142
- };
143
-
144
- return linterEvent;
145
- }
146
-
147
- // Retrieve npm-groovy-lint package.json
148
- function getPackageJson() {
149
- const FindPackageJson = require("find-package-json");
150
- const finder = FindPackageJson(__dirname);
151
- const packageJsonFileNm = finder.next().filename;
152
- let pkg;
153
- if (packageJsonFileNm) {
154
- pkg = require(packageJsonFileNm);
155
- } else {
156
- pkg = { name: "npm-groovy-lint", version: "0.0.0" };
157
- console.warn(`package.json not found, use default value ${JSON.stringify(pkg)} instead`);
158
- }
159
- return pkg;
160
- }
161
-
162
- // Get unique anonymous user identifier
163
- function getUuidV4() {
164
- if (globalThis.anonymousUserId) {
165
- return globalThis.anonymousUserId;
166
- }
167
- const localStorageFileNm = path.resolve(os.homedir() + "/.node-stats/local-storage.json");
168
- let usrLocalStorage = {};
169
- if (fse.existsSync(localStorageFileNm)) {
170
- usrLocalStorage = fse.readJsonSync(localStorageFileNm);
171
- }
172
- if (usrLocalStorage.anonymousUserId) {
173
- return usrLocalStorage.anonymousUserId;
174
- }
175
- const { v4: uuidv4 } = require("uuid");
176
- const anonUsrId = uuidv4();
177
- usrLocalStorage.anonymousUserId = anonUsrId;
178
- globalThis.anonymousUserId = usrLocalStorage.anonymousUserId;
179
- try {
180
- fse.ensureDirSync(path.resolve(os.homedir() + "/.node-stats"), { mode: "0777" });
181
- fse.writeJsonSync(localStorageFileNm, usrLocalStorage);
182
- } catch (e) {
183
- debug(`Unable to write anonymous user id in ${localStorageFileNm}
184
- ${e.message}`);
185
- }
186
- return usrLocalStorage.anonymousUserId;
187
- }
188
-
189
- // Build event related to framework usage in files
190
- async function buildFileStatsEvents(linterEvent, data) {
191
- const fileStatsEvents = [];
192
- if (data.fileList) {
193
- for (const file of data.fileList) {
194
- const fileStatEvent = Object.assign({}, linterEvent);
195
- fileStatEvent.event_type = "file-stat";
196
- fileStatEvent.event_properties = await getFileStats(file);
197
- fileStatsEvents.push(fileStatEvent);
198
- }
199
- }
200
- return fileStatsEvents;
201
- }
202
-
203
- async function getFileStats(file) {
204
- const fileStatEventProps = {};
205
- const source = await fse.readFile(file).catch(err => {
206
- throw new Error(`Unable to read stats: ${err}`); // Ensure that we have a stack trace.
207
- });
208
- const sourceLines = await getSourceLines(source);
209
- fileStatEventProps.fileId = crypto
210
- .createHash("sha1")
211
- .update(file)
212
- .digest("base64");
213
- fileStatEventProps.fileName = path.basename(file);
214
- fileStatEventProps.fileExtension = path.extname(fileStatEventProps.fileName);
215
- fileStatEventProps.linesNumber = sourceLines.length;
216
- fileStatEventProps.statsVersion = STATS_VERSION;
217
- const { mainFrameworkKey, frameworkKeys } = listFileUsedFrameworks(file, source);
218
- fileStatEventProps.frameworks = frameworkKeys;
219
- if (mainFrameworkKey != null) {
220
- fileStatEventProps.mainFramework = mainFrameworkKey;
221
- }
222
- for (const fwKey of fileStatEventProps.frameworks) {
223
- fileStatEventProps[`use_${fwKey}`] = true;
224
- }
225
- return fileStatEventProps;
226
- }
227
-
228
- const frameworkDefs = [
229
- { name: "beakerx", priority: 3 }, // UNDEFINED
230
- { name: "codenarc", priority: 5, sourceIncludes: ["codenarc", "groovylint"] },
231
- { name: "dru", priority: 2, packages: ["com.agorapulse.dru"] },
232
- { name: "ersatz", priority: 2, packages: ["com.stehno.ersatz"] },
233
- { name: "gaelyk", priority: 2, packages: ["groovyx.gaelyk"] },
234
- { name: "gaiden", priority: 3, packages: ["gaiden"] },
235
- { name: "gpars", priority: 5, packages: ["groovyx.gpars"] },
236
- { name: "geb", priority: 3, packages: ["geb"] },
237
- { name: "gperfutils", priority: 5, packages: ["groovyx.gbench", "groovyx.gprof"] },
238
- { name: "gradle", priority: 1, fileExtensions: [".gradle"] },
239
- { name: "grails", priority: 1, filePathIncludes: ["grails-app"] },
240
- { name: "grain", priority: 2, packages: ["com.sysgears"] },
241
- { name: "grapes", priority: 5, sourceIncludes: ["@Grab"] },
242
- { name: "griffon", priority: 2, sourceIncludes: ["griffon"] },
243
- { name: "groocss", priority: 2, packages: ["org.groocss"] },
244
- { name: "groovyant", priority: 5, packages: ["groovy.ant"] },
245
- { name: "groovymbean", priority: 5, sourceIncludes: ["GroovyMBean"] },
246
- { name: "groovysh", priority: 2, sourceIncludes: ["groovysh"] },
247
- { name: "groovysql", priority: 5, packages: ["groovy.sql"] },
248
- { name: "groovyswing", priority: 5, packages: ["groovy.swing"] },
249
- { name: "gru", priority: 2, packages: ["com.agorapulse.gru"] },
250
- { name: "httpbuilder", priority: 5, packages: ["groovyx.net.http"] },
251
- { name: "infrastructor", priority: 2, sourceIncludes: ["inlineInventory", "infrastructor"] },
252
- { name: "jenkinsjobdsl", priority: 5, sourceIncludes: ["job("] },
253
- { name: "jenkinspipeline", priority: 5, fileNameIncludes: ["Jenkinsfile"], sourceIncludes: ["pipeline {", "pipeline{"] },
254
- { name: "jenkinssharedlib", priority: 5, sourceIncludes: ["@NonCPS"] },
255
- { name: "jenkins", priority: 1, frameworksUsesIncludes: ["jenkinsjobdsl", "jenkinspipeline", "jenkinssharedlib"] },
256
- { name: "jirascriptrunner", priority: 2, packages: ["com.atlassian.jira"] },
257
- { name: "jmeter", priority: 2, packages: ["org.apache.jmeter"] },
258
- { name: "katalon", priority: 2, packages: ["com.kms.katalon"] },
259
- { name: "kisswebframework", priority: 2 }, // UNDEFINED
260
- { name: "micronaut", priority: 2, packages: ["io.micronaut"] },
261
- { name: "nextflow", priority: 1, fileExtensions: [".nf"], sourceIncludes: ["#!/usr/bin/env nextflow"] },
262
- { name: "picocli", priority: 2, packages: ["picocli"] },
263
- { name: "ratpack", priority: 2, packages: ["ratpack"] },
264
- { name: "restassured", priority: 3 }, // UNDEFINED
265
- { name: "soapui", priority: 3 }, // UNDEFINED
266
- { name: "spock", priority: 3, packages: ["spock"] },
267
- { name: "spreadsheetbuilder", priority: 2, packages: ["org.modelcatalogue.spreadsheet"] },
268
- { name: "springboot", priority: 1, sourceIncludes: ["@SpringBootApplication"] },
269
- { name: "springcloudcontract", priority: 2, sourceIncludes: ["org.springframework.cloud.spec.Contract"] },
270
- { name: "sshoogr", priority: 2, packages: ["com.aestasit.infrastructure"] },
271
- { name: "vertx", priority: 3 } // UNDEFINED
272
- ];
273
-
274
- function listFileUsedFrameworks(file, source) {
275
- const frameworksUsed = [];
276
- const unorderedFrameworkKeys = [];
277
- const fileBaseName = path.basename(file);
278
- const fileExtname = path.extname(file);
279
- for (const frameworkDef of frameworkDefs) {
280
- if (isUsedFramework(frameworkDef, file, source, fileExtname, fileBaseName, unorderedFrameworkKeys)) {
281
- frameworksUsed.push(frameworkDef);
282
- unorderedFrameworkKeys.push(frameworkDef.name);
283
- }
284
- }
285
- // Sort by priority
286
- frameworksUsed.sort((a, b) => a.priority - b.priority);
287
- // Calculate main framework
288
- let mainFrameworkKey = null;
289
- const validMainFrameworks = frameworksUsed.filter(frameworkDef => frameworkDef.priority <= 3);
290
- if (validMainFrameworks[0]) {
291
- mainFrameworkKey = validMainFrameworks[0].name;
292
- }
293
- // Only return keys
294
- const frameworkKeys = frameworksUsed.map(frameworkDef => frameworkDef.name);
295
- return { mainFrameworkKey, frameworkKeys };
296
- }
297
-
298
- function isUsedFramework(frameworkDef, file, source, fileExtname, fileBaseName, frameworksUsed) {
299
- // Check file extension
300
- if (frameworkDef.fileExtensions) {
301
- for (const ext of frameworkDef.fileExtensions) {
302
- if (fileExtname === ext) {
303
- return true;
304
- }
305
- }
306
- }
307
- // Check if another framework has been detected
308
- if (frameworkDef.frameworksUsesIncludes) {
309
- for (const fwKey of frameworkDef.frameworksUsesIncludes) {
310
- if (frameworksUsed.includes(fwKey)) {
311
- return true;
312
- }
313
- }
314
- }
315
-
316
- // Check use of package
317
- if (frameworkDef.packages) {
318
- for (const pckg of frameworkDef.packages) {
319
- if (source.includes(`${pckg}.`)) {
320
- return true;
321
- }
322
- }
323
- }
324
- // Check presence in sources
325
- if (frameworkDef.sourceIncludes) {
326
- for (const text of frameworkDef.sourceIncludes) {
327
- if (source.includes(text)) {
328
- return true;
329
- }
330
- }
331
- }
332
- // Check file name
333
- if (frameworkDef.fileNameIncludes) {
334
- for (const str of frameworkDef.fileNameIncludes) {
335
- if (fileBaseName.includes(str)) {
336
- return true;
337
- }
338
- }
339
- }
340
-
341
- // Check file path
342
- if (frameworkDef.filePathIncludes) {
343
- for (const str of frameworkDef.filePathIncludes) {
344
- if (file.includes(str)) {
345
- return true;
346
- }
347
- }
348
- }
349
-
350
- return false;
351
- }
352
-
353
- module.exports = { recordAnonymousEvent };