Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6bd2d35992 | |||
| d038b1d681 | |||
| d60de5cef7 | |||
| e1076a60ad | |||
| 6deba06443 | |||
| 00cd9dc8e7 | |||
| 6b0941eea9 | |||
| 7348567a62 |
34
changelog.md
34
changelog.md
@@ -1,5 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-12-14 - 2.6.1 - fix(npmextra)
|
||||
Normalize npmextra.json: move tsdoc legal entry and reposition @git.zone/cli configuration
|
||||
|
||||
- Move TSDoc legal text into a top-level "tsdoc.legal" property in npmextra.json
|
||||
- Remove the misplaced "gitzone" object that previously contained the TSDoc legal block
|
||||
- Reorder and properly nest the "@git.zone/cli" configuration to follow the new "tsdoc" section
|
||||
- Pure configuration change (JSON structure) — no functional code changes
|
||||
|
||||
## 2025-12-14 - 2.6.0 - feat(mod_commit)
|
||||
Add execution plan output to commit command
|
||||
|
||||
- Print an execution plan at the start of the commit flow (shows active options and planned steps)
|
||||
- New printExecutionPlan(options) added to ts/mod_commit/mod.ui.ts
|
||||
- Commit command now calls ui.printExecutionPlan(...) before running format or other steps
|
||||
- Execution plan reflects flags: auto-accept (-y), push (-p), build (-b), release (-r), --format, and target registries
|
||||
|
||||
## 2025-12-14 - 2.5.0 - feat(mod_standard)
|
||||
Add interactive main menu and help to standard CLI module; route commands via dynamic imports
|
||||
|
||||
- Introduce interactive CLI menu using @push.rocks/smartinteract to prompt user for actions.
|
||||
- Add routing to commands (commit, format, config, template, services, open, help) using dynamic imports.
|
||||
- Add showHelp() helper to display usage and available commands.
|
||||
- Remove previous static template listing and logger.warn placeholder.
|
||||
|
||||
## 2025-12-14 - 2.4.0 - feat(cli)
|
||||
Add optional build step to release flow and auto-format npmextra config when registries change
|
||||
|
||||
- Introduce a --build/-b flag in the commit/release flow to run 'pnpm build' before pushing/releases
|
||||
- Verify the working tree is clean after the build and abort the release if build produces uncommitted changes
|
||||
- Increase total step counting to include build and verification steps in the UI progress output
|
||||
- Add a runFormatter utility to the formatting module to execute a single formatter programmatically
|
||||
- Wire runFormatter('npmextra') into mod_config so npmextra.json is formatted automatically after add/remove/clear/access operations
|
||||
- Add npmextra registry config entry (https://verdaccio.lossless.digital) to npmextra.json
|
||||
|
||||
## 2025-12-14 - 2.3.0 - feat(config)
|
||||
Add interactive menu and help to config command, handle unknown commands, and bump dependencies
|
||||
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
"npmAccessLevel": "private",
|
||||
"npmRegistryUrl": "verdaccio.lossless.one"
|
||||
},
|
||||
"gitzone": {
|
||||
"tsdoc": {
|
||||
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
|
||||
},
|
||||
"@git.zone/cli": {
|
||||
"projectType": "npm",
|
||||
"module": {
|
||||
"githost": "gitlab.com",
|
||||
@@ -31,9 +34,13 @@
|
||||
"Docker",
|
||||
"CI/CD"
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
"registries": [
|
||||
"https://verdaccio.lossless.digital",
|
||||
"https://registry.npmjs.org"
|
||||
],
|
||||
"accessLevel": "public"
|
||||
}
|
||||
},
|
||||
"tsdoc": {
|
||||
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@git.zone/cli",
|
||||
"private": false,
|
||||
"version": "2.3.0",
|
||||
"version": "2.6.1",
|
||||
"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: '2.3.0',
|
||||
version: '2.6.1',
|
||||
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.'
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ReleaseConfig } from '../mod_config/classes.releaseconfig.js';
|
||||
export const run = async (argvArg: any) => {
|
||||
// Check if release flag is set and validate registries early
|
||||
const wantsRelease = !!(argvArg.r || argvArg.release);
|
||||
const wantsBuild = !!(argvArg.b || argvArg.build);
|
||||
let releaseConfig: ReleaseConfig | null = null;
|
||||
|
||||
if (wantsRelease) {
|
||||
@@ -23,6 +24,16 @@ export const run = async (argvArg: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Print execution plan at the start
|
||||
ui.printExecutionPlan({
|
||||
autoAccept: !!(argvArg.y || argvArg.yes),
|
||||
push: !!(argvArg.p || argvArg.push),
|
||||
build: wantsBuild,
|
||||
release: wantsRelease,
|
||||
format: !!argvArg.format,
|
||||
registries: releaseConfig?.getRegistries(),
|
||||
});
|
||||
|
||||
if (argvArg.format) {
|
||||
const formatMod = await import('../mod_format/index.js');
|
||||
await formatMod.run();
|
||||
@@ -153,6 +164,7 @@ export const run = async (argvArg: any) => {
|
||||
const willPush = answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true');
|
||||
const willRelease = answerBucket.getAnswerFor('createRelease') && releaseConfig?.hasRegistries();
|
||||
let totalSteps = 5; // Base steps: commitinfo, changelog, staging, commit, version
|
||||
if (wantsBuild) totalSteps += 2; // build step + verification step
|
||||
if (willPush) totalSteps++;
|
||||
if (willRelease) totalSteps++;
|
||||
let currentStep = 0;
|
||||
@@ -215,7 +227,34 @@ export const run = async (argvArg: any) => {
|
||||
const projectType = await helpers.detectProjectType();
|
||||
const newVersion = await helpers.bumpProjectVersion(projectType, commitVersionType, currentStep, totalSteps);
|
||||
|
||||
// Step 6: Push to remote (optional)
|
||||
// Step 6: Run build (optional)
|
||||
if (wantsBuild) {
|
||||
currentStep++;
|
||||
ui.printStep(currentStep, totalSteps, '🔨 Running build', 'in-progress');
|
||||
const buildResult = await smartshellInstance.exec('pnpm build');
|
||||
if (buildResult.exitCode !== 0) {
|
||||
ui.printStep(currentStep, totalSteps, '🔨 Running build', 'error');
|
||||
logger.log('error', 'Build failed. Aborting release.');
|
||||
process.exit(1);
|
||||
}
|
||||
ui.printStep(currentStep, totalSteps, '🔨 Running build', 'done');
|
||||
|
||||
// Step 7: Verify no uncommitted changes
|
||||
currentStep++;
|
||||
ui.printStep(currentStep, totalSteps, '🔍 Verifying clean working tree', 'in-progress');
|
||||
const statusResult = await smartshellInstance.exec('git status --porcelain');
|
||||
if (statusResult.stdout.trim() !== '') {
|
||||
ui.printStep(currentStep, totalSteps, '🔍 Verifying clean working tree', 'error');
|
||||
logger.log('error', 'Build produced uncommitted changes. This usually means build output is not gitignored.');
|
||||
logger.log('error', 'Uncommitted files:');
|
||||
console.log(statusResult.stdout);
|
||||
logger.log('error', 'Aborting release. Please ensure build artifacts are in .gitignore');
|
||||
process.exit(1);
|
||||
}
|
||||
ui.printStep(currentStep, totalSteps, '🔍 Verifying clean working tree', 'done');
|
||||
}
|
||||
|
||||
// Step: Push to remote (optional)
|
||||
const currentBranch = await helpers.detectCurrentBranch();
|
||||
if (willPush) {
|
||||
currentStep++;
|
||||
|
||||
@@ -18,6 +18,15 @@ interface ICommitSummary {
|
||||
releasedRegistries?: string[];
|
||||
}
|
||||
|
||||
interface IExecutionPlanOptions {
|
||||
autoAccept: boolean;
|
||||
push: boolean;
|
||||
build: boolean;
|
||||
release: boolean;
|
||||
format: boolean;
|
||||
registries?: string[];
|
||||
}
|
||||
|
||||
interface IRecommendation {
|
||||
recommendedNextVersion: string;
|
||||
recommendedNextVersionLevel: string;
|
||||
@@ -41,6 +50,54 @@ export function printHeader(title: string): void {
|
||||
console.log('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Print execution plan at the start of commit
|
||||
*/
|
||||
export function printExecutionPlan(options: IExecutionPlanOptions): void {
|
||||
console.log('');
|
||||
console.log('╭───────────────────────────────────────────────────────────────╮');
|
||||
console.log('│ gitzone commit - Execution Plan │');
|
||||
console.log('╰───────────────────────────────────────────────────────────────╯');
|
||||
console.log('');
|
||||
|
||||
// Show active options
|
||||
console.log(' Options:');
|
||||
console.log(` Auto-accept ${options.autoAccept ? '✓ enabled (-y)' : '○ interactive mode'}`);
|
||||
console.log(` Push to remote ${options.push ? '✓ enabled (-p)' : '○ disabled'}`);
|
||||
console.log(` Build & verify ${options.build ? '✓ enabled (-b)' : '○ disabled'}`);
|
||||
console.log(` Release to npm ${options.release ? '✓ enabled (-r)' : '○ disabled'}`);
|
||||
if (options.format) {
|
||||
console.log(` Format first ✓ enabled (--format)`);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// Show steps
|
||||
console.log(' Steps:');
|
||||
let stepNum = 1;
|
||||
if (options.format) {
|
||||
console.log(` ${stepNum++}. Format project files`);
|
||||
}
|
||||
console.log(` ${stepNum++}. Analyze repository changes`);
|
||||
console.log(` ${stepNum++}. Bake commit info into code`);
|
||||
console.log(` ${stepNum++}. Generate changelog.md`);
|
||||
console.log(` ${stepNum++}. Stage and commit files`);
|
||||
console.log(` ${stepNum++}. Bump version`);
|
||||
if (options.build) {
|
||||
console.log(` ${stepNum++}. Run build`);
|
||||
console.log(` ${stepNum++}. Verify clean working tree`);
|
||||
}
|
||||
if (options.push) {
|
||||
console.log(` ${stepNum++}. Push to remote`);
|
||||
}
|
||||
if (options.release) {
|
||||
console.log(` ${stepNum++}. Publish to npm registries`);
|
||||
if (options.registries && options.registries.length > 0) {
|
||||
options.registries.forEach(r => console.log(` → ${r}`));
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a section with a border
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import * as plugins from './mod.plugins.js';
|
||||
import { ReleaseConfig } from './classes.releaseconfig.js';
|
||||
import { runFormatter } from '../mod_format/index.js';
|
||||
|
||||
export { ReleaseConfig };
|
||||
|
||||
@@ -148,6 +149,7 @@ async function handleAdd(url?: string): Promise<void> {
|
||||
|
||||
if (added) {
|
||||
await config.save();
|
||||
await runFormatter('npmextra', { silent: true });
|
||||
plugins.logger.log('success', `Added registry: ${url}`);
|
||||
} else {
|
||||
plugins.logger.log('warn', `Registry already exists: ${url}`);
|
||||
@@ -183,6 +185,7 @@ async function handleRemove(url?: string): Promise<void> {
|
||||
|
||||
if (removed) {
|
||||
await config.save();
|
||||
await runFormatter('npmextra', { silent: true });
|
||||
plugins.logger.log('success', `Removed registry: ${url}`);
|
||||
} else {
|
||||
plugins.logger.log('warn', `Registry not found: ${url}`);
|
||||
@@ -209,6 +212,7 @@ async function handleClear(): Promise<void> {
|
||||
if (confirmed) {
|
||||
config.clearRegistries();
|
||||
await config.save();
|
||||
await runFormatter('npmextra', { silent: true });
|
||||
plugins.logger.log('success', 'All registries cleared.');
|
||||
} else {
|
||||
plugins.logger.log('info', 'Operation cancelled.');
|
||||
@@ -248,6 +252,7 @@ async function handleAccessLevel(level?: string): Promise<void> {
|
||||
|
||||
config.setAccessLevel(level as 'public' | 'private');
|
||||
await config.save();
|
||||
await runFormatter('npmextra', { silent: true });
|
||||
plugins.logger.log('success', `Access level set to: ${level}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as plugins from './mod.plugins.js';
|
||||
import { Project } from '../classes.project.js';
|
||||
import { FormatContext } from './classes.formatcontext.js';
|
||||
import { FormatPlanner } from './classes.formatplanner.js';
|
||||
import { BaseFormatter } from './classes.baseformatter.js';
|
||||
import { logger, setVerboseMode } from '../gitzone.logging.js';
|
||||
|
||||
// Import wrapper classes for formatters
|
||||
@@ -195,3 +196,44 @@ export const handleCleanBackups = async (): Promise<void> => {
|
||||
'Backup cleaning has been disabled - backup system removed',
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a single formatter by name (for use by other modules)
|
||||
*/
|
||||
export const runFormatter = async (
|
||||
formatterName: string,
|
||||
options: { silent?: boolean } = {}
|
||||
): Promise<void> => {
|
||||
const project = await Project.fromCwd();
|
||||
const context = new FormatContext();
|
||||
|
||||
// Map formatter names to classes
|
||||
const formatterMap: Record<string, new (ctx: FormatContext, proj: Project) => BaseFormatter> = {
|
||||
cleanup: CleanupFormatter,
|
||||
npmextra: NpmextraFormatter,
|
||||
license: LicenseFormatter,
|
||||
packagejson: PackageJsonFormatter,
|
||||
templates: TemplatesFormatter,
|
||||
gitignore: GitignoreFormatter,
|
||||
tsconfig: TsconfigFormatter,
|
||||
prettier: PrettierFormatter,
|
||||
readme: ReadmeFormatter,
|
||||
copy: CopyFormatter,
|
||||
};
|
||||
|
||||
const FormatterClass = formatterMap[formatterName];
|
||||
if (!FormatterClass) {
|
||||
throw new Error(`Unknown formatter: ${formatterName}`);
|
||||
}
|
||||
|
||||
const formatter = new FormatterClass(context, project);
|
||||
const changes = await formatter.analyze();
|
||||
|
||||
for (const change of changes) {
|
||||
await formatter.applyChange(change);
|
||||
}
|
||||
|
||||
if (!options.silent) {
|
||||
logger.log('success', `Formatter '${formatterName}' completed`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,38 +7,85 @@ import * as paths from '../paths.js';
|
||||
import { logger } from '../gitzone.logging.js';
|
||||
|
||||
export let run = async () => {
|
||||
const done = plugins.smartpromise.defer();
|
||||
logger.log('warn', 'no action specified');
|
||||
console.log('');
|
||||
console.log('╭─────────────────────────────────────────────────────────────╮');
|
||||
console.log('│ gitzone - Development Workflow CLI │');
|
||||
console.log('╰─────────────────────────────────────────────────────────────╯');
|
||||
console.log('');
|
||||
|
||||
const dirEntries = await plugins.smartfs.directory(paths.templatesDir).list();
|
||||
const templates: string[] = [];
|
||||
for (const entry of dirEntries) {
|
||||
try {
|
||||
const stats = await plugins.smartfs
|
||||
.file(plugins.path.join(paths.templatesDir, entry.path))
|
||||
.stat();
|
||||
if (stats.isDirectory) {
|
||||
templates.push(entry.name);
|
||||
}
|
||||
} catch {
|
||||
// Skip entries that can't be accessed
|
||||
const interactInstance = new plugins.smartinteract.SmartInteract();
|
||||
const response = await interactInstance.askQuestion({
|
||||
type: 'list',
|
||||
name: 'action',
|
||||
message: 'What would you like to do?',
|
||||
default: 'commit',
|
||||
choices: [
|
||||
{ name: 'Commit changes (semantic versioning)', value: 'commit' },
|
||||
{ name: 'Format project files', value: 'format' },
|
||||
{ name: 'Configure release settings', value: 'config' },
|
||||
{ name: 'Create from template', value: 'template' },
|
||||
{ name: 'Manage dev services (MongoDB, S3)', value: 'services' },
|
||||
{ name: 'Open project assets', value: 'open' },
|
||||
{ name: 'Show help', value: 'help' },
|
||||
],
|
||||
});
|
||||
|
||||
const action = (response as any).value;
|
||||
|
||||
switch (action) {
|
||||
case 'commit': {
|
||||
const modCommit = await import('../mod_commit/index.js');
|
||||
await modCommit.run({ _: ['commit'] });
|
||||
break;
|
||||
}
|
||||
case 'format': {
|
||||
const modFormat = await import('../mod_format/index.js');
|
||||
await modFormat.run({ interactive: true });
|
||||
break;
|
||||
}
|
||||
case 'config': {
|
||||
const modConfig = await import('../mod_config/index.js');
|
||||
await modConfig.run({ _: ['config'] });
|
||||
break;
|
||||
}
|
||||
case 'template': {
|
||||
const modTemplate = await import('../mod_template/index.js');
|
||||
await modTemplate.run({ _: ['template'] });
|
||||
break;
|
||||
}
|
||||
case 'services': {
|
||||
const modServices = await import('../mod_services/index.js');
|
||||
await modServices.run({ _: ['services'] });
|
||||
break;
|
||||
}
|
||||
case 'open': {
|
||||
const modOpen = await import('../mod_open/index.js');
|
||||
await modOpen.run({ _: ['open'] });
|
||||
break;
|
||||
}
|
||||
case 'help':
|
||||
showHelp();
|
||||
break;
|
||||
}
|
||||
|
||||
let projects = `\n`;
|
||||
for (const template of templates) {
|
||||
projects += ` - ${template}\n`;
|
||||
}
|
||||
|
||||
logger.log(
|
||||
'info',
|
||||
`
|
||||
You can do one of the following things:
|
||||
* create a new project with 'gitzone template [template]'
|
||||
the following templates exist: ${projects}
|
||||
* format a project with 'gitzone format'
|
||||
`,
|
||||
);
|
||||
done.resolve();
|
||||
return done.promise;
|
||||
};
|
||||
|
||||
function showHelp(): void {
|
||||
console.log('');
|
||||
console.log('Usage: gitzone <command> [options]');
|
||||
console.log('');
|
||||
console.log('Commands:');
|
||||
console.log(' commit Create a semantic commit with versioning');
|
||||
console.log(' format Format and standardize project files');
|
||||
console.log(' config Manage release registry configuration');
|
||||
console.log(' template Create a new project from template');
|
||||
console.log(' services Manage dev services (MongoDB, S3/MinIO)');
|
||||
console.log(' open Open project assets (GitLab, npm, etc.)');
|
||||
console.log(' docker Docker-related operations');
|
||||
console.log(' deprecate Deprecate a package on npm');
|
||||
console.log(' meta Run meta commands');
|
||||
console.log(' start Start working on a project');
|
||||
console.log(' helpers Run helper utilities');
|
||||
console.log('');
|
||||
console.log('Run gitzone <command> --help for more information on a command.');
|
||||
console.log('');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user