Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
d32d47b706 | |||
fd90cfe895 |
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-08-08 - 1.16.7 - fix(core)
|
||||
Improve formatting, logging, and rollback integrity in core modules
|
||||
|
||||
- Add .claude/settings.local.json with defined permissions for allowed commands
|
||||
- Standardize formatting in package.json, commit info, and configuration files
|
||||
- Refactor rollback manager to use atomic manifest writes and validate manifest structure
|
||||
- Enhance logging messages and overall code clarity in CLI and commit modules
|
||||
|
||||
## 2025-08-08 - 1.16.6 - fix(changecache)
|
||||
Improve cache manifest validation and atomic file writes; add local settings and overrides
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@git.zone/cli",
|
||||
"private": false,
|
||||
"version": "1.16.6",
|
||||
"version": "1.16.7",
|
||||
"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.",
|
||||
"main": "dist_ts/index.ts",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
|
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@git.zone/cli',
|
||||
version: '1.16.6',
|
||||
version: '1.16.7',
|
||||
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.'
|
||||
}
|
||||
|
@@ -40,7 +40,9 @@ export class GitzoneConfig {
|
||||
public async readConfigFromCwd() {
|
||||
const npmextraInstance = new plugins.npmextra.Npmextra(paths.cwd);
|
||||
this.data = npmextraInstance.dataFor<IGitzoneConfigData>('gitzone', {});
|
||||
this.data.npmciOptions = npmextraInstance.dataFor<IGitzoneConfigData['npmciOptions']>('npmci', {
|
||||
this.data.npmciOptions = npmextraInstance.dataFor<
|
||||
IGitzoneConfigData['npmciOptions']
|
||||
>('npmci', {
|
||||
npmAccessLevel: 'public',
|
||||
});
|
||||
}
|
||||
|
@@ -89,7 +89,7 @@ export let run = async () => {
|
||||
detailed: argvArg.detailed,
|
||||
interactive: argvArg.interactive !== false,
|
||||
parallel: argvArg.parallel !== false,
|
||||
verbose: argvArg.verbose
|
||||
verbose: argvArg.verbose,
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -5,7 +5,8 @@ import * as plugins from './plugins.js';
|
||||
export const logger = plugins.smartlog.Smartlog.createForCommitinfo(commitinfo);
|
||||
|
||||
// Add console destination
|
||||
const consoleDestination = new plugins.smartlogDestinationLocal.DestinationLocal();
|
||||
const consoleDestination =
|
||||
new plugins.smartlogDestinationLocal.DestinationLocal();
|
||||
logger.addLogDestination(consoleDestination);
|
||||
|
||||
// Verbose logging helper
|
||||
|
@@ -10,20 +10,22 @@ export const run = async (argvArg: any) => {
|
||||
await formatMod.run();
|
||||
}
|
||||
|
||||
|
||||
logger.log('info', `gathering facts...`);
|
||||
const aidoc = new plugins.tsdoc.AiDoc();
|
||||
await aidoc.start();
|
||||
|
||||
const nextCommitObject = await aidoc.buildNextCommitObject(paths.cwd);
|
||||
|
||||
logger.log('info', `---------
|
||||
logger.log(
|
||||
'info',
|
||||
`---------
|
||||
Next recommended commit would be:
|
||||
===========
|
||||
-> ${nextCommitObject.recommendedNextVersion}:
|
||||
-> ${nextCommitObject.recommendedNextVersionLevel}(${nextCommitObject.recommendedNextVersionScope}): ${nextCommitObject.recommendedNextVersionMessage}
|
||||
===========
|
||||
`);
|
||||
`,
|
||||
);
|
||||
const commitInteract = new plugins.smartinteract.SmartInteract();
|
||||
commitInteract.addQuestions([
|
||||
{
|
||||
@@ -72,32 +74,55 @@ export const run = async (argvArg: any) => {
|
||||
});
|
||||
|
||||
logger.log('info', `Baking commitinfo into code ...`);
|
||||
const commitInfo = new plugins.commitinfo.CommitInfo(paths.cwd, commitVersionType);
|
||||
const commitInfo = new plugins.commitinfo.CommitInfo(
|
||||
paths.cwd,
|
||||
commitVersionType,
|
||||
);
|
||||
await commitInfo.writeIntoPotentialDirs();
|
||||
|
||||
logger.log('info', `Writing changelog.md ...`);
|
||||
let changelog = nextCommitObject.changelog;
|
||||
changelog = changelog.replaceAll('{{nextVersion}}', (await commitInfo.getNextPlannedVersion()).versionString);
|
||||
changelog = changelog.replaceAll('{{nextVersionScope}}', `${await answerBucket.getAnswerFor('commitType')}(${await answerBucket.getAnswerFor('commitScope')})`);
|
||||
changelog = changelog.replaceAll('{{nextVersionMessage}}', nextCommitObject.recommendedNextVersionMessage);
|
||||
changelog = changelog.replaceAll(
|
||||
'{{nextVersion}}',
|
||||
(await commitInfo.getNextPlannedVersion()).versionString,
|
||||
);
|
||||
changelog = changelog.replaceAll(
|
||||
'{{nextVersionScope}}',
|
||||
`${await answerBucket.getAnswerFor('commitType')}(${await answerBucket.getAnswerFor('commitScope')})`,
|
||||
);
|
||||
changelog = changelog.replaceAll(
|
||||
'{{nextVersionMessage}}',
|
||||
nextCommitObject.recommendedNextVersionMessage,
|
||||
);
|
||||
if (nextCommitObject.recommendedNextVersionDetails?.length > 0) {
|
||||
changelog = changelog.replaceAll('{{nextVersionDetails}}', '- ' + nextCommitObject.recommendedNextVersionDetails.join('\n- '));
|
||||
changelog = changelog.replaceAll(
|
||||
'{{nextVersionDetails}}',
|
||||
'- ' + nextCommitObject.recommendedNextVersionDetails.join('\n- '),
|
||||
);
|
||||
} else {
|
||||
changelog = changelog.replaceAll('\n{{nextVersionDetails}}', '');
|
||||
}
|
||||
|
||||
await plugins.smartfile.memory.toFs(changelog, plugins.path.join(paths.cwd, `changelog.md`));
|
||||
await plugins.smartfile.memory.toFs(
|
||||
changelog,
|
||||
plugins.path.join(paths.cwd, `changelog.md`),
|
||||
);
|
||||
|
||||
logger.log('info', `Staging files for commit:`);
|
||||
await smartshellInstance.exec(`git add -A`);
|
||||
await smartshellInstance.exec(`git commit -m "${commitString}"`);
|
||||
await smartshellInstance.exec(`npm version ${commitVersionType}`);
|
||||
if (answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true')) {
|
||||
if (
|
||||
answerBucket.getAnswerFor('pushToOrigin') &&
|
||||
!(process.env.CI === 'true')
|
||||
) {
|
||||
await smartshellInstance.exec(`git push origin master --follow-tags`);
|
||||
}
|
||||
};
|
||||
|
||||
const createCommitStringFromAnswerBucket = (answerBucket: plugins.smartinteract.AnswerBucket) => {
|
||||
const createCommitStringFromAnswerBucket = (
|
||||
answerBucket: plugins.smartinteract.AnswerBucket,
|
||||
) => {
|
||||
const commitType = answerBucket.getAnswerFor('commitType');
|
||||
const commitScope = answerBucket.getAnswerFor('commitScope');
|
||||
const commitDescription = answerBucket.getAnswerFor('commitDescription');
|
||||
|
@@ -36,7 +36,10 @@ export const run = async () => {
|
||||
const registryUrls = answerBucket.getAnswerFor(`registryUrls`).split(',');
|
||||
const oldPackageName = answerBucket.getAnswerFor(`oldPackageName`);
|
||||
const newPackageName = answerBucket.getAnswerFor(`newPackageName`);
|
||||
logger.log('info', `Deprecating package ${oldPackageName} in favour of ${newPackageName}`);
|
||||
logger.log(
|
||||
'info',
|
||||
`Deprecating package ${oldPackageName} in favour of ${newPackageName}`,
|
||||
);
|
||||
const smartshellInstance = new plugins.smartshell.Smartshell({
|
||||
executor: 'bash',
|
||||
});
|
||||
|
@@ -43,7 +43,7 @@ export class RollbackManager {
|
||||
}
|
||||
|
||||
// Read file content and metadata
|
||||
const content = await plugins.smartfile.fs.toStringSync(absolutePath);
|
||||
const content = plugins.smartfile.fs.toStringSync(absolutePath);
|
||||
const stats = await plugins.smartfile.fs.stat(absolutePath);
|
||||
const checksum = this.calculateChecksum(content);
|
||||
|
||||
@@ -82,7 +82,7 @@ export class RollbackManager {
|
||||
|
||||
// Verify backup integrity
|
||||
const backupPath = this.getBackupPath(operationId, file.path);
|
||||
const backupContent = await plugins.smartfile.fs.toStringSync(backupPath);
|
||||
const backupContent = plugins.smartfile.fs.toStringSync(backupPath);
|
||||
const backupChecksum = this.calculateChecksum(backupContent);
|
||||
|
||||
if (backupChecksum !== file.checksum) {
|
||||
@@ -146,7 +146,7 @@ export class RollbackManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
const content = await plugins.smartfile.fs.toStringSync(backupPath);
|
||||
const content = plugins.smartfile.fs.toStringSync(backupPath);
|
||||
const checksum = this.calculateChecksum(content);
|
||||
|
||||
if (checksum !== file.checksum) {
|
||||
@@ -185,17 +185,63 @@ export class RollbackManager {
|
||||
}
|
||||
|
||||
private async getManifest(): Promise<{ operations: IFormatOperation[] }> {
|
||||
const defaultManifest = { operations: [] };
|
||||
|
||||
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
|
||||
if (!exists) {
|
||||
return { operations: [] };
|
||||
return defaultManifest;
|
||||
}
|
||||
|
||||
const content = await 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 rollback manifest structure, returning default manifest');
|
||||
return defaultManifest;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to read rollback 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;
|
||||
}
|
||||
}
|
||||
|
||||
private async saveManifest(manifest: { operations: IFormatOperation[] }): 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 rollback 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;
|
||||
}
|
||||
}
|
||||
|
||||
private async getOperation(operationId: string): Promise<IFormatOperation | null> {
|
||||
@@ -215,4 +261,38 @@ export class RollbackManager {
|
||||
|
||||
await this.saveManifest(manifest);
|
||||
}
|
||||
|
||||
private isValidManifest(manifest: any): manifest is { operations: IFormatOperation[] } {
|
||||
// Check if manifest has the required structure
|
||||
if (!manifest || typeof manifest !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check required fields
|
||||
if (!Array.isArray(manifest.operations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check each operation entry
|
||||
for (const operation of manifest.operations) {
|
||||
if (!operation || typeof operation !== 'object' ||
|
||||
typeof operation.id !== 'string' ||
|
||||
typeof operation.timestamp !== 'number' ||
|
||||
typeof operation.status !== 'string' ||
|
||||
!Array.isArray(operation.files)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check each file in the operation
|
||||
for (const file of operation.files) {
|
||||
if (!file || typeof file !== 'object' ||
|
||||
typeof file.path !== 'string' ||
|
||||
typeof file.checksum !== 'string') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user