From fc6ca47cf35deb0c675df34b0dc7114de68803fa Mon Sep 17 00:00:00 2001 From: btopro Date: Thu, 12 Dec 2024 15:44:03 -0500 Subject: [PATCH] https://github.com/haxtheweb/issues/issues/2193 clean up from end of day --- src/create.js | 29 ++++++++++++------ src/lib/logging.js | 4 +-- src/lib/programs/site.js | 64 +++++++++++++++++++++++++++------------- 3 files changed, 65 insertions(+), 32 deletions(-) diff --git a/src/create.js b/src/create.js index acb2146..95874a3 100644 --- a/src/create.js +++ b/src/create.js @@ -60,13 +60,14 @@ async function main() { .option('--title-scrape ', 'CSS Selector for `title` in resource') .option('--content-scrape ', 'CSS Selector for `body` in resource') .option('--items-import ', 'import items from a file / site') + .option('--recipe ', 'path to recipe file') .version(await HAXCMS.getHAXCMSVersion()) .helpCommand(true); // default command which runs interactively program .command('start') - .description('Interactive program to pick options') + .description('Select which hax sub-program to run') .action(() => { commandRun = { command: 'start', @@ -81,8 +82,8 @@ async function main() { strActions+= `${action.value} - ${action.label}` + "\n\r"; }); let siteProg = program - .command('site'); - siteProg + .command('site') + .description('create or administer a HAXsite') .argument('[action]', 'Actions to perform on site include:' + "\n\r" + strActions) .action((action) => { commandRun = { @@ -95,19 +96,29 @@ async function main() { commandRun.options.skip = true; } }) - .option('--path ', 'path the project should be created in') + .option('--v', 'Verbose output') + .option('--debug', 'Output for developers') + .option('--format ', 'Output format; json (default), yaml') + .option('--path ', 'where to perform operation') + .option('--npm-client ', 'npm client to use (must be installed) npm, yarn, pnpm', 'npm') + .option('--y', 'yes to all questions') + .option('--skip', 'skip frills like animations') + .option('--quiet', 'remove console logging') + .option('--auto', 'yes to all questions, alias of y') + .option('--no-i', 'prevent interactions / sub-process, good for scripting') + .option('--to-file ', 'redirect command output to a file') + .option('--no-extras', 'skip all extra / automatic command processing') + .option('--root ', 'root location to execute the command from') + .option('--import-site ', 'URL of site to import') .option('--import-structure ', `import method to use:\n\rpressbooksToSite\n\relmslnToSite\n\rhaxcmsToSite\n\rnotionToSite\n\rgitbookToSite\n\revolutionToSite\n\rhtmlToSite\n\rdocxToSite`) .option('--name ', 'name of the site (when creating a new one)') .option('--domain ', 'published domain name') .option('--node-op ', 'node operation to perform') - .option('--no-i', 'prevent interactions / sub-process, good for scripting') .option('--title-scrape ', 'CSS Selector for `title` in resource') .option('--content-scrape ', 'CSS Selector for `body` in resource') - .option('--to-file ', 'redirect command output to a file') - .option('--no-extras', 'skip all extra / automatic command processing') .option('--item-import ', 'import items from a file / site') - .option('--root ', 'root location to execute the command from') + .option('--recipe ', 'path to recipe file') .version(await HAXCMS.getHAXCMSVersion()); let siteNodeOps = siteNodeOperations(); for (var i in siteNodeOps) { @@ -415,7 +426,7 @@ async function main() { if (results.type === "site" && !commandRun.options.theme) { // support having no theme but autoselecting if (commandRun.options.auto && commandRun.options.skip) { - commandRun.options.theme = themes[0]; + commandRun.options.theme = themes[0].value; } else { return p.select({ diff --git a/src/lib/logging.js b/src/lib/logging.js index 992c7af..ba23ca2 100644 --- a/src/lib/logging.js +++ b/src/lib/logging.js @@ -31,7 +31,7 @@ export const logger = winston.createLogger({ }); export function haxCliEnvOptions() { - return ['skip','npmClient','i','extras','root','path','org','author']; + return ['skip','npmClient','i','extras','root','path','org','author', 'y', 'auto']; } // wrapper so we can silence all log messages at the same time @@ -51,7 +51,7 @@ export function commandString(commandRun) { comStr+= ` --no-${camelToDash(key)}`; } else { - comStr+= ` --${camelToDash(key)}="${commandRun.options[key]}"`; + comStr+= ` --${camelToDash(key)} "${commandRun.options[key]}"`; } } } diff --git a/src/lib/programs/site.js b/src/lib/programs/site.js index 68eb01a..c4b9bc7 100644 --- a/src/lib/programs/site.js +++ b/src/lib/programs/site.js @@ -36,6 +36,9 @@ exec('surge --version', error => { }); const siteRecipeFile = 'create-cli.recipe'; +const siteLoggingName = 'cli'; +const logLevels = {}; +logLevels[siteLoggingName] = 0; // fake response class so we can capture the response from the headless route as opposed to print to console // this way we can handle as data or if use is requesting output format to change we can respond @@ -92,10 +95,8 @@ export async function siteCommandDetected(commandRun) { filename: recipeFileName }); const recipe = winston.createLogger({ - levels: { - hax: 0 - }, - level: 'hax', + levels: logLevels, + level: siteLoggingName, transports: [ recipeLogTransport ], @@ -327,7 +328,7 @@ export async function siteCommandDetected(commandRun) { if (!commandRun.options.quiet) { log(`${josImport.items.length} nodes imported`); } - recipe.log('hax', commandString(commandRun)); + recipe.log(siteLoggingName, commandString(commandRun)); } else if (!commandRun.options.quiet) { log('Must specify --item-import as path to valid item export file or URL', 'error'); @@ -509,7 +510,7 @@ export async function siteCommandDetected(commandRun) { createNodeBody.node.contents = locationContent; } let resp = await haxcmsNodejsCli.cliBridge('createNode', createNodeBody); - recipe.log('hax', commandString(commandRun)); + recipe.log(siteLoggingName, commandString(commandRun)); if (commandRun.options.v) { log(resp.res.data, 'silly'); } @@ -633,7 +634,7 @@ export async function siteCommandDetected(commandRun) { } // if we have content (meaning it's not blank) then try to write the page location if (locationContent && await page.writeLocation(locationContent)) { - recipe.log('hax', commandString(commandRun)); + recipe.log(siteLoggingName, commandString(commandRun)); if (!commandRun.options.quiet) { log(`node:edit success updated page content: "${page.id}`); } @@ -654,7 +655,7 @@ export async function siteCommandDetected(commandRun) { page[commandRun.options.nodeOp] = commandRun.options[commandRun.options.nodeOp]; } let resp = await activeHaxsite.updateNode(page); - recipe.log('hax', commandString(commandRun)); + recipe.log(siteLoggingName, commandString(commandRun)); if (commandRun.options.v) { log(resp, 'silly'); } @@ -693,7 +694,7 @@ export async function siteCommandDetected(commandRun) { console.warn(`node:delete failed "${commandRun.options.itemId} not found`); } else { - recipe.log('hax', commandString(commandRun)); + recipe.log(siteLoggingName, commandString(commandRun)); log(`"${commandRun.options.itemId}" deleted`); } } @@ -732,7 +733,7 @@ export async function siteCommandDetected(commandRun) { if (themes && commandRun.options.theme && themes[commandRun.options.theme]) { activeHaxsite.manifest.metadata.theme = themes[commandRun.options.theme]; activeHaxsite.manifest.save(false); - recipe.log('hax', commandString(commandRun)); + recipe.log(siteLoggingName, commandString(commandRun)); } } } @@ -868,9 +869,8 @@ export async function siteCommandDetected(commandRun) { case "recipe:read": // just print the recipe out if (fs.existsSync(path.join(process.cwd(), `${siteRecipeFile}`))) { - let recContents = await fs.readFileSync(path.join(process.cwd(), `${siteRecipeFile}`)); + let recContents = await fs.readFileSync(path.join(process.cwd(), `${siteRecipeFile}`),'utf8'); console.log(recContents); - log(recContents); } break; case "recipe:play": @@ -881,13 +881,37 @@ export async function siteCommandDetected(commandRun) { message: `Select recipe:`, defaultValue: process.cwd(), initialValue: process.cwd(), + validate: (val) => { + if (!val.endsWith('.recipe')) { + return 'HAX Recipe files must end in .recipe'; + } + } }); } if (fs.existsSync(commandRun.options.recipe)) { - let recContents = await fs.readFileSync(commandRun.options.recipe); + let recContents = await fs.readFileSync(commandRun.options.recipe,'utf8'); // split into commands - let commandList = recContents.split('hax: '); - console.log(commandList); + let commandList = recContents.replaceAll('cli: ', '').split("\n"); + // confirm each command or allow --y so that it auto applies + for (var i in commandList) { + // verify every command starts this way for safety + if (commandList[i].startsWith('hax site')) { + let confirmation; + if (commandRun.options.y) { + confirmation = true; + } + else { + confirmation = await p.confirm({ + message: `Do you want to run ${commandList[i]}? (This cannot be undone)`, + initialValue: true, + }); + } + // confirmed; let's run! + if (confirmation) { + await exec(`${commandList[i]} --y --no-i --auto --quiet`); + } + } + } } break; case "quit": @@ -1100,15 +1124,13 @@ export async function siteProcess(commandRun, project, port = '3000') { // au await hax.RoutesMap.post.createSite({body: siteRequest}, res); // path different for this one as it's on the fly produced const recipeFileName = path.join(project.path, '/', project.name, `${siteRecipeFile}`); - console.log(recipeFileName); const recipeLogTransport = new winston.transports.File({ filename: recipeFileName }); + const recipe = winston.createLogger({ - levels: { - hax: 0 - }, - level: 'hax', + levels: logLevels, + level: siteLoggingName, transports: [ recipeLogTransport ], @@ -1117,7 +1139,7 @@ export async function siteProcess(commandRun, project, port = '3000') { // au // matching the common object elsewhere tho different reference in this command since it creates from nothing // capture this if use input on the fly commandRun.options.theme = project.theme; - recipe.log('hax', commandString(commandRun)); + recipe.log(siteLoggingName, commandString(commandRun)); if (commandRun.options.v) { if (commandRun.options.format === 'yaml') { log(dump(res.data), 'silly');