@git.zone/cli 2.6.1 โ 2.8.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.project.d.ts +3 -1
- package/dist_ts/classes.project.js +4 -3
- package/dist_ts/gitzone.cli.js +3 -1
- package/dist_ts/mod_commit/index.js +24 -3
- package/dist_ts/mod_commit/mod.ui.d.ts +1 -0
- package/dist_ts/mod_commit/mod.ui.js +5 -1
- package/dist_ts/mod_config/classes.commitconfig.d.ts +41 -0
- package/dist_ts/mod_config/classes.commitconfig.js +83 -0
- package/dist_ts/mod_config/index.d.ts +2 -1
- package/dist_ts/mod_config/index.js +140 -7
- package/dist_ts/mod_format/classes.baseformatter.d.ts +14 -1
- package/dist_ts/mod_format/classes.baseformatter.js +88 -1
- package/dist_ts/mod_format/index.d.ts +6 -1
- package/dist_ts/mod_format/index.js +22 -6
- package/dist_ts/mod_format/interfaces.format.d.ts +10 -0
- package/package.json +2 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.project.ts +3 -2
- package/ts/gitzone.cli.ts +2 -0
- package/ts/mod_commit/index.ts +30 -2
- package/ts/mod_commit/mod.ui.ts +5 -0
- package/ts/mod_config/classes.commitconfig.ts +104 -0
- package/ts/mod_config/index.ts +156 -7
- package/ts/mod_format/classes.baseformatter.ts +91 -1
- package/ts/mod_format/index.ts +37 -8
- package/ts/mod_format/interfaces.format.ts +12 -1
package/ts/mod_commit/index.ts
CHANGED
|
@@ -8,9 +8,20 @@ import * as ui from './mod.ui.js';
|
|
|
8
8
|
import { ReleaseConfig } from '../mod_config/classes.releaseconfig.js';
|
|
9
9
|
|
|
10
10
|
export const run = async (argvArg: any) => {
|
|
11
|
-
//
|
|
11
|
+
// Read commit config from npmextra.json
|
|
12
|
+
const npmextraConfig = new plugins.npmextra.Npmextra();
|
|
13
|
+
const gitzoneConfig = npmextraConfig.dataFor<{
|
|
14
|
+
commit?: {
|
|
15
|
+
alwaysTest?: boolean;
|
|
16
|
+
alwaysBuild?: boolean;
|
|
17
|
+
};
|
|
18
|
+
}>('@git.zone/cli', {});
|
|
19
|
+
const commitConfig = gitzoneConfig.commit || {};
|
|
20
|
+
|
|
21
|
+
// Check flags and merge with config options
|
|
12
22
|
const wantsRelease = !!(argvArg.r || argvArg.release);
|
|
13
|
-
const
|
|
23
|
+
const wantsTest = !!(argvArg.t || argvArg.test || commitConfig.alwaysTest);
|
|
24
|
+
const wantsBuild = !!(argvArg.b || argvArg.build || commitConfig.alwaysBuild);
|
|
14
25
|
let releaseConfig: ReleaseConfig | null = null;
|
|
15
26
|
|
|
16
27
|
if (wantsRelease) {
|
|
@@ -28,6 +39,7 @@ export const run = async (argvArg: any) => {
|
|
|
28
39
|
ui.printExecutionPlan({
|
|
29
40
|
autoAccept: !!(argvArg.y || argvArg.yes),
|
|
30
41
|
push: !!(argvArg.p || argvArg.push),
|
|
42
|
+
test: wantsTest,
|
|
31
43
|
build: wantsBuild,
|
|
32
44
|
release: wantsRelease,
|
|
33
45
|
format: !!argvArg.format,
|
|
@@ -39,6 +51,21 @@ export const run = async (argvArg: any) => {
|
|
|
39
51
|
await formatMod.run();
|
|
40
52
|
}
|
|
41
53
|
|
|
54
|
+
// Run tests early to fail fast before analysis
|
|
55
|
+
if (wantsTest) {
|
|
56
|
+
ui.printHeader('๐งช Running tests...');
|
|
57
|
+
const smartshellForTest = new plugins.smartshell.Smartshell({
|
|
58
|
+
executor: 'bash',
|
|
59
|
+
sourceFilePaths: [],
|
|
60
|
+
});
|
|
61
|
+
const testResult = await smartshellForTest.exec('pnpm test');
|
|
62
|
+
if (testResult.exitCode !== 0) {
|
|
63
|
+
logger.log('error', 'Tests failed. Aborting commit.');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
logger.log('success', 'All tests passed.');
|
|
67
|
+
}
|
|
68
|
+
|
|
42
69
|
ui.printHeader('๐ Analyzing repository changes...');
|
|
43
70
|
|
|
44
71
|
const aidoc = new plugins.tsdoc.AiDoc();
|
|
@@ -161,6 +188,7 @@ export const run = async (argvArg: any) => {
|
|
|
161
188
|
}
|
|
162
189
|
|
|
163
190
|
// Determine total steps based on options
|
|
191
|
+
// Note: test runs early (like format) so not counted in numbered steps
|
|
164
192
|
const willPush = answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true');
|
|
165
193
|
const willRelease = answerBucket.getAnswerFor('createRelease') && releaseConfig?.hasRegistries();
|
|
166
194
|
let totalSteps = 5; // Base steps: commitinfo, changelog, staging, commit, version
|
package/ts/mod_commit/mod.ui.ts
CHANGED
|
@@ -21,6 +21,7 @@ interface ICommitSummary {
|
|
|
21
21
|
interface IExecutionPlanOptions {
|
|
22
22
|
autoAccept: boolean;
|
|
23
23
|
push: boolean;
|
|
24
|
+
test: boolean;
|
|
24
25
|
build: boolean;
|
|
25
26
|
release: boolean;
|
|
26
27
|
format: boolean;
|
|
@@ -64,6 +65,7 @@ export function printExecutionPlan(options: IExecutionPlanOptions): void {
|
|
|
64
65
|
console.log(' Options:');
|
|
65
66
|
console.log(` Auto-accept ${options.autoAccept ? 'โ enabled (-y)' : 'โ interactive mode'}`);
|
|
66
67
|
console.log(` Push to remote ${options.push ? 'โ enabled (-p)' : 'โ disabled'}`);
|
|
68
|
+
console.log(` Test first ${options.test ? 'โ enabled (-t)' : 'โ disabled'}`);
|
|
67
69
|
console.log(` Build & verify ${options.build ? 'โ enabled (-b)' : 'โ disabled'}`);
|
|
68
70
|
console.log(` Release to npm ${options.release ? 'โ enabled (-r)' : 'โ disabled'}`);
|
|
69
71
|
if (options.format) {
|
|
@@ -77,6 +79,9 @@ export function printExecutionPlan(options: IExecutionPlanOptions): void {
|
|
|
77
79
|
if (options.format) {
|
|
78
80
|
console.log(` ${stepNum++}. Format project files`);
|
|
79
81
|
}
|
|
82
|
+
if (options.test) {
|
|
83
|
+
console.log(` ${stepNum++}. Run tests`);
|
|
84
|
+
}
|
|
80
85
|
console.log(` ${stepNum++}. Analyze repository changes`);
|
|
81
86
|
console.log(` ${stepNum++}. Bake commit info into code`);
|
|
82
87
|
console.log(` ${stepNum++}. Generate changelog.md`);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as plugins from './mod.plugins.js';
|
|
2
|
+
|
|
3
|
+
export interface ICommitConfig {
|
|
4
|
+
alwaysTest: boolean;
|
|
5
|
+
alwaysBuild: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Manages commit configuration stored in npmextra.json
|
|
10
|
+
* under @git.zone/cli.commit namespace
|
|
11
|
+
*/
|
|
12
|
+
export class CommitConfig {
|
|
13
|
+
private cwd: string;
|
|
14
|
+
private config: ICommitConfig;
|
|
15
|
+
|
|
16
|
+
constructor(cwd: string = process.cwd()) {
|
|
17
|
+
this.cwd = cwd;
|
|
18
|
+
this.config = { alwaysTest: false, alwaysBuild: false };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a CommitConfig instance from current working directory
|
|
23
|
+
*/
|
|
24
|
+
public static async fromCwd(cwd: string = process.cwd()): Promise<CommitConfig> {
|
|
25
|
+
const instance = new CommitConfig(cwd);
|
|
26
|
+
await instance.load();
|
|
27
|
+
return instance;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Load configuration from npmextra.json
|
|
32
|
+
*/
|
|
33
|
+
public async load(): Promise<void> {
|
|
34
|
+
const npmextraInstance = new plugins.npmextra.Npmextra(this.cwd);
|
|
35
|
+
const gitzoneConfig = npmextraInstance.dataFor<any>('@git.zone/cli', {});
|
|
36
|
+
|
|
37
|
+
this.config = {
|
|
38
|
+
alwaysTest: gitzoneConfig?.commit?.alwaysTest ?? false,
|
|
39
|
+
alwaysBuild: gitzoneConfig?.commit?.alwaysBuild ?? false,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Save configuration to npmextra.json
|
|
45
|
+
*/
|
|
46
|
+
public async save(): Promise<void> {
|
|
47
|
+
const npmextraPath = plugins.path.join(this.cwd, 'npmextra.json');
|
|
48
|
+
let npmextraData: any = {};
|
|
49
|
+
|
|
50
|
+
// Read existing npmextra.json
|
|
51
|
+
if (await plugins.smartfs.file(npmextraPath).exists()) {
|
|
52
|
+
const content = await plugins.smartfs.file(npmextraPath).encoding('utf8').read();
|
|
53
|
+
npmextraData = JSON.parse(content as string);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Ensure @git.zone/cli namespace exists
|
|
57
|
+
if (!npmextraData['@git.zone/cli']) {
|
|
58
|
+
npmextraData['@git.zone/cli'] = {};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Ensure commit object exists
|
|
62
|
+
if (!npmextraData['@git.zone/cli'].commit) {
|
|
63
|
+
npmextraData['@git.zone/cli'].commit = {};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Update commit settings
|
|
67
|
+
npmextraData['@git.zone/cli'].commit.alwaysTest = this.config.alwaysTest;
|
|
68
|
+
npmextraData['@git.zone/cli'].commit.alwaysBuild = this.config.alwaysBuild;
|
|
69
|
+
|
|
70
|
+
// Write back to file
|
|
71
|
+
await plugins.smartfs
|
|
72
|
+
.file(npmextraPath)
|
|
73
|
+
.encoding('utf8')
|
|
74
|
+
.write(JSON.stringify(npmextraData, null, 2));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get alwaysTest setting
|
|
79
|
+
*/
|
|
80
|
+
public getAlwaysTest(): boolean {
|
|
81
|
+
return this.config.alwaysTest;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Set alwaysTest setting
|
|
86
|
+
*/
|
|
87
|
+
public setAlwaysTest(value: boolean): void {
|
|
88
|
+
this.config.alwaysTest = value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get alwaysBuild setting
|
|
93
|
+
*/
|
|
94
|
+
public getAlwaysBuild(): boolean {
|
|
95
|
+
return this.config.alwaysBuild;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Set alwaysBuild setting
|
|
100
|
+
*/
|
|
101
|
+
public setAlwaysBuild(value: boolean): void {
|
|
102
|
+
this.config.alwaysBuild = value;
|
|
103
|
+
}
|
|
104
|
+
}
|
package/ts/mod_config/index.ts
CHANGED
|
@@ -2,9 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
import * as plugins from './mod.plugins.js';
|
|
4
4
|
import { ReleaseConfig } from './classes.releaseconfig.js';
|
|
5
|
-
import {
|
|
5
|
+
import { CommitConfig } from './classes.commitconfig.js';
|
|
6
|
+
import { runFormatter, type ICheckResult } from '../mod_format/index.js';
|
|
6
7
|
|
|
7
|
-
export { ReleaseConfig };
|
|
8
|
+
export { ReleaseConfig, CommitConfig };
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Format npmextra.json with diff preview
|
|
12
|
+
* Shows diff first, asks for confirmation, then applies
|
|
13
|
+
*/
|
|
14
|
+
async function formatNpmextraWithDiff(): Promise<void> {
|
|
15
|
+
// Check for diffs first
|
|
16
|
+
const checkResult = await runFormatter('npmextra', {
|
|
17
|
+
checkOnly: true,
|
|
18
|
+
showDiff: true,
|
|
19
|
+
}) as ICheckResult | void;
|
|
20
|
+
|
|
21
|
+
if (checkResult && checkResult.hasDiff) {
|
|
22
|
+
const shouldApply = await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
|
23
|
+
'Apply formatting changes to npmextra.json?',
|
|
24
|
+
true
|
|
25
|
+
);
|
|
26
|
+
if (shouldApply) {
|
|
27
|
+
await runFormatter('npmextra', { silent: true });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
8
31
|
|
|
9
32
|
export const run = async (argvArg: any) => {
|
|
10
33
|
const command = argvArg._?.[1];
|
|
@@ -33,6 +56,12 @@ export const run = async (argvArg: any) => {
|
|
|
33
56
|
case 'accessLevel':
|
|
34
57
|
await handleAccessLevel(value);
|
|
35
58
|
break;
|
|
59
|
+
case 'commit':
|
|
60
|
+
await handleCommit(argvArg._?.[2], argvArg._?.[3]);
|
|
61
|
+
break;
|
|
62
|
+
case 'services':
|
|
63
|
+
await handleServices();
|
|
64
|
+
break;
|
|
36
65
|
case 'help':
|
|
37
66
|
showHelp();
|
|
38
67
|
break;
|
|
@@ -48,7 +77,7 @@ export const run = async (argvArg: any) => {
|
|
|
48
77
|
async function handleInteractiveMenu(): Promise<void> {
|
|
49
78
|
console.log('');
|
|
50
79
|
console.log('โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ');
|
|
51
|
-
console.log('โ gitzone config -
|
|
80
|
+
console.log('โ gitzone config - Project Configuration โ');
|
|
52
81
|
console.log('โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ');
|
|
53
82
|
console.log('');
|
|
54
83
|
|
|
@@ -64,6 +93,8 @@ async function handleInteractiveMenu(): Promise<void> {
|
|
|
64
93
|
{ name: 'Remove a registry', value: 'remove' },
|
|
65
94
|
{ name: 'Clear all registries', value: 'clear' },
|
|
66
95
|
{ name: 'Set access level (public/private)', value: 'access' },
|
|
96
|
+
{ name: 'Configure commit options', value: 'commit' },
|
|
97
|
+
{ name: 'Configure services', value: 'services' },
|
|
67
98
|
{ name: 'Show help', value: 'help' },
|
|
68
99
|
],
|
|
69
100
|
});
|
|
@@ -86,6 +117,12 @@ async function handleInteractiveMenu(): Promise<void> {
|
|
|
86
117
|
case 'access':
|
|
87
118
|
await handleAccessLevel();
|
|
88
119
|
break;
|
|
120
|
+
case 'commit':
|
|
121
|
+
await handleCommit();
|
|
122
|
+
break;
|
|
123
|
+
case 'services':
|
|
124
|
+
await handleServices();
|
|
125
|
+
break;
|
|
89
126
|
case 'help':
|
|
90
127
|
showHelp();
|
|
91
128
|
break;
|
|
@@ -149,8 +186,8 @@ async function handleAdd(url?: string): Promise<void> {
|
|
|
149
186
|
|
|
150
187
|
if (added) {
|
|
151
188
|
await config.save();
|
|
152
|
-
await runFormatter('npmextra', { silent: true });
|
|
153
189
|
plugins.logger.log('success', `Added registry: ${url}`);
|
|
190
|
+
await formatNpmextraWithDiff();
|
|
154
191
|
} else {
|
|
155
192
|
plugins.logger.log('warn', `Registry already exists: ${url}`);
|
|
156
193
|
}
|
|
@@ -185,8 +222,8 @@ async function handleRemove(url?: string): Promise<void> {
|
|
|
185
222
|
|
|
186
223
|
if (removed) {
|
|
187
224
|
await config.save();
|
|
188
|
-
await runFormatter('npmextra', { silent: true });
|
|
189
225
|
plugins.logger.log('success', `Removed registry: ${url}`);
|
|
226
|
+
await formatNpmextraWithDiff();
|
|
190
227
|
} else {
|
|
191
228
|
plugins.logger.log('warn', `Registry not found: ${url}`);
|
|
192
229
|
}
|
|
@@ -212,8 +249,8 @@ async function handleClear(): Promise<void> {
|
|
|
212
249
|
if (confirmed) {
|
|
213
250
|
config.clearRegistries();
|
|
214
251
|
await config.save();
|
|
215
|
-
await runFormatter('npmextra', { silent: true });
|
|
216
252
|
plugins.logger.log('success', 'All registries cleared.');
|
|
253
|
+
await formatNpmextraWithDiff();
|
|
217
254
|
} else {
|
|
218
255
|
plugins.logger.log('info', 'Operation cancelled.');
|
|
219
256
|
}
|
|
@@ -252,8 +289,115 @@ async function handleAccessLevel(level?: string): Promise<void> {
|
|
|
252
289
|
|
|
253
290
|
config.setAccessLevel(level as 'public' | 'private');
|
|
254
291
|
await config.save();
|
|
255
|
-
await runFormatter('npmextra', { silent: true });
|
|
256
292
|
plugins.logger.log('success', `Access level set to: ${level}`);
|
|
293
|
+
await formatNpmextraWithDiff();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Handle commit configuration
|
|
298
|
+
*/
|
|
299
|
+
async function handleCommit(setting?: string, value?: string): Promise<void> {
|
|
300
|
+
const config = await CommitConfig.fromCwd();
|
|
301
|
+
|
|
302
|
+
// No setting = interactive mode
|
|
303
|
+
if (!setting) {
|
|
304
|
+
await handleCommitInteractive(config);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Direct setting
|
|
309
|
+
switch (setting) {
|
|
310
|
+
case 'alwaysTest':
|
|
311
|
+
await handleCommitSetting(config, 'alwaysTest', value);
|
|
312
|
+
break;
|
|
313
|
+
case 'alwaysBuild':
|
|
314
|
+
await handleCommitSetting(config, 'alwaysBuild', value);
|
|
315
|
+
break;
|
|
316
|
+
default:
|
|
317
|
+
plugins.logger.log('error', `Unknown commit setting: ${setting}`);
|
|
318
|
+
showCommitHelp();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Interactive commit configuration
|
|
324
|
+
*/
|
|
325
|
+
async function handleCommitInteractive(config: CommitConfig): Promise<void> {
|
|
326
|
+
console.log('');
|
|
327
|
+
console.log('โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ');
|
|
328
|
+
console.log('โ Commit Configuration โ');
|
|
329
|
+
console.log('โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ');
|
|
330
|
+
console.log('');
|
|
331
|
+
|
|
332
|
+
const interactInstance = new plugins.smartinteract.SmartInteract();
|
|
333
|
+
const response = await interactInstance.askQuestion({
|
|
334
|
+
type: 'checkbox',
|
|
335
|
+
name: 'commitOptions',
|
|
336
|
+
message: 'Select commit options to enable:',
|
|
337
|
+
choices: [
|
|
338
|
+
{ name: 'Always run tests before commit (-t)', value: 'alwaysTest' },
|
|
339
|
+
{ name: 'Always build after commit (-b)', value: 'alwaysBuild' },
|
|
340
|
+
],
|
|
341
|
+
default: [
|
|
342
|
+
...(config.getAlwaysTest() ? ['alwaysTest'] : []),
|
|
343
|
+
...(config.getAlwaysBuild() ? ['alwaysBuild'] : []),
|
|
344
|
+
],
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const selected = (response as any).value || [];
|
|
348
|
+
config.setAlwaysTest(selected.includes('alwaysTest'));
|
|
349
|
+
config.setAlwaysBuild(selected.includes('alwaysBuild'));
|
|
350
|
+
await config.save();
|
|
351
|
+
|
|
352
|
+
plugins.logger.log('success', 'Commit configuration updated');
|
|
353
|
+
await formatNpmextraWithDiff();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Set a specific commit setting
|
|
358
|
+
*/
|
|
359
|
+
async function handleCommitSetting(config: CommitConfig, setting: string, value?: string): Promise<void> {
|
|
360
|
+
// Parse boolean value
|
|
361
|
+
const boolValue = value === 'true' || value === '1' || value === 'on';
|
|
362
|
+
|
|
363
|
+
if (setting === 'alwaysTest') {
|
|
364
|
+
config.setAlwaysTest(boolValue);
|
|
365
|
+
} else if (setting === 'alwaysBuild') {
|
|
366
|
+
config.setAlwaysBuild(boolValue);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
await config.save();
|
|
370
|
+
plugins.logger.log('success', `Set ${setting} to ${boolValue}`);
|
|
371
|
+
await formatNpmextraWithDiff();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Show help for commit subcommand
|
|
376
|
+
*/
|
|
377
|
+
function showCommitHelp(): void {
|
|
378
|
+
console.log('');
|
|
379
|
+
console.log('Usage: gitzone config commit [setting] [value]');
|
|
380
|
+
console.log('');
|
|
381
|
+
console.log('Settings:');
|
|
382
|
+
console.log(' alwaysTest [true|false] Always run tests before commit');
|
|
383
|
+
console.log(' alwaysBuild [true|false] Always build after commit');
|
|
384
|
+
console.log('');
|
|
385
|
+
console.log('Examples:');
|
|
386
|
+
console.log(' gitzone config commit # Interactive mode');
|
|
387
|
+
console.log(' gitzone config commit alwaysTest true');
|
|
388
|
+
console.log(' gitzone config commit alwaysBuild false');
|
|
389
|
+
console.log('');
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Handle services configuration
|
|
394
|
+
*/
|
|
395
|
+
async function handleServices(): Promise<void> {
|
|
396
|
+
// Import and use ServiceManager's configureServices
|
|
397
|
+
const { ServiceManager } = await import('../mod_services/classes.servicemanager.js');
|
|
398
|
+
const serviceManager = new ServiceManager();
|
|
399
|
+
await serviceManager.init();
|
|
400
|
+
await serviceManager.configureServices();
|
|
257
401
|
}
|
|
258
402
|
|
|
259
403
|
/**
|
|
@@ -269,6 +413,8 @@ function showHelp(): void {
|
|
|
269
413
|
console.log(' remove [url] Remove a registry URL');
|
|
270
414
|
console.log(' clear Clear all registries');
|
|
271
415
|
console.log(' access [public|private] Set npm access level for publishing');
|
|
416
|
+
console.log(' commit [setting] [value] Configure commit options');
|
|
417
|
+
console.log(' services Configure which services are enabled');
|
|
272
418
|
console.log('');
|
|
273
419
|
console.log('Examples:');
|
|
274
420
|
console.log(' gitzone config show');
|
|
@@ -278,5 +424,8 @@ function showHelp(): void {
|
|
|
278
424
|
console.log(' gitzone config clear');
|
|
279
425
|
console.log(' gitzone config access public');
|
|
280
426
|
console.log(' gitzone config access private');
|
|
427
|
+
console.log(' gitzone config commit # Interactive');
|
|
428
|
+
console.log(' gitzone config commit alwaysTest true');
|
|
429
|
+
console.log(' gitzone config services # Interactive');
|
|
281
430
|
console.log('');
|
|
282
431
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as plugins from './mod.plugins.js';
|
|
2
2
|
import { FormatContext } from './classes.formatcontext.js';
|
|
3
|
-
import type { IPlannedChange } from './interfaces.format.js';
|
|
3
|
+
import type { IPlannedChange, ICheckResult } from './interfaces.format.js';
|
|
4
4
|
import { Project } from '../classes.project.js';
|
|
5
5
|
|
|
6
6
|
export abstract class BaseFormatter {
|
|
@@ -79,4 +79,94 @@ export abstract class BaseFormatter {
|
|
|
79
79
|
protected async shouldProcessFile(filepath: string): Promise<boolean> {
|
|
80
80
|
return true;
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check for diffs without applying changes
|
|
85
|
+
* Returns information about what would change
|
|
86
|
+
*/
|
|
87
|
+
async check(): Promise<ICheckResult> {
|
|
88
|
+
const changes = await this.analyze();
|
|
89
|
+
const diffs: ICheckResult['diffs'] = [];
|
|
90
|
+
|
|
91
|
+
for (const change of changes) {
|
|
92
|
+
// Skip generic changes that don't have actual content
|
|
93
|
+
if (change.path === '<various files>') {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (change.type === 'modify' || change.type === 'create') {
|
|
98
|
+
// Read current content if file exists
|
|
99
|
+
let currentContent: string | undefined;
|
|
100
|
+
try {
|
|
101
|
+
currentContent = await plugins.smartfs.file(change.path).encoding('utf8').read() as string;
|
|
102
|
+
} catch {
|
|
103
|
+
// File doesn't exist yet
|
|
104
|
+
currentContent = undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const newContent = change.content;
|
|
108
|
+
|
|
109
|
+
// Check if there's an actual diff
|
|
110
|
+
if (currentContent !== newContent && newContent !== undefined) {
|
|
111
|
+
diffs.push({
|
|
112
|
+
path: change.path,
|
|
113
|
+
type: change.type,
|
|
114
|
+
before: currentContent,
|
|
115
|
+
after: newContent,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
} else if (change.type === 'delete') {
|
|
119
|
+
// Check if file exists before marking for deletion
|
|
120
|
+
try {
|
|
121
|
+
const currentContent = await plugins.smartfs.file(change.path).encoding('utf8').read() as string;
|
|
122
|
+
diffs.push({
|
|
123
|
+
path: change.path,
|
|
124
|
+
type: 'delete',
|
|
125
|
+
before: currentContent,
|
|
126
|
+
after: undefined,
|
|
127
|
+
});
|
|
128
|
+
} catch {
|
|
129
|
+
// File doesn't exist, nothing to delete
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
hasDiff: diffs.length > 0,
|
|
136
|
+
diffs,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Display a single diff using smartdiff
|
|
142
|
+
*/
|
|
143
|
+
displayDiff(diff: ICheckResult['diffs'][0]): void {
|
|
144
|
+
console.log(`\n--- ${diff.path}`);
|
|
145
|
+
if (diff.before && diff.after) {
|
|
146
|
+
console.log(plugins.smartdiff.formatLineDiffForConsole(diff.before, diff.after));
|
|
147
|
+
} else if (diff.after && !diff.before) {
|
|
148
|
+
console.log(' (new file)');
|
|
149
|
+
// Show first few lines of new content
|
|
150
|
+
const lines = diff.after.split('\n').slice(0, 10);
|
|
151
|
+
lines.forEach(line => console.log(` + ${line}`));
|
|
152
|
+
if (diff.after.split('\n').length > 10) {
|
|
153
|
+
console.log(' ... (truncated)');
|
|
154
|
+
}
|
|
155
|
+
} else if (diff.before && !diff.after) {
|
|
156
|
+
console.log(' (file will be deleted)');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Display all diffs from a check result
|
|
162
|
+
*/
|
|
163
|
+
displayAllDiffs(result: ICheckResult): void {
|
|
164
|
+
if (!result.hasDiff) {
|
|
165
|
+
console.log(' No changes detected');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
for (const diff of result.diffs) {
|
|
169
|
+
this.displayDiff(diff);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
82
172
|
}
|
package/ts/mod_format/index.ts
CHANGED
|
@@ -19,7 +19,8 @@ import { CopyFormatter } from './formatters/copy.formatter.js';
|
|
|
19
19
|
|
|
20
20
|
export let run = async (
|
|
21
21
|
options: {
|
|
22
|
-
|
|
22
|
+
write?: boolean; // Explicitly write changes (default: false, dry-mode)
|
|
23
|
+
dryRun?: boolean; // Deprecated, kept for compatibility
|
|
23
24
|
yes?: boolean;
|
|
24
25
|
planOnly?: boolean;
|
|
25
26
|
savePlan?: string;
|
|
@@ -35,7 +36,11 @@ export let run = async (
|
|
|
35
36
|
setVerboseMode(true);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
// Determine if we should write changes
|
|
40
|
+
// Default is dry-mode (no writing) unless --write/-w is specified
|
|
41
|
+
const shouldWrite = options.write ?? (options.dryRun === false);
|
|
42
|
+
|
|
43
|
+
const project = await Project.fromCwd({ requireProjectType: false });
|
|
39
44
|
const context = new FormatContext();
|
|
40
45
|
// Cache system removed - no longer needed
|
|
41
46
|
const planner = new FormatPlanner();
|
|
@@ -127,9 +132,9 @@ export let run = async (
|
|
|
127
132
|
return;
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
// Dry-run mode
|
|
131
|
-
if (
|
|
132
|
-
logger.log('info', 'Dry-run mode -
|
|
135
|
+
// Dry-run mode (default behavior)
|
|
136
|
+
if (!shouldWrite) {
|
|
137
|
+
logger.log('info', 'Dry-run mode - use --write (-w) to apply changes');
|
|
133
138
|
return;
|
|
134
139
|
}
|
|
135
140
|
|
|
@@ -197,14 +202,27 @@ export const handleCleanBackups = async (): Promise<void> => {
|
|
|
197
202
|
);
|
|
198
203
|
};
|
|
199
204
|
|
|
205
|
+
// Import the ICheckResult type for external use
|
|
206
|
+
import type { ICheckResult } from './interfaces.format.js';
|
|
207
|
+
export type { ICheckResult };
|
|
208
|
+
|
|
209
|
+
// Formatters that don't require projectType to be set
|
|
210
|
+
const formattersNotRequiringProjectType = ['npmextra', 'prettier', 'cleanup', 'packagejson'];
|
|
211
|
+
|
|
200
212
|
/**
|
|
201
213
|
* Run a single formatter by name (for use by other modules)
|
|
202
214
|
*/
|
|
203
215
|
export const runFormatter = async (
|
|
204
216
|
formatterName: string,
|
|
205
|
-
options: {
|
|
206
|
-
|
|
207
|
-
|
|
217
|
+
options: {
|
|
218
|
+
silent?: boolean;
|
|
219
|
+
checkOnly?: boolean; // Only check for diffs, don't apply
|
|
220
|
+
showDiff?: boolean; // Show the diff output
|
|
221
|
+
} = {}
|
|
222
|
+
): Promise<ICheckResult | void> => {
|
|
223
|
+
// Determine if this formatter requires projectType
|
|
224
|
+
const requireProjectType = !formattersNotRequiringProjectType.includes(formatterName);
|
|
225
|
+
const project = await Project.fromCwd({ requireProjectType });
|
|
208
226
|
const context = new FormatContext();
|
|
209
227
|
|
|
210
228
|
// Map formatter names to classes
|
|
@@ -227,6 +245,17 @@ export const runFormatter = async (
|
|
|
227
245
|
}
|
|
228
246
|
|
|
229
247
|
const formatter = new FormatterClass(context, project);
|
|
248
|
+
|
|
249
|
+
// Check-only mode: just check for diffs and optionally display them
|
|
250
|
+
if (options.checkOnly) {
|
|
251
|
+
const result = await formatter.check();
|
|
252
|
+
if (result.hasDiff && options.showDiff) {
|
|
253
|
+
formatter.displayAllDiffs(result);
|
|
254
|
+
}
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Normal mode: analyze and apply changes
|
|
230
259
|
const changes = await formatter.analyze();
|
|
231
260
|
|
|
232
261
|
for (const change of changes) {
|
|
@@ -39,7 +39,18 @@ export type IPlannedChange = {
|
|
|
39
39
|
path: string;
|
|
40
40
|
module: string;
|
|
41
41
|
description: string;
|
|
42
|
-
content?: string; //
|
|
42
|
+
content?: string; // New content for create/modify operations
|
|
43
|
+
originalContent?: string; // Original content for comparison
|
|
43
44
|
diff?: string;
|
|
44
45
|
size?: number;
|
|
45
46
|
};
|
|
47
|
+
|
|
48
|
+
export interface ICheckResult {
|
|
49
|
+
hasDiff: boolean;
|
|
50
|
+
diffs: Array<{
|
|
51
|
+
path: string;
|
|
52
|
+
type: 'create' | 'modify' | 'delete';
|
|
53
|
+
before?: string;
|
|
54
|
+
after?: string;
|
|
55
|
+
}>;
|
|
56
|
+
}
|