feat(cli): add machine-readable CLI help, recommendation, and configuration flows
This commit is contained in:
+490
-182
@@ -1,73 +1,116 @@
|
||||
// gitzone config - manage release registry configuration
|
||||
|
||||
import * as plugins from './mod.plugins.js';
|
||||
import { ReleaseConfig } from './classes.releaseconfig.js';
|
||||
import { CommitConfig } from './classes.commitconfig.js';
|
||||
import { runFormatter, type ICheckResult } from '../mod_format/index.js';
|
||||
import * as plugins from "./mod.plugins.js";
|
||||
import { ReleaseConfig } from "./classes.releaseconfig.js";
|
||||
import { CommitConfig } from "./classes.commitconfig.js";
|
||||
import { runFormatter, type ICheckResult } from "../mod_format/index.js";
|
||||
import type { ICliMode } from "../helpers.climode.js";
|
||||
import { getCliMode, printJson } from "../helpers.climode.js";
|
||||
import {
|
||||
getCliConfigValueFromData,
|
||||
readSmartconfigFile,
|
||||
setCliConfigValueInData,
|
||||
unsetCliConfigValueInData,
|
||||
writeSmartconfigFile,
|
||||
} from "../helpers.smartconfig.js";
|
||||
|
||||
export { ReleaseConfig, CommitConfig };
|
||||
|
||||
const defaultCliMode: ICliMode = {
|
||||
output: "human",
|
||||
interactive: true,
|
||||
json: false,
|
||||
plain: false,
|
||||
quiet: false,
|
||||
yes: false,
|
||||
help: false,
|
||||
agent: false,
|
||||
checkUpdates: true,
|
||||
isTty: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Format .smartconfig.json with diff preview
|
||||
* Shows diff first, asks for confirmation, then applies
|
||||
*/
|
||||
async function formatSmartconfigWithDiff(): Promise<void> {
|
||||
async function formatSmartconfigWithDiff(mode: ICliMode): Promise<void> {
|
||||
if (!mode.interactive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for diffs first
|
||||
const checkResult = await runFormatter('smartconfig', {
|
||||
const checkResult = (await runFormatter("smartconfig", {
|
||||
checkOnly: true,
|
||||
showDiff: true,
|
||||
}) as ICheckResult | void;
|
||||
})) as ICheckResult | void;
|
||||
|
||||
if (checkResult && checkResult.hasDiff) {
|
||||
const shouldApply = await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
||||
'Apply formatting changes to .smartconfig.json?',
|
||||
true
|
||||
);
|
||||
const shouldApply =
|
||||
await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
||||
"Apply formatting changes to .smartconfig.json?",
|
||||
true,
|
||||
);
|
||||
if (shouldApply) {
|
||||
await runFormatter('smartconfig', { silent: true });
|
||||
await runFormatter("smartconfig", { silent: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const run = async (argvArg: any) => {
|
||||
const mode = await getCliMode(argvArg);
|
||||
const command = argvArg._?.[1];
|
||||
const value = argvArg._?.[2];
|
||||
|
||||
if (mode.help || command === "help") {
|
||||
showHelp(mode);
|
||||
return;
|
||||
}
|
||||
|
||||
// If no command provided, show interactive menu
|
||||
if (!command) {
|
||||
if (!mode.interactive) {
|
||||
showHelp(mode);
|
||||
return;
|
||||
}
|
||||
await handleInteractiveMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case 'show':
|
||||
await handleShow();
|
||||
case "show":
|
||||
await handleShow(mode);
|
||||
break;
|
||||
case 'add':
|
||||
await handleAdd(value);
|
||||
case "add":
|
||||
await handleAdd(value, mode);
|
||||
break;
|
||||
case 'remove':
|
||||
await handleRemove(value);
|
||||
case "remove":
|
||||
await handleRemove(value, mode);
|
||||
break;
|
||||
case 'clear':
|
||||
await handleClear();
|
||||
case "clear":
|
||||
await handleClear(mode);
|
||||
break;
|
||||
case 'access':
|
||||
case 'accessLevel':
|
||||
await handleAccessLevel(value);
|
||||
case "access":
|
||||
case "accessLevel":
|
||||
await handleAccessLevel(value, mode);
|
||||
break;
|
||||
case 'commit':
|
||||
await handleCommit(argvArg._?.[2], argvArg._?.[3]);
|
||||
case "commit":
|
||||
await handleCommit(argvArg._?.[2], argvArg._?.[3], mode);
|
||||
break;
|
||||
case 'services':
|
||||
await handleServices();
|
||||
case "services":
|
||||
await handleServices(mode);
|
||||
break;
|
||||
case 'help':
|
||||
showHelp();
|
||||
case "get":
|
||||
await handleGet(value, mode);
|
||||
break;
|
||||
case "set":
|
||||
await handleSet(value, argvArg._?.[3], mode);
|
||||
break;
|
||||
case "unset":
|
||||
await handleUnset(value, mode);
|
||||
break;
|
||||
default:
|
||||
plugins.logger.log('error', `Unknown command: ${command}`);
|
||||
showHelp();
|
||||
plugins.logger.log("error", `Unknown command: ${command}`);
|
||||
showHelp(mode);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,55 +118,61 @@ export const run = async (argvArg: any) => {
|
||||
* Interactive menu for config command
|
||||
*/
|
||||
async function handleInteractiveMenu(): Promise<void> {
|
||||
console.log('');
|
||||
console.log('╭─────────────────────────────────────────────────────────────╮');
|
||||
console.log('│ gitzone config - Project Configuration │');
|
||||
console.log('╰─────────────────────────────────────────────────────────────╯');
|
||||
console.log('');
|
||||
console.log("");
|
||||
console.log(
|
||||
"╭─────────────────────────────────────────────────────────────╮",
|
||||
);
|
||||
console.log(
|
||||
"│ gitzone config - Project Configuration │",
|
||||
);
|
||||
console.log(
|
||||
"╰─────────────────────────────────────────────────────────────╯",
|
||||
);
|
||||
console.log("");
|
||||
|
||||
const interactInstance = new plugins.smartinteract.SmartInteract();
|
||||
const response = await interactInstance.askQuestion({
|
||||
type: 'list',
|
||||
name: 'action',
|
||||
message: 'What would you like to do?',
|
||||
default: 'show',
|
||||
type: "list",
|
||||
name: "action",
|
||||
message: "What would you like to do?",
|
||||
default: "show",
|
||||
choices: [
|
||||
{ name: 'Show current configuration', value: 'show' },
|
||||
{ name: 'Add a registry', value: 'add' },
|
||||
{ name: 'Remove a registry', value: 'remove' },
|
||||
{ name: 'Clear all registries', value: 'clear' },
|
||||
{ name: 'Set access level (public/private)', value: 'access' },
|
||||
{ name: 'Configure commit options', value: 'commit' },
|
||||
{ name: 'Configure services', value: 'services' },
|
||||
{ name: 'Show help', value: 'help' },
|
||||
{ name: "Show current configuration", value: "show" },
|
||||
{ name: "Add a registry", value: "add" },
|
||||
{ name: "Remove a registry", value: "remove" },
|
||||
{ name: "Clear all registries", value: "clear" },
|
||||
{ name: "Set access level (public/private)", value: "access" },
|
||||
{ name: "Configure commit options", value: "commit" },
|
||||
{ name: "Configure services", value: "services" },
|
||||
{ name: "Show help", value: "help" },
|
||||
],
|
||||
});
|
||||
|
||||
const action = (response as any).value;
|
||||
|
||||
switch (action) {
|
||||
case 'show':
|
||||
await handleShow();
|
||||
case "show":
|
||||
await handleShow(defaultCliMode);
|
||||
break;
|
||||
case 'add':
|
||||
await handleAdd();
|
||||
case "add":
|
||||
await handleAdd(undefined, defaultCliMode);
|
||||
break;
|
||||
case 'remove':
|
||||
await handleRemove();
|
||||
case "remove":
|
||||
await handleRemove(undefined, defaultCliMode);
|
||||
break;
|
||||
case 'clear':
|
||||
await handleClear();
|
||||
case "clear":
|
||||
await handleClear(defaultCliMode);
|
||||
break;
|
||||
case 'access':
|
||||
await handleAccessLevel();
|
||||
case "access":
|
||||
await handleAccessLevel(undefined, defaultCliMode);
|
||||
break;
|
||||
case 'commit':
|
||||
await handleCommit();
|
||||
case "commit":
|
||||
await handleCommit(undefined, undefined, defaultCliMode);
|
||||
break;
|
||||
case 'services':
|
||||
await handleServices();
|
||||
case "services":
|
||||
await handleServices(defaultCliMode);
|
||||
break;
|
||||
case 'help':
|
||||
case "help":
|
||||
showHelp();
|
||||
break;
|
||||
}
|
||||
@@ -132,50 +181,69 @@ async function handleInteractiveMenu(): Promise<void> {
|
||||
/**
|
||||
* Show current registry configuration
|
||||
*/
|
||||
async function handleShow(): Promise<void> {
|
||||
async function handleShow(mode: ICliMode): Promise<void> {
|
||||
if (mode.json) {
|
||||
const smartconfigData = await readSmartconfigFile();
|
||||
printJson(getCliConfigValueFromData(smartconfigData, ""));
|
||||
return;
|
||||
}
|
||||
|
||||
const config = await ReleaseConfig.fromCwd();
|
||||
const registries = config.getRegistries();
|
||||
const accessLevel = config.getAccessLevel();
|
||||
|
||||
console.log('');
|
||||
console.log('╭─────────────────────────────────────────────────────────────╮');
|
||||
console.log('│ Release Configuration │');
|
||||
console.log('╰─────────────────────────────────────────────────────────────╯');
|
||||
console.log('');
|
||||
console.log("");
|
||||
console.log(
|
||||
"╭─────────────────────────────────────────────────────────────╮",
|
||||
);
|
||||
console.log(
|
||||
"│ Release Configuration │",
|
||||
);
|
||||
console.log(
|
||||
"╰─────────────────────────────────────────────────────────────╯",
|
||||
);
|
||||
console.log("");
|
||||
|
||||
// Show access level
|
||||
plugins.logger.log('info', `Access Level: ${accessLevel}`);
|
||||
console.log('');
|
||||
plugins.logger.log("info", `Access Level: ${accessLevel}`);
|
||||
console.log("");
|
||||
|
||||
if (registries.length === 0) {
|
||||
plugins.logger.log('info', 'No release registries configured.');
|
||||
console.log('');
|
||||
console.log(' Run `gitzone config add <registry-url>` to add one.');
|
||||
console.log('');
|
||||
plugins.logger.log("info", "No release registries configured.");
|
||||
console.log("");
|
||||
console.log(" Run `gitzone config add <registry-url>` to add one.");
|
||||
console.log("");
|
||||
} else {
|
||||
plugins.logger.log('info', `Configured registries (${registries.length}):`);
|
||||
console.log('');
|
||||
plugins.logger.log("info", `Configured registries (${registries.length}):`);
|
||||
console.log("");
|
||||
registries.forEach((url, index) => {
|
||||
console.log(` ${index + 1}. ${url}`);
|
||||
});
|
||||
console.log('');
|
||||
console.log("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a registry URL
|
||||
*/
|
||||
async function handleAdd(url?: string): Promise<void> {
|
||||
async function handleAdd(
|
||||
url: string | undefined,
|
||||
mode: ICliMode,
|
||||
): Promise<void> {
|
||||
if (!url) {
|
||||
if (!mode.interactive) {
|
||||
throw new Error("Registry URL is required in non-interactive mode");
|
||||
}
|
||||
|
||||
// Interactive mode
|
||||
const interactInstance = new plugins.smartinteract.SmartInteract();
|
||||
const response = await interactInstance.askQuestion({
|
||||
type: 'input',
|
||||
name: 'registryUrl',
|
||||
message: 'Enter registry URL:',
|
||||
default: 'https://registry.npmjs.org',
|
||||
type: "input",
|
||||
name: "registryUrl",
|
||||
message: "Enter registry URL:",
|
||||
default: "https://registry.npmjs.org",
|
||||
validate: (input: string) => {
|
||||
return !!(input && input.trim() !== '');
|
||||
return !!(input && input.trim() !== "");
|
||||
},
|
||||
});
|
||||
url = (response as any).value;
|
||||
@@ -186,32 +254,48 @@ async function handleAdd(url?: string): Promise<void> {
|
||||
|
||||
if (added) {
|
||||
await config.save();
|
||||
plugins.logger.log('success', `Added registry: ${url}`);
|
||||
await formatSmartconfigWithDiff();
|
||||
if (mode.json) {
|
||||
printJson({
|
||||
ok: true,
|
||||
action: "add",
|
||||
registry: url,
|
||||
registries: config.getRegistries(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
plugins.logger.log("success", `Added registry: ${url}`);
|
||||
await formatSmartconfigWithDiff(mode);
|
||||
} else {
|
||||
plugins.logger.log('warn', `Registry already exists: ${url}`);
|
||||
plugins.logger.log("warn", `Registry already exists: ${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a registry URL
|
||||
*/
|
||||
async function handleRemove(url?: string): Promise<void> {
|
||||
async function handleRemove(
|
||||
url: string | undefined,
|
||||
mode: ICliMode,
|
||||
): Promise<void> {
|
||||
const config = await ReleaseConfig.fromCwd();
|
||||
const registries = config.getRegistries();
|
||||
|
||||
if (registries.length === 0) {
|
||||
plugins.logger.log('warn', 'No registries configured to remove.');
|
||||
plugins.logger.log("warn", "No registries configured to remove.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
if (!mode.interactive) {
|
||||
throw new Error("Registry URL is required in non-interactive mode");
|
||||
}
|
||||
|
||||
// Interactive mode - show list to select from
|
||||
const interactInstance = new plugins.smartinteract.SmartInteract();
|
||||
const response = await interactInstance.askQuestion({
|
||||
type: 'list',
|
||||
name: 'registryUrl',
|
||||
message: 'Select registry to remove:',
|
||||
type: "list",
|
||||
name: "registryUrl",
|
||||
message: "Select registry to remove:",
|
||||
choices: registries,
|
||||
default: registries[0],
|
||||
});
|
||||
@@ -222,99 +306,135 @@ async function handleRemove(url?: string): Promise<void> {
|
||||
|
||||
if (removed) {
|
||||
await config.save();
|
||||
plugins.logger.log('success', `Removed registry: ${url}`);
|
||||
await formatSmartconfigWithDiff();
|
||||
if (mode.json) {
|
||||
printJson({
|
||||
ok: true,
|
||||
action: "remove",
|
||||
registry: url,
|
||||
registries: config.getRegistries(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
plugins.logger.log("success", `Removed registry: ${url}`);
|
||||
await formatSmartconfigWithDiff(mode);
|
||||
} else {
|
||||
plugins.logger.log('warn', `Registry not found: ${url}`);
|
||||
plugins.logger.log("warn", `Registry not found: ${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all registries
|
||||
*/
|
||||
async function handleClear(): Promise<void> {
|
||||
async function handleClear(mode: ICliMode): Promise<void> {
|
||||
const config = await ReleaseConfig.fromCwd();
|
||||
|
||||
if (!config.hasRegistries()) {
|
||||
plugins.logger.log('info', 'No registries to clear.');
|
||||
plugins.logger.log("info", "No registries to clear.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm before clearing
|
||||
const confirmed = await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
||||
'Clear all configured registries?',
|
||||
false
|
||||
);
|
||||
const confirmed = mode.interactive
|
||||
? await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
||||
"Clear all configured registries?",
|
||||
false,
|
||||
)
|
||||
: true;
|
||||
|
||||
if (confirmed) {
|
||||
config.clearRegistries();
|
||||
await config.save();
|
||||
plugins.logger.log('success', 'All registries cleared.');
|
||||
await formatSmartconfigWithDiff();
|
||||
if (mode.json) {
|
||||
printJson({ ok: true, action: "clear", registries: [] });
|
||||
return;
|
||||
}
|
||||
plugins.logger.log("success", "All registries cleared.");
|
||||
await formatSmartconfigWithDiff(mode);
|
||||
} else {
|
||||
plugins.logger.log('info', 'Operation cancelled.');
|
||||
plugins.logger.log("info", "Operation cancelled.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or toggle access level
|
||||
*/
|
||||
async function handleAccessLevel(level?: string): Promise<void> {
|
||||
async function handleAccessLevel(
|
||||
level: string | undefined,
|
||||
mode: ICliMode,
|
||||
): Promise<void> {
|
||||
const config = await ReleaseConfig.fromCwd();
|
||||
const currentLevel = config.getAccessLevel();
|
||||
|
||||
if (!level) {
|
||||
if (!mode.interactive) {
|
||||
throw new Error("Access level is required in non-interactive mode");
|
||||
}
|
||||
|
||||
// Interactive mode - toggle or ask
|
||||
const interactInstance = new plugins.smartinteract.SmartInteract();
|
||||
const response = await interactInstance.askQuestion({
|
||||
type: 'list',
|
||||
name: 'accessLevel',
|
||||
message: 'Select npm access level for publishing:',
|
||||
choices: ['public', 'private'],
|
||||
type: "list",
|
||||
name: "accessLevel",
|
||||
message: "Select npm access level for publishing:",
|
||||
choices: ["public", "private"],
|
||||
default: currentLevel,
|
||||
});
|
||||
level = (response as any).value;
|
||||
}
|
||||
|
||||
// Validate the level
|
||||
if (level !== 'public' && level !== 'private') {
|
||||
plugins.logger.log('error', `Invalid access level: ${level}. Must be 'public' or 'private'.`);
|
||||
if (level !== "public" && level !== "private") {
|
||||
plugins.logger.log(
|
||||
"error",
|
||||
`Invalid access level: ${level}. Must be 'public' or 'private'.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (level === currentLevel) {
|
||||
plugins.logger.log('info', `Access level is already set to: ${level}`);
|
||||
plugins.logger.log("info", `Access level is already set to: ${level}`);
|
||||
return;
|
||||
}
|
||||
|
||||
config.setAccessLevel(level as 'public' | 'private');
|
||||
config.setAccessLevel(level as "public" | "private");
|
||||
await config.save();
|
||||
plugins.logger.log('success', `Access level set to: ${level}`);
|
||||
await formatSmartconfigWithDiff();
|
||||
if (mode.json) {
|
||||
printJson({ ok: true, action: "access", accessLevel: level });
|
||||
return;
|
||||
}
|
||||
plugins.logger.log("success", `Access level set to: ${level}`);
|
||||
await formatSmartconfigWithDiff(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle commit configuration
|
||||
*/
|
||||
async function handleCommit(setting?: string, value?: string): Promise<void> {
|
||||
async function handleCommit(
|
||||
setting: string | undefined,
|
||||
value: string | undefined,
|
||||
mode: ICliMode,
|
||||
): Promise<void> {
|
||||
const config = await CommitConfig.fromCwd();
|
||||
|
||||
// No setting = interactive mode
|
||||
if (!setting) {
|
||||
if (!mode.interactive) {
|
||||
throw new Error("Commit setting is required in non-interactive mode");
|
||||
}
|
||||
await handleCommitInteractive(config);
|
||||
return;
|
||||
}
|
||||
|
||||
// Direct setting
|
||||
switch (setting) {
|
||||
case 'alwaysTest':
|
||||
await handleCommitSetting(config, 'alwaysTest', value);
|
||||
case "alwaysTest":
|
||||
await handleCommitSetting(config, "alwaysTest", value, mode);
|
||||
break;
|
||||
case 'alwaysBuild':
|
||||
await handleCommitSetting(config, 'alwaysBuild', value);
|
||||
case "alwaysBuild":
|
||||
await handleCommitSetting(config, "alwaysBuild", value, mode);
|
||||
break;
|
||||
default:
|
||||
plugins.logger.log('error', `Unknown commit setting: ${setting}`);
|
||||
plugins.logger.log("error", `Unknown commit setting: ${setting}`);
|
||||
showCommitHelp();
|
||||
}
|
||||
}
|
||||
@@ -323,109 +443,297 @@ async function handleCommit(setting?: string, value?: string): Promise<void> {
|
||||
* Interactive commit configuration
|
||||
*/
|
||||
async function handleCommitInteractive(config: CommitConfig): Promise<void> {
|
||||
console.log('');
|
||||
console.log('╭─────────────────────────────────────────────────────────────╮');
|
||||
console.log('│ Commit Configuration │');
|
||||
console.log('╰─────────────────────────────────────────────────────────────╯');
|
||||
console.log('');
|
||||
console.log("");
|
||||
console.log(
|
||||
"╭─────────────────────────────────────────────────────────────╮",
|
||||
);
|
||||
console.log(
|
||||
"│ Commit Configuration │",
|
||||
);
|
||||
console.log(
|
||||
"╰─────────────────────────────────────────────────────────────╯",
|
||||
);
|
||||
console.log("");
|
||||
|
||||
const interactInstance = new plugins.smartinteract.SmartInteract();
|
||||
const response = await interactInstance.askQuestion({
|
||||
type: 'checkbox',
|
||||
name: 'commitOptions',
|
||||
message: 'Select commit options to enable:',
|
||||
type: "checkbox",
|
||||
name: "commitOptions",
|
||||
message: "Select commit options to enable:",
|
||||
choices: [
|
||||
{ name: 'Always run tests before commit (-t)', value: 'alwaysTest' },
|
||||
{ name: 'Always build after commit (-b)', value: 'alwaysBuild' },
|
||||
{ name: "Always run tests before commit (-t)", value: "alwaysTest" },
|
||||
{ name: "Always build after commit (-b)", value: "alwaysBuild" },
|
||||
],
|
||||
default: [
|
||||
...(config.getAlwaysTest() ? ['alwaysTest'] : []),
|
||||
...(config.getAlwaysBuild() ? ['alwaysBuild'] : []),
|
||||
...(config.getAlwaysTest() ? ["alwaysTest"] : []),
|
||||
...(config.getAlwaysBuild() ? ["alwaysBuild"] : []),
|
||||
],
|
||||
});
|
||||
|
||||
const selected = (response as any).value || [];
|
||||
config.setAlwaysTest(selected.includes('alwaysTest'));
|
||||
config.setAlwaysBuild(selected.includes('alwaysBuild'));
|
||||
config.setAlwaysTest(selected.includes("alwaysTest"));
|
||||
config.setAlwaysBuild(selected.includes("alwaysBuild"));
|
||||
await config.save();
|
||||
|
||||
plugins.logger.log('success', 'Commit configuration updated');
|
||||
await formatSmartconfigWithDiff();
|
||||
plugins.logger.log("success", "Commit configuration updated");
|
||||
await formatSmartconfigWithDiff(defaultCliMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific commit setting
|
||||
*/
|
||||
async function handleCommitSetting(config: CommitConfig, setting: string, value?: string): Promise<void> {
|
||||
async function handleCommitSetting(
|
||||
config: CommitConfig,
|
||||
setting: string,
|
||||
value: string | undefined,
|
||||
mode: ICliMode,
|
||||
): Promise<void> {
|
||||
// Parse boolean value
|
||||
const boolValue = value === 'true' || value === '1' || value === 'on';
|
||||
const boolValue = value === "true" || value === "1" || value === "on";
|
||||
|
||||
if (setting === 'alwaysTest') {
|
||||
if (setting === "alwaysTest") {
|
||||
config.setAlwaysTest(boolValue);
|
||||
} else if (setting === 'alwaysBuild') {
|
||||
} else if (setting === "alwaysBuild") {
|
||||
config.setAlwaysBuild(boolValue);
|
||||
}
|
||||
|
||||
await config.save();
|
||||
plugins.logger.log('success', `Set ${setting} to ${boolValue}`);
|
||||
await formatSmartconfigWithDiff();
|
||||
if (mode.json) {
|
||||
printJson({ ok: true, action: "commit", setting, value: boolValue });
|
||||
return;
|
||||
}
|
||||
plugins.logger.log("success", `Set ${setting} to ${boolValue}`);
|
||||
await formatSmartconfigWithDiff(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show help for commit subcommand
|
||||
*/
|
||||
function showCommitHelp(): void {
|
||||
console.log('');
|
||||
console.log('Usage: gitzone config commit [setting] [value]');
|
||||
console.log('');
|
||||
console.log('Settings:');
|
||||
console.log(' alwaysTest [true|false] Always run tests before commit');
|
||||
console.log(' alwaysBuild [true|false] Always build after commit');
|
||||
console.log('');
|
||||
console.log('Examples:');
|
||||
console.log(' gitzone config commit # Interactive mode');
|
||||
console.log(' gitzone config commit alwaysTest true');
|
||||
console.log(' gitzone config commit alwaysBuild false');
|
||||
console.log('');
|
||||
console.log("");
|
||||
console.log("Usage: gitzone config commit [setting] [value]");
|
||||
console.log("");
|
||||
console.log("Settings:");
|
||||
console.log(" alwaysTest [true|false] Always run tests before commit");
|
||||
console.log(" alwaysBuild [true|false] Always build after commit");
|
||||
console.log("");
|
||||
console.log("Examples:");
|
||||
console.log(" gitzone config commit # Interactive mode");
|
||||
console.log(" gitzone config commit alwaysTest true");
|
||||
console.log(" gitzone config commit alwaysBuild false");
|
||||
console.log("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle services configuration
|
||||
*/
|
||||
async function handleServices(): Promise<void> {
|
||||
async function handleServices(mode: ICliMode): Promise<void> {
|
||||
if (!mode.interactive) {
|
||||
throw new Error(
|
||||
"Use `gitzone services config --json` or `gitzone services set ...` in non-interactive mode",
|
||||
);
|
||||
}
|
||||
|
||||
// Import and use ServiceManager's configureServices
|
||||
const { ServiceManager } = await import('../mod_services/classes.servicemanager.js');
|
||||
const { ServiceManager } =
|
||||
await import("../mod_services/classes.servicemanager.js");
|
||||
const serviceManager = new ServiceManager();
|
||||
await serviceManager.init();
|
||||
await serviceManager.configureServices();
|
||||
}
|
||||
|
||||
async function handleGet(
|
||||
configPath: string | undefined,
|
||||
mode: ICliMode,
|
||||
): Promise<void> {
|
||||
if (!configPath) {
|
||||
throw new Error("Configuration path is required");
|
||||
}
|
||||
|
||||
const smartconfigData = await readSmartconfigFile();
|
||||
const value = getCliConfigValueFromData(smartconfigData, configPath);
|
||||
|
||||
if (mode.json) {
|
||||
printJson({ path: configPath, value, exists: value !== undefined });
|
||||
return;
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
plugins.logger.log("warn", `No value set for ${configPath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
console.log(value);
|
||||
return;
|
||||
}
|
||||
|
||||
printJson(value);
|
||||
}
|
||||
|
||||
async function handleSet(
|
||||
configPath: string | undefined,
|
||||
rawValue: string | undefined,
|
||||
mode: ICliMode,
|
||||
): Promise<void> {
|
||||
if (!configPath) {
|
||||
throw new Error("Configuration path is required");
|
||||
}
|
||||
if (rawValue === undefined) {
|
||||
throw new Error("Configuration value is required");
|
||||
}
|
||||
|
||||
const smartconfigData = await readSmartconfigFile();
|
||||
const parsedValue = parseConfigValue(rawValue);
|
||||
setCliConfigValueInData(smartconfigData, configPath, parsedValue);
|
||||
await writeSmartconfigFile(smartconfigData);
|
||||
|
||||
if (mode.json) {
|
||||
printJson({
|
||||
ok: true,
|
||||
action: "set",
|
||||
path: configPath,
|
||||
value: parsedValue,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
plugins.logger.log("success", `Set ${configPath}`);
|
||||
}
|
||||
|
||||
async function handleUnset(
|
||||
configPath: string | undefined,
|
||||
mode: ICliMode,
|
||||
): Promise<void> {
|
||||
if (!configPath) {
|
||||
throw new Error("Configuration path is required");
|
||||
}
|
||||
|
||||
const smartconfigData = await readSmartconfigFile();
|
||||
const removed = unsetCliConfigValueInData(smartconfigData, configPath);
|
||||
if (!removed) {
|
||||
if (mode.json) {
|
||||
printJson({
|
||||
ok: false,
|
||||
action: "unset",
|
||||
path: configPath,
|
||||
removed: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
plugins.logger.log("warn", `No value set for ${configPath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
await writeSmartconfigFile(smartconfigData);
|
||||
|
||||
if (mode.json) {
|
||||
printJson({ ok: true, action: "unset", path: configPath, removed: true });
|
||||
return;
|
||||
}
|
||||
|
||||
plugins.logger.log("success", `Unset ${configPath}`);
|
||||
}
|
||||
|
||||
function parseConfigValue(rawValue: string): any {
|
||||
const trimmedValue = rawValue.trim();
|
||||
if (trimmedValue === "true") {
|
||||
return true;
|
||||
}
|
||||
if (trimmedValue === "false") {
|
||||
return false;
|
||||
}
|
||||
if (trimmedValue === "null") {
|
||||
return null;
|
||||
}
|
||||
if (/^-?\d+(\.\d+)?$/.test(trimmedValue)) {
|
||||
return Number(trimmedValue);
|
||||
}
|
||||
if (
|
||||
(trimmedValue.startsWith("{") && trimmedValue.endsWith("}")) ||
|
||||
(trimmedValue.startsWith("[") && trimmedValue.endsWith("]")) ||
|
||||
(trimmedValue.startsWith('"') && trimmedValue.endsWith('"'))
|
||||
) {
|
||||
return JSON.parse(trimmedValue);
|
||||
}
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show help for config command
|
||||
*/
|
||||
function showHelp(): void {
|
||||
console.log('');
|
||||
console.log('Usage: gitzone config <command> [options]');
|
||||
console.log('');
|
||||
console.log('Commands:');
|
||||
console.log(' show Display current release configuration');
|
||||
console.log(' add [url] Add a registry URL');
|
||||
console.log(' remove [url] Remove a registry URL');
|
||||
console.log(' clear Clear all registries');
|
||||
console.log(' access [public|private] Set npm access level for publishing');
|
||||
console.log(' commit [setting] [value] Configure commit options');
|
||||
console.log(' services Configure which services are enabled');
|
||||
console.log('');
|
||||
console.log('Examples:');
|
||||
console.log(' gitzone config show');
|
||||
console.log(' gitzone config add https://registry.npmjs.org');
|
||||
console.log(' gitzone config add https://verdaccio.example.com');
|
||||
console.log(' gitzone config remove https://registry.npmjs.org');
|
||||
console.log(' gitzone config clear');
|
||||
console.log(' gitzone config access public');
|
||||
console.log(' gitzone config access private');
|
||||
console.log(' gitzone config commit # Interactive');
|
||||
console.log(' gitzone config commit alwaysTest true');
|
||||
console.log(' gitzone config services # Interactive');
|
||||
console.log('');
|
||||
export function showHelp(mode?: ICliMode): void {
|
||||
if (mode?.json) {
|
||||
printJson({
|
||||
command: "config",
|
||||
usage: "gitzone config <command> [options]",
|
||||
commands: [
|
||||
{
|
||||
name: "show",
|
||||
description: "Display current @git.zone/cli configuration",
|
||||
},
|
||||
{ name: "get <path>", description: "Read a single config value" },
|
||||
{ name: "set <path> <value>", description: "Write a config value" },
|
||||
{ name: "unset <path>", description: "Delete a config value" },
|
||||
{ name: "add [url]", description: "Add a release registry" },
|
||||
{ name: "remove [url]", description: "Remove a release registry" },
|
||||
{ name: "clear", description: "Clear all release registries" },
|
||||
{
|
||||
name: "access [public|private]",
|
||||
description: "Set npm publish access level",
|
||||
},
|
||||
{
|
||||
name: "commit <setting> <value>",
|
||||
description: "Set commit defaults",
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
"gitzone config show --json",
|
||||
"gitzone config get release.accessLevel",
|
||||
"gitzone config set cli.interactive false",
|
||||
"gitzone config set cli.output json",
|
||||
],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("");
|
||||
console.log("Usage: gitzone config <command> [options]");
|
||||
console.log("");
|
||||
console.log("Commands:");
|
||||
console.log(
|
||||
" show Display current @git.zone/cli configuration",
|
||||
);
|
||||
console.log(" get <path> Read a single config value");
|
||||
console.log(" set <path> <value> Write a config value");
|
||||
console.log(" unset <path> Delete a config value");
|
||||
console.log(" add [url] Add a registry URL");
|
||||
console.log(" remove [url] Remove a registry URL");
|
||||
console.log(" clear Clear all registries");
|
||||
console.log(
|
||||
" access [public|private] Set npm access level for publishing",
|
||||
);
|
||||
console.log(" commit [setting] [value] Configure commit options");
|
||||
console.log(
|
||||
" services Configure which services are enabled",
|
||||
);
|
||||
console.log("");
|
||||
console.log("Examples:");
|
||||
console.log(" gitzone config show");
|
||||
console.log(" gitzone config show --json");
|
||||
console.log(" gitzone config get release.accessLevel");
|
||||
console.log(" gitzone config set cli.interactive false");
|
||||
console.log(" gitzone config set cli.output json");
|
||||
console.log(" gitzone config unset cli.output");
|
||||
console.log(" gitzone config add https://registry.npmjs.org");
|
||||
console.log(" gitzone config add https://verdaccio.example.com");
|
||||
console.log(" gitzone config remove https://registry.npmjs.org");
|
||||
console.log(" gitzone config clear");
|
||||
console.log(" gitzone config access public");
|
||||
console.log(" gitzone config access private");
|
||||
console.log(" gitzone config commit # Interactive");
|
||||
console.log(" gitzone config commit alwaysTest true");
|
||||
console.log(" gitzone config services # Interactive");
|
||||
console.log("");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user