fix(changecache): Improve cache manifest validation and atomic file writes; add local settings and overrides

This commit is contained in:
2025-08-08 05:43:34 +00:00
parent 5f561527f9
commit e21e7f0850
4 changed files with 93 additions and 10 deletions

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@git.zone/cli',
version: '1.16.5',
version: '1.16.6',
description: 'A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.'
}

View File

@@ -29,21 +29,67 @@ export class ChangeCache {
}
async getManifest(): Promise<ICacheManifest> {
const defaultManifest: ICacheManifest = {
version: this.cacheVersion,
lastFormat: 0,
files: []
};
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
if (!exists) {
return {
version: this.cacheVersion,
lastFormat: 0,
files: []
};
return defaultManifest;
}
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
return JSON.parse(content);
try {
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
const manifest = JSON.parse(content);
// Validate the manifest structure
if (this.isValidManifest(manifest)) {
return manifest;
} else {
console.warn('Invalid manifest structure, returning default manifest');
return defaultManifest;
}
} catch (error) {
console.warn(`Failed to read cache manifest: ${error.message}, returning default manifest`);
// Try to delete the corrupted file
try {
await plugins.smartfile.fs.remove(this.manifestPath);
} catch (removeError) {
// Ignore removal errors
}
return defaultManifest;
}
}
async saveManifest(manifest: ICacheManifest): Promise<void> {
await plugins.smartfile.memory.toFs(JSON.stringify(manifest, null, 2), this.manifestPath);
// Validate before saving
if (!this.isValidManifest(manifest)) {
throw new Error('Invalid manifest structure, cannot save');
}
// Use atomic write: write to temp file, then move it
const tempPath = `${this.manifestPath}.tmp`;
try {
// Write to temporary file
const jsonContent = JSON.stringify(manifest, null, 2);
await plugins.smartfile.memory.toFs(jsonContent, tempPath);
// Move temp file to actual manifest (atomic-like operation)
// Since smartfile doesn't have rename, we copy and delete
await plugins.smartfile.fs.copy(tempPath, this.manifestPath);
await plugins.smartfile.fs.remove(tempPath);
} catch (error) {
// Clean up temp file if it exists
try {
await plugins.smartfile.fs.remove(tempPath);
} catch (removeError) {
// Ignore removal errors
}
throw error;
}
}
async hasFileChanged(filePath: string): Promise<boolean> {
@@ -95,7 +141,7 @@ export class ChangeCache {
return; // Don't cache directories
}
const content = await plugins.smartfile.fs.toStringSync(absolutePath);
const content = plugins.smartfile.fs.toStringSync(absolutePath);
const checksum = this.calculateChecksum(content);
// Update manifest
@@ -153,4 +199,31 @@ export class ChangeCache {
private calculateChecksum(content: string | Buffer): string {
return plugins.crypto.createHash('sha256').update(content).digest('hex');
}
private isValidManifest(manifest: any): manifest is ICacheManifest {
// Check if manifest has the required structure
if (!manifest || typeof manifest !== 'object') {
return false;
}
// Check required fields
if (typeof manifest.version !== 'string' ||
typeof manifest.lastFormat !== 'number' ||
!Array.isArray(manifest.files)) {
return false;
}
// Check each file entry
for (const file of manifest.files) {
if (!file || typeof file !== 'object' ||
typeof file.path !== 'string' ||
typeof file.checksum !== 'string' ||
typeof file.modified !== 'number' ||
typeof file.size !== 'number') {
return false;
}
}
return true;
}
}