Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b0941eea9 | |||
| 7348567a62 |
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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)
|
## 2025-12-14 - 2.3.0 - feat(config)
|
||||||
Add interactive menu and help to config command, handle unknown commands, and bump dependencies
|
Add interactive menu and help to config command, handle unknown commands, and bump dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -35,5 +35,13 @@
|
|||||||
},
|
},
|
||||||
"tsdoc": {
|
"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"
|
"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": {
|
||||||
|
"release": {
|
||||||
|
"registries": [
|
||||||
|
"https://verdaccio.lossless.digital"
|
||||||
|
],
|
||||||
|
"accessLevel": "public"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/cli",
|
"name": "@git.zone/cli",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "2.3.0",
|
"version": "2.4.0",
|
||||||
"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.",
|
"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",
|
"main": "dist_ts/index.ts",
|
||||||
"typings": "dist_ts/index.d.ts",
|
"typings": "dist_ts/index.d.ts",
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/cli',
|
name: '@git.zone/cli',
|
||||||
version: '2.3.0',
|
version: '2.4.0',
|
||||||
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.'
|
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) => {
|
export const run = async (argvArg: any) => {
|
||||||
// Check if release flag is set and validate registries early
|
// Check if release flag is set and validate registries early
|
||||||
const wantsRelease = !!(argvArg.r || argvArg.release);
|
const wantsRelease = !!(argvArg.r || argvArg.release);
|
||||||
|
const wantsBuild = !!(argvArg.b || argvArg.build);
|
||||||
let releaseConfig: ReleaseConfig | null = null;
|
let releaseConfig: ReleaseConfig | null = null;
|
||||||
|
|
||||||
if (wantsRelease) {
|
if (wantsRelease) {
|
||||||
@@ -153,6 +154,7 @@ export const run = async (argvArg: any) => {
|
|||||||
const willPush = answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true');
|
const willPush = answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true');
|
||||||
const willRelease = answerBucket.getAnswerFor('createRelease') && releaseConfig?.hasRegistries();
|
const willRelease = answerBucket.getAnswerFor('createRelease') && releaseConfig?.hasRegistries();
|
||||||
let totalSteps = 5; // Base steps: commitinfo, changelog, staging, commit, version
|
let totalSteps = 5; // Base steps: commitinfo, changelog, staging, commit, version
|
||||||
|
if (wantsBuild) totalSteps += 2; // build step + verification step
|
||||||
if (willPush) totalSteps++;
|
if (willPush) totalSteps++;
|
||||||
if (willRelease) totalSteps++;
|
if (willRelease) totalSteps++;
|
||||||
let currentStep = 0;
|
let currentStep = 0;
|
||||||
@@ -215,7 +217,34 @@ export const run = async (argvArg: any) => {
|
|||||||
const projectType = await helpers.detectProjectType();
|
const projectType = await helpers.detectProjectType();
|
||||||
const newVersion = await helpers.bumpProjectVersion(projectType, commitVersionType, currentStep, totalSteps);
|
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();
|
const currentBranch = await helpers.detectCurrentBranch();
|
||||||
if (willPush) {
|
if (willPush) {
|
||||||
currentStep++;
|
currentStep++;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.js';
|
||||||
import { ReleaseConfig } from './classes.releaseconfig.js';
|
import { ReleaseConfig } from './classes.releaseconfig.js';
|
||||||
|
import { runFormatter } from '../mod_format/index.js';
|
||||||
|
|
||||||
export { ReleaseConfig };
|
export { ReleaseConfig };
|
||||||
|
|
||||||
@@ -148,6 +149,7 @@ async function handleAdd(url?: string): Promise<void> {
|
|||||||
|
|
||||||
if (added) {
|
if (added) {
|
||||||
await config.save();
|
await config.save();
|
||||||
|
await runFormatter('npmextra', { silent: true });
|
||||||
plugins.logger.log('success', `Added registry: ${url}`);
|
plugins.logger.log('success', `Added registry: ${url}`);
|
||||||
} else {
|
} else {
|
||||||
plugins.logger.log('warn', `Registry already exists: ${url}`);
|
plugins.logger.log('warn', `Registry already exists: ${url}`);
|
||||||
@@ -183,6 +185,7 @@ async function handleRemove(url?: string): Promise<void> {
|
|||||||
|
|
||||||
if (removed) {
|
if (removed) {
|
||||||
await config.save();
|
await config.save();
|
||||||
|
await runFormatter('npmextra', { silent: true });
|
||||||
plugins.logger.log('success', `Removed registry: ${url}`);
|
plugins.logger.log('success', `Removed registry: ${url}`);
|
||||||
} else {
|
} else {
|
||||||
plugins.logger.log('warn', `Registry not found: ${url}`);
|
plugins.logger.log('warn', `Registry not found: ${url}`);
|
||||||
@@ -209,6 +212,7 @@ async function handleClear(): Promise<void> {
|
|||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
config.clearRegistries();
|
config.clearRegistries();
|
||||||
await config.save();
|
await config.save();
|
||||||
|
await runFormatter('npmextra', { silent: true });
|
||||||
plugins.logger.log('success', 'All registries cleared.');
|
plugins.logger.log('success', 'All registries cleared.');
|
||||||
} else {
|
} else {
|
||||||
plugins.logger.log('info', 'Operation cancelled.');
|
plugins.logger.log('info', 'Operation cancelled.');
|
||||||
@@ -248,6 +252,7 @@ async function handleAccessLevel(level?: string): Promise<void> {
|
|||||||
|
|
||||||
config.setAccessLevel(level as 'public' | 'private');
|
config.setAccessLevel(level as 'public' | 'private');
|
||||||
await config.save();
|
await config.save();
|
||||||
|
await runFormatter('npmextra', { silent: true });
|
||||||
plugins.logger.log('success', `Access level set to: ${level}`);
|
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 { Project } from '../classes.project.js';
|
||||||
import { FormatContext } from './classes.formatcontext.js';
|
import { FormatContext } from './classes.formatcontext.js';
|
||||||
import { FormatPlanner } from './classes.formatplanner.js';
|
import { FormatPlanner } from './classes.formatplanner.js';
|
||||||
|
import { BaseFormatter } from './classes.baseformatter.js';
|
||||||
import { logger, setVerboseMode } from '../gitzone.logging.js';
|
import { logger, setVerboseMode } from '../gitzone.logging.js';
|
||||||
|
|
||||||
// Import wrapper classes for formatters
|
// Import wrapper classes for formatters
|
||||||
@@ -195,3 +196,44 @@ export const handleCleanBackups = async (): Promise<void> => {
|
|||||||
'Backup cleaning has been disabled - backup system removed',
|
'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`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user