feat(cli): Add optional build step to release flow and auto-format npmextra config when registries change
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
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.'
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -153,6 +154,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 +217,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++;
|
||||
|
||||
@@ -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`);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user