@git.zone/cli 1.16.7 → 1.16.9
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/mod_format/classes.baseformatter.d.ts +0 -2
- package/dist_ts/mod_format/classes.baseformatter.js +14 -23
- package/dist_ts/mod_format/classes.changecache.js +14 -29
- package/dist_ts/mod_format/classes.dependency-analyzer.js +24 -18
- package/dist_ts/mod_format/classes.diffreporter.js +4 -4
- package/dist_ts/mod_format/classes.formatcontext.d.ts +0 -13
- package/dist_ts/mod_format/classes.formatcontext.js +1 -41
- package/dist_ts/mod_format/classes.formatplanner.js +25 -45
- package/dist_ts/mod_format/classes.formatstats.js +22 -20
- package/dist_ts/mod_format/classes.rollbackmanager.js +20 -32
- package/dist_ts/mod_format/format.cleanup.js +7 -2
- package/dist_ts/mod_format/format.copy.d.ts +1 -1
- package/dist_ts/mod_format/format.copy.js +3 -3
- package/dist_ts/mod_format/format.gitignore.js +27 -7
- package/dist_ts/mod_format/format.license.js +1 -1
- package/dist_ts/mod_format/format.npmextra.js +1 -1
- package/dist_ts/mod_format/format.packagejson.js +5 -3
- package/dist_ts/mod_format/format.prettier.js +7 -2
- package/dist_ts/mod_format/format.readme.js +1 -1
- package/dist_ts/mod_format/format.templates.js +1 -1
- package/dist_ts/mod_format/format.tsconfig.js +4 -2
- package/dist_ts/mod_format/formatters/cleanup.formatter.js +8 -3
- package/dist_ts/mod_format/formatters/legacy.formatter.js +6 -4
- package/dist_ts/mod_format/formatters/prettier.formatter.js +68 -51
- package/dist_ts/mod_format/formatters/readme.formatter.js +6 -4
- package/dist_ts/mod_format/index.js +13 -71
- package/dist_ts/mod_meta/meta.classes.meta.js +1 -1
- package/dist_ts/mod_standard/index.js +1 -1
- package/dist_ts/mod_start/index.js +1 -1
- package/dist_ts/mod_template/index.js +1 -1
- package/npmextra.json +1 -1
- package/package.json +1 -1
- package/readme.hints.md +8 -3
- package/readme.md +30 -3
- package/readme.plan.md +21 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/mod_format/classes.baseformatter.ts +28 -39
- package/ts/mod_format/classes.changecache.ts +74 -80
- package/ts/mod_format/classes.dependency-analyzer.ts +46 -36
- package/ts/mod_format/classes.diffreporter.ts +44 -28
- package/ts/mod_format/classes.formatcontext.ts +3 -54
- package/ts/mod_format/classes.formatplanner.ts +56 -70
- package/ts/mod_format/classes.formatstats.ts +69 -49
- package/ts/mod_format/classes.rollbackmanager.ts +123 -103
- package/ts/mod_format/format.cleanup.ts +9 -2
- package/ts/mod_format/format.copy.ts +17 -14
- package/ts/mod_format/format.gitignore.ts +33 -7
- package/ts/mod_format/format.license.ts +3 -1
- package/ts/mod_format/format.npmextra.ts +9 -2
- package/ts/mod_format/format.packagejson.ts +47 -12
- package/ts/mod_format/format.prettier.ts +10 -2
- package/ts/mod_format/format.readme.ts +2 -1
- package/ts/mod_format/format.templates.ts +15 -7
- package/ts/mod_format/format.tsconfig.ts +6 -2
- package/ts/mod_format/formatters/cleanup.formatter.ts +13 -8
- package/ts/mod_format/formatters/copy.formatter.ts +1 -1
- package/ts/mod_format/formatters/gitignore.formatter.ts +1 -1
- package/ts/mod_format/formatters/legacy.formatter.ts +19 -12
- package/ts/mod_format/formatters/license.formatter.ts +1 -1
- package/ts/mod_format/formatters/npmextra.formatter.ts +1 -1
- package/ts/mod_format/formatters/packagejson.formatter.ts +1 -1
- package/ts/mod_format/formatters/prettier.formatter.ts +114 -78
- package/ts/mod_format/formatters/readme.formatter.ts +11 -9
- package/ts/mod_format/formatters/templates.formatter.ts +1 -1
- package/ts/mod_format/formatters/tsconfig.formatter.ts +1 -1
- package/ts/mod_format/index.ts +57 -113
- package/ts/mod_format/interfaces.format.ts +3 -3
- package/ts/mod_meta/meta.classes.meta.ts +57 -19
- package/ts/mod_standard/index.ts +3 -1
- package/ts/mod_start/index.ts +3 -1
- package/ts/mod_template/index.ts +5 -2
|
@@ -5,206 +5,227 @@ import type { IFormatOperation } from './interfaces.format.js';
|
|
|
5
5
|
export class RollbackManager {
|
|
6
6
|
private backupDir: string;
|
|
7
7
|
private manifestPath: string;
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
constructor() {
|
|
10
10
|
this.backupDir = plugins.path.join(paths.cwd, '.nogit', 'gitzone-backups');
|
|
11
11
|
this.manifestPath = plugins.path.join(this.backupDir, 'manifest.json');
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
async createOperation(): Promise<IFormatOperation> {
|
|
15
15
|
await this.ensureBackupDir();
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
const operation: IFormatOperation = {
|
|
18
18
|
id: this.generateOperationId(),
|
|
19
19
|
timestamp: Date.now(),
|
|
20
20
|
files: [],
|
|
21
|
-
status: 'pending'
|
|
21
|
+
status: 'pending',
|
|
22
22
|
};
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
await this.updateManifest(operation);
|
|
25
25
|
return operation;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
async backupFile(filepath: string, operationId: string): Promise<void> {
|
|
29
29
|
const operation = await this.getOperation(operationId);
|
|
30
30
|
if (!operation) {
|
|
31
31
|
throw new Error(`Operation ${operationId} not found`);
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
const absolutePath = plugins.path.isAbsolute(filepath)
|
|
35
|
-
? filepath
|
|
33
|
+
|
|
34
|
+
const absolutePath = plugins.path.isAbsolute(filepath)
|
|
35
|
+
? filepath
|
|
36
36
|
: plugins.path.join(paths.cwd, filepath);
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
// Check if file exists
|
|
39
39
|
const exists = await plugins.smartfile.fs.fileExists(absolutePath);
|
|
40
40
|
if (!exists) {
|
|
41
41
|
// File doesn't exist yet (will be created), so we skip backup
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
// Read file content and metadata
|
|
46
46
|
const content = plugins.smartfile.fs.toStringSync(absolutePath);
|
|
47
47
|
const stats = await plugins.smartfile.fs.stat(absolutePath);
|
|
48
48
|
const checksum = this.calculateChecksum(content);
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
// Create backup
|
|
51
51
|
const backupPath = this.getBackupPath(operationId, filepath);
|
|
52
52
|
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(backupPath));
|
|
53
53
|
await plugins.smartfile.memory.toFs(content, backupPath);
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
// Update operation
|
|
56
56
|
operation.files.push({
|
|
57
57
|
path: filepath,
|
|
58
58
|
originalContent: content,
|
|
59
59
|
checksum,
|
|
60
|
-
permissions: stats.mode.toString(8)
|
|
60
|
+
permissions: stats.mode.toString(8),
|
|
61
61
|
});
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
await this.updateManifest(operation);
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
async rollback(operationId: string): Promise<void> {
|
|
67
67
|
const operation = await this.getOperation(operationId);
|
|
68
68
|
if (!operation) {
|
|
69
|
-
|
|
69
|
+
// Operation doesn't exist, might have already been rolled back or never created
|
|
70
|
+
console.warn(`Operation ${operationId} not found for rollback, skipping`);
|
|
71
|
+
return;
|
|
70
72
|
}
|
|
71
|
-
|
|
73
|
+
|
|
72
74
|
if (operation.status === 'rolled-back') {
|
|
73
75
|
throw new Error(`Operation ${operationId} has already been rolled back`);
|
|
74
76
|
}
|
|
75
|
-
|
|
77
|
+
|
|
76
78
|
// Restore files in reverse order
|
|
77
79
|
for (let i = operation.files.length - 1; i >= 0; i--) {
|
|
78
80
|
const file = operation.files[i];
|
|
79
|
-
const absolutePath = plugins.path.isAbsolute(file.path)
|
|
80
|
-
? file.path
|
|
81
|
+
const absolutePath = plugins.path.isAbsolute(file.path)
|
|
82
|
+
? file.path
|
|
81
83
|
: plugins.path.join(paths.cwd, file.path);
|
|
82
|
-
|
|
84
|
+
|
|
83
85
|
// Verify backup integrity
|
|
84
86
|
const backupPath = this.getBackupPath(operationId, file.path);
|
|
85
87
|
const backupContent = plugins.smartfile.fs.toStringSync(backupPath);
|
|
86
88
|
const backupChecksum = this.calculateChecksum(backupContent);
|
|
87
|
-
|
|
89
|
+
|
|
88
90
|
if (backupChecksum !== file.checksum) {
|
|
89
91
|
throw new Error(`Backup integrity check failed for ${file.path}`);
|
|
90
92
|
}
|
|
91
|
-
|
|
93
|
+
|
|
92
94
|
// Restore file
|
|
93
95
|
await plugins.smartfile.memory.toFs(file.originalContent, absolutePath);
|
|
94
|
-
|
|
96
|
+
|
|
95
97
|
// Restore permissions
|
|
96
98
|
const mode = parseInt(file.permissions, 8);
|
|
97
99
|
// Note: Permissions restoration may not work on all platforms
|
|
98
100
|
}
|
|
99
|
-
|
|
101
|
+
|
|
100
102
|
// Update operation status
|
|
101
103
|
operation.status = 'rolled-back';
|
|
102
104
|
await this.updateManifest(operation);
|
|
103
105
|
}
|
|
104
|
-
|
|
106
|
+
|
|
105
107
|
async markComplete(operationId: string): Promise<void> {
|
|
106
108
|
const operation = await this.getOperation(operationId);
|
|
107
109
|
if (!operation) {
|
|
108
110
|
throw new Error(`Operation ${operationId} not found`);
|
|
109
111
|
}
|
|
110
|
-
|
|
112
|
+
|
|
111
113
|
operation.status = 'completed';
|
|
112
114
|
await this.updateManifest(operation);
|
|
113
115
|
}
|
|
114
|
-
|
|
116
|
+
|
|
115
117
|
async cleanOldBackups(retentionDays: number): Promise<void> {
|
|
116
118
|
const manifest = await this.getManifest();
|
|
117
|
-
const cutoffTime = Date.now() -
|
|
118
|
-
|
|
119
|
-
const operationsToDelete = manifest.operations.filter(
|
|
120
|
-
op.timestamp < cutoffTime && op.status === 'completed'
|
|
119
|
+
const cutoffTime = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
120
|
+
|
|
121
|
+
const operationsToDelete = manifest.operations.filter(
|
|
122
|
+
(op) => op.timestamp < cutoffTime && op.status === 'completed',
|
|
121
123
|
);
|
|
122
|
-
|
|
124
|
+
|
|
123
125
|
for (const operation of operationsToDelete) {
|
|
124
126
|
// Remove backup files
|
|
125
|
-
const operationDir = plugins.path.join(
|
|
127
|
+
const operationDir = plugins.path.join(
|
|
128
|
+
this.backupDir,
|
|
129
|
+
'operations',
|
|
130
|
+
operation.id,
|
|
131
|
+
);
|
|
126
132
|
await plugins.smartfile.fs.remove(operationDir);
|
|
127
|
-
|
|
133
|
+
|
|
128
134
|
// Remove from manifest
|
|
129
|
-
manifest.operations = manifest.operations.filter(
|
|
135
|
+
manifest.operations = manifest.operations.filter(
|
|
136
|
+
(op) => op.id !== operation.id,
|
|
137
|
+
);
|
|
130
138
|
}
|
|
131
|
-
|
|
139
|
+
|
|
132
140
|
await this.saveManifest(manifest);
|
|
133
141
|
}
|
|
134
|
-
|
|
142
|
+
|
|
135
143
|
async verifyBackup(operationId: string): Promise<boolean> {
|
|
136
144
|
const operation = await this.getOperation(operationId);
|
|
137
145
|
if (!operation) {
|
|
138
146
|
return false;
|
|
139
147
|
}
|
|
140
|
-
|
|
148
|
+
|
|
141
149
|
for (const file of operation.files) {
|
|
142
150
|
const backupPath = this.getBackupPath(operationId, file.path);
|
|
143
151
|
const exists = await plugins.smartfile.fs.fileExists(backupPath);
|
|
144
|
-
|
|
152
|
+
|
|
145
153
|
if (!exists) {
|
|
146
154
|
return false;
|
|
147
155
|
}
|
|
148
|
-
|
|
156
|
+
|
|
149
157
|
const content = plugins.smartfile.fs.toStringSync(backupPath);
|
|
150
158
|
const checksum = this.calculateChecksum(content);
|
|
151
|
-
|
|
159
|
+
|
|
152
160
|
if (checksum !== file.checksum) {
|
|
153
161
|
return false;
|
|
154
162
|
}
|
|
155
163
|
}
|
|
156
|
-
|
|
164
|
+
|
|
157
165
|
return true;
|
|
158
166
|
}
|
|
159
|
-
|
|
167
|
+
|
|
160
168
|
async listBackups(): Promise<IFormatOperation[]> {
|
|
161
169
|
const manifest = await this.getManifest();
|
|
162
170
|
return manifest.operations;
|
|
163
171
|
}
|
|
164
|
-
|
|
172
|
+
|
|
165
173
|
private async ensureBackupDir(): Promise<void> {
|
|
166
174
|
await plugins.smartfile.fs.ensureDir(this.backupDir);
|
|
167
|
-
await plugins.smartfile.fs.ensureDir(
|
|
175
|
+
await plugins.smartfile.fs.ensureDir(
|
|
176
|
+
plugins.path.join(this.backupDir, 'operations'),
|
|
177
|
+
);
|
|
168
178
|
}
|
|
169
|
-
|
|
179
|
+
|
|
170
180
|
private generateOperationId(): string {
|
|
171
181
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
172
182
|
const random = Math.random().toString(36).substring(2, 8);
|
|
173
183
|
return `${timestamp}-${random}`;
|
|
174
184
|
}
|
|
175
|
-
|
|
185
|
+
|
|
176
186
|
private getBackupPath(operationId: string, filepath: string): string {
|
|
177
187
|
const filename = plugins.path.basename(filepath);
|
|
178
188
|
const dir = plugins.path.dirname(filepath);
|
|
179
189
|
const safeDir = dir.replace(/[/\\]/g, '__');
|
|
180
|
-
return plugins.path.join(
|
|
190
|
+
return plugins.path.join(
|
|
191
|
+
this.backupDir,
|
|
192
|
+
'operations',
|
|
193
|
+
operationId,
|
|
194
|
+
'files',
|
|
195
|
+
safeDir,
|
|
196
|
+
`${filename}.backup`,
|
|
197
|
+
);
|
|
181
198
|
}
|
|
182
|
-
|
|
199
|
+
|
|
183
200
|
private calculateChecksum(content: string | Buffer): string {
|
|
184
201
|
return plugins.crypto.createHash('sha256').update(content).digest('hex');
|
|
185
202
|
}
|
|
186
|
-
|
|
203
|
+
|
|
187
204
|
private async getManifest(): Promise<{ operations: IFormatOperation[] }> {
|
|
188
205
|
const defaultManifest = { operations: [] };
|
|
189
|
-
|
|
206
|
+
|
|
190
207
|
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
|
|
191
208
|
if (!exists) {
|
|
192
209
|
return defaultManifest;
|
|
193
210
|
}
|
|
194
|
-
|
|
211
|
+
|
|
195
212
|
try {
|
|
196
213
|
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
|
|
197
214
|
const manifest = JSON.parse(content);
|
|
198
|
-
|
|
215
|
+
|
|
199
216
|
// Validate the manifest structure
|
|
200
217
|
if (this.isValidManifest(manifest)) {
|
|
201
218
|
return manifest;
|
|
202
219
|
} else {
|
|
203
|
-
console.warn(
|
|
220
|
+
console.warn(
|
|
221
|
+
'Invalid rollback manifest structure, returning default manifest',
|
|
222
|
+
);
|
|
204
223
|
return defaultManifest;
|
|
205
224
|
}
|
|
206
225
|
} catch (error) {
|
|
207
|
-
console.warn(
|
|
226
|
+
console.warn(
|
|
227
|
+
`Failed to read rollback manifest: ${error.message}, returning default manifest`,
|
|
228
|
+
);
|
|
208
229
|
// Try to delete the corrupted file
|
|
209
230
|
try {
|
|
210
231
|
await plugins.smartfile.fs.remove(this.manifestPath);
|
|
@@ -214,85 +235,84 @@ export class RollbackManager {
|
|
|
214
235
|
return defaultManifest;
|
|
215
236
|
}
|
|
216
237
|
}
|
|
217
|
-
|
|
218
|
-
private async saveManifest(manifest: {
|
|
238
|
+
|
|
239
|
+
private async saveManifest(manifest: {
|
|
240
|
+
operations: IFormatOperation[];
|
|
241
|
+
}): Promise<void> {
|
|
219
242
|
// Validate before saving
|
|
220
243
|
if (!this.isValidManifest(manifest)) {
|
|
221
244
|
throw new Error('Invalid rollback manifest structure, cannot save');
|
|
222
245
|
}
|
|
223
|
-
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
await plugins.smartfile.memory.toFs(jsonContent, tempPath);
|
|
231
|
-
|
|
232
|
-
// Move temp file to actual manifest (atomic-like operation)
|
|
233
|
-
// Since smartfile doesn't have rename, we copy and delete
|
|
234
|
-
await plugins.smartfile.fs.copy(tempPath, this.manifestPath);
|
|
235
|
-
await plugins.smartfile.fs.remove(tempPath);
|
|
236
|
-
} catch (error) {
|
|
237
|
-
// Clean up temp file if it exists
|
|
238
|
-
try {
|
|
239
|
-
await plugins.smartfile.fs.remove(tempPath);
|
|
240
|
-
} catch (removeError) {
|
|
241
|
-
// Ignore removal errors
|
|
242
|
-
}
|
|
243
|
-
throw error;
|
|
244
|
-
}
|
|
246
|
+
|
|
247
|
+
// Ensure directory exists
|
|
248
|
+
await this.ensureBackupDir();
|
|
249
|
+
|
|
250
|
+
// Write directly with proper JSON stringification
|
|
251
|
+
const jsonContent = JSON.stringify(manifest, null, 2);
|
|
252
|
+
await plugins.smartfile.memory.toFs(jsonContent, this.manifestPath);
|
|
245
253
|
}
|
|
246
|
-
|
|
247
|
-
private async getOperation(
|
|
254
|
+
|
|
255
|
+
private async getOperation(
|
|
256
|
+
operationId: string,
|
|
257
|
+
): Promise<IFormatOperation | null> {
|
|
248
258
|
const manifest = await this.getManifest();
|
|
249
|
-
return manifest.operations.find(op => op.id === operationId) || null;
|
|
259
|
+
return manifest.operations.find((op) => op.id === operationId) || null;
|
|
250
260
|
}
|
|
251
|
-
|
|
261
|
+
|
|
252
262
|
private async updateManifest(operation: IFormatOperation): Promise<void> {
|
|
253
263
|
const manifest = await this.getManifest();
|
|
254
|
-
const existingIndex = manifest.operations.findIndex(
|
|
255
|
-
|
|
264
|
+
const existingIndex = manifest.operations.findIndex(
|
|
265
|
+
(op) => op.id === operation.id,
|
|
266
|
+
);
|
|
267
|
+
|
|
256
268
|
if (existingIndex !== -1) {
|
|
257
269
|
manifest.operations[existingIndex] = operation;
|
|
258
270
|
} else {
|
|
259
271
|
manifest.operations.push(operation);
|
|
260
272
|
}
|
|
261
|
-
|
|
273
|
+
|
|
262
274
|
await this.saveManifest(manifest);
|
|
263
275
|
}
|
|
264
|
-
|
|
265
|
-
private isValidManifest(
|
|
276
|
+
|
|
277
|
+
private isValidManifest(
|
|
278
|
+
manifest: any,
|
|
279
|
+
): manifest is { operations: IFormatOperation[] } {
|
|
266
280
|
// Check if manifest has the required structure
|
|
267
281
|
if (!manifest || typeof manifest !== 'object') {
|
|
268
282
|
return false;
|
|
269
283
|
}
|
|
270
|
-
|
|
284
|
+
|
|
271
285
|
// Check required fields
|
|
272
286
|
if (!Array.isArray(manifest.operations)) {
|
|
273
287
|
return false;
|
|
274
288
|
}
|
|
275
|
-
|
|
289
|
+
|
|
276
290
|
// Check each operation entry
|
|
277
291
|
for (const operation of manifest.operations) {
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
292
|
+
if (
|
|
293
|
+
!operation ||
|
|
294
|
+
typeof operation !== 'object' ||
|
|
295
|
+
typeof operation.id !== 'string' ||
|
|
296
|
+
typeof operation.timestamp !== 'number' ||
|
|
297
|
+
typeof operation.status !== 'string' ||
|
|
298
|
+
!Array.isArray(operation.files)
|
|
299
|
+
) {
|
|
283
300
|
return false;
|
|
284
301
|
}
|
|
285
|
-
|
|
302
|
+
|
|
286
303
|
// Check each file in the operation
|
|
287
304
|
for (const file of operation.files) {
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
305
|
+
if (
|
|
306
|
+
!file ||
|
|
307
|
+
typeof file !== 'object' ||
|
|
308
|
+
typeof file.path !== 'string' ||
|
|
309
|
+
typeof file.checksum !== 'string'
|
|
310
|
+
) {
|
|
291
311
|
return false;
|
|
292
312
|
}
|
|
293
313
|
}
|
|
294
314
|
}
|
|
295
|
-
|
|
315
|
+
|
|
296
316
|
return true;
|
|
297
317
|
}
|
|
298
|
-
}
|
|
318
|
+
}
|
|
@@ -4,14 +4,21 @@ import * as paths from '../paths.js';
|
|
|
4
4
|
import { logger } from '../gitzone.logging.js';
|
|
5
5
|
import { Project } from '../classes.project.js';
|
|
6
6
|
|
|
7
|
-
const filesToDelete = [
|
|
7
|
+
const filesToDelete = [
|
|
8
|
+
'defaults.yml',
|
|
9
|
+
'yarn.lock',
|
|
10
|
+
'package-lock.json',
|
|
11
|
+
'tslint.json',
|
|
12
|
+
];
|
|
8
13
|
|
|
9
14
|
export const run = async (projectArg: Project) => {
|
|
10
15
|
for (const relativeFilePath of filesToDelete) {
|
|
11
16
|
const fileExists = plugins.smartfile.fs.fileExistsSync(relativeFilePath);
|
|
12
17
|
if (fileExists) {
|
|
13
18
|
logger.log('info', `Found ${relativeFilePath}! Removing it!`);
|
|
14
|
-
plugins.smartfile.fs.removeSync(
|
|
19
|
+
plugins.smartfile.fs.removeSync(
|
|
20
|
+
plugins.path.join(paths.cwd, relativeFilePath),
|
|
21
|
+
);
|
|
15
22
|
} else {
|
|
16
23
|
logger.log('info', `Project is free of ${relativeFilePath}`);
|
|
17
24
|
}
|
|
@@ -4,56 +4,59 @@ import { logger } from '../gitzone.logging.js';
|
|
|
4
4
|
|
|
5
5
|
export const run = async (projectArg: Project) => {
|
|
6
6
|
const gitzoneConfig = await projectArg.gitzoneConfig;
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
// Get copy configuration from npmextra.json
|
|
9
9
|
const npmextraConfig = new plugins.npmextra.Npmextra();
|
|
10
10
|
const copyConfig = npmextraConfig.dataFor<any>('gitzone.format.copy', {
|
|
11
|
-
patterns: []
|
|
11
|
+
patterns: [],
|
|
12
12
|
});
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
if (!copyConfig.patterns || copyConfig.patterns.length === 0) {
|
|
15
15
|
logger.log('info', 'No copy patterns configured in npmextra.json');
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
for (const pattern of copyConfig.patterns) {
|
|
20
20
|
if (!pattern.from || !pattern.to) {
|
|
21
21
|
logger.log('warn', 'Invalid copy pattern - missing "from" or "to" field');
|
|
22
22
|
continue;
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
try {
|
|
26
26
|
// Handle glob patterns
|
|
27
27
|
const files = await plugins.smartfile.fs.listFileTree('.', pattern.from);
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
for (const file of files) {
|
|
30
30
|
const sourcePath = file;
|
|
31
31
|
let destPath = pattern.to;
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
// If destination is a directory, preserve filename
|
|
34
34
|
if (pattern.to.endsWith('/')) {
|
|
35
35
|
const filename = plugins.path.basename(file);
|
|
36
36
|
destPath = plugins.path.join(pattern.to, filename);
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
// Handle template variables in destination path
|
|
40
40
|
if (pattern.preservePath) {
|
|
41
41
|
const relativePath = plugins.path.relative(
|
|
42
|
-
plugins.path.dirname(pattern.from.replace(/\*/g, '')),
|
|
43
|
-
file
|
|
42
|
+
plugins.path.dirname(pattern.from.replace(/\*/g, '')),
|
|
43
|
+
file,
|
|
44
44
|
);
|
|
45
45
|
destPath = plugins.path.join(pattern.to, relativePath);
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
// Ensure destination directory exists
|
|
49
49
|
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(destPath));
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
// Copy file
|
|
52
52
|
await plugins.smartfile.fs.copy(sourcePath, destPath);
|
|
53
53
|
logger.log('info', `Copied ${sourcePath} to ${destPath}`);
|
|
54
54
|
}
|
|
55
55
|
} catch (error) {
|
|
56
|
-
logger.log(
|
|
56
|
+
logger.log(
|
|
57
|
+
'error',
|
|
58
|
+
`Failed to copy pattern ${pattern.from}: ${error.message}`,
|
|
59
|
+
);
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
};
|
|
@@ -79,4 +82,4 @@ export const run = async (projectArg: Project) => {
|
|
|
79
82
|
* }
|
|
80
83
|
* }
|
|
81
84
|
* }
|
|
82
|
-
*/
|
|
85
|
+
*/
|
|
@@ -8,14 +8,40 @@ const gitignorePath = plugins.path.join(paths.cwd, './.gitignore');
|
|
|
8
8
|
|
|
9
9
|
export const run = async (projectArg: Project) => {
|
|
10
10
|
const gitignoreExists = await plugins.smartfile.fs.fileExists(gitignorePath);
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
let customContent = '';
|
|
12
|
+
|
|
13
13
|
if (gitignoreExists) {
|
|
14
14
|
// lets get the existing gitignore file
|
|
15
|
-
const existingGitIgnoreString =
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const existingGitIgnoreString =
|
|
16
|
+
plugins.smartfile.fs.toStringSync(gitignorePath);
|
|
17
|
+
|
|
18
|
+
// Check for different custom section markers
|
|
19
|
+
const customMarkers = ['#------# custom', '# custom'];
|
|
20
|
+
for (const marker of customMarkers) {
|
|
21
|
+
const splitResult = existingGitIgnoreString.split(marker);
|
|
22
|
+
if (splitResult.length > 1) {
|
|
23
|
+
// Get everything after the marker (excluding the marker itself)
|
|
24
|
+
customContent = splitResult[1].trim();
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Write the template
|
|
31
|
+
const templateModule = await import('../mod_template/index.js');
|
|
32
|
+
const ciTemplate = await templateModule.getTemplate('gitignore');
|
|
33
|
+
await ciTemplate.writeToDisk(paths.cwd);
|
|
34
|
+
|
|
35
|
+
// Append the custom content if it exists
|
|
36
|
+
if (customContent) {
|
|
37
|
+
const newGitignoreContent =
|
|
38
|
+
plugins.smartfile.fs.toStringSync(gitignorePath);
|
|
39
|
+
// The template already ends with "#------# custom", so just append the content
|
|
40
|
+
const finalContent =
|
|
41
|
+
newGitignoreContent.trimEnd() + '\n' + customContent + '\n';
|
|
42
|
+
await plugins.smartfile.fs.toFs(finalContent, gitignorePath);
|
|
43
|
+
logger.log('info', 'Updated .gitignore while preserving custom section!');
|
|
44
|
+
} else {
|
|
45
|
+
logger.log('info', 'Added a .gitignore!');
|
|
18
46
|
}
|
|
19
|
-
ciTemplate.writeToDisk(paths.cwd);
|
|
20
|
-
logger.log('info', 'Added a .gitignore!');
|
|
21
47
|
};
|
|
@@ -24,7 +24,9 @@ export const run = async (projectArg: Project) => {
|
|
|
24
24
|
} else {
|
|
25
25
|
logger.log('error', 'Error -> licenses failed. Here is why:');
|
|
26
26
|
for (const failedModule of licenseCheckResult.failingModules) {
|
|
27
|
-
console.log(
|
|
27
|
+
console.log(
|
|
28
|
+
`${failedModule.name} fails with license ${failedModule.license}`,
|
|
29
|
+
);
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
};
|
|
@@ -29,7 +29,12 @@ export const run = async (projectArg: Project) => {
|
|
|
29
29
|
|
|
30
30
|
const interactInstance = new plugins.smartinteract.SmartInteract();
|
|
31
31
|
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
|
32
|
-
if (
|
|
32
|
+
if (
|
|
33
|
+
!plugins.smartobject.smartGet(
|
|
34
|
+
npmextraJson.gitzone,
|
|
35
|
+
expectedRepoInformationItem,
|
|
36
|
+
)
|
|
37
|
+
) {
|
|
33
38
|
interactInstance.addQuestions([
|
|
34
39
|
{
|
|
35
40
|
message: `What is the value of ${expectedRepoInformationItem}`,
|
|
@@ -43,7 +48,9 @@ export const run = async (projectArg: Project) => {
|
|
|
43
48
|
|
|
44
49
|
const answerbucket = await interactInstance.runQueue();
|
|
45
50
|
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
|
46
|
-
const cliProvidedValue = answerbucket.getAnswerFor(
|
|
51
|
+
const cliProvidedValue = answerbucket.getAnswerFor(
|
|
52
|
+
expectedRepoInformationItem,
|
|
53
|
+
);
|
|
47
54
|
if (cliProvidedValue) {
|
|
48
55
|
plugins.smartobject.smartAdd(
|
|
49
56
|
npmextraJson.gitzone,
|