x-fidelity 1.4.0 → 1.5.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 +22 -0
- package/README.md +178 -70
- package/dist/archetypes/index.js +6 -3
- package/dist/core/cli.js +24 -20
- package/dist/core/engine.js +88 -15
- package/dist/core/engine.test.js +21 -46
- package/dist/facts/openaiAnalysisFacts.js +2 -3
- package/dist/facts/openaiAnalysisFacts.test.js +2 -2
- package/dist/facts/repoDependencyFacts.js +5 -1
- package/dist/index.js +32 -17
- package/dist/operators/fileContains.test.js +37 -2
- package/dist/operators/openaiAnalysisHighSeverity.js +11 -7
- package/dist/operators/openaiAnalysisHighSeverity.test.js +41 -0
- package/dist/rules/index.js +10 -6
- package/dist/rules/index.test.js +11 -2
- package/dist/rules/{noDatabases-rule.json → noDatabases-iterative-rule.json} +2 -2
- package/dist/rules/{nonStandardDirectoryStructure-rule.json → nonStandardDirectoryStructure-global-rule.json} +3 -3
- package/{src/rules/openaiAnalysisA11y-rule.json → dist/rules/openaiAnalysisA11y-global-rule.json} +1 -1
- package/{src/rules/openaiAnalysisTop5-rule.json → dist/rules/openaiAnalysisTop5-global-rule.json} +1 -1
- package/dist/rules/{outdatedFramework-rule.json → outdatedFramework-global-rule.json} +4 -3
- package/dist/rules/{sensitiveLogging-rule.json → sensitiveLogging-iterative-rule.json} +6 -4
- package/dist/server/configServer.js +26 -15
- package/dist/server/expressLogger.js +36 -0
- package/dist/utils/config.js +9 -4
- package/dist/utils/logger.js +26 -4
- package/dist/utils/telemetry.js +29 -0
- package/dist/xfidelity +32 -17
- package/package.json +7 -4
- package/src/archetypes/index.ts +7 -4
- package/src/core/cli.ts +28 -25
- package/src/core/engine.test.ts +21 -53
- package/src/core/engine.ts +111 -23
- package/src/facts/openaiAnalysisFacts.test.ts +2 -2
- package/src/facts/openaiAnalysisFacts.ts +2 -3
- package/src/facts/repoDependencyFacts.ts +9 -2
- package/src/facts/repoFilesystemFacts.test.ts +1 -1
- package/src/facts/repoFilesystemFacts.ts +1 -1
- package/src/index.ts +30 -16
- package/src/operators/fileContains.test.ts +43 -2
- package/src/operators/fileContains.ts +1 -1
- package/src/operators/index.ts +1 -1
- package/src/operators/nonStandardDirectoryStructure.ts +2 -1
- package/src/operators/openaiAnalysisHighSeverity.test.ts +46 -0
- package/src/operators/openaiAnalysisHighSeverity.ts +14 -8
- package/src/operators/outdatedFramework.ts +1 -1
- package/src/rules/index.test.ts +12 -2
- package/src/rules/index.ts +13 -7
- package/src/rules/{noDatabases-rule.json → noDatabases-iterative-rule.json} +2 -2
- package/src/rules/{nonStandardDirectoryStructure-rule.json → nonStandardDirectoryStructure-global-rule.json} +3 -3
- package/{dist/rules/openaiAnalysisA11y-rule.json → src/rules/openaiAnalysisA11y-global-rule.json} +1 -1
- package/{dist/rules/openaiAnalysisTop5-rule.json → src/rules/openaiAnalysisTop5-global-rule.json} +1 -1
- package/src/rules/{outdatedFramework-rule.json → outdatedFramework-global-rule.json} +4 -3
- package/src/rules/{sensitiveLogging-rule.json → sensitiveLogging-iterative-rule.json} +6 -4
- package/src/server/configServer.ts +27 -17
- package/src/server/expressLogger.ts +37 -0
- package/src/utils/config.ts +10 -6
- package/src/utils/logger.ts +30 -4
- package/src/utils/telemetry.ts +23 -0
- /package/dist/{typeDefs.js → types/typeDefs.js} +0 -0
- /package/src/{typeDefs.ts → types/typeDefs.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
# [1.5.0](https://github.com/zotoio/x-fidelity/compare/v1.4.1...v1.5.0) (2024-07-24)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **logger:** console transport ([69c62b1](https://github.com/zotoio/x-fidelity/commit/69c62b1b9859bc71801c576b969e9698ecc53a61))
|
|
7
|
+
* **log:** remove console and add process exit codes ([3ec801f](https://github.com/zotoio/x-fidelity/commit/3ec801fe3f913452d1f6ef14723fd0f8cb3a8439))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **telemetry:** basic start ([47faf3b](https://github.com/zotoio/x-fidelity/commit/47faf3b5f7a88afc07be1b256a611ce46ef64872))
|
|
13
|
+
* **telemetry:** basics including tracing ([f07f6b4](https://github.com/zotoio/x-fidelity/commit/f07f6b4d7ad886008cd20d3ccfa80e0e980bfd34))
|
|
14
|
+
* **telemetry:** request ids ([e19489e](https://github.com/zotoio/x-fidelity/commit/e19489eadd08b06119244a8e1798c2f22bac898b))
|
|
15
|
+
|
|
16
|
+
## [1.4.1](https://github.com/zotoio/x-fidelity/compare/v1.4.0...v1.4.1) (2024-07-14)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **rules:** change default sensitive strings rule ([3e14e24](https://github.com/zotoio/x-fidelity/commit/3e14e248cc8ae95a6a79a7f3d5f75b3db6f9d35f))
|
|
22
|
+
|
|
1
23
|
# [1.4.0](https://github.com/zotoio/x-fidelity/compare/v1.3.0...v1.4.0) (2024-07-14)
|
|
2
24
|
|
|
3
25
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# x-fidelity
|
|
2
2
|
|
|
3
|
-
CLI
|
|
3
|
+
x-fidelity is an advanced CLI tool designed to enforce opinionated framework adherence checks within a codebase. It provides a flexible and extensible way to ensure your projects follow specific standards and best practices.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
=====================================
|
|
@@ -16,130 +16,238 @@ CLI for opinionated framework adherence checks
|
|
|
16
16
|
-------------------------------------
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Table of Contents
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
1. [Intent and Purpose](#intent-and-purpose)
|
|
22
|
+
2. [Key Features](#key-features)
|
|
23
|
+
3. [Installation](#installation)
|
|
24
|
+
4. [Usage](#usage)
|
|
25
|
+
5. [Configuration](#configuration)
|
|
26
|
+
6. [Extending x-fidelity](#extending-x-fidelity)
|
|
27
|
+
7. [OpenAI Integration](#openai-integration)
|
|
28
|
+
8. [Hosting Config Servers](#hosting-config-servers)
|
|
29
|
+
9. [Best Practices](#best-practices)
|
|
30
|
+
10. [Contributing](#contributing)
|
|
31
|
+
11. [License](#license)
|
|
22
32
|
|
|
23
|
-
##
|
|
33
|
+
## Intent and Purpose
|
|
24
34
|
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
35
|
+
x-fidelity aims to streamline the process of maintaining code quality and consistency across projects. By providing a flexible, rule-based system, it allows teams to:
|
|
36
|
+
|
|
37
|
+
- Enforce coding standards and best practices
|
|
38
|
+
- Ensure consistent project structures
|
|
39
|
+
- Maintain up-to-date dependencies
|
|
40
|
+
- Catch potential issues early in the development process
|
|
41
|
+
- Integrate advanced code analysis using AI (via OpenAI)
|
|
42
|
+
|
|
43
|
+
The tool is designed to be highly customizable, allowing teams to define their own archetypes, rules, and checks tailored to their specific needs and tech stacks.
|
|
44
|
+
|
|
45
|
+
## Key Features
|
|
46
|
+
|
|
47
|
+
- **Flexible Archetype System**: Define custom project archetypes with specific rules and configurations.
|
|
48
|
+
- **Customizable Rules**: Create and apply rules for various aspects of your codebase.
|
|
49
|
+
- **Directory Structure Validation**: Ensure your project follows a predefined directory structure.
|
|
50
|
+
- **Dependency Version Checking**: Verify that your project uses up-to-date dependencies.
|
|
51
|
+
- **Content Analysis**: Search for specific patterns or strings within your codebase.
|
|
52
|
+
- **Remote Configuration**: Fetch configurations from a remote server for centralized management.
|
|
53
|
+
- **OpenAI Integration**: Leverage AI for advanced code analysis and suggestions.
|
|
54
|
+
- **Extensible Architecture**: Easily add new operators, facts, and rules to suit your needs.
|
|
30
55
|
|
|
31
56
|
## Installation
|
|
32
57
|
|
|
33
|
-
Install x-fidelity
|
|
58
|
+
Install x-fidelity using Node.js 18+ and Yarn:
|
|
34
59
|
|
|
35
60
|
```sh
|
|
36
61
|
yarn global add x-fidelity
|
|
37
62
|
export PATH="$PATH:$(yarn global bin)"
|
|
38
63
|
```
|
|
39
64
|
|
|
40
|
-
|
|
65
|
+
For persistent access, add the PATH line to your `~/.bashrc` or `~/.zshrc` file.
|
|
41
66
|
|
|
42
67
|
## Usage
|
|
43
68
|
|
|
44
|
-
###
|
|
69
|
+
### Basic Usage
|
|
45
70
|
|
|
46
|
-
|
|
71
|
+
Run x-fidelity in your project directory:
|
|
47
72
|
|
|
48
73
|
```sh
|
|
49
74
|
xfidelity
|
|
50
|
-
|
|
51
|
-
# you can use the following options for more advanced setups such as the remote config server
|
|
52
|
-
xfidelity [-d --dir <directory>] [-c --configUrl <url>] [-a --archtype <archetype>]
|
|
53
75
|
```
|
|
54
76
|
|
|
55
|
-
|
|
56
|
-
- `-c --configUrl <url>`: (Optional) The URL to fetch the configuration from.
|
|
57
|
-
- `-a --archetype <archetype>`: (Optional) The archetype to use for analysis. 'node-fullstack' is the default, or 'java-microservice' and these are extensible)
|
|
77
|
+
### Advanced Usage
|
|
58
78
|
|
|
59
|
-
|
|
79
|
+
Use command-line options for more control:
|
|
60
80
|
|
|
61
81
|
```sh
|
|
62
|
-
xfidelity --
|
|
82
|
+
xfidelity [-d --dir <directory>] [-c --configServer <url>] [-a --archetype <archetype>] [-m --mode <mode>] [-p --port <port>]
|
|
63
83
|
```
|
|
64
84
|
|
|
65
|
-
|
|
85
|
+
- `-d --dir <directory>`: Specify the root directory to analyze (default: current directory)
|
|
86
|
+
- `-c --configServer <url>`: URL to fetch the configuration from
|
|
87
|
+
- `-a --archetype <archetype>`: Archetype to use for analysis (default: 'node-fullstack')
|
|
88
|
+
- `-m --mode <mode>`: Run mode: 'cli' or 'server' (default: 'cli')
|
|
89
|
+
- `-p --port <port>`: Port number for server mode (default: 8888)
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
66
92
|
|
|
67
93
|
```sh
|
|
68
|
-
|
|
94
|
+
# Use remote config server
|
|
95
|
+
xfidelity --configServer https://localhost:8888
|
|
96
|
+
|
|
97
|
+
# Analyze parent directory with java-microservice archetype
|
|
98
|
+
xfidelity -d .. -a java-microservice -c https://localhost:8888
|
|
99
|
+
|
|
100
|
+
# Run in server mode with custom port
|
|
101
|
+
xfidelity --mode server --port 9999
|
|
102
|
+
|
|
69
103
|
```
|
|
70
104
|
|
|
71
105
|
### Configuration Server
|
|
72
106
|
|
|
73
|
-
|
|
107
|
+
Start the built-in configuration server:
|
|
74
108
|
|
|
75
|
-
|
|
109
|
+
```sh
|
|
110
|
+
yarn start-server
|
|
111
|
+
```
|
|
76
112
|
|
|
77
|
-
|
|
113
|
+
Or use the CLI:
|
|
78
114
|
|
|
79
|
-
|
|
80
|
-
|
|
115
|
+
```sh
|
|
116
|
+
xfidelity --mode server
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Set a custom port:
|
|
81
120
|
|
|
82
121
|
```sh
|
|
83
|
-
|
|
122
|
+
xfidelity --mode server --port 9999
|
|
84
123
|
```
|
|
85
124
|
|
|
86
|
-
|
|
125
|
+
You can also set the port using an environment variable:
|
|
126
|
+
|
|
87
127
|
```sh
|
|
88
128
|
export XFI_SERVER_PORT=8888
|
|
129
|
+
xfidelity --mode server
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Configuration
|
|
133
|
+
|
|
134
|
+
x-fidelity uses archetypes to define project-specific configurations. Archetypes specify:
|
|
135
|
+
|
|
136
|
+
- Rules to apply
|
|
137
|
+
- Operators to use
|
|
138
|
+
- Facts to gather
|
|
139
|
+
- Dependency version requirements
|
|
140
|
+
- Standard directory structure
|
|
141
|
+
- File patterns to include or exclude
|
|
142
|
+
|
|
143
|
+
Example archetype structure:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
interface ArchetypeConfig {
|
|
147
|
+
rules: string[];
|
|
148
|
+
operators: string[];
|
|
149
|
+
facts: string[];
|
|
150
|
+
config: {
|
|
151
|
+
minimumDependencyVersions: Record<string, string>;
|
|
152
|
+
standardStructure: Record<string, any>;
|
|
153
|
+
blacklistPatterns: string[];
|
|
154
|
+
whitelistPatterns: string[];
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Extending x-fidelity
|
|
160
|
+
|
|
161
|
+
x-fidelity is designed to be highly extensible:
|
|
162
|
+
|
|
163
|
+
1. **Custom Rules**: Add new JSON rule files in `src/rules`.
|
|
164
|
+
2. **Custom Operators**: Implement new operators in `src/operators` and add them to `src/operators/index.ts`.
|
|
165
|
+
3. **Custom Facts**: Create new fact providers in `src/facts` and add them to `src/facts/index.ts`.
|
|
166
|
+
4. **New Archetypes**: Define new archetypes in `src/archetypes` and include them in `src/archetypes/index.ts`.
|
|
167
|
+
|
|
168
|
+
Example of creating a new archetype:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// src/archetypes/myNewArchetype.ts
|
|
172
|
+
export const myNewArchetype: ArchetypeConfig = {
|
|
173
|
+
rules: ['myCustomRule', 'standardRule1', 'standardRule2'],
|
|
174
|
+
operators: ['myCustomOperator', 'standardOperator1'],
|
|
175
|
+
facts: ['myCustomFact', 'standardFact1'],
|
|
176
|
+
config: {
|
|
177
|
+
minimumDependencyVersions: {
|
|
178
|
+
'my-framework': '^2.0.0'
|
|
179
|
+
},
|
|
180
|
+
standardStructure: {
|
|
181
|
+
src: {
|
|
182
|
+
components: null,
|
|
183
|
+
utils: null
|
|
184
|
+
},
|
|
185
|
+
tests: null
|
|
186
|
+
},
|
|
187
|
+
blacklistPatterns: ['.*\\/\\..*', '.*\\/(dist|build)(\\/.*|$)'],
|
|
188
|
+
whitelistPatterns: ['.*\\.(ts|tsx|js|jsx)$']
|
|
189
|
+
}
|
|
190
|
+
};
|
|
89
191
|
```
|
|
90
192
|
|
|
91
193
|
## OpenAI Integration
|
|
92
194
|
|
|
93
|
-
To enable
|
|
195
|
+
To enable AI-powered code analysis:
|
|
94
196
|
|
|
95
|
-
1. Sign up for
|
|
96
|
-
2.
|
|
97
|
-
3. Set the `OPENAI_API_KEY` environment variable:
|
|
197
|
+
1. Sign up for an [OpenAI API key](https://platform.openai.com).
|
|
198
|
+
2. Set environment variables:
|
|
98
199
|
|
|
99
200
|
```sh
|
|
100
201
|
export OPENAI_API_KEY=your_openai_api_key
|
|
202
|
+
export OPENAI_MODEL=gpt-4 # Optional, default is gpt-4o
|
|
101
203
|
```
|
|
102
|
-
4. Optionally set the OPENAI_MODEL environment var (default is gpt-4o):
|
|
103
|
-
```sh
|
|
104
|
-
export OPENAI_MODEL=gpt-4
|
|
105
|
-
```
|
|
106
|
-
Note that not all models consistently return parseable JSON results, so some experimentation is required.
|
|
107
204
|
|
|
108
205
|
> [!IMPORTANT]
|
|
109
|
-
>
|
|
110
|
-
>
|
|
111
|
-
>The 'collectOpenaiAnalysisFacts' function will concatenate all files that are not blacklisted but are included in the whitelist and send this to OpenAI. Ensure you consider any sensitive data that may be sent, and the cost based on the token count this will be per rule check that is executed.
|
|
206
|
+
> Be aware of potential costs and data privacy concerns when using OpenAI's API.
|
|
112
207
|
|
|
113
|
-
##
|
|
208
|
+
## Hosting Config Servers
|
|
114
209
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
210
|
+
To host a config server for x-fidelity:
|
|
211
|
+
|
|
212
|
+
1. Set up a Node.js server (e.g., Express.js)
|
|
213
|
+
2. Implement endpoints for archetype configurations and rules
|
|
214
|
+
3. Ensure security, scalability, and performance
|
|
215
|
+
4. Use HTTPS and implement proper authentication
|
|
216
|
+
5. Consider using a CDN for global distribution
|
|
217
|
+
|
|
218
|
+
Example server setup:
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
const express = require('express');
|
|
222
|
+
const app = express();
|
|
223
|
+
|
|
224
|
+
app.get('/archetypes/:archetype', (req, res) => {
|
|
225
|
+
// Fetch and return archetype configuration
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
app.get('/archetypes/:archetype/rules/:rule', (req, res) => {
|
|
229
|
+
// Fetch and return specific rule
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
app.listen(8888, () => {
|
|
233
|
+
console.log('Config server running on port 8888');
|
|
234
|
+
});
|
|
141
235
|
```
|
|
142
236
|
|
|
237
|
+
## Best Practices
|
|
238
|
+
|
|
239
|
+
1. **Version Control**: Keep your x-fidelity configurations in version control.
|
|
240
|
+
2. **Continuous Integration**: Integrate x-fidelity checks into your CI/CD pipeline.
|
|
241
|
+
3. **Regular Updates**: Keep your archetypes, rules, and dependencies up to date.
|
|
242
|
+
4. **Documentation**: Document custom rules, operators, and archetypes for your team.
|
|
243
|
+
5. **Gradual Implementation**: When introducing x-fidelity to an existing project, start with basic checks and gradually increase strictness.
|
|
244
|
+
6. **Team Alignment**: Ensure your team understands and agrees on the rules being enforced.
|
|
245
|
+
7. **Performance**: Be mindful of the performance impact, especially for large codebases.
|
|
246
|
+
|
|
247
|
+
## Contributing
|
|
248
|
+
|
|
249
|
+
Contributions to x-fidelity are welcome! Please refer to the `CONTRIBUTING.md` file for guidelines on how to contribute to this project.
|
|
250
|
+
|
|
143
251
|
## License
|
|
144
252
|
|
|
145
|
-
This project is licensed under the MIT License.
|
|
253
|
+
This project is licensed under the MIT License. See the `LICENSE` file for details.
|
package/dist/archetypes/index.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.archetypes = void 0;
|
|
4
4
|
exports.archetypes = {
|
|
5
5
|
'node-fullstack': {
|
|
6
|
-
rules: ['sensitiveLogging', 'outdatedFramework', 'noDatabases', 'nonStandardDirectoryStructure', 'openaiAnalysisTop5', 'openaiAnalysisA11y'],
|
|
6
|
+
rules: ['sensitiveLogging-iterative', 'outdatedFramework-global', 'noDatabases-iterative', 'nonStandardDirectoryStructure-global', 'openaiAnalysisTop5-global', 'openaiAnalysisA11y-global'],
|
|
7
7
|
operators: ['fileContains', 'outdatedFramework', 'nonStandardDirectoryStructure', 'openaiAnalysisHighSeverity'],
|
|
8
8
|
facts: ['repoFilesystemFacts', 'repoDependencyFacts', 'openaiAnalysisFacts'],
|
|
9
9
|
config: {
|
|
@@ -14,9 +14,12 @@ exports.archetypes = {
|
|
|
14
14
|
standardStructure: {
|
|
15
15
|
app: {
|
|
16
16
|
frontend: null,
|
|
17
|
-
common: null,
|
|
18
17
|
server: null
|
|
19
18
|
}
|
|
19
|
+
// app: {
|
|
20
|
+
// frontend: 'required',
|
|
21
|
+
// server: 'required'
|
|
22
|
+
// }
|
|
20
23
|
},
|
|
21
24
|
blacklistPatterns: [
|
|
22
25
|
'.*\\/\\..*', // dot files
|
|
@@ -29,7 +32,7 @@ exports.archetypes = {
|
|
|
29
32
|
}
|
|
30
33
|
},
|
|
31
34
|
'java-microservice': {
|
|
32
|
-
rules: ['sensitiveLogging', 'outdatedFramework', 'noDatabases', 'nonStandardDirectoryStructure'],
|
|
35
|
+
rules: ['sensitiveLogging-iterative', 'outdatedFramework-global', 'noDatabases-iterative', 'nonStandardDirectoryStructure-global'],
|
|
33
36
|
operators: ['fileContains', 'outdatedFramework', 'nonStandardDirectoryStructure'],
|
|
34
37
|
facts: ['repoFilesystemFacts', 'repoDependencyFacts'],
|
|
35
38
|
config: {
|
package/dist/core/cli.js
CHANGED
|
@@ -3,6 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.options = void 0;
|
|
4
4
|
const logger_1 = require("../utils/logger");
|
|
5
5
|
const commander_1 = require("commander");
|
|
6
|
+
// Ensure logger is initialized
|
|
7
|
+
if (!logger_1.logger || typeof logger_1.logger.info !== 'function') {
|
|
8
|
+
console.error('Logger is not properly initialized');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
commander_1.program
|
|
12
|
+
.option("-d, --dir <directory>", "The checkout directory to analyze", ".")
|
|
13
|
+
.option("-a, --archetype <archetype>", "The archetype to use for analysis", "node-fullstack")
|
|
14
|
+
.option("-c, --configServer <configServer>", "The config server URL for fetching remote archetype configurations and rules")
|
|
15
|
+
.option("-m, --mode <mode>", "Run mode: 'cli' or 'server'", "cli")
|
|
16
|
+
.option("-p, --port <port>", "Port number for server mode", "8888");
|
|
17
|
+
commander_1.program.parse();
|
|
18
|
+
const options = commander_1.program.opts();
|
|
19
|
+
exports.options = options;
|
|
6
20
|
const banner = (`
|
|
7
21
|
=====================================
|
|
8
22
|
__ __ ________ ______
|
|
@@ -14,26 +28,16 @@ const banner = (`
|
|
|
14
28
|
| ## | ## | ## | ## \\
|
|
15
29
|
\\## \\## \\## \\######
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
${new Date().toString()}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
exports.options = options;
|
|
31
|
+
--------------------
|
|
32
|
+
${new Date().toString().slice(0, 24)}
|
|
33
|
+
archetype: ${options.archetype}
|
|
34
|
+
directory: ${process.env.PWD}/${options.dir}
|
|
35
|
+
configServer: ${options.configServer ? options.configServer : 'none'}
|
|
36
|
+
mode: ${options.mode}
|
|
37
|
+
port: ${options.mode === 'server' ? options.port : 'N/A'}
|
|
38
|
+
for available options run: xfidelity --help
|
|
39
|
+
=====================================`);
|
|
40
|
+
logger_1.logger.info(banner);
|
|
28
41
|
// print help if no arguments are passed
|
|
29
42
|
if (commander_1.program.options.length === 0)
|
|
30
43
|
commander_1.program.help();
|
|
31
|
-
if (!options.dir) {
|
|
32
|
-
console.error("Checkout directory not provided. Defaulting to current directory.");
|
|
33
|
-
}
|
|
34
|
-
let msg = `Archetype ${options.archetype}: analysis of: ${process.env.PWD}/${options.dir}`;
|
|
35
|
-
logger_1.logger.info(msg) && console.log(msg);
|
|
36
|
-
msg = `configServer: ${options.configServer ? options.configServer : 'local'}`;
|
|
37
|
-
logger_1.logger.info(msg) && console.log(msg);
|
|
38
|
-
msg = '=====================================';
|
|
39
|
-
logger_1.logger.info(msg) && console.log(msg);
|
package/dist/core/engine.js
CHANGED
|
@@ -19,6 +19,7 @@ const operators_1 = require("../operators");
|
|
|
19
19
|
const facts_1 = require("../facts");
|
|
20
20
|
const rules_1 = require("../rules");
|
|
21
21
|
const config_1 = require("../utils/config");
|
|
22
|
+
const telemetry_1 = require("../utils/telemetry");
|
|
22
23
|
function analyzeCodebase(repoPath_1) {
|
|
23
24
|
return __awaiter(this, arguments, void 0, function* (repoPath, archetype = 'node-fullstack', configServer = '') {
|
|
24
25
|
const configManager = config_1.ConfigManager.getInstance();
|
|
@@ -35,23 +36,34 @@ function analyzeCodebase(repoPath_1) {
|
|
|
35
36
|
const { minimumDependencyVersions, standardStructure } = archetypeConfig.config;
|
|
36
37
|
const openaiSystemPrompt = yield (0, openaiAnalysisFacts_1.collectOpenaiAnalysisFacts)(fileData);
|
|
37
38
|
const engine = new json_rules_engine_1.Engine([], { replaceFactsInEventParams: true, allowUndefinedFacts: true });
|
|
39
|
+
// Send telemetry for analysis start
|
|
40
|
+
yield (0, telemetry_1.sendTelemetry)({
|
|
41
|
+
eventType: 'analysisStart',
|
|
42
|
+
metadata: {
|
|
43
|
+
archetype,
|
|
44
|
+
repoPath,
|
|
45
|
+
fileCount: fileData.length,
|
|
46
|
+
configServer: configServer || 'none'
|
|
47
|
+
},
|
|
48
|
+
timestamp: new Date().toISOString()
|
|
49
|
+
});
|
|
38
50
|
// Add operators to engine
|
|
39
|
-
|
|
51
|
+
logger_1.logger.info(`### loading custom operators..`);
|
|
40
52
|
const operators = yield (0, operators_1.loadOperators)(archetypeConfig.operators);
|
|
41
53
|
operators.forEach((operator) => {
|
|
42
54
|
var _a, _b;
|
|
43
55
|
if (!((_a = operator === null || operator === void 0 ? void 0 : operator.name) === null || _a === void 0 ? void 0 : _a.includes('openai')) || (process.env.OPENAI_API_KEY && ((_b = operator === null || operator === void 0 ? void 0 : operator.name) === null || _b === void 0 ? void 0 : _b.includes('openai')))) {
|
|
44
|
-
|
|
56
|
+
logger_1.logger.info(`adding custom operator: ${operator.name}`);
|
|
45
57
|
engine.addOperator(operator.name, operator.fn);
|
|
46
58
|
}
|
|
47
59
|
});
|
|
48
60
|
// Add rules to engine
|
|
49
|
-
|
|
50
|
-
const rules = yield (0, rules_1.loadRules)(archetype, archetypeConfig.rules, configManager.configServer);
|
|
61
|
+
logger_1.logger.info(`### loading json rules..`);
|
|
62
|
+
const rules = yield (0, rules_1.loadRules)(archetype, archetypeConfig.rules, configManager.configServer, logger_1.logPrefix);
|
|
51
63
|
logger_1.logger.debug(rules);
|
|
52
64
|
rules.forEach((rule) => {
|
|
53
65
|
try {
|
|
54
|
-
|
|
66
|
+
logger_1.logger.info(`adding rule: ${rule === null || rule === void 0 ? void 0 : rule.name}`);
|
|
55
67
|
engine.addRule(rule);
|
|
56
68
|
}
|
|
57
69
|
catch (e) {
|
|
@@ -61,38 +73,52 @@ function analyzeCodebase(repoPath_1) {
|
|
|
61
73
|
});
|
|
62
74
|
engine.on('success', (_a, almanac_1) => __awaiter(this, [_a, almanac_1], void 0, function* ({ type, params }, almanac) {
|
|
63
75
|
if (type === 'violation') {
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
logger_1.logger.warn(`violation detected: ${JSON.stringify(params)}}`);
|
|
77
|
+
yield (0, telemetry_1.sendTelemetry)({
|
|
78
|
+
eventType: 'violation',
|
|
79
|
+
metadata: Object.assign({ archetype,
|
|
80
|
+
repoPath }, params),
|
|
81
|
+
timestamp: new Date().toISOString()
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (type === 'fatality') {
|
|
85
|
+
logger_1.logger.error(`fatality detected: ${JSON.stringify(params)}}`);
|
|
86
|
+
yield (0, telemetry_1.sendTelemetry)({
|
|
87
|
+
eventType: 'fatality',
|
|
88
|
+
metadata: Object.assign({ archetype,
|
|
89
|
+
repoPath }, params),
|
|
90
|
+
timestamp: new Date().toISOString()
|
|
91
|
+
});
|
|
66
92
|
}
|
|
67
93
|
}));
|
|
68
94
|
// Add facts to engine
|
|
69
|
-
|
|
95
|
+
logger_1.logger.info(`### loading facts..`);
|
|
70
96
|
const facts = yield (0, facts_1.loadFacts)(archetypeConfig.facts);
|
|
71
97
|
facts.forEach((fact) => {
|
|
72
98
|
var _a, _b;
|
|
73
99
|
if (!((_a = fact === null || fact === void 0 ? void 0 : fact.name) === null || _a === void 0 ? void 0 : _a.includes('openai')) || (process.env.OPENAI_API_KEY && ((_b = fact === null || fact === void 0 ? void 0 : fact.name) === null || _b === void 0 ? void 0 : _b.includes('openai')))) {
|
|
74
|
-
|
|
100
|
+
logger_1.logger.info(`adding fact: ${fact.name}`);
|
|
75
101
|
engine.addFact(fact.name, fact.fn);
|
|
76
102
|
}
|
|
77
103
|
});
|
|
78
104
|
if (process.env.OPENAI_API_KEY && archetypeConfig.facts.includes('openaiAnalysisFacts')) {
|
|
79
|
-
|
|
105
|
+
logger_1.logger.info(`adding additional openai facts to engine..`);
|
|
80
106
|
engine.addFact('openaiAnalysis', openaiAnalysisFacts_1.openaiAnalysis);
|
|
81
107
|
engine.addFact('openaiSystemPrompt', openaiSystemPrompt);
|
|
82
108
|
}
|
|
83
109
|
// add output facts
|
|
84
110
|
engine.addFact('repoDependencyAnalysis', repoDependencyFacts_1.repoDependencyAnalysis);
|
|
85
111
|
// Run the engine for each file's data
|
|
86
|
-
|
|
112
|
+
logger_1.logger.info(`### Executing rules..`);
|
|
87
113
|
let failures = [];
|
|
88
114
|
for (const file of fileData) {
|
|
89
|
-
if (file.fileName ===
|
|
115
|
+
if (file.fileName === config_1.REPO_GLOBAL_CHECK) {
|
|
90
116
|
let msg = `\n==========================\nSTARTING GLOBAL REPO CHECKS..\n==========================`;
|
|
91
|
-
logger_1.logger.info(msg)
|
|
117
|
+
logger_1.logger.info(msg);
|
|
92
118
|
}
|
|
93
119
|
else {
|
|
94
120
|
let msg = `running engine for ${file.filePath}`;
|
|
95
|
-
logger_1.logger.info(msg)
|
|
121
|
+
logger_1.logger.info(msg);
|
|
96
122
|
}
|
|
97
123
|
const facts = {
|
|
98
124
|
fileData: file,
|
|
@@ -105,7 +131,6 @@ function analyzeCodebase(repoPath_1) {
|
|
|
105
131
|
let fileFailures = [];
|
|
106
132
|
yield engine.run(facts)
|
|
107
133
|
.then(({ results }) => {
|
|
108
|
-
//console.log(events);
|
|
109
134
|
results.map((result) => {
|
|
110
135
|
var _a;
|
|
111
136
|
logger_1.logger.debug(result);
|
|
@@ -122,6 +147,54 @@ function analyzeCodebase(repoPath_1) {
|
|
|
122
147
|
}
|
|
123
148
|
}
|
|
124
149
|
logger_1.logger.info(`${fileData.length} files analyzed. ${failures.length} files with errors.`);
|
|
150
|
+
const fatalities = findKeyValuePair(failures, 'level', 'fatality');
|
|
151
|
+
// Send telemetry for analysis end
|
|
152
|
+
yield (0, telemetry_1.sendTelemetry)({
|
|
153
|
+
eventType: 'analysisEnd',
|
|
154
|
+
metadata: {
|
|
155
|
+
archetype,
|
|
156
|
+
repoPath,
|
|
157
|
+
fileCount: fileData.length,
|
|
158
|
+
failureCount: failures.length,
|
|
159
|
+
fatalityCount: fatalities.length
|
|
160
|
+
},
|
|
161
|
+
timestamp: new Date().toISOString()
|
|
162
|
+
});
|
|
163
|
+
if (fatalities.length > 0) {
|
|
164
|
+
throw new Error(JSON.stringify(fatalities));
|
|
165
|
+
}
|
|
125
166
|
return failures;
|
|
126
167
|
});
|
|
127
168
|
}
|
|
169
|
+
const findKeyValuePair = (data, targetKey, targetValue) => {
|
|
170
|
+
let results = [];
|
|
171
|
+
const recursiveSearch = (obj) => {
|
|
172
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
173
|
+
for (let key in obj) {
|
|
174
|
+
if (obj.hasOwnProperty(key)) {
|
|
175
|
+
if (key === targetKey && obj[key] === targetValue) {
|
|
176
|
+
results.push(obj);
|
|
177
|
+
return; // Stop searching this branch as we've found the target in this object
|
|
178
|
+
}
|
|
179
|
+
if (typeof obj[key] === 'object' || Array.isArray(obj[key])) {
|
|
180
|
+
recursiveSearch(obj[key]);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else if (Array.isArray(obj)) {
|
|
186
|
+
obj.forEach((item) => {
|
|
187
|
+
recursiveSearch(item);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
if (Array.isArray(data)) {
|
|
192
|
+
data.forEach((item) => {
|
|
193
|
+
recursiveSearch(item);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
recursiveSearch(data);
|
|
198
|
+
}
|
|
199
|
+
return results;
|
|
200
|
+
};
|