helm-env-delta 1.1.2 → 1.2.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/README.md +22 -15
- package/dist/commandLine.d.ts +3 -0
- package/dist/commandLine.js +12 -2
- package/dist/configLoader.d.ts +1 -1
- package/dist/configLoader.js +4 -3
- package/dist/configMerger.d.ts +1 -1
- package/dist/configMerger.js +8 -2
- package/dist/consoleFormatter.d.ts +4 -5
- package/dist/fileDiff.d.ts +1 -1
- package/dist/fileDiff.js +12 -1
- package/dist/fileLoader.d.ts +1 -1
- package/dist/fileLoader.js +21 -1
- package/dist/fileUpdater.d.ts +2 -1
- package/dist/fileUpdater.js +33 -29
- package/dist/htmlReporter.d.ts +1 -1
- package/dist/htmlReporter.js +4 -4
- package/dist/index.js +48 -22
- package/dist/logger.d.ts +20 -0
- package/dist/logger.js +53 -0
- package/dist/stopRulesValidator.d.ts +1 -1
- package/dist/stopRulesValidator.js +9 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +5 -1
- package/dist/utils/versionChecker.d.ts +19 -0
- package/dist/utils/versionChecker.js +117 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# HelmEnvDelta
|
|
1
|
+
# HelmEnvDelta
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/helm-env-delta)
|
|
4
4
|
[](https://opensource.org/licenses/ISC)
|
|
@@ -248,6 +248,7 @@ flowchart LR
|
|
|
248
248
|
- **Multiple Reporting Formats**: Console diff, HTML report (visual side-by-side), JSON output (CI/CD integration)
|
|
249
249
|
- **Prune Mode**: Remove destination files not present in source
|
|
250
250
|
- **Dry-Run Preview**: Review all changes before applying them
|
|
251
|
+
- **Automatic Update Notifications**: Notifies when newer versions are available on npm (skips in CI/CD environments)
|
|
251
252
|
|
|
252
253
|
---
|
|
253
254
|
|
|
@@ -256,9 +257,6 @@ flowchart LR
|
|
|
256
257
|
```bash
|
|
257
258
|
# Global installation
|
|
258
259
|
npm install -g helm-env-delta
|
|
259
|
-
|
|
260
|
-
# Verify installation
|
|
261
|
-
helm-env-delta --version
|
|
262
260
|
```
|
|
263
261
|
|
|
264
262
|
**Prerequisites:**
|
|
@@ -266,6 +264,8 @@ helm-env-delta --version
|
|
|
266
264
|
- Node.js >= 22
|
|
267
265
|
- npm >= 9
|
|
268
266
|
|
|
267
|
+
**Note:** The tool automatically checks for updates on every run and displays a notification if a newer version is available. This check is skipped in CI/CD environments and fails silently if the npm registry is unreachable.
|
|
268
|
+
|
|
269
269
|
---
|
|
270
270
|
|
|
271
271
|
## Quick Start
|
|
@@ -666,21 +666,28 @@ hed --config <file> [options]
|
|
|
666
666
|
|
|
667
667
|
### Options
|
|
668
668
|
|
|
669
|
-
| Option | Short | Description
|
|
670
|
-
| ----------------- | ----- |
|
|
671
|
-
| `--config <path>` | `-c` | Path to YAML configuration file
|
|
672
|
-
| `--
|
|
673
|
-
| `--
|
|
674
|
-
| `--
|
|
675
|
-
| `--diff
|
|
676
|
-
| `--diff-
|
|
677
|
-
| `--
|
|
678
|
-
| `--
|
|
679
|
-
| `--
|
|
669
|
+
| Option | Short | Description | Default |
|
|
670
|
+
| ----------------- | ----- | ---------------------------------------------------------- | ------------ |
|
|
671
|
+
| `--config <path>` | `-c` | Path to YAML configuration file | **required** |
|
|
672
|
+
| `--validate` | | Validate configuration file and exit | `false` |
|
|
673
|
+
| `--dry-run` | | Preview changes without writing files | `false` |
|
|
674
|
+
| `--force` | | Override stop rules and proceed | `false` |
|
|
675
|
+
| `--diff` | | Display console diff for changed files | `false` |
|
|
676
|
+
| `--diff-html` | | Generate HTML report and open in browser | `false` |
|
|
677
|
+
| `--diff-json` | | Output diff as JSON to stdout | `false` |
|
|
678
|
+
| `--skip-format` | | Skip YAML formatting (outputFormat section) | `false` |
|
|
679
|
+
| `--verbose` | | Show detailed debug information (config, transforms, etc.) | `false` |
|
|
680
|
+
| `--quiet` | | Suppress all output except critical errors | `false` |
|
|
681
|
+
| `--help` | `-h` | Display help | |
|
|
682
|
+
|
|
683
|
+
**Note:** `--verbose` and `--quiet` are mutually exclusive. Machine-readable output (`--diff-json`) always outputs regardless of verbosity.
|
|
680
684
|
|
|
681
685
|
### Examples
|
|
682
686
|
|
|
683
687
|
```bash
|
|
688
|
+
# Validate configuration file
|
|
689
|
+
helm-env-delta --config config.yaml --validate
|
|
690
|
+
|
|
684
691
|
# Basic sync
|
|
685
692
|
helm-env-delta --config config.yaml
|
|
686
693
|
|
package/dist/commandLine.d.ts
CHANGED
package/dist/commandLine.js
CHANGED
|
@@ -18,9 +18,16 @@ const parseCommandLine = (argv) => {
|
|
|
18
18
|
.option('--diff', 'Display console diff for changed files', false)
|
|
19
19
|
.option('--diff-html', 'Generate and open HTML diff report in browser', false)
|
|
20
20
|
.option('--diff-json', 'Output diff as JSON to stdout', false)
|
|
21
|
-
.option('--skip-format', 'Skip YAML formatting (outputFormat section)', false)
|
|
21
|
+
.option('--skip-format', 'Skip YAML formatting (outputFormat section)', false)
|
|
22
|
+
.option('--validate', 'Validate configuration file and exit', false)
|
|
23
|
+
.option('--verbose', 'Show detailed debug information', false)
|
|
24
|
+
.option('--quiet', 'Suppress all output except critical errors', false);
|
|
22
25
|
program.parse(argv || process.argv);
|
|
23
26
|
const options = program.opts();
|
|
27
|
+
if (options['verbose'] && options['quiet']) {
|
|
28
|
+
console.error('Error: --verbose and --quiet flags are mutually exclusive');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
24
31
|
return {
|
|
25
32
|
config: options['config'],
|
|
26
33
|
dryRun: options['dryRun'],
|
|
@@ -28,7 +35,10 @@ const parseCommandLine = (argv) => {
|
|
|
28
35
|
diff: options['diff'],
|
|
29
36
|
diffHtml: options['diffHtml'],
|
|
30
37
|
diffJson: options['diffJson'],
|
|
31
|
-
skipFormat: options['skipFormat']
|
|
38
|
+
skipFormat: options['skipFormat'],
|
|
39
|
+
validate: options['validate'],
|
|
40
|
+
verbose: options['verbose'],
|
|
41
|
+
quiet: options['quiet']
|
|
32
42
|
};
|
|
33
43
|
};
|
|
34
44
|
exports.parseCommandLine = parseCommandLine;
|
package/dist/configLoader.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type FinalConfig } from './configFile';
|
|
2
2
|
export type Config = FinalConfig;
|
|
3
|
-
export declare const loadConfigFile: (configPath: string) => FinalConfig;
|
|
3
|
+
export declare const loadConfigFile: (configPath: string, quiet?: boolean, logger?: import("./logger").Logger) => FinalConfig;
|
package/dist/configLoader.js
CHANGED
|
@@ -3,10 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.loadConfigFile = void 0;
|
|
4
4
|
const configFile_1 = require("./configFile");
|
|
5
5
|
const configMerger_1 = require("./configMerger");
|
|
6
|
-
const loadConfigFile = (configPath) => {
|
|
7
|
-
const mergedConfig = (0, configMerger_1.resolveConfigWithExtends)(configPath);
|
|
6
|
+
const loadConfigFile = (configPath, quiet = false, logger) => {
|
|
7
|
+
const mergedConfig = (0, configMerger_1.resolveConfigWithExtends)(configPath, new Set(), 0, logger);
|
|
8
8
|
const config = (0, configFile_1.parseFinalConfig)(mergedConfig, configPath);
|
|
9
|
-
|
|
9
|
+
if (!quiet)
|
|
10
|
+
console.log(`\nConfiguration loaded: ${config.source} -> ${config.destination}` + (config.prune ? ' [prune!]' : ''));
|
|
10
11
|
return config;
|
|
11
12
|
};
|
|
12
13
|
exports.loadConfigFile = loadConfigFile;
|
package/dist/configMerger.d.ts
CHANGED
|
@@ -17,5 +17,5 @@ export declare class ConfigMergerError extends ConfigMergerErrorClass {
|
|
|
17
17
|
}
|
|
18
18
|
export declare const isConfigMergerError: (error: unknown) => error is ConfigMergerError;
|
|
19
19
|
export declare const mergeConfigs: (parent: BaseConfig, child: BaseConfig) => BaseConfig;
|
|
20
|
-
export declare const resolveConfigWithExtends: (configPath: string, visited?: Set<string>, depth?: number) => BaseConfig;
|
|
20
|
+
export declare const resolveConfigWithExtends: (configPath: string, visited?: Set<string>, depth?: number, logger?: import("./logger").Logger) => BaseConfig;
|
|
21
21
|
export {};
|
package/dist/configMerger.js
CHANGED
|
@@ -133,7 +133,7 @@ const mergePerFileRecords = (parent, child) => {
|
|
|
133
133
|
merged[pattern] = merged[pattern] === undefined ? [...rules] : [...merged[pattern], ...rules];
|
|
134
134
|
return merged;
|
|
135
135
|
};
|
|
136
|
-
const resolveConfigWithExtends = (configPath, visited = new Set(), depth = 0) => {
|
|
136
|
+
const resolveConfigWithExtends = (configPath, visited = new Set(), depth = 0, logger) => {
|
|
137
137
|
if (depth > MAX_EXTENDS_DEPTH) {
|
|
138
138
|
const depthError = new ConfigMergerError('Extends chain exceeds maximum depth of 5', {
|
|
139
139
|
code: 'MAX_DEPTH_EXCEEDED',
|
|
@@ -202,6 +202,10 @@ const resolveConfigWithExtends = (configPath, visited = new Set(), depth = 0) =>
|
|
|
202
202
|
});
|
|
203
203
|
}
|
|
204
204
|
const config = (0, configFile_1.parseBaseConfig)(rawConfig, absolutePath);
|
|
205
|
+
if (logger?.shouldShow('debug')) {
|
|
206
|
+
const filename = absolutePath.split('/').pop();
|
|
207
|
+
logger.debug(`Loading config: ${filename} (depth: ${depth})`);
|
|
208
|
+
}
|
|
205
209
|
if (config.extends === undefined)
|
|
206
210
|
return config;
|
|
207
211
|
const configDirectory = node_path_1.default.dirname(absolutePath);
|
|
@@ -232,7 +236,9 @@ const resolveConfigWithExtends = (configPath, visited = new Set(), depth = 0) =>
|
|
|
232
236
|
cause: error
|
|
233
237
|
});
|
|
234
238
|
}
|
|
235
|
-
|
|
239
|
+
if (logger?.shouldShow('debug'))
|
|
240
|
+
logger.debug(` Extends: ${config.extends} → ${parentPath.split('/').pop()}`);
|
|
241
|
+
const parentConfig = (0, exports.resolveConfigWithExtends)(parentPath, visitedWithCurrent, depth + 1, logger);
|
|
236
242
|
return (0, exports.mergeConfigs)(parentConfig, config);
|
|
237
243
|
};
|
|
238
244
|
exports.resolveConfigWithExtends = resolveConfigWithExtends;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { StopRuleViolation } from './stopRulesValidator';
|
|
2
|
-
type BoxStyle = 'success' | 'warning' | 'error' | 'info';
|
|
3
|
-
type ProgressStyle = 'loading' | 'success' | 'info';
|
|
4
|
-
type ViolationMode = 'error' | 'warning' | 'force';
|
|
5
|
-
type FileOperation = 'add' | 'update' | 'delete' | 'format';
|
|
2
|
+
export type BoxStyle = 'success' | 'warning' | 'error' | 'info';
|
|
3
|
+
export type ProgressStyle = 'loading' | 'success' | 'info';
|
|
4
|
+
export type ViolationMode = 'error' | 'warning' | 'force';
|
|
5
|
+
export type FileOperation = 'add' | 'update' | 'delete' | 'format';
|
|
6
6
|
export declare const formatBox: (title: string, content: string[], style?: BoxStyle, width?: number) => string;
|
|
7
7
|
export declare const formatStopRuleViolation: (violation: StopRuleViolation, mode: ViolationMode) => string;
|
|
8
8
|
export declare const colorizeFileOperation: (operation: FileOperation, filePath: string, isDryRun: boolean, alreadyDeleted?: boolean) => string;
|
|
9
9
|
export declare const formatProgressMessage: (message: string, style: ProgressStyle) => string;
|
|
10
|
-
export {};
|
package/dist/fileDiff.d.ts
CHANGED
|
@@ -37,5 +37,5 @@ export declare class FileDiffError extends FileDiffErrorClass {
|
|
|
37
37
|
}
|
|
38
38
|
export declare const isFileDiffError: (error: unknown) => error is FileDiffError;
|
|
39
39
|
export declare const getSkipPathsForFile: (filePath: string, skipPath?: Record<string, string[]>) => string[];
|
|
40
|
-
export declare const computeFileDiff: (sourceFiles: FileMap, destinationFiles: FileMap, config: Config) => FileDiffResult;
|
|
40
|
+
export declare const computeFileDiff: (sourceFiles: FileMap, destinationFiles: FileMap, config: Config, logger?: import("./logger").Logger) => FileDiffResult;
|
|
41
41
|
export {};
|
package/dist/fileDiff.js
CHANGED
|
@@ -162,7 +162,18 @@ const processChangedFiles = (sourceFiles, destinationFiles, skipPath, transforms
|
|
|
162
162
|
}
|
|
163
163
|
return { changedFiles, unchangedFiles };
|
|
164
164
|
};
|
|
165
|
-
const computeFileDiff = (sourceFiles, destinationFiles, config) => {
|
|
165
|
+
const computeFileDiff = (sourceFiles, destinationFiles, config, logger) => {
|
|
166
|
+
if (logger?.shouldShow('debug')) {
|
|
167
|
+
logger.debug('Computing file differences:');
|
|
168
|
+
logger.debug(` Source files: ${sourceFiles.size}`);
|
|
169
|
+
logger.debug(` Destination files: ${destinationFiles.size}`);
|
|
170
|
+
const transformCount = Object.keys(config.transforms || {}).length;
|
|
171
|
+
if (transformCount > 0)
|
|
172
|
+
logger.debug(` Content transform patterns: ${transformCount}`);
|
|
173
|
+
const skipPathCount = Object.keys(config.skipPath || {}).length;
|
|
174
|
+
if (skipPathCount > 0)
|
|
175
|
+
logger.debug(` SkipPath patterns: ${skipPathCount}`);
|
|
176
|
+
}
|
|
166
177
|
const addedFiles = detectAddedFiles(sourceFiles, destinationFiles);
|
|
167
178
|
const deletedFiles = config.prune ? detectDeletedFiles(sourceFiles, destinationFiles) : [];
|
|
168
179
|
const { changedFiles, unchangedFiles } = processChangedFiles(sourceFiles, destinationFiles, config.skipPath, config.transforms);
|
package/dist/fileLoader.d.ts
CHANGED
|
@@ -23,5 +23,5 @@ declare const FileLoaderErrorClass: {
|
|
|
23
23
|
export declare class FileLoaderError extends FileLoaderErrorClass {
|
|
24
24
|
}
|
|
25
25
|
export declare const isFileLoaderError: (error: unknown) => error is FileLoaderError;
|
|
26
|
-
export declare const loadFiles: (options: FileLoaderOptions) => Promise<FileMap>;
|
|
26
|
+
export declare const loadFiles: (options: FileLoaderOptions, logger?: import("./logger").Logger) => Promise<FileMap>;
|
|
27
27
|
export {};
|
package/dist/fileLoader.js
CHANGED
|
@@ -147,13 +147,33 @@ const readFilesIntoMap = async (baseDirectory, absoluteFilePaths) => {
|
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
149
|
};
|
|
150
|
-
const loadFiles = async (options) => {
|
|
150
|
+
const loadFiles = async (options, logger) => {
|
|
151
151
|
const absoluteBaseDirectory = await validateAndResolveBaseDirectory(options.baseDirectory);
|
|
152
152
|
const includePatterns = options.include ?? ['**/*'];
|
|
153
153
|
const excludePatterns = options.exclude ?? [];
|
|
154
154
|
const files = await findMatchingFiles(absoluteBaseDirectory, includePatterns, excludePatterns, options.transforms);
|
|
155
|
+
if (logger?.shouldShow('debug')) {
|
|
156
|
+
logger.debug('Glob matching:');
|
|
157
|
+
logger.debug(` Directory: ${absoluteBaseDirectory}`);
|
|
158
|
+
logger.debug(` Include patterns: ${includePatterns.join(', ')}`);
|
|
159
|
+
logger.debug(` Exclude patterns: ${excludePatterns.join(', ')}`);
|
|
160
|
+
logger.debug(` Matched: ${files.length} file(s)`);
|
|
161
|
+
}
|
|
155
162
|
const fileMap = await readFilesIntoMap(absoluteBaseDirectory, files);
|
|
156
163
|
const transformedMap = options.transforms ? (0, filenameTransformer_1.transformFilenameMap)(fileMap, options.transforms) : fileMap;
|
|
164
|
+
if (options.transforms && logger?.shouldShow('debug')) {
|
|
165
|
+
logger.debug(`Filename transforms applied: ${fileMap.size} → ${transformedMap.size} files`);
|
|
166
|
+
let exampleCount = 0;
|
|
167
|
+
for (const [transformed, content] of transformedMap.entries()) {
|
|
168
|
+
const original = [...fileMap.entries()].find(([_key, c]) => c === content)?.[0];
|
|
169
|
+
if (original && original !== transformed) {
|
|
170
|
+
logger.debug(` ${original} → ${transformed}`);
|
|
171
|
+
exampleCount++;
|
|
172
|
+
if (exampleCount >= 3)
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
157
177
|
return sortMapByKeys(transformedMap);
|
|
158
178
|
};
|
|
159
179
|
exports.loadFiles = loadFiles;
|
package/dist/fileUpdater.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Config } from './configFile';
|
|
2
2
|
import { FileDiffResult } from './fileDiff';
|
|
3
3
|
import { FileMap } from './fileLoader';
|
|
4
|
+
import { Logger } from './logger';
|
|
4
5
|
export interface FileUpdateError {
|
|
5
6
|
operation: 'add' | 'update' | 'delete';
|
|
6
7
|
path: string;
|
|
@@ -23,5 +24,5 @@ declare const FileUpdaterErrorClass: {
|
|
|
23
24
|
export declare class FileUpdaterError extends FileUpdaterErrorClass {
|
|
24
25
|
}
|
|
25
26
|
export declare const isFileUpdaterError: (error: unknown) => error is FileUpdaterError;
|
|
26
|
-
export declare const updateFiles: (diffResult: FileDiffResult, sourceFiles: FileMap, destinationFiles: FileMap, config: Config, dryRun: boolean, skipFormat
|
|
27
|
+
export declare const updateFiles: (diffResult: FileDiffResult, sourceFiles: FileMap, destinationFiles: FileMap, config: Config, dryRun: boolean, skipFormat: boolean, logger: Logger) => Promise<string[]>;
|
|
27
28
|
export {};
|
package/dist/fileUpdater.js
CHANGED
|
@@ -117,10 +117,10 @@ const mergeYamlContent = (destinationContent, processedSourceContent, filePath)
|
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
119
|
};
|
|
120
|
-
const addFile = async (relativePath, content, absoluteDestinationDirectory, config, dryRun, skipFormat
|
|
120
|
+
const addFile = async (relativePath, content, absoluteDestinationDirectory, config, dryRun, skipFormat, logger) => {
|
|
121
121
|
const absolutePath = node_path_1.default.join(absoluteDestinationDirectory, relativePath);
|
|
122
122
|
if (dryRun) {
|
|
123
|
-
|
|
123
|
+
logger.fileOp('add', relativePath, true);
|
|
124
124
|
return;
|
|
125
125
|
}
|
|
126
126
|
let contentToWrite = content;
|
|
@@ -142,7 +142,7 @@ const addFile = async (relativePath, content, absoluteDestinationDirectory, conf
|
|
|
142
142
|
try {
|
|
143
143
|
await ensureParentDirectory(absolutePath);
|
|
144
144
|
await (0, promises_1.writeFile)(absolutePath, contentToWrite, 'utf8');
|
|
145
|
-
|
|
145
|
+
logger.fileOp('add', relativePath, false);
|
|
146
146
|
}
|
|
147
147
|
catch (error) {
|
|
148
148
|
throw new FileUpdaterError('Failed to add file', {
|
|
@@ -152,10 +152,10 @@ const addFile = async (relativePath, content, absoluteDestinationDirectory, conf
|
|
|
152
152
|
});
|
|
153
153
|
}
|
|
154
154
|
};
|
|
155
|
-
const updateFile = async (changedFile, absoluteDestinationDirectory, config, dryRun, skipFormat
|
|
155
|
+
const updateFile = async (changedFile, absoluteDestinationDirectory, config, dryRun, skipFormat, logger) => {
|
|
156
156
|
const absolutePath = node_path_1.default.join(absoluteDestinationDirectory, changedFile.path);
|
|
157
157
|
if (dryRun) {
|
|
158
|
-
|
|
158
|
+
logger.fileOp('update', changedFile.path, true);
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
161
|
let contentToWrite = (0, fileType_1.isYamlFile)(changedFile.path)
|
|
@@ -168,7 +168,7 @@ const updateFile = async (changedFile, absoluteDestinationDirectory, config, dry
|
|
|
168
168
|
try {
|
|
169
169
|
await ensureParentDirectory(absolutePath);
|
|
170
170
|
await (0, promises_1.writeFile)(absolutePath, contentToWrite, 'utf8');
|
|
171
|
-
|
|
171
|
+
logger.fileOp('update', changedFile.path, false);
|
|
172
172
|
}
|
|
173
173
|
catch (error) {
|
|
174
174
|
throw new FileUpdaterError('Failed to update file', {
|
|
@@ -178,19 +178,19 @@ const updateFile = async (changedFile, absoluteDestinationDirectory, config, dry
|
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
};
|
|
181
|
-
const deleteFile = async (relativePath, absoluteDestinationDirectory, dryRun) => {
|
|
181
|
+
const deleteFile = async (relativePath, absoluteDestinationDirectory, dryRun, logger) => {
|
|
182
182
|
const absolutePath = node_path_1.default.join(absoluteDestinationDirectory, relativePath);
|
|
183
183
|
if (dryRun) {
|
|
184
|
-
|
|
184
|
+
logger.fileOp('delete', relativePath, true);
|
|
185
185
|
return;
|
|
186
186
|
}
|
|
187
187
|
try {
|
|
188
188
|
await (0, promises_1.unlink)(absolutePath);
|
|
189
|
-
|
|
189
|
+
logger.fileOp('delete', relativePath, false);
|
|
190
190
|
}
|
|
191
191
|
catch (error) {
|
|
192
192
|
if (error.code === 'ENOENT') {
|
|
193
|
-
|
|
193
|
+
logger.fileOp('delete', relativePath, false, true);
|
|
194
194
|
return;
|
|
195
195
|
}
|
|
196
196
|
throw new FileUpdaterError('Failed to delete file', {
|
|
@@ -200,21 +200,25 @@ const deleteFile = async (relativePath, absoluteDestinationDirectory, dryRun) =>
|
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
202
|
};
|
|
203
|
-
const updateFiles = async (diffResult, sourceFiles, destinationFiles, config, dryRun, skipFormat
|
|
204
|
-
|
|
203
|
+
const updateFiles = async (diffResult, sourceFiles, destinationFiles, config, dryRun, skipFormat, logger) => {
|
|
204
|
+
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Updating files...', 'info'));
|
|
205
205
|
const absoluteDestinationDirectory = await validateDestinationDirectory(config.destination);
|
|
206
206
|
const errors = [];
|
|
207
|
+
if (logger.shouldShow('debug'))
|
|
208
|
+
logger.debug(`Processing ${diffResult.addedFiles.length} new files`);
|
|
207
209
|
for (const relativePath of diffResult.addedFiles)
|
|
208
210
|
try {
|
|
209
211
|
const content = sourceFiles.get(relativePath);
|
|
210
|
-
await addFile(relativePath, content, absoluteDestinationDirectory, config, dryRun, skipFormat);
|
|
212
|
+
await addFile(relativePath, content, absoluteDestinationDirectory, config, dryRun, skipFormat, logger);
|
|
211
213
|
}
|
|
212
214
|
catch (error) {
|
|
213
215
|
errors.push({ operation: 'add', path: relativePath, error: error });
|
|
214
216
|
}
|
|
217
|
+
if (logger.shouldShow('debug'))
|
|
218
|
+
logger.debug(`Updating ${diffResult.changedFiles.length} changed files`);
|
|
215
219
|
for (const changedFile of diffResult.changedFiles)
|
|
216
220
|
try {
|
|
217
|
-
await updateFile(changedFile, absoluteDestinationDirectory, config, dryRun, skipFormat);
|
|
221
|
+
await updateFile(changedFile, absoluteDestinationDirectory, config, dryRun, skipFormat, logger);
|
|
218
222
|
}
|
|
219
223
|
catch (error) {
|
|
220
224
|
errors.push({ operation: 'update', path: changedFile.path, error: error });
|
|
@@ -229,11 +233,11 @@ const updateFiles = async (diffResult, sourceFiles, destinationFiles, config, dr
|
|
|
229
233
|
if (formatted !== content) {
|
|
230
234
|
const absolutePath = node_path_1.default.join(absoluteDestinationDirectory, relativePath);
|
|
231
235
|
if (dryRun)
|
|
232
|
-
|
|
236
|
+
logger.fileOp('format', relativePath, true);
|
|
233
237
|
else {
|
|
234
238
|
await ensureParentDirectory(absolutePath);
|
|
235
239
|
await (0, promises_1.writeFile)(absolutePath, formatted, 'utf8');
|
|
236
|
-
|
|
240
|
+
logger.fileOp('format', relativePath, false);
|
|
237
241
|
}
|
|
238
242
|
formattedFiles.push(relativePath);
|
|
239
243
|
}
|
|
@@ -243,29 +247,29 @@ const updateFiles = async (diffResult, sourceFiles, destinationFiles, config, dr
|
|
|
243
247
|
}
|
|
244
248
|
for (const relativePath of diffResult.deletedFiles)
|
|
245
249
|
try {
|
|
246
|
-
await deleteFile(relativePath, absoluteDestinationDirectory, dryRun);
|
|
250
|
+
await deleteFile(relativePath, absoluteDestinationDirectory, dryRun, logger);
|
|
247
251
|
}
|
|
248
252
|
catch (error) {
|
|
249
253
|
errors.push({ operation: 'delete', path: relativePath, error: error });
|
|
250
254
|
}
|
|
251
255
|
if (dryRun) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
256
|
+
logger.log('\n[DRY RUN] Would perform:');
|
|
257
|
+
logger.log(` ${diffResult.addedFiles.length} files would be added`);
|
|
258
|
+
logger.log(` ${diffResult.changedFiles.length} files would be updated`);
|
|
259
|
+
logger.log(` ${formattedFiles.length} files would be formatted`);
|
|
260
|
+
logger.log(` ${diffResult.deletedFiles.length} files would be deleted`);
|
|
257
261
|
}
|
|
258
262
|
else {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
263
|
+
logger.log('\n✓ Files updated successfully:');
|
|
264
|
+
logger.log(` ${diffResult.addedFiles.length} files added`);
|
|
265
|
+
logger.log(` ${diffResult.changedFiles.length} files updated`);
|
|
266
|
+
logger.log(` ${formattedFiles.length} files formatted`);
|
|
267
|
+
logger.log(` ${diffResult.deletedFiles.length} files deleted`);
|
|
264
268
|
}
|
|
265
269
|
if (errors.length > 0) {
|
|
266
|
-
|
|
270
|
+
logger.error(`\n❌ Encountered ${errors.length} error(s):`, 'critical');
|
|
267
271
|
for (const { operation, path: errorPath, error } of errors)
|
|
268
|
-
|
|
272
|
+
logger.error(` [${operation}] ${errorPath}: ${error.message}`, 'critical');
|
|
269
273
|
throw new FileUpdaterError(`Failed to update ${errors.length} file(s)`, { code: 'UPDATE_FAILED' });
|
|
270
274
|
}
|
|
271
275
|
return formattedFiles;
|
package/dist/htmlReporter.d.ts
CHANGED
|
@@ -23,5 +23,5 @@ declare const HtmlReporterErrorClass: {
|
|
|
23
23
|
export declare class HtmlReporterError extends HtmlReporterErrorClass {
|
|
24
24
|
}
|
|
25
25
|
export declare const isHtmlReporterError: (error: unknown) => error is HtmlReporterError;
|
|
26
|
-
export declare const generateHtmlReport: (diffResult: FileDiffResult, formattedFiles: string[], config: Config, dryRun: boolean) => Promise<void>;
|
|
26
|
+
export declare const generateHtmlReport: (diffResult: FileDiffResult, formattedFiles: string[], config: Config, dryRun: boolean, logger?: import("./logger").Logger) => Promise<void>;
|
|
27
27
|
export {};
|
package/dist/htmlReporter.js
CHANGED
|
@@ -585,7 +585,7 @@ const openInBrowser = async (filePath) => {
|
|
|
585
585
|
throw openError;
|
|
586
586
|
}
|
|
587
587
|
};
|
|
588
|
-
const generateHtmlReport = async (diffResult, formattedFiles, config, dryRun) => {
|
|
588
|
+
const generateHtmlReport = async (diffResult, formattedFiles, config, dryRun, logger) => {
|
|
589
589
|
const reportPath = generateTemporaryFilePath();
|
|
590
590
|
const metadata = {
|
|
591
591
|
timestamp: new Date().toISOString(),
|
|
@@ -598,14 +598,14 @@ const generateHtmlReport = async (diffResult, formattedFiles, config, dryRun) =>
|
|
|
598
598
|
const changedSections = diffResult.changedFiles.map((file) => generateChangedFileSection(file));
|
|
599
599
|
const htmlContent = generateHtmlTemplate(diffResult, formattedFiles, trulyUnchangedFiles, metadata, changedSections);
|
|
600
600
|
await writeHtmlFile(htmlContent, reportPath);
|
|
601
|
-
|
|
601
|
+
logger?.log(`✓ HTML report generated: ${reportPath}, opening in browser...`);
|
|
602
602
|
try {
|
|
603
603
|
await openInBrowser(reportPath);
|
|
604
604
|
}
|
|
605
605
|
catch {
|
|
606
606
|
const absolutePath = node_path_1.default.resolve(reportPath);
|
|
607
|
-
|
|
608
|
-
|
|
607
|
+
logger?.log('⚠ Could not open browser automatically. Please open manually:');
|
|
608
|
+
logger?.log(` file://${absolutePath}`);
|
|
609
609
|
}
|
|
610
610
|
};
|
|
611
611
|
exports.generateHtmlReport = generateHtmlReport;
|
package/dist/index.js
CHANGED
|
@@ -14,58 +14,79 @@ const fileLoader_1 = require("./fileLoader");
|
|
|
14
14
|
const fileUpdater_1 = require("./fileUpdater");
|
|
15
15
|
const htmlReporter_1 = require("./htmlReporter");
|
|
16
16
|
const jsonReporter_1 = require("./jsonReporter");
|
|
17
|
+
const logger_1 = require("./logger");
|
|
17
18
|
const stopRulesValidator_1 = require("./stopRulesValidator");
|
|
18
19
|
const collisionDetector_1 = require("./utils/collisionDetector");
|
|
19
20
|
const filenameTransformer_1 = require("./utils/filenameTransformer");
|
|
21
|
+
const versionChecker_1 = require("./utils/versionChecker");
|
|
20
22
|
const ZodError_1 = require("./ZodError");
|
|
21
23
|
const main = async () => {
|
|
22
|
-
console.log(`Now you run ${package_json_1.default.name} v${package_json_1.default.version}...`);
|
|
23
24
|
const command = (0, commandLine_1.parseCommandLine)();
|
|
24
|
-
const
|
|
25
|
-
|
|
25
|
+
const verbosityLevel = command.verbose ? 'verbose' : command.quiet ? 'quiet' : 'normal';
|
|
26
|
+
const logger = new logger_1.Logger({ level: verbosityLevel, isDiffJson: command.diffJson });
|
|
27
|
+
logger.log(`Now you run ${package_json_1.default.name} v${package_json_1.default.version}...`);
|
|
28
|
+
const config = (0, configLoader_1.loadConfigFile)(command.config, command.quiet, logger);
|
|
29
|
+
if (command.validate) {
|
|
30
|
+
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Configuration is valid', 'success'));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (logger.shouldShow('debug')) {
|
|
34
|
+
logger.debug('\nConfig details:');
|
|
35
|
+
logger.debug(` Source: ${config.source}`);
|
|
36
|
+
logger.debug(` Destination: ${config.destination}`);
|
|
37
|
+
logger.debug(` Include patterns: ${config.include.join(', ')}`);
|
|
38
|
+
logger.debug(` Exclude patterns: ${config.exclude.join(', ')}`);
|
|
39
|
+
logger.debug(` Transforms: ${Object.keys(config.transforms || {}).length} pattern(s)`);
|
|
40
|
+
logger.debug(` Prune enabled: ${config.prune}`);
|
|
41
|
+
}
|
|
42
|
+
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Loading files...', 'loading'));
|
|
26
43
|
const sourceFiles = await (0, fileLoader_1.loadFiles)({
|
|
27
44
|
baseDirectory: config.source,
|
|
28
45
|
include: config.include,
|
|
29
46
|
exclude: config.exclude,
|
|
30
47
|
transforms: config.transforms
|
|
31
|
-
});
|
|
32
|
-
|
|
48
|
+
}, logger);
|
|
49
|
+
logger.progress(`Loaded ${sourceFiles.size} source file(s)`, 'success');
|
|
33
50
|
const collisions = (0, collisionDetector_1.detectCollisions)(sourceFiles, config.transforms);
|
|
34
51
|
if (collisions.length > 0)
|
|
35
52
|
(0, collisionDetector_1.validateNoCollisions)(collisions);
|
|
53
|
+
if (logger.shouldShow('debug'))
|
|
54
|
+
logger.debug('Filename collision check: passed');
|
|
36
55
|
const destinationFiles = await (0, fileLoader_1.loadFiles)({
|
|
37
56
|
baseDirectory: config.destination,
|
|
38
57
|
include: config.include,
|
|
39
58
|
exclude: config.exclude
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const diffResult = (0, fileDiff_1.computeFileDiff)(sourceFiles, destinationFiles, config);
|
|
44
|
-
if (
|
|
59
|
+
}, logger);
|
|
60
|
+
logger.progress(`Loaded ${destinationFiles.size} destination file(s)`, 'success');
|
|
61
|
+
logger.log('\n' + (0, consoleFormatter_1.formatProgressMessage)('Computing differences...', 'info'));
|
|
62
|
+
const diffResult = (0, fileDiff_1.computeFileDiff)(sourceFiles, destinationFiles, config, logger);
|
|
63
|
+
if (logger.shouldShow('debug'))
|
|
64
|
+
logger.debug('Diff pipeline: parse → transforms → skipPath → normalize → compare');
|
|
65
|
+
if (command.diff && !command.quiet)
|
|
45
66
|
(0, consoleDiffReporter_1.showConsoleDiff)(diffResult, config);
|
|
46
67
|
else {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
68
|
+
logger.log(` New files: ${diffResult.addedFiles.length}`);
|
|
69
|
+
logger.log(` Deleted files: ${diffResult.deletedFiles.length}`);
|
|
70
|
+
logger.log(` Changed files: ${diffResult.changedFiles.length}`);
|
|
71
|
+
logger.log(` Unchanged files: ${diffResult.unchangedFiles.length}`);
|
|
51
72
|
}
|
|
52
|
-
const validationResult = (0, stopRulesValidator_1.validateStopRules)(diffResult, config.stopRules);
|
|
73
|
+
const validationResult = (0, stopRulesValidator_1.validateStopRules)(diffResult, config.stopRules, logger);
|
|
53
74
|
if (validationResult.violations.length > 0)
|
|
54
75
|
if (command.force)
|
|
55
76
|
for (const violation of validationResult.violations)
|
|
56
|
-
|
|
77
|
+
logger.stopRule(violation, 'force');
|
|
57
78
|
else if (command.dryRun)
|
|
58
79
|
for (const violation of validationResult.violations)
|
|
59
|
-
|
|
80
|
+
logger.stopRule(violation, 'warning');
|
|
60
81
|
else {
|
|
61
82
|
for (const violation of validationResult.violations)
|
|
62
|
-
|
|
63
|
-
|
|
83
|
+
logger.stopRule(violation, 'error');
|
|
84
|
+
logger.error('\nUse --force to override stop rules or --dry-run to preview changes.', 'critical');
|
|
64
85
|
process.exit(1);
|
|
65
86
|
}
|
|
66
|
-
const formattedFiles = await (0, fileUpdater_1.updateFiles)(diffResult, sourceFiles, destinationFiles, config, command.dryRun, command.skipFormat);
|
|
67
|
-
if (command.diffHtml)
|
|
68
|
-
await (0, htmlReporter_1.generateHtmlReport)(diffResult, formattedFiles, config, command.dryRun);
|
|
87
|
+
const formattedFiles = await (0, fileUpdater_1.updateFiles)(diffResult, sourceFiles, destinationFiles, config, command.dryRun, command.skipFormat, logger);
|
|
88
|
+
if (command.diffHtml && !command.quiet)
|
|
89
|
+
await (0, htmlReporter_1.generateHtmlReport)(diffResult, formattedFiles, config, command.dryRun, logger);
|
|
69
90
|
if (command.diffJson)
|
|
70
91
|
(0, jsonReporter_1.generateJsonReport)(diffResult, formattedFiles, validationResult, config, command.dryRun, package_json_1.default.version);
|
|
71
92
|
};
|
|
@@ -98,4 +119,9 @@ const main = async () => {
|
|
|
98
119
|
console.error('Unexpected error:', error);
|
|
99
120
|
process.exit(1);
|
|
100
121
|
}
|
|
122
|
+
finally {
|
|
123
|
+
const command = (0, commandLine_1.parseCommandLine)();
|
|
124
|
+
if (!command.quiet)
|
|
125
|
+
void (0, versionChecker_1.checkForUpdates)(package_json_1.default.version);
|
|
126
|
+
}
|
|
101
127
|
})();
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FileOperation, ProgressStyle, ViolationMode } from './consoleFormatter';
|
|
2
|
+
import { StopRuleViolation } from './stopRulesValidator';
|
|
3
|
+
export type VerbosityLevel = 'quiet' | 'normal' | 'verbose';
|
|
4
|
+
export type OutputCategory = 'critical' | 'normal' | 'debug' | 'special';
|
|
5
|
+
export type LoggerOptions = {
|
|
6
|
+
level: VerbosityLevel;
|
|
7
|
+
isDiffJson: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare class Logger {
|
|
10
|
+
private level;
|
|
11
|
+
constructor(options: LoggerOptions);
|
|
12
|
+
shouldShow(category: OutputCategory): boolean;
|
|
13
|
+
log(message: string, category?: OutputCategory): void;
|
|
14
|
+
warn(message: string, category?: OutputCategory): void;
|
|
15
|
+
error(message: string, category?: OutputCategory): void;
|
|
16
|
+
debug(message: string): void;
|
|
17
|
+
progress(message: string, style: ProgressStyle): void;
|
|
18
|
+
fileOp(operation: FileOperation, path: string, isDryRun: boolean, alreadyDeleted?: boolean): void;
|
|
19
|
+
stopRule(violation: StopRuleViolation, mode: ViolationMode): void;
|
|
20
|
+
}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Logger = void 0;
|
|
4
|
+
const consoleFormatter_1 = require("./consoleFormatter");
|
|
5
|
+
class Logger {
|
|
6
|
+
level;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.level = options.level;
|
|
9
|
+
}
|
|
10
|
+
shouldShow(category) {
|
|
11
|
+
if (category === 'special')
|
|
12
|
+
return true;
|
|
13
|
+
if (category === 'critical')
|
|
14
|
+
return true;
|
|
15
|
+
if (this.level === 'quiet')
|
|
16
|
+
return false;
|
|
17
|
+
if (this.level === 'verbose')
|
|
18
|
+
return true;
|
|
19
|
+
return category === 'normal';
|
|
20
|
+
}
|
|
21
|
+
log(message, category = 'normal') {
|
|
22
|
+
if (this.shouldShow(category))
|
|
23
|
+
console.log(message);
|
|
24
|
+
}
|
|
25
|
+
warn(message, category = 'normal') {
|
|
26
|
+
if (this.shouldShow(category))
|
|
27
|
+
console.warn(message);
|
|
28
|
+
}
|
|
29
|
+
error(message, category = 'critical') {
|
|
30
|
+
if (this.shouldShow(category))
|
|
31
|
+
console.error(message);
|
|
32
|
+
}
|
|
33
|
+
debug(message) {
|
|
34
|
+
if (this.shouldShow('debug'))
|
|
35
|
+
console.log(message);
|
|
36
|
+
}
|
|
37
|
+
progress(message, style) {
|
|
38
|
+
if (this.shouldShow('normal'))
|
|
39
|
+
console.log((0, consoleFormatter_1.formatProgressMessage)(message, style));
|
|
40
|
+
}
|
|
41
|
+
fileOp(operation, path, isDryRun, alreadyDeleted = false) {
|
|
42
|
+
if (this.shouldShow('normal'))
|
|
43
|
+
console.log((0, consoleFormatter_1.colorizeFileOperation)(operation, path, isDryRun, alreadyDeleted));
|
|
44
|
+
}
|
|
45
|
+
stopRule(violation, mode) {
|
|
46
|
+
const message = '\n' + (0, consoleFormatter_1.formatStopRuleViolation)(violation, mode);
|
|
47
|
+
if (mode === 'error')
|
|
48
|
+
this.error(message, 'critical');
|
|
49
|
+
else
|
|
50
|
+
this.warn(message, 'critical');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.Logger = Logger;
|
|
@@ -29,5 +29,5 @@ export interface ValidationResult {
|
|
|
29
29
|
violations: StopRuleViolation[];
|
|
30
30
|
isValid: boolean;
|
|
31
31
|
}
|
|
32
|
-
export declare const validateStopRules: (diffResult: FileDiffResult, stopRulesConfig?: Record<string, StopRule[]
|
|
32
|
+
export declare const validateStopRules: (diffResult: FileDiffResult, stopRulesConfig?: Record<string, StopRule[]>, logger?: import("./logger").Logger) => ValidationResult;
|
|
33
33
|
export {};
|
|
@@ -26,14 +26,22 @@ class StopRulesValidatorError extends StopRulesValidatorErrorClass {
|
|
|
26
26
|
}
|
|
27
27
|
exports.StopRulesValidatorError = StopRulesValidatorError;
|
|
28
28
|
exports.isStopRulesValidatorError = (0, errors_1.createErrorTypeGuard)(StopRulesValidatorError);
|
|
29
|
-
const validateStopRules = (diffResult, stopRulesConfig) => {
|
|
29
|
+
const validateStopRules = (diffResult, stopRulesConfig, logger) => {
|
|
30
30
|
if (!stopRulesConfig)
|
|
31
31
|
return { violations: [], isValid: true };
|
|
32
|
+
if (logger?.shouldShow('debug')) {
|
|
33
|
+
const totalRules = Object.values(stopRulesConfig).reduce((sum, rules) => sum + rules.length, 0);
|
|
34
|
+
logger.debug('Stop rule validation:');
|
|
35
|
+
logger.debug(` Total rules: ${totalRules}`);
|
|
36
|
+
logger.debug(` Files to check: ${diffResult.changedFiles.length}`);
|
|
37
|
+
}
|
|
32
38
|
const violations = [];
|
|
33
39
|
for (const changedFile of diffResult.changedFiles) {
|
|
34
40
|
const fileViolations = validateFileAgainstRules(changedFile, stopRulesConfig);
|
|
35
41
|
violations.push(...fileViolations);
|
|
36
42
|
}
|
|
43
|
+
if (logger?.shouldShow('debug'))
|
|
44
|
+
logger.debug(`Stop rules: ${violations.length} violation(s) found`);
|
|
37
45
|
return {
|
|
38
46
|
violations,
|
|
39
47
|
isValid: violations.length === 0
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export { normalizeForComparison, serializeForDiff } from './serialization';
|
|
|
5
5
|
export { getValueAtPath, parseJsonPath } from './jsonPath';
|
|
6
6
|
export { isYamlFile } from './fileType';
|
|
7
7
|
export { generateUnifiedDiff } from './diffGenerator';
|
|
8
|
+
export { checkForUpdates, isVersionCheckerError, VersionCheckerError } from './versionChecker';
|
package/dist/utils/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateUnifiedDiff = exports.isYamlFile = exports.parseJsonPath = exports.getValueAtPath = exports.serializeForDiff = exports.normalizeForComparison = exports.deepEqual = exports.createErrorTypeGuard = exports.createErrorClass = void 0;
|
|
3
|
+
exports.VersionCheckerError = exports.isVersionCheckerError = exports.checkForUpdates = exports.generateUnifiedDiff = exports.isYamlFile = exports.parseJsonPath = exports.getValueAtPath = exports.serializeForDiff = exports.normalizeForComparison = exports.deepEqual = exports.createErrorTypeGuard = exports.createErrorClass = void 0;
|
|
4
4
|
var errors_1 = require("./errors");
|
|
5
5
|
Object.defineProperty(exports, "createErrorClass", { enumerable: true, get: function () { return errors_1.createErrorClass; } });
|
|
6
6
|
Object.defineProperty(exports, "createErrorTypeGuard", { enumerable: true, get: function () { return errors_1.createErrorTypeGuard; } });
|
|
@@ -16,3 +16,7 @@ var fileType_1 = require("./fileType");
|
|
|
16
16
|
Object.defineProperty(exports, "isYamlFile", { enumerable: true, get: function () { return fileType_1.isYamlFile; } });
|
|
17
17
|
var diffGenerator_1 = require("./diffGenerator");
|
|
18
18
|
Object.defineProperty(exports, "generateUnifiedDiff", { enumerable: true, get: function () { return diffGenerator_1.generateUnifiedDiff; } });
|
|
19
|
+
var versionChecker_1 = require("./versionChecker");
|
|
20
|
+
Object.defineProperty(exports, "checkForUpdates", { enumerable: true, get: function () { return versionChecker_1.checkForUpdates; } });
|
|
21
|
+
Object.defineProperty(exports, "isVersionCheckerError", { enumerable: true, get: function () { return versionChecker_1.isVersionCheckerError; } });
|
|
22
|
+
Object.defineProperty(exports, "VersionCheckerError", { enumerable: true, get: function () { return versionChecker_1.VersionCheckerError; } });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare const VersionCheckerErrorClass: {
|
|
2
|
+
new (message: string, options?: import("./errors").ErrorOptions): {
|
|
3
|
+
[key: string]: unknown;
|
|
4
|
+
readonly code?: string;
|
|
5
|
+
readonly path?: string;
|
|
6
|
+
readonly cause?: Error;
|
|
7
|
+
name: string;
|
|
8
|
+
message: string;
|
|
9
|
+
stack?: string;
|
|
10
|
+
};
|
|
11
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
12
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
13
|
+
stackTraceLimit: number;
|
|
14
|
+
};
|
|
15
|
+
export declare class VersionCheckerError extends VersionCheckerErrorClass {
|
|
16
|
+
}
|
|
17
|
+
export declare const isVersionCheckerError: (error: unknown) => error is VersionCheckerError;
|
|
18
|
+
export declare const checkForUpdates: (currentVersion: string) => Promise<void>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.checkForUpdates = exports.isVersionCheckerError = exports.VersionCheckerError = void 0;
|
|
7
|
+
const node_https_1 = __importDefault(require("node:https"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
10
|
+
const errors_1 = require("./errors");
|
|
11
|
+
const VersionCheckerErrorClass = (0, errors_1.createErrorClass)('Version Checker Error', {
|
|
12
|
+
TIMEOUT: 'Request timed out',
|
|
13
|
+
NETWORK: 'Network request failed',
|
|
14
|
+
PARSE: 'Failed to parse response',
|
|
15
|
+
INVALID: 'Invalid response format'
|
|
16
|
+
});
|
|
17
|
+
class VersionCheckerError extends VersionCheckerErrorClass {
|
|
18
|
+
}
|
|
19
|
+
exports.VersionCheckerError = VersionCheckerError;
|
|
20
|
+
exports.isVersionCheckerError = (0, errors_1.createErrorTypeGuard)(VersionCheckerError);
|
|
21
|
+
const isCiEnvironment = () => {
|
|
22
|
+
const ciEnvironmentVariables = [
|
|
23
|
+
'CI',
|
|
24
|
+
'CONTINUOUS_INTEGRATION',
|
|
25
|
+
'BUILD_NUMBER',
|
|
26
|
+
'GITHUB_ACTIONS',
|
|
27
|
+
'GITLAB_CI',
|
|
28
|
+
'CIRCLECI',
|
|
29
|
+
'TRAVIS',
|
|
30
|
+
'JENKINS_HOME',
|
|
31
|
+
'TEAMCITY_VERSION',
|
|
32
|
+
'TF_BUILD'
|
|
33
|
+
];
|
|
34
|
+
return ciEnvironmentVariables.some((environmentVariable) => process.env[environmentVariable]);
|
|
35
|
+
};
|
|
36
|
+
const parseVersion = (version) => {
|
|
37
|
+
const semverRegex = /^v?(\d+)\.(\d+)\.(\d+)/;
|
|
38
|
+
const match = semverRegex.exec(version);
|
|
39
|
+
if (!match)
|
|
40
|
+
return undefined;
|
|
41
|
+
return {
|
|
42
|
+
major: Number.parseInt(match[1], 10),
|
|
43
|
+
minor: Number.parseInt(match[2], 10),
|
|
44
|
+
patch: Number.parseInt(match[3], 10)
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const isNewerVersion = (current, latest) => {
|
|
48
|
+
const currentParts = parseVersion(current);
|
|
49
|
+
const latestParts = parseVersion(latest);
|
|
50
|
+
if (!currentParts || !latestParts)
|
|
51
|
+
return false;
|
|
52
|
+
if (latestParts.major > currentParts.major)
|
|
53
|
+
return true;
|
|
54
|
+
if (latestParts.major < currentParts.major)
|
|
55
|
+
return false;
|
|
56
|
+
if (latestParts.minor > currentParts.minor)
|
|
57
|
+
return true;
|
|
58
|
+
if (latestParts.minor < currentParts.minor)
|
|
59
|
+
return false;
|
|
60
|
+
return latestParts.patch > currentParts.patch;
|
|
61
|
+
};
|
|
62
|
+
const fetchLatestVersion = (packageName, timeout) => {
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const url = `https://registry.npmjs.org/${packageName}/latest`;
|
|
65
|
+
const request = node_https_1.default.request(url, {
|
|
66
|
+
headers: {
|
|
67
|
+
Accept: 'application/json',
|
|
68
|
+
'User-Agent': `helm-env-delta/${package_json_1.default.version}`
|
|
69
|
+
},
|
|
70
|
+
signal: AbortSignal.timeout(timeout)
|
|
71
|
+
}, (response) => {
|
|
72
|
+
let data = '';
|
|
73
|
+
response.on('data', (chunk) => {
|
|
74
|
+
data += chunk;
|
|
75
|
+
});
|
|
76
|
+
response.on('end', () => {
|
|
77
|
+
if (response.statusCode !== 200) {
|
|
78
|
+
reject(new VersionCheckerError(`HTTP ${response.statusCode}`, { code: 'NETWORK' }));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const json = JSON.parse(data);
|
|
83
|
+
if (!json.version) {
|
|
84
|
+
reject(new VersionCheckerError('Missing version field', { code: 'INVALID' }));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
resolve(json.version);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
reject(new VersionCheckerError('Invalid JSON response', { code: 'PARSE' }));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
request.on('error', (error) => {
|
|
95
|
+
const isTimeout = error.name === 'AbortError';
|
|
96
|
+
const code = isTimeout ? 'TIMEOUT' : 'NETWORK';
|
|
97
|
+
reject(new VersionCheckerError(error.message, { code, cause: error }));
|
|
98
|
+
});
|
|
99
|
+
request.end();
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
const displayUpdateNotification = (currentVersion, latestVersion) => {
|
|
103
|
+
console.log('\n' + chalk_1.default.yellow(`⚠ Update available! v${currentVersion} → v${latestVersion}`));
|
|
104
|
+
console.log(chalk_1.default.yellow('Run: npm install -g helm-env-delta@latest'));
|
|
105
|
+
};
|
|
106
|
+
const checkForUpdates = async (currentVersion) => {
|
|
107
|
+
if (isCiEnvironment())
|
|
108
|
+
return;
|
|
109
|
+
try {
|
|
110
|
+
const latestVersion = await fetchLatestVersion('helm-env-delta', 3000);
|
|
111
|
+
if (isNewerVersion(currentVersion, latestVersion))
|
|
112
|
+
displayUpdateNotification(currentVersion, latestVersion);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
exports.checkForUpdates = checkForUpdates;
|