@appland/scanner 1.71.1 → 1.71.3

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,18 @@
1
+ # [@appland/scanner-v1.71.3](https://github.com/getappmap/appmap-js/compare/@appland/scanner-v1.71.2...@appland/scanner-v1.71.3) (2022-10-11)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Handle appmap paths in an OS-agnostic manner in scan --watch ([cc473a8](https://github.com/getappmap/appmap-js/commit/cc473a89c60130b4b518a56b7e50f8617d88dd59))
7
+ * scan --watch works even if the appmap dir doesn't initially exist ([938bb76](https://github.com/getappmap/appmap-js/commit/938bb76d8fd827874731f4cd3258c4b9e0988a35))
8
+
9
+ # [@appland/scanner-v1.71.2](https://github.com/getappmap/appmap-js/compare/@appland/scanner-v1.71.1...@appland/scanner-v1.71.2) (2022-10-04)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * Use default configuration timestamp in scan --watch if none provided ([3d246f2](https://github.com/getappmap/appmap-js/commit/3d246f2b350af2dd14a097519ea9af6fe410e543))
15
+
1
16
  # [@appland/scanner-v1.71.1](https://github.com/getappmap/appmap-js/compare/@appland/scanner-v1.71.0...@appland/scanner-v1.71.1) (2022-10-04)
2
17
 
3
18
 
@@ -69,11 +69,11 @@ exports.default = {
69
69
  if (reportAllFindings && watch) {
70
70
  throw new errors_1.ValidationError(`Don't use --all with --watch, because in watch mode all findings are reported`);
71
71
  }
72
+ if (appmapDir)
73
+ yield (0, validateFile_1.default)('directory', appmapDir);
72
74
  if (!appmapFile && !appmapDir) {
73
75
  appmapDir = (yield (0, appmapDirFromConfig_1.appmapDirFromConfig)()) || '.';
74
76
  }
75
- if (appmapDir)
76
- yield (0, validateFile_1.default)('directory', appmapDir);
77
77
  let appId = appIdArg;
78
78
  if (!watch && !reportAllFindings)
79
79
  appId = yield (0, resolveAppId_1.default)(appIdArg, appmapDir);
@@ -44,6 +44,29 @@ const configurationProvider_1 = require("../../configuration/configurationProvid
44
44
  const assert_1 = __importDefault(require("assert"));
45
45
  const path_1 = __importDefault(require("path"));
46
46
  const async_1 = require("async");
47
+ function isDir(targetPath) {
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ try {
50
+ return (yield (0, promises_1.stat)(targetPath)).isDirectory();
51
+ }
52
+ catch (_a) {
53
+ return false;
54
+ }
55
+ });
56
+ }
57
+ function existingParent(targetPath) {
58
+ return __awaiter(this, void 0, void 0, function* () {
59
+ while (targetPath.length > 1) {
60
+ if (yield isDir(targetPath))
61
+ break;
62
+ targetPath = path_1.default.dirname(targetPath);
63
+ }
64
+ return targetPath;
65
+ });
66
+ }
67
+ function isAncestorPath(ancestor, descendant) {
68
+ return !path_1.default.relative(ancestor, descendant).startsWith('..');
69
+ }
47
70
  class Watcher {
48
71
  constructor(options) {
49
72
  this.options = options;
@@ -58,20 +81,34 @@ class Watcher {
58
81
  this.configWatcher
59
82
  .on('add', this.reloadConfig.bind(this))
60
83
  .on('change', this.reloadConfig.bind(this));
61
- // Chokidar struggles with relative paths. Make sure the watch pattern is absolute.
62
- const watchPattern = path_1.default.resolve(this.options.appmapDir, '**', 'mtime');
63
- this.appmapWatcher = chokidar.watch(watchPattern, {
84
+ const appmapDir = path_1.default.resolve(this.options.appmapDir);
85
+ // If the appmap directory is a descendant of cwd, watch cwd (presumably project directory).
86
+ // This ensures the watch will survive even if the appmap dir is removed and recreated.
87
+ // Otherwise, make sure to use an existing directory. Chokidar struggles with missing directories.
88
+ const watchDir = isAncestorPath(process.cwd(), appmapDir)
89
+ ? process.cwd()
90
+ : yield existingParent(appmapDir);
91
+ // Custom ignore function needed to cut down the watch tree to just what we need.
92
+ const ignored = (targetPath) => {
93
+ // Ignore anything that isn't an ancestor or descendant of the appmap dir.
94
+ if (!(isAncestorPath(targetPath, appmapDir) || isAncestorPath(appmapDir, targetPath)))
95
+ return true;
96
+ // Also make sure to not try to recurse down node_modules or .git
97
+ const basename = path_1.default.basename(targetPath);
98
+ return basename === 'node_modules' || basename === '.git';
99
+ };
100
+ this.appmapWatcher = chokidar.watch(watchDir, {
64
101
  ignoreInitial: true,
65
- ignored: ['**/node_modules/**', '**/.git/**'],
102
+ ignored,
66
103
  });
67
- this.appmapPoller = chokidar.watch(watchPattern, {
104
+ this.appmapPoller = chokidar.watch(watchDir, {
68
105
  ignoreInitial: false,
69
- ignored: ['**/node_modules/**', '**/.git/**'],
106
+ ignored,
70
107
  usePolling: true,
71
108
  interval: 1000,
72
109
  persistent: false,
73
110
  });
74
- const enqueue = this.enqueue.bind(this);
111
+ const enqueue = (filePath) => path_1.default.basename(filePath) === 'mtime' && this.enqueue(filePath);
75
112
  for (const ch of [this.appmapWatcher, this.appmapPoller])
76
113
  ch.on('add', enqueue).on('change', enqueue);
77
114
  });
@@ -94,15 +131,14 @@ class Watcher {
94
131
  scan(mtimePath) {
95
132
  return __awaiter(this, void 0, void 0, function* () {
96
133
  (0, assert_1.default)(this.config, `config should always be loaded before appmapWatcher triggers a scan`);
97
- (0, assert_1.default)(this.configTime);
98
- const appmapFile = mtimePath.replace(/\/mtime$/, '.appmap.json');
134
+ const appmapFile = [path_1.default.dirname(mtimePath), 'appmap.json'].join('.');
99
135
  const reportFile = mtimePath.replace(/mtime$/, 'appmap-findings.json');
100
136
  const [appmapStats, reportStats] = yield Promise.all([appmapFile, reportFile].map((f) => (0, promises_1.stat)(f).catch(() => null)));
101
137
  if (!appmapStats)
102
138
  return;
103
139
  if (reportStats &&
104
140
  reportStats.mtimeMs > appmapStats.mtimeMs &&
105
- reportStats.mtimeMs > this.configTime)
141
+ reportStats.mtimeMs > this.config.timestampMs)
106
142
  return; // report is up to date
107
143
  const scanner = yield (0, scanner_1.default)(true, this.config, [appmapFile]);
108
144
  const rawScanResults = yield scanner.scan();
@@ -113,7 +149,6 @@ class Watcher {
113
149
  reloadConfig() {
114
150
  return __awaiter(this, void 0, void 0, function* () {
115
151
  this.config = yield (0, configurationProvider_1.parseConfigFile)(this.options.configFile);
116
- this.configTime = (yield (0, promises_1.stat)(this.options.configFile)).mtimeMs;
117
152
  });
118
153
  }
119
154
  }
@@ -211,10 +211,12 @@ function parseConfigFile(configPath) {
211
211
  configPath = (0, path_1.join)(__dirname, '../sampleConfig/default.yml');
212
212
  }
213
213
  console.log(`Using scanner configuration file ${configPath}`);
214
+ const timestampMs = (yield fs_1.promises.stat(configPath)).mtimeMs;
214
215
  const yamlConfig = yield fs_1.promises.readFile(configPath, 'utf-8');
215
- return js_yaml_1.default.load(yamlConfig, {
216
+ const config = js_yaml_1.default.load(yamlConfig, {
216
217
  filename: configPath,
217
218
  });
219
+ return Object.assign(Object.assign({}, config), { timestampMs });
218
220
  });
219
221
  }
220
222
  exports.parseConfigFile = parseConfigFile;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appland/scanner",
3
- "version": "1.71.1",
3
+ "version": "1.71.3",
4
4
  "description": "",
5
5
  "bin": "built/cli.js",
6
6
  "files": [