@appland/scanner 1.76.1 → 1.76.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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # [@appland/scanner-v1.76.2](https://github.com/getappmap/appmap-js/compare/@appland/scanner-v1.76.1...@appland/scanner-v1.76.2) (2023-02-22)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Include contributor counts in `scan` ([1c5de40](https://github.com/getappmap/appmap-js/commit/1c5de40410ae2e1794509fb29e9ba128eaac18c0))
7
+ * Report git state ([a5d3797](https://github.com/getappmap/appmap-js/commit/a5d3797815b283ed114a57351067ed911ca36fa2))
8
+
1
9
  # [@appland/scanner-v1.76.1](https://github.com/getappmap/appmap-js/compare/@appland/scanner-v1.76.0...@appland/scanner-v1.76.1) (2023-02-16)
2
10
 
3
11
 
@@ -64,6 +64,7 @@ function singleScan(options) {
64
64
  numAppMaps: scanResults.summary.numAppMaps,
65
65
  numFindings: scanResults.summary.numFindings,
66
66
  elapsedMs: elapsed,
67
+ appmapDir: options.appmapDir,
67
68
  });
68
69
  });
69
70
  }
@@ -76,7 +76,7 @@ class Watcher {
76
76
  // do not remove callbackify, apparently on windows
77
77
  // passing plain async function doesn't work (?)
78
78
  this.queue = (0, async_1.queue)((0, node_util_1.callbackify)(this.scan.bind(this)), 2);
79
- watchScanTelemetry_1.WatchScanTelemetry.watch(this.scanEventEmitter);
79
+ watchScanTelemetry_1.WatchScanTelemetry.watch(this.scanEventEmitter, options.appmapDir);
80
80
  this.queue.error((error, task) => console.warn(`Problem processing ${task}:\n`, error));
81
81
  }
82
82
  watch() {
@@ -7,7 +7,8 @@ exports.WatchScanTelemetry = void 0;
7
7
  const eventAggregator_1 = __importDefault(require("../../util/eventAggregator"));
8
8
  const scanResults_1 = require("../../report/scanResults");
9
9
  class WatchScanTelemetry {
10
- constructor(scanEvents) {
10
+ constructor(scanEvents, appmapDir) {
11
+ this.appmapDir = appmapDir;
11
12
  this.cancelFn = new eventAggregator_1.default((events) => {
12
13
  const scanEvents = events.map((e) => e.arg);
13
14
  this.sendTelemetry(scanEvents);
@@ -18,8 +19,8 @@ class WatchScanTelemetry {
18
19
  this.cancelFn();
19
20
  this.cancelFn = undefined;
20
21
  }
21
- static watch(scanEvents) {
22
- const telemetry = new WatchScanTelemetry(scanEvents);
22
+ static watch(scanEvents, appmapDir) {
23
+ const telemetry = new WatchScanTelemetry(scanEvents, appmapDir);
23
24
  return () => telemetry.cancel();
24
25
  }
25
26
  sendTelemetry(scanEvents) {
@@ -36,6 +37,7 @@ class WatchScanTelemetry {
36
37
  numAppMaps: telemetryScanResults.summary.numAppMaps,
37
38
  numFindings: telemetryScanResults.summary.numFindings,
38
39
  elapsedMs: elapsed,
40
+ appmapDir: this.appmapDir,
39
41
  });
40
42
  }
41
43
  }
@@ -1,10 +1,39 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
4
33
  };
5
34
  Object.defineProperty(exports, "__esModule", { value: true });
6
35
  exports.sendScanResultsTelemetry = exports.ScanResults = void 0;
7
- const telemetry_1 = __importDefault(require("../telemetry"));
36
+ const telemetry_1 = __importStar(require("../telemetry"));
8
37
  class DistinctItems {
9
38
  constructor() {
10
39
  this.members = {};
@@ -90,18 +119,23 @@ class ScanResults {
90
119
  }
91
120
  exports.ScanResults = ScanResults;
92
121
  function sendScanResultsTelemetry(telemetry) {
93
- telemetry_1.default.sendEvent({
94
- name: 'scan:completed',
95
- properties: {
96
- rules: telemetry.ruleIds.sort().join(', '),
97
- },
98
- metrics: {
99
- duration: telemetry.elapsedMs / 1000,
100
- numRules: telemetry.ruleIds.length,
101
- numAppMaps: telemetry.numAppMaps,
102
- numFindings: telemetry.numFindings,
103
- },
104
- }),
105
- { includeEnvironmentVariables: true };
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ const gitState = telemetry_1.GitState[yield telemetry_1.Git.state(telemetry.appmapDir)];
124
+ const contributors = (yield telemetry_1.Git.contributors(60, telemetry.appmapDir)).length;
125
+ telemetry_1.default.sendEvent({
126
+ name: 'scan:completed',
127
+ properties: {
128
+ rules: telemetry.ruleIds.sort().join(', '),
129
+ git_state: gitState,
130
+ },
131
+ metrics: {
132
+ duration: telemetry.elapsedMs / 1000,
133
+ numRules: telemetry.ruleIds.length,
134
+ numAppMaps: telemetry.numAppMaps,
135
+ numFindings: telemetry.numFindings,
136
+ contributors: contributors,
137
+ },
138
+ }, { includeEnvironment: true });
139
+ });
106
140
  }
107
141
  exports.sendScanResultsTelemetry = sendScanResultsTelemetry;
@@ -22,16 +22,29 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
25
34
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
36
  };
28
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.Git = exports.GitState = void 0;
29
39
  const os_1 = require("os");
30
40
  const crypto_1 = require("crypto");
31
41
  const os = __importStar(require("os"));
32
42
  const read_pkg_up_1 = require("read-pkg-up");
33
43
  const applicationinsights_1 = require("applicationinsights");
34
44
  const conf_1 = __importDefault(require("conf"));
45
+ const child_process_1 = require("child_process");
46
+ const util_1 = require("util");
47
+ const exec = (0, util_1.promisify)(child_process_1.exec);
35
48
  const { name, version } = (() => {
36
49
  var _a;
37
50
  const result = (_a = (0, read_pkg_up_1.sync)({ cwd: __dirname })) === null || _a === void 0 ? void 0 : _a.packageJson;
@@ -215,3 +228,93 @@ class Telemetry {
215
228
  exports.default = Telemetry;
216
229
  Telemetry.debug = process.env.APPMAP_TELEMETRY_DEBUG !== undefined;
217
230
  Telemetry.machineId = getMachineId();
231
+ var GitState;
232
+ (function (GitState) {
233
+ GitState[GitState["NotInstalled"] = 0] = "NotInstalled";
234
+ GitState[GitState["NoRepository"] = 1] = "NoRepository";
235
+ GitState[GitState["Ok"] = 2] = "Ok";
236
+ })(GitState = exports.GitState || (exports.GitState = {}));
237
+ class GitProperties {
238
+ static contributors(sinceDaysAgo, cwd) {
239
+ return __awaiter(this, void 0, void 0, function* () {
240
+ const unixTimeNow = Math.floor(Number(new Date()) / 1000);
241
+ const unixTimeAgo = unixTimeNow - sinceDaysAgo * 24 * 60 * 60;
242
+ try {
243
+ const { stdout } = yield exec([
244
+ 'git',
245
+ cwd && `-C ${cwd.toString()}`,
246
+ '--no-pager',
247
+ 'log',
248
+ `--since=${unixTimeAgo}`,
249
+ '--format="%ae"',
250
+ ].join(' '));
251
+ return [
252
+ ...stdout
253
+ .trim()
254
+ .split('\n')
255
+ .reduce((acc, email) => {
256
+ acc.add(email);
257
+ return acc;
258
+ }, new Set()),
259
+ ];
260
+ }
261
+ catch (_a) {
262
+ return [];
263
+ }
264
+ });
265
+ }
266
+ static state(cwd) {
267
+ return __awaiter(this, void 0, void 0, function* () {
268
+ return new Promise((resolve) => {
269
+ try {
270
+ const commandProcess = (0, child_process_1.spawn)('git', ['status'], {
271
+ shell: true,
272
+ cwd: cwd === null || cwd === void 0 ? void 0 : cwd.toString(),
273
+ });
274
+ commandProcess.on('close', (code) => {
275
+ switch (code) {
276
+ case 127:
277
+ return resolve(GitState.NotInstalled);
278
+ case 128:
279
+ return resolve(GitState.NoRepository);
280
+ default:
281
+ return resolve(GitState.Ok);
282
+ }
283
+ });
284
+ commandProcess.on('error', () => GitState.NotInstalled);
285
+ }
286
+ catch (_a) {
287
+ resolve(GitState.NotInstalled);
288
+ }
289
+ });
290
+ });
291
+ }
292
+ }
293
+ const gitCache = new Map();
294
+ // GitProperties is available externally as Git.
295
+ // This export provides a simple caching layer around GitProperties to avoid
296
+ // excessive shelling out to git.
297
+ exports.Git = new Proxy(GitProperties, {
298
+ get(target, prop) {
299
+ if (typeof target[prop] === 'function') {
300
+ return new Proxy(target[prop], {
301
+ apply(target, thisArg, argArray) {
302
+ const cacheKey = `${prop.toString()}(${JSON.stringify(argArray)})`;
303
+ if (gitCache.has(cacheKey)) {
304
+ return gitCache.get(cacheKey);
305
+ }
306
+ /* eslint-disable-next-line @typescript-eslint/ban-types */
307
+ const result = Reflect.apply(target, thisArg, argArray);
308
+ if (result instanceof Promise) {
309
+ return result.then((r) => {
310
+ gitCache.set(cacheKey, r);
311
+ return r;
312
+ });
313
+ }
314
+ return result;
315
+ },
316
+ });
317
+ }
318
+ return Reflect.get(target, prop);
319
+ },
320
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appland/scanner",
3
- "version": "1.76.1",
3
+ "version": "1.76.2",
4
4
  "description": "Analyze AppMaps for code flaws",
5
5
  "bin": "built/cli.js",
6
6
  "files": [