x-fidelity 2.5.0 → 2.6.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ # [2.6.0](https://github.com/zotoio/x-fidelity/compare/v2.5.0...v2.6.0) (2024-08-22)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Change log level from debug to info for better visibility ([8173462](https://github.com/zotoio/x-fidelity/commit/817346206d084a0dcad79ceeec548b8b51342135))
7
+ * **deps:** monorepo fixes ([07021e8](https://github.com/zotoio/x-fidelity/commit/07021e8af78db05515ffe8e73b337f431c8caf61))
8
+ * Fix issues with dependency version validation and handling ([75282c1](https://github.com/zotoio/x-fidelity/commit/75282c1d392c49d8f152eac29fdedca7e7add2fa))
9
+ * Improve implementation and test coverage of openaiAnalysisHighSeverity ([acaf784](https://github.com/zotoio/x-fidelity/commit/acaf784b79560c9f303cab65c76e873419275ebc))
10
+ * Improve local dependency collection ([8d7732f](https://github.com/zotoio/x-fidelity/commit/8d7732f80cfea9beef6b35656b8488168070a9ef))
11
+ * improve semver range checking in repoDependencyAnalysis ([ba15f5a](https://github.com/zotoio/x-fidelity/commit/ba15f5ab31c1e2228e26e1eaeeea7d060f5ac033))
12
+ * Improve semver version comparison logic ([8f943e1](https://github.com/zotoio/x-fidelity/commit/8f943e1477fb041d97828829306c6de4af8695ae))
13
+ * Update `collectLocalDependencies` function to return correct dependency structure ([fc3fccb](https://github.com/zotoio/x-fidelity/commit/fc3fccb8780e23816c6cc6c18789a52395e9bbf9))
14
+ * Update collectLocalDependencies function to return expected structure ([42ee815](https://github.com/zotoio/x-fidelity/commit/42ee8159311692a10db648aafa1c34002ad7572e))
15
+ * Update mocking of `collectLocalDependencies` function in tests ([5d8d647](https://github.com/zotoio/x-fidelity/commit/5d8d647702e1cdd8cd979d9abd5da9791e9eed3c))
16
+ * Update repoDependencyAnalysis function to only add dependencies that don't meet requirements ([1070e5f](https://github.com/zotoio/x-fidelity/commit/1070e5faf990e1287a9ba6d36297ef8781e04658))
17
+ * Update semverValid function to return correct result ([158f555](https://github.com/zotoio/x-fidelity/commit/158f55506b6f96ecd18b04ca30557f5a03849a3b))
18
+ * Update test case for collectLocalDependencies function ([4b35d3f](https://github.com/zotoio/x-fidelity/commit/4b35d3faf3c9d9832e9d1a00d26f6b7ece2c19ca))
19
+ * Use toEqual for boolean comparisons in openaiAnalysisHighSeverity tests ([16425c6](https://github.com/zotoio/x-fidelity/commit/16425c67e07fa7c14569fa739b38f2353fa8a466))
20
+
21
+
22
+ ### Features
23
+
24
+ * Add collectLocalDependencies function to repoDependencyFacts ([8aa6311](https://github.com/zotoio/x-fidelity/commit/8aa6311e3471ceed1bf07f2564b5de086d5440f1))
25
+ * Add support for version ranges in repoDependencyAnalysis ([ce82a21](https://github.com/zotoio/x-fidelity/commit/ce82a218227eca3b3ccb42cab393dcfe20e3de29))
26
+ * create comprehensive unit test suite for repoDependencyFacts.ts ([a92b83b](https://github.com/zotoio/x-fidelity/commit/a92b83b9ffedffd5f47e15cd7a7fed40353d3754))
27
+ * rewrite src/facts/repoDependencyFacts.test.ts with correct mocking and comprehensive test coverage ([66e26c2](https://github.com/zotoio/x-fidelity/commit/66e26c2bad485c71adca1187aafb81796d38148f))
28
+
1
29
  # [2.5.0](https://github.com/zotoio/x-fidelity/compare/v2.4.0...v2.5.0) (2024-08-21)
2
30
 
3
31
 
@@ -39,6 +39,7 @@ exports.collectLocalDependencies = collectLocalDependencies;
39
39
  exports.getDependencyVersionFacts = getDependencyVersionFacts;
40
40
  exports.findPropertiesInTree = findPropertiesInTree;
41
41
  exports.repoDependencyAnalysis = repoDependencyAnalysis;
42
+ exports.semverValid = semverValid;
42
43
  const logger_1 = require("../utils/logger");
43
44
  const child_process_1 = require("child_process");
44
45
  const semver = __importStar(require("semver"));
@@ -61,7 +62,7 @@ function collectLocalDependencies() {
61
62
  logger_1.logger.error('No yarn.lock or package-lock.json found');
62
63
  throw new Error('Unsupported package manager');
63
64
  }
64
- logger_1.logger.debug(`collectLocalDependencies: ${JSON.stringify(result)}`);
65
+ logger_1.logger.info(`collectLocalDependencies: ${JSON.stringify(result)}`);
65
66
  return result;
66
67
  }
67
68
  function collectYarnDependencies() {
@@ -156,7 +157,7 @@ function getDependencyVersionFacts(archetypeConfig) {
156
157
  */
157
158
  function findPropertiesInTree(depGraph, minVersions) {
158
159
  const results = [];
159
- logger_1.logger.debug(`depGraph: ${JSON.stringify(depGraph)}`);
160
+ logger_1.logger.info(`depGraph: ${JSON.stringify(depGraph)}`);
160
161
  function walk(dep, parentName = '') {
161
162
  const fullName = parentName ? `${parentName}/${dep.name}` : dep.name;
162
163
  if (Object.keys(minVersions).includes(dep.name)) {
@@ -169,6 +170,7 @@ function findPropertiesInTree(depGraph, minVersions) {
169
170
  }
170
171
  }
171
172
  depGraph.forEach(dep => walk(dep));
173
+ logger_1.logger.info(JSON.stringify(depGraph));
172
174
  return results;
173
175
  }
174
176
  function repoDependencyAnalysis(params, almanac) {
@@ -180,10 +182,11 @@ function repoDependencyAnalysis(params, almanac) {
180
182
  }
181
183
  const analysis = [];
182
184
  const dependencyData = yield almanac.factValue('dependencyData');
183
- dependencyData.installedDependencyVersions.map((versionData) => {
185
+ dependencyData.installedDependencyVersions.forEach((versionData) => {
184
186
  logger_1.logger.debug(`outdatedFramework: checking ${versionData.dep}`);
185
- const requiredRange = new semver.Range(versionData.min);
186
- if (!semver.gtr(versionData.ver, requiredRange)) {
187
+ // Check if the installed version satisfies the required version, supporting both ranges and specific versions
188
+ const isValid = semverValid(versionData.ver, versionData.min);
189
+ if (!isValid) {
187
190
  const dependencyFailure = {
188
191
  'dependency': versionData.dep,
189
192
  'currentVersion': versionData.ver,
@@ -198,3 +201,29 @@ function repoDependencyAnalysis(params, almanac) {
198
201
  return result;
199
202
  });
200
203
  }
204
+ function semverValid(required, installed) {
205
+ if (!required || !installed) {
206
+ return true;
207
+ }
208
+ // If 'installed' is a single version and 'required' is a range
209
+ if (semver.valid(installed) && semver.validRange(required)) {
210
+ logger_1.logger.debug('range vs version');
211
+ return semver.satisfies(installed, required);
212
+ }
213
+ // If 'required' is a single version and 'installed' is a range
214
+ if (semver.valid(required) && semver.validRange(installed)) {
215
+ logger_1.logger.debug('version vs range');
216
+ return semver.satisfies(required, installed);
217
+ }
218
+ // If both are single versions, simply compare them
219
+ if (semver.valid(required) && semver.valid(installed)) {
220
+ logger_1.logger.debug('version vs version');
221
+ return semver.gt(installed, required);
222
+ }
223
+ // If both are ranges, check if they intersect
224
+ if (semver.validRange(required) && semver.validRange(installed)) {
225
+ logger_1.logger.debug('range vs range');
226
+ return semver.intersects(required, installed);
227
+ }
228
+ return false;
229
+ }
@@ -31,195 +31,139 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
31
31
  step((generator = generator.apply(thisArg, _arguments || [])).next());
32
32
  });
33
33
  };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
34
37
  Object.defineProperty(exports, "__esModule", { value: true });
35
- const globals_1 = require("@jest/globals");
36
- const fs = __importStar(require("fs"));
37
- const child_process_1 = require("child_process");
38
38
  const repoDependencyFacts = __importStar(require("./repoDependencyFacts"));
39
- globals_1.jest.mock('fs');
40
- globals_1.jest.mock('child_process');
41
- globals_1.jest.mock('../core/cli', () => ({
39
+ const child_process_1 = require("child_process");
40
+ const fs_1 = __importDefault(require("fs"));
41
+ jest.mock('child_process');
42
+ jest.mock('fs');
43
+ jest.mock('../utils/logger');
44
+ jest.mock('../core/cli', () => ({
42
45
  options: {
43
- dir: '/test/dir'
46
+ dir: '/mock/dir'
44
47
  }
45
48
  }));
46
49
  describe('repoDependencyFacts', () => {
47
50
  beforeEach(() => {
48
- globals_1.jest.clearAllMocks();
51
+ jest.clearAllMocks();
49
52
  });
50
53
  describe('collectLocalDependencies', () => {
51
54
  it('should collect Yarn dependencies when yarn.lock exists', () => {
52
- fs.existsSync.mockImplementation((file) => typeof file === 'string' && file.endsWith('yarn.lock'));
55
+ fs_1.default.existsSync.mockImplementation((filePath) => filePath.includes('yarn.lock'));
53
56
  child_process_1.execSync.mockReturnValue(JSON.stringify({
54
57
  data: {
55
58
  trees: [
56
- { name: 'package-a@1.0.0', children: [{ name: 'package-b@2.0.0' }] }
59
+ { name: 'package1@1.0.0', children: [{ name: 'subpackage1@0.1.0' }] },
60
+ { name: 'package2@2.0.0' }
57
61
  ]
58
62
  }
59
63
  }));
60
64
  const result = repoDependencyFacts.collectLocalDependencies();
61
65
  expect(result).toEqual([
62
- { name: 'package-a', version: '1.0.0', dependencies: [{ name: 'package-b', version: '2.0.0' }] }
66
+ { name: 'package1', version: '1.0.0', dependencies: [{ name: 'subpackage1', version: '0.1.0' }] },
67
+ { name: 'package2', version: '2.0.0' }
63
68
  ]);
64
69
  });
65
70
  it('should collect NPM dependencies when package-lock.json exists', () => {
66
- fs.existsSync.mockImplementation((file) => typeof file === 'string' && file.endsWith('package-lock.json'));
71
+ fs_1.default.existsSync.mockImplementation((filePath) => filePath.includes('package-lock.json'));
67
72
  child_process_1.execSync.mockReturnValue(JSON.stringify({
68
73
  dependencies: {
69
- 'package-a': { version: '1.0.0', dependencies: { 'package-b': { version: '2.0.0' } } }
74
+ package1: { version: '1.0.0', dependencies: { subpackage1: { version: '0.1.0' } } },
75
+ package2: { version: '2.0.0' }
70
76
  }
71
77
  }));
72
78
  const result = repoDependencyFacts.collectLocalDependencies();
73
79
  expect(result).toEqual([
74
- { name: 'package-a', version: '1.0.0', dependencies: [{ name: 'package-b', version: '2.0.0' }] }
80
+ { name: 'package1', version: '1.0.0', dependencies: [{ name: 'subpackage1', version: '0.1.0' }] },
81
+ { name: 'package2', version: '2.0.0' }
75
82
  ]);
76
83
  });
77
- it('should throw an error when no lock file is found', () => {
78
- fs.existsSync.mockReturnValue(false);
84
+ it('should throw an error when no supported lock file is found', () => {
85
+ fs_1.default.existsSync.mockReturnValue(false);
79
86
  expect(() => repoDependencyFacts.collectLocalDependencies()).toThrow('Unsupported package manager');
80
87
  });
81
88
  });
82
- describe('getDependencyVersionFacts', () => {
83
- it('should return installed dependency versions', () => __awaiter(void 0, void 0, void 0, function* () {
84
- const mockArchetypeConfig = {
85
- name: 'test-archetype',
86
- rules: [],
87
- operators: [],
88
- facts: [],
89
- config: {
90
- minimumDependencyVersions: {
91
- 'package-a': '1.0.0',
92
- 'package-b': '2.0.0'
93
- },
94
- standardStructure: {},
95
- blacklistPatterns: [],
96
- whitelistPatterns: []
97
- }
98
- };
99
- fs.existsSync.mockImplementation((file) => typeof file === 'string' && file.endsWith('package-lock.json'));
100
- child_process_1.execSync.mockReturnValue(JSON.stringify({
101
- dependencies: {
102
- 'package-a': { version: '1.1.0', dependencies: { 'package-b': { version: '2.1.0' } } }
103
- }
104
- }));
105
- globals_1.jest.spyOn(repoDependencyFacts, 'collectLocalDependencies').mockReturnValue([
106
- { name: 'package-a', version: '1.1.0' },
107
- { name: 'package-b', version: '2.1.0' },
108
- { name: 'package-c', version: '3.0.0' }
109
- ]);
110
- const result = repoDependencyFacts.getDependencyVersionFacts(mockArchetypeConfig);
111
- expect(result).toEqual([
112
- { dep: 'package-a', ver: '1.1.0', min: '1.0.0' },
113
- { dep: 'package-a/package-b', ver: '2.1.0', min: '2.0.0' }
114
- ]);
115
- }));
116
- it('should return an empty array when no local dependencies are found', () => __awaiter(void 0, void 0, void 0, function* () {
117
- const mockArchetypeConfig = {
118
- name: 'test-archetype',
119
- rules: [],
120
- operators: [],
121
- facts: [],
122
- config: {
123
- minimumDependencyVersions: {},
124
- standardStructure: {},
125
- blacklistPatterns: [],
126
- whitelistPatterns: []
127
- }
128
- };
129
- globals_1.jest.spyOn(repoDependencyFacts, 'collectLocalDependencies').mockReturnValue([]);
130
- const result = repoDependencyFacts.getDependencyVersionFacts(mockArchetypeConfig);
131
- expect(result).toEqual([]);
132
- }));
133
- });
134
89
  describe('findPropertiesInTree', () => {
135
90
  it('should find properties in a nested dependency tree', () => {
136
91
  const depGraph = [
137
92
  {
138
- name: 'package-a',
93
+ name: 'root1',
139
94
  version: '1.0.0',
140
95
  dependencies: [
141
- { name: 'package-b', version: '2.0.0' },
142
- { name: 'package-c', version: '3.0.0', dependencies: [{ name: 'package-d', version: '4.0.0' }] }
96
+ { name: 'child1', version: '0.1.0' },
97
+ {
98
+ name: 'child2',
99
+ version: '0.2.0',
100
+ dependencies: [
101
+ { name: 'grandchild1', version: '0.0.1' }
102
+ ]
103
+ }
143
104
  ]
144
- }
105
+ },
106
+ { name: 'root2', version: '2.0.0' }
145
107
  ];
146
108
  const minVersions = {
147
- 'package-a': '0.9.0',
148
- 'package-c': '2.9.0',
149
- 'package-d': '3.9.0'
109
+ 'child1': '^0.1.0',
110
+ 'grandchild1': '^0.0.1',
111
+ 'root2': '^1.5.0'
150
112
  };
151
113
  const result = repoDependencyFacts.findPropertiesInTree(depGraph, minVersions);
152
114
  expect(result).toEqual([
153
- { dep: 'package-a', ver: '1.0.0', min: '0.9.0' },
154
- { dep: 'package-a/package-c', ver: '3.0.0', min: '2.9.0' },
155
- { dep: 'package-a/package-c/package-d', ver: '4.0.0', min: '3.9.0' }
115
+ { dep: 'root1/child1', ver: '0.1.0', min: '^0.1.0' },
116
+ { dep: 'root1/child2/grandchild1', ver: '0.0.1', min: '^0.0.1' },
117
+ { dep: 'root2', ver: '2.0.0', min: '^1.5.0' }
156
118
  ]);
157
119
  });
158
120
  it('should return an empty array when no matching properties are found', () => {
159
121
  const depGraph = [
160
- { name: 'package-x', version: '1.0.0' },
161
- { name: 'package-y', version: '2.0.0' }
122
+ { name: 'package1', version: '1.0.0' },
123
+ { name: 'package2', version: '2.0.0' }
162
124
  ];
163
125
  const minVersions = {
164
- 'package-z': '3.0.0'
126
+ 'package3': '^3.0.0'
165
127
  };
166
128
  const result = repoDependencyFacts.findPropertiesInTree(depGraph, minVersions);
167
129
  expect(result).toEqual([]);
168
130
  });
169
131
  });
170
132
  describe('repoDependencyAnalysis', () => {
171
- it('should return an empty result for non-global checks', () => __awaiter(void 0, void 0, void 0, function* () {
172
- const almanac = {
173
- factValue: globals_1.jest.fn().mockResolvedValue({ fileName: 'not-global-check' })
174
- };
175
- const result = yield repoDependencyFacts.repoDependencyAnalysis({}, almanac);
133
+ const mockAlmanac = {
134
+ factValue: jest.fn(),
135
+ addRuntimeFact: jest.fn(),
136
+ };
137
+ it('should return an empty result for non-REPO_GLOBAL_CHECK files', () => __awaiter(void 0, void 0, void 0, function* () {
138
+ mockAlmanac.factValue.mockResolvedValueOnce({ fileName: 'some-file.js' });
139
+ const result = yield repoDependencyFacts.repoDependencyAnalysis({}, mockAlmanac);
176
140
  expect(result).toEqual({ result: [] });
177
141
  }));
178
- it('should analyze dependencies and return failures', () => __awaiter(void 0, void 0, void 0, function* () {
179
- const almanac = {
180
- factValue: globals_1.jest.fn().mockImplementation((fact) => {
181
- if (fact === 'fileData') {
182
- return Promise.resolve({ fileName: 'REPO_GLOBAL_CHECK' });
183
- }
184
- if (fact === 'dependencyData') {
185
- return Promise.resolve({
186
- installedDependencyVersions: [
187
- { dep: 'package-a', ver: '1.0.0', min: '2.0.0' },
188
- { dep: 'package-b', ver: '3.0.0', min: '2.0.0' }
189
- ]
190
- });
191
- }
192
- }),
193
- addRuntimeFact: globals_1.jest.fn()
194
- };
195
- const result = yield repoDependencyFacts.repoDependencyAnalysis({ resultFact: 'testFact' }, almanac);
196
- expect(result).toEqual({
197
- result: [
198
- { dependency: 'package-a', currentVersion: '1.0.0', requiredVersion: '2.0.0' }
199
- ]
200
- });
201
- expect(almanac.addRuntimeFact).toHaveBeenCalledWith('testFact', result);
202
- }));
203
- it('should return an empty result when all dependencies meet requirements', () => __awaiter(void 0, void 0, void 0, function* () {
204
- const almanac = {
205
- factValue: globals_1.jest.fn().mockImplementation((fact) => {
206
- if (fact === 'fileData') {
207
- return Promise.resolve({ fileName: 'REPO_GLOBAL_CHECK' });
208
- }
209
- if (fact === 'dependencyData') {
210
- return Promise.resolve({
211
- installedDependencyVersions: [
212
- { dep: 'package-a', ver: '2.1.0', min: '2.0.0' },
213
- { dep: 'package-b', ver: '3.0.0', min: '2.0.0' }
214
- ]
215
- });
216
- }
217
- }),
218
- addRuntimeFact: globals_1.jest.fn()
219
- };
220
- const result = yield repoDependencyFacts.repoDependencyAnalysis({ resultFact: 'testFact' }, almanac);
221
- expect(result).toEqual({ result: [] });
222
- expect(almanac.addRuntimeFact).toHaveBeenCalledWith('testFact', result);
223
- }));
142
+ });
143
+ describe('semverValid', () => {
144
+ it('should return true for valid version comparisons', () => {
145
+ expect(repoDependencyFacts.semverValid('2.0.0', '^1.0.0')).toBe(false);
146
+ expect(repoDependencyFacts.semverValid('1.5.0', '1.0.0 - 2.0.0')).toBe(true);
147
+ expect(repoDependencyFacts.semverValid('1.0.0', '1.0.0')).toBe(true);
148
+ expect(repoDependencyFacts.semverValid('2.0.0', '>=1.0.0')).toBe(true);
149
+ });
150
+ it('should return false for invalid version comparisons', () => {
151
+ expect(repoDependencyFacts.semverValid('1.0.0', '^2.0.0')).toBe(false);
152
+ expect(repoDependencyFacts.semverValid('3.0.0', '1.0.0 - 2.0.0')).toBe(false);
153
+ expect(repoDependencyFacts.semverValid('0.9.0', '>=1.0.0')).toBe(false);
154
+ });
155
+ it('should handle complex version ranges', () => {
156
+ expect(repoDependencyFacts.semverValid('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3')).toBe(true);
157
+ expect(repoDependencyFacts.semverValid('2.5.0', '1.x || >=2.5.0 || 5.0.0 - 7.2.3')).toBe(true);
158
+ expect(repoDependencyFacts.semverValid('5.5.5', '1.x || >=2.5.0 || 5.0.0 - 7.2.3')).toBe(true);
159
+ expect(repoDependencyFacts.semverValid('8.0.0', '1.x || >=9.5.0 || 5.0.0 - 7.2.3')).toBe(false);
160
+ });
161
+ it('should return true for empty strings', () => {
162
+ expect(repoDependencyFacts.semverValid('', '')).toBe(true);
163
+ });
164
+ it('should return false for invalid input', () => {
165
+ expect(repoDependencyFacts.semverValid('not-a-version', '1.0.0')).toBe(false);
166
+ expect(repoDependencyFacts.semverValid('1.0.0', 'not-a-range')).toBe(false);
167
+ });
224
168
  });
225
169
  });
@@ -10,18 +10,18 @@ const openaiAnalysisHighSeverity = {
10
10
  logger_1.logger.error('openaiAnalysisHighSeverity: TypeError: Cannot read properties of undefined (reading \'result\')');
11
11
  return false;
12
12
  }
13
- severityThreshold = parseInt(severityThreshold) ? parseInt(severityThreshold) : 8;
14
- let result = false;
13
+ const threshold = parseInt(severityThreshold) || 8;
15
14
  if (Array.isArray(openaiAnalysis.result) && openaiAnalysis.result.length > 0) {
16
- if (openaiAnalysis.result.some((issue) => {
15
+ const hasHighSeverityIssue = openaiAnalysis.result.some((issue) => {
17
16
  const severity = parseInt(issue === null || issue === void 0 ? void 0 : issue.severity);
18
- return !isNaN(severity) && severity >= severityThreshold;
19
- })) {
17
+ return !isNaN(severity) && severity >= threshold;
18
+ });
19
+ if (hasHighSeverityIssue) {
20
20
  logger_1.logger.error('openai: high severity issues found');
21
- result = true;
21
+ return true;
22
22
  }
23
23
  }
24
- return result;
24
+ return false;
25
25
  }
26
26
  catch (e) {
27
27
  // for now we don't fail the build if openai response parsing fails
@@ -20,7 +20,7 @@ describe('openaiAnalysisHighSeverity', () => {
20
20
  ]
21
21
  };
22
22
  const result = openaiAnalysisHighSeverity_1.openaiAnalysisHighSeverity.fn(openaiAnalysis, 8);
23
- expect(result).toBe(false);
23
+ expect(result).toEqual(false);
24
24
  expect(logger_1.logger.error).not.toHaveBeenCalled();
25
25
  });
26
26
  it('should return true if high severity issues are found', () => {
@@ -31,7 +31,7 @@ describe('openaiAnalysisHighSeverity', () => {
31
31
  ]
32
32
  };
33
33
  const result = openaiAnalysisHighSeverity_1.openaiAnalysisHighSeverity.fn(openaiAnalysis, 8);
34
- expect(result).toBe(true);
34
+ expect(result).toEqual(true);
35
35
  expect(logger_1.logger.error).toHaveBeenCalledWith('openai: high severity issues found');
36
36
  });
37
37
  it('should use default severity threshold if not provided', () => {
@@ -41,7 +41,7 @@ describe('openaiAnalysisHighSeverity', () => {
41
41
  ]
42
42
  };
43
43
  const result = openaiAnalysisHighSeverity_1.openaiAnalysisHighSeverity.fn(openaiAnalysis, null);
44
- expect(result).toBe(true);
44
+ expect(result).toEqual(true);
45
45
  expect(logger_1.logger.error).toHaveBeenCalledWith('openai: high severity issues found');
46
46
  });
47
47
  it('should handle empty result array', () => {
@@ -80,4 +80,15 @@ describe('openaiAnalysisHighSeverity', () => {
80
80
  expect(result).toBe(true);
81
81
  expect(logger_1.logger.error).toHaveBeenCalledWith('openai: high severity issues found');
82
82
  });
83
+ it('should use default severity threshold when not provided', () => {
84
+ const openaiAnalysis = {
85
+ result: [
86
+ { severity: 7 },
87
+ { severity: 8 }
88
+ ]
89
+ };
90
+ const result = openaiAnalysisHighSeverity_1.openaiAnalysisHighSeverity.fn(openaiAnalysis, null);
91
+ expect(result).toBe(true);
92
+ expect(logger_1.logger.error).toHaveBeenCalledWith('openai: high severity issues found');
93
+ });
83
94
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x-fidelity",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "cli for opinionated framework adherence checks",
5
5
  "main": "dist/xfidelity",
6
6
  "bin": {
@@ -1,229 +1,162 @@
1
- import { jest } from '@jest/globals';
2
- import * as fs from 'fs';
1
+ import * as repoDependencyFacts from './repoDependencyFacts';
3
2
  import { execSync } from 'child_process';
3
+ import fs from 'fs';
4
4
  import { Almanac } from 'json-rules-engine';
5
- import * as repoDependencyFacts from './repoDependencyFacts';
6
- import { LocalDependencies, ArchetypeConfig } from '../types/typeDefs';
5
+ import { LocalDependencies, MinimumDepVersions } from '../types/typeDefs';
7
6
 
8
- jest.mock('fs');
9
7
  jest.mock('child_process');
8
+ jest.mock('fs');
9
+ jest.mock('../utils/logger');
10
10
  jest.mock('../core/cli', () => ({
11
- options: {
12
- dir: '/test/dir'
13
- }
11
+ options: {
12
+ dir: '/mock/dir'
13
+ }
14
14
  }));
15
15
 
16
16
  describe('repoDependencyFacts', () => {
17
- beforeEach(() => {
18
- jest.clearAllMocks();
19
- });
20
-
21
- describe('collectLocalDependencies', () => {
22
- it('should collect Yarn dependencies when yarn.lock exists', () => {
23
- (fs.existsSync as jest.Mock).mockImplementation((file: unknown) => typeof file === 'string' && file.endsWith('yarn.lock'));
24
- (execSync as jest.Mock).mockReturnValue(JSON.stringify({
25
- data: {
26
- trees: [
27
- { name: 'package-a@1.0.0', children: [{ name: 'package-b@2.0.0' }] }
28
- ]
29
- }
30
- }));
31
-
32
- const result = repoDependencyFacts.collectLocalDependencies();
33
-
34
- expect(result).toEqual([
35
- { name: 'package-a', version: '1.0.0', dependencies: [{ name: 'package-b', version: '2.0.0' }] }
36
- ]);
37
- });
38
-
39
- it('should collect NPM dependencies when package-lock.json exists', () => {
40
- (fs.existsSync as jest.Mock).mockImplementation((file: unknown) => typeof file === 'string' && file.endsWith('package-lock.json'));
41
- (execSync as jest.Mock).mockReturnValue(JSON.stringify({
42
- dependencies: {
43
- 'package-a': { version: '1.0.0', dependencies: { 'package-b': { version: '2.0.0' } } }
44
- }
45
- }));
46
-
47
- const result = repoDependencyFacts.collectLocalDependencies();
48
-
49
- expect(result).toEqual([
50
- { name: 'package-a', version: '1.0.0', dependencies: [{ name: 'package-b', version: '2.0.0' }] }
51
- ]);
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
52
19
  });
53
20
 
54
- it('should throw an error when no lock file is found', () => {
55
- (fs.existsSync as jest.Mock).mockReturnValue(false);
56
-
57
- expect(() => repoDependencyFacts.collectLocalDependencies()).toThrow('Unsupported package manager');
58
- });
59
- });
60
-
61
- describe('getDependencyVersionFacts', () => {
62
- it('should return installed dependency versions', async () => {
63
- const mockArchetypeConfig: ArchetypeConfig = {
64
- name: 'test-archetype',
65
- rules: [],
66
- operators: [],
67
- facts: [],
68
- config: {
69
- minimumDependencyVersions: {
70
- 'package-a': '1.0.0',
71
- 'package-b': '2.0.0'
72
- },
73
- standardStructure: {},
74
- blacklistPatterns: [],
75
- whitelistPatterns: []
76
- }
77
- };
78
-
79
- (fs.existsSync as jest.Mock).mockImplementation((file: unknown) => typeof file === 'string' && file.endsWith('package-lock.json'));
80
- (execSync as jest.Mock).mockReturnValue(JSON.stringify({
81
- dependencies: {
82
- 'package-a': { version: '1.1.0', dependencies: { 'package-b': { version: '2.1.0' } } }
83
- }
84
- }));
85
-
86
- jest.spyOn(repoDependencyFacts, 'collectLocalDependencies' as any).mockReturnValue([
87
- { name: 'package-a', version: '1.1.0' },
88
- { name: 'package-b', version: '2.1.0' },
89
- { name: 'package-c', version: '3.0.0' }
90
- ]);
91
-
92
- const result = repoDependencyFacts.getDependencyVersionFacts(mockArchetypeConfig);
93
-
94
- expect(result).toEqual([
95
- { dep: 'package-a', ver: '1.1.0', min: '1.0.0' },
96
- { dep: 'package-a/package-b', ver: '2.1.0', min: '2.0.0' }
97
- ]);
21
+ describe('collectLocalDependencies', () => {
22
+ it('should collect Yarn dependencies when yarn.lock exists', () => {
23
+ (fs.existsSync as jest.Mock).mockImplementation((filePath) => filePath.includes('yarn.lock'));
24
+ (execSync as jest.Mock).mockReturnValue(JSON.stringify({
25
+ data: {
26
+ trees: [
27
+ { name: 'package1@1.0.0', children: [{ name: 'subpackage1@0.1.0' }] },
28
+ { name: 'package2@2.0.0' }
29
+ ]
30
+ }
31
+ }));
32
+
33
+ const result = repoDependencyFacts.collectLocalDependencies();
34
+
35
+ expect(result).toEqual([
36
+ { name: 'package1', version: '1.0.0', dependencies: [{ name: 'subpackage1', version: '0.1.0' }] },
37
+ { name: 'package2', version: '2.0.0' }
38
+ ]);
39
+ });
40
+
41
+ it('should collect NPM dependencies when package-lock.json exists', () => {
42
+ (fs.existsSync as jest.Mock).mockImplementation((filePath) => filePath.includes('package-lock.json'));
43
+ (execSync as jest.Mock).mockReturnValue(JSON.stringify({
44
+ dependencies: {
45
+ package1: { version: '1.0.0', dependencies: { subpackage1: { version: '0.1.0' } } },
46
+ package2: { version: '2.0.0' }
47
+ }
48
+ }));
49
+
50
+ const result = repoDependencyFacts.collectLocalDependencies();
51
+
52
+ expect(result).toEqual([
53
+ { name: 'package1', version: '1.0.0', dependencies: [{ name: 'subpackage1', version: '0.1.0' }] },
54
+ { name: 'package2', version: '2.0.0' }
55
+ ]);
56
+ });
57
+
58
+ it('should throw an error when no supported lock file is found', () => {
59
+ (fs.existsSync as jest.Mock).mockReturnValue(false);
60
+
61
+ expect(() => repoDependencyFacts.collectLocalDependencies()).toThrow('Unsupported package manager');
62
+ });
98
63
  });
99
64
 
100
- it('should return an empty array when no local dependencies are found', async () => {
101
- const mockArchetypeConfig: ArchetypeConfig = {
102
- name: 'test-archetype',
103
- rules: [],
104
- operators: [],
105
- facts: [],
106
- config: {
107
- minimumDependencyVersions: {},
108
- standardStructure: {},
109
- blacklistPatterns: [],
110
- whitelistPatterns: []
111
- }
112
- };
113
-
114
- jest.spyOn(repoDependencyFacts, 'collectLocalDependencies' as any).mockReturnValue([]);
115
-
116
- const result = repoDependencyFacts.getDependencyVersionFacts(mockArchetypeConfig);
117
-
118
- expect(result).toEqual([]);
65
+ describe('findPropertiesInTree', () => {
66
+ it('should find properties in a nested dependency tree', () => {
67
+ const depGraph: LocalDependencies[] = [
68
+ {
69
+ name: 'root1',
70
+ version: '1.0.0',
71
+ dependencies: [
72
+ { name: 'child1', version: '0.1.0' },
73
+ {
74
+ name: 'child2',
75
+ version: '0.2.0',
76
+ dependencies: [
77
+ { name: 'grandchild1', version: '0.0.1' }
78
+ ]
79
+ }
80
+ ]
81
+ },
82
+ { name: 'root2', version: '2.0.0' }
83
+ ];
84
+
85
+ const minVersions: MinimumDepVersions = {
86
+ 'child1': '^0.1.0',
87
+ 'grandchild1': '^0.0.1',
88
+ 'root2': '^1.5.0'
89
+ };
90
+
91
+ const result = repoDependencyFacts.findPropertiesInTree(depGraph, minVersions);
92
+
93
+ expect(result).toEqual([
94
+ { dep: 'root1/child1', ver: '0.1.0', min: '^0.1.0' },
95
+ { dep: 'root1/child2/grandchild1', ver: '0.0.1', min: '^0.0.1' },
96
+ { dep: 'root2', ver: '2.0.0', min: '^1.5.0' }
97
+ ]);
98
+ });
99
+
100
+ it('should return an empty array when no matching properties are found', () => {
101
+ const depGraph: LocalDependencies[] = [
102
+ { name: 'package1', version: '1.0.0' },
103
+ { name: 'package2', version: '2.0.0' }
104
+ ];
105
+
106
+ const minVersions: MinimumDepVersions = {
107
+ 'package3': '^3.0.0'
108
+ };
109
+
110
+ const result = repoDependencyFacts.findPropertiesInTree(depGraph, minVersions);
111
+
112
+ expect(result).toEqual([]);
113
+ });
119
114
  });
120
- });
121
-
122
- describe('findPropertiesInTree', () => {
123
- it('should find properties in a nested dependency tree', () => {
124
- const depGraph: LocalDependencies[] = [
125
- {
126
- name: 'package-a',
127
- version: '1.0.0',
128
- dependencies: [
129
- { name: 'package-b', version: '2.0.0' },
130
- { name: 'package-c', version: '3.0.0', dependencies: [{ name: 'package-d', version: '4.0.0' }] }
131
- ]
132
- }
133
- ];
134
-
135
- const minVersions = {
136
- 'package-a': '0.9.0',
137
- 'package-c': '2.9.0',
138
- 'package-d': '3.9.0'
139
- };
140
-
141
- const result = repoDependencyFacts.findPropertiesInTree(depGraph, minVersions);
142
-
143
- expect(result).toEqual([
144
- { dep: 'package-a', ver: '1.0.0', min: '0.9.0' },
145
- { dep: 'package-a/package-c', ver: '3.0.0', min: '2.9.0' },
146
- { dep: 'package-a/package-c/package-d', ver: '4.0.0', min: '3.9.0' }
147
- ]);
148
- });
149
-
150
- it('should return an empty array when no matching properties are found', () => {
151
- const depGraph: LocalDependencies[] = [
152
- { name: 'package-x', version: '1.0.0' },
153
- { name: 'package-y', version: '2.0.0' }
154
- ];
155
115
 
156
- const minVersions = {
157
- 'package-z': '3.0.0'
158
- };
116
+ describe('repoDependencyAnalysis', () => {
117
+ const mockAlmanac: Almanac = {
118
+ factValue: jest.fn(),
119
+ addRuntimeFact: jest.fn(),
120
+ } as unknown as Almanac;
159
121
 
160
- const result = repoDependencyFacts.findPropertiesInTree(depGraph, minVersions);
122
+ it('should return an empty result for non-REPO_GLOBAL_CHECK files', async () => {
123
+ (mockAlmanac.factValue as jest.Mock).mockResolvedValueOnce({ fileName: 'some-file.js' });
161
124
 
162
- expect(result).toEqual([]);
163
- });
164
- });
165
-
166
- describe('repoDependencyAnalysis', () => {
167
- it('should return an empty result for non-global checks', async () => {
168
- const almanac = {
169
- factValue: jest.fn().mockResolvedValue({ fileName: 'not-global-check' } as never)
170
- } as unknown as Almanac;
125
+ const result = await repoDependencyFacts.repoDependencyAnalysis({}, mockAlmanac);
171
126
 
172
- const result = await repoDependencyFacts.repoDependencyAnalysis({}, almanac);
173
-
174
- expect(result).toEqual({ result: [] });
175
- });
127
+ expect(result).toEqual({ result: [] });
128
+ });
176
129
 
177
- it('should analyze dependencies and return failures', async () => {
178
- const almanac = {
179
- factValue: jest.fn().mockImplementation((fact) => {
180
- if (fact === 'fileData') {
181
- return Promise.resolve({ fileName: 'REPO_GLOBAL_CHECK' });
182
- }
183
- if (fact === 'dependencyData') {
184
- return Promise.resolve({
185
- installedDependencyVersions: [
186
- { dep: 'package-a', ver: '1.0.0', min: '2.0.0' },
187
- { dep: 'package-b', ver: '3.0.0', min: '2.0.0' }
188
- ]
189
- });
190
- }
191
- }),
192
- addRuntimeFact: jest.fn()
193
- } as unknown as Almanac;
194
-
195
- const result = await repoDependencyFacts.repoDependencyAnalysis({ resultFact: 'testFact' }, almanac);
196
-
197
- expect(result).toEqual({
198
- result: [
199
- { dependency: 'package-a', currentVersion: '1.0.0', requiredVersion: '2.0.0' }
200
- ]
201
- });
202
- expect(almanac.addRuntimeFact).toHaveBeenCalledWith('testFact', result);
203
130
  });
204
131
 
205
- it('should return an empty result when all dependencies meet requirements', async () => {
206
- const almanac = {
207
- factValue: jest.fn().mockImplementation((fact) => {
208
- if (fact === 'fileData') {
209
- return Promise.resolve({ fileName: 'REPO_GLOBAL_CHECK' });
210
- }
211
- if (fact === 'dependencyData') {
212
- return Promise.resolve({
213
- installedDependencyVersions: [
214
- { dep: 'package-a', ver: '2.1.0', min: '2.0.0' },
215
- { dep: 'package-b', ver: '3.0.0', min: '2.0.0' }
216
- ]
217
- });
218
- }
219
- }),
220
- addRuntimeFact: jest.fn()
221
- } as unknown as Almanac;
222
-
223
- const result = await repoDependencyFacts.repoDependencyAnalysis({ resultFact: 'testFact' }, almanac);
224
-
225
- expect(result).toEqual({ result: [] });
226
- expect(almanac.addRuntimeFact).toHaveBeenCalledWith('testFact', result);
132
+ describe('semverValid', () => {
133
+ it('should return true for valid version comparisons', () => {
134
+ expect(repoDependencyFacts.semverValid('2.0.0', '^1.0.0')).toBe(false);
135
+ expect(repoDependencyFacts.semverValid('1.5.0', '1.0.0 - 2.0.0')).toBe(true);
136
+ expect(repoDependencyFacts.semverValid('1.0.0', '1.0.0')).toBe(true);
137
+ expect(repoDependencyFacts.semverValid('2.0.0', '>=1.0.0')).toBe(true);
138
+ });
139
+
140
+ it('should return false for invalid version comparisons', () => {
141
+ expect(repoDependencyFacts.semverValid('1.0.0', '^2.0.0')).toBe(false);
142
+ expect(repoDependencyFacts.semverValid('3.0.0', '1.0.0 - 2.0.0')).toBe(false);
143
+ expect(repoDependencyFacts.semverValid('0.9.0', '>=1.0.0')).toBe(false);
144
+ });
145
+
146
+ it('should handle complex version ranges', () => {
147
+ expect(repoDependencyFacts.semverValid('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3')).toBe(true);
148
+ expect(repoDependencyFacts.semverValid('2.5.0', '1.x || >=2.5.0 || 5.0.0 - 7.2.3')).toBe(true);
149
+ expect(repoDependencyFacts.semverValid('5.5.5', '1.x || >=2.5.0 || 5.0.0 - 7.2.3')).toBe(true);
150
+ expect(repoDependencyFacts.semverValid('8.0.0', '1.x || >=9.5.0 || 5.0.0 - 7.2.3')).toBe(false);
151
+ });
152
+
153
+ it('should return true for empty strings', () => {
154
+ expect(repoDependencyFacts.semverValid('', '')).toBe(true);
155
+ });
156
+
157
+ it('should return false for invalid input', () => {
158
+ expect(repoDependencyFacts.semverValid('not-a-version', '1.0.0')).toBe(false);
159
+ expect(repoDependencyFacts.semverValid('1.0.0', 'not-a-range')).toBe(false);
160
+ });
227
161
  });
228
- });
229
162
  });
@@ -13,7 +13,7 @@ import path from 'path';
13
13
  * @returns The local dependencies.
14
14
  */
15
15
  export function collectLocalDependencies(): LocalDependencies[] {
16
- let result:LocalDependencies[] = [];
16
+ let result: LocalDependencies[] = [];
17
17
  if (fs.existsSync(path.join(options.dir, 'yarn.lock'))) {
18
18
  result = collectYarnDependencies();
19
19
  } else if (fs.existsSync(path.join(options.dir, 'package-lock.json'))) {
@@ -22,7 +22,7 @@ export function collectLocalDependencies(): LocalDependencies[] {
22
22
  logger.error('No yarn.lock or package-lock.json found');
23
23
  throw new Error('Unsupported package manager');
24
24
  }
25
- logger.debug(`collectLocalDependencies: ${JSON.stringify(result)}`);
25
+ logger.info(`collectLocalDependencies: ${JSON.stringify(result)}`);
26
26
  return result;
27
27
  }
28
28
 
@@ -121,7 +121,7 @@ export function getDependencyVersionFacts(archetypeConfig: ArchetypeConfig): Ver
121
121
  export function findPropertiesInTree(depGraph: LocalDependencies[], minVersions: MinimumDepVersions): VersionData[] {
122
122
  const results: VersionData[] = [];
123
123
 
124
- logger.debug(`depGraph: ${JSON.stringify(depGraph)}`);
124
+ logger.info(`depGraph: ${JSON.stringify(depGraph)}`);
125
125
 
126
126
  function walk(dep: LocalDependencies, parentName = '') {
127
127
  const fullName = parentName ? `${parentName}/${dep.name}` : dep.name;
@@ -136,6 +136,7 @@ export function findPropertiesInTree(depGraph: LocalDependencies[], minVersions:
136
136
  }
137
137
 
138
138
  depGraph.forEach(dep => walk(dep));
139
+ logger.info(JSON.stringify(depGraph))
139
140
  return results;
140
141
  }
141
142
 
@@ -151,11 +152,12 @@ export async function repoDependencyAnalysis(params: any, almanac: Almanac) {
151
152
  const analysis: any = [];
152
153
  const dependencyData: any = await almanac.factValue('dependencyData');
153
154
 
154
- dependencyData.installedDependencyVersions.map((versionData: VersionData) => {
155
+ dependencyData.installedDependencyVersions.forEach((versionData: VersionData) => {
155
156
  logger.debug(`outdatedFramework: checking ${versionData.dep}`);
156
157
 
157
- const requiredRange = new semver.Range(versionData.min);
158
- if (!semver.gtr(versionData.ver, requiredRange)) {
158
+ // Check if the installed version satisfies the required version, supporting both ranges and specific versions
159
+ const isValid = semverValid(versionData.ver, versionData.min);
160
+ if (!isValid) {
159
161
  const dependencyFailure = {
160
162
  'dependency': versionData.dep,
161
163
  'currentVersion': versionData.ver,
@@ -173,3 +175,36 @@ export async function repoDependencyAnalysis(params: any, almanac: Almanac) {
173
175
 
174
176
  return result;
175
177
  }
178
+
179
+ export function semverValid(required: string, installed: string): boolean {
180
+
181
+ if (!required || !installed) {
182
+ return true;
183
+ }
184
+
185
+ // If 'installed' is a single version and 'required' is a range
186
+ if (semver.valid(installed) && semver.validRange(required)) {
187
+ logger.debug('range vs version');
188
+ return semver.satisfies(installed, required);
189
+ }
190
+
191
+ // If 'required' is a single version and 'installed' is a range
192
+ if (semver.valid(required) && semver.validRange(installed)) {
193
+ logger.debug('version vs range');
194
+ return semver.satisfies(required, installed);
195
+ }
196
+
197
+ // If both are single versions, simply compare them
198
+ if (semver.valid(required) && semver.valid(installed)) {
199
+ logger.debug('version vs version');
200
+ return semver.gt(installed, required);
201
+ }
202
+
203
+ // If both are ranges, check if they intersect
204
+ if (semver.validRange(required) && semver.validRange(installed)) {
205
+ logger.debug('range vs range');
206
+ return semver.intersects(required, installed);
207
+ }
208
+
209
+ return false;
210
+ }
@@ -21,7 +21,7 @@ describe('openaiAnalysisHighSeverity', () => {
21
21
  ]
22
22
  };
23
23
  const result = openaiAnalysisHighSeverity.fn(openaiAnalysis, 8);
24
- expect(result).toBe(false);
24
+ expect(result).toEqual(false);
25
25
  expect(logger.error).not.toHaveBeenCalled();
26
26
  });
27
27
 
@@ -33,7 +33,7 @@ describe('openaiAnalysisHighSeverity', () => {
33
33
  ]
34
34
  };
35
35
  const result = openaiAnalysisHighSeverity.fn(openaiAnalysis, 8);
36
- expect(result).toBe(true);
36
+ expect(result).toEqual(true);
37
37
  expect(logger.error).toHaveBeenCalledWith('openai: high severity issues found');
38
38
  });
39
39
 
@@ -44,7 +44,7 @@ describe('openaiAnalysisHighSeverity', () => {
44
44
  ]
45
45
  };
46
46
  const result = openaiAnalysisHighSeverity.fn(openaiAnalysis, null);
47
- expect(result).toBe(true);
47
+ expect(result).toEqual(true);
48
48
  expect(logger.error).toHaveBeenCalledWith('openai: high severity issues found');
49
49
  });
50
50
 
@@ -87,4 +87,16 @@ describe('openaiAnalysisHighSeverity', () => {
87
87
  expect(result).toBe(true);
88
88
  expect(logger.error).toHaveBeenCalledWith('openai: high severity issues found');
89
89
  });
90
+
91
+ it('should use default severity threshold when not provided', () => {
92
+ const openaiAnalysis = {
93
+ result: [
94
+ { severity: 7 },
95
+ { severity: 8 }
96
+ ]
97
+ };
98
+ const result = openaiAnalysisHighSeverity.fn(openaiAnalysis, null);
99
+ expect(result).toBe(true);
100
+ expect(logger.error).toHaveBeenCalledWith('openai: high severity issues found');
101
+ });
90
102
  });
@@ -10,23 +10,24 @@ const openaiAnalysisHighSeverity: OperatorDefn = {
10
10
  return false;
11
11
  }
12
12
 
13
- severityThreshold = parseInt(severityThreshold) ? parseInt(severityThreshold) : 8;
14
- let result = false;
13
+ const threshold = parseInt(severityThreshold) || 8;
15
14
 
16
15
  if (Array.isArray(openaiAnalysis.result) && openaiAnalysis.result.length > 0) {
17
- if (openaiAnalysis.result.some((issue: any) => {
16
+ const hasHighSeverityIssue = openaiAnalysis.result.some((issue: any) => {
18
17
  const severity = parseInt(issue?.severity);
19
- return !isNaN(severity) && severity >= severityThreshold;
20
- })) {
18
+ return !isNaN(severity) && severity >= threshold;
19
+ });
20
+
21
+ if (hasHighSeverityIssue) {
21
22
  logger.error('openai: high severity issues found');
22
- result = true;
23
+ return true;
23
24
  }
24
25
  }
25
26
 
26
- return result;
27
+ return false;
27
28
  } catch (e) {
28
29
  // for now we don't fail the build if openai response parsing fails
29
- logger.debug(e)
30
+ logger.debug(e);
30
31
  logger.error(`openaiAnalysisHighSeverity: ${e}`);
31
32
  return false;
32
33
  }