Skip to content

Commit

Permalink
Merge pull request #18869 from storybookjs/yann/sb-635-command-silent…
Browse files Browse the repository at this point in the history
…ly-fails-if-sandbox-doesnt

improve sandbox command error handling and debugging
  • Loading branch information
tmeasday authored Aug 9, 2022
2 parents 6cca929 + 4d41f44 commit 308de76
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 46 deletions.
26 changes: 16 additions & 10 deletions code/lib/cli/src/repro-next.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import prompts from 'prompts';
import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import boxen from 'boxen';
import { dedent } from 'ts-dedent';
import degit from 'degit';

import { existsSync } from 'fs-extra';
import TEMPLATES from './repro-templates';

const logger = console;
Expand Down Expand Up @@ -56,17 +56,18 @@ export const reproNext = async ({
boxen(
dedent`
🔎 You filtered out all templates. 🔍
After filtering all the templates with "${chalk.yellow(
filterValue
)}", we found no templates.
)}", we found no results. Please try again with a different filter.
Available templates:
${keys.map((key) => chalk.blue`- ${key}`).join('\n')}
`.trim(),
{ borderStyle: 'round', padding: 1, borderColor: '#F1618C' } as any
)
);
return;
process.exit(1);
}

let selectedTemplate: Choice | null = null;
Expand Down Expand Up @@ -106,26 +107,31 @@ export const reproNext = async ({
}

let selectedDirectory = outputDirectory;
const outputDirectoryName = outputDirectory || selectedTemplate;
if (selectedDirectory && existsSync(`${selectedDirectory}`)) {
logger.info(`⚠️ ${selectedDirectory} already exists! Overwriting...`);
}

if (!selectedDirectory) {
const { directory } = await prompts({
type: 'text',
message: 'Enter the output directory',
name: 'directory',
initial: selectedTemplate,
validate: (directoryName) =>
fs.existsSync(directoryName)
initial: outputDirectoryName,
validate: async (directoryName) =>
existsSync(directoryName)
? `${directoryName} already exists. Please choose another name.`
: true,
});
selectedDirectory = directory;
}

try {
const cwd = path.isAbsolute(selectedDirectory)
const templateDestination = path.isAbsolute(selectedDirectory)
? selectedDirectory
: path.join(process.cwd(), selectedDirectory);

logger.info(`🏃 Adding ${selectedConfig.name} into ${cwd}`);
logger.info(`🏃 Adding ${selectedConfig.name} into ${templateDestination}`);

logger.log('📦 Downloading repro template...');
try {
Expand All @@ -136,10 +142,10 @@ export const reproNext = async ({
{
force: true,
}
).clone(selectedTemplate.replace('/', '-'));
).clone(templateDestination);
} catch (err) {
logger.error(`🚨 Failed to download repro template: ${err.message}`);
return;
throw err;
}

const initMessage = init
Expand Down
32 changes: 28 additions & 4 deletions scripts/sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
/* eslint-disable no-restricted-syntax, no-await-in-loop */
import path from 'path';
import { remove, pathExists, readJSON, writeJSON, ensureSymlink } from 'fs-extra';
import {
remove,
pathExists,
readJSON,
writeJSON,
ensureSymlink,
ensureDir,
existsSync,
} from 'fs-extra';
import prompts from 'prompts';

import { getOptionsOrPrompt } from './utils/options';
Expand Down Expand Up @@ -75,6 +83,10 @@ async function getOptions() {
dryRun: {
description: "Don't execute commands, just list them (dry run)?",
},
debug: {
description: 'Print all the logs to the console',
promptType: false,
},
});
}

Expand Down Expand Up @@ -147,6 +159,12 @@ async function addPackageScripts({

async function readMainConfig({ cwd }: { cwd: string }) {
const configDir = path.join(cwd, '.storybook');
if (!existsSync(configDir)) {
throw new Error(
`Unable to find the Storybook folder in "${configDir}". Are you sure it exists? Or maybe this folder uses a custom Storybook config directory?`
);
}

const mainConfigPath = getInterpretedFile(path.resolve(configDir, 'main'));
return readConfig(mainConfigPath);
}
Expand Down Expand Up @@ -197,7 +215,10 @@ async function addStories(paths: string[], { mainConfig }: { mainConfig: ConfigF
async function main() {
const optionValues = await getOptions();

const { template, forceDelete, forceReuse, link, dryRun } = optionValues;
const { template, forceDelete, forceReuse, link, dryRun, debug } = optionValues;

await ensureDir(sandboxDir);

const cwd = path.join(sandboxDir, template.replace('/', '-'));

const exists = await pathExists(cwd);
Expand All @@ -222,6 +243,7 @@ async function main() {
optionValues: { output: cwd, branch: 'next' },
cwd: sandboxDir,
dryRun,
debug,
});

const mainConfig = await readMainConfig({ cwd });
Expand Down Expand Up @@ -251,7 +273,7 @@ async function main() {

for (const addon of optionValues.addon) {
const addonName = `@storybook/addon-${addon}`;
await executeCLIStep(steps.add, { argument: addonName, cwd, dryRun });
await executeCLIStep(steps.add, { argument: addonName, cwd, dryRun, debug });
}

for (const addon of [...defaultAddons, ...optionValues.addon]) {
Expand All @@ -268,6 +290,7 @@ async function main() {
cwd: codeDir,
dryRun,
optionValues: { local: true, start: false },
debug,
});
} else {
await exec('yarn local-registry --publish', { cwd: codeDir }, { dryRun });
Expand Down Expand Up @@ -313,10 +336,11 @@ async function main() {
dryRun,
startMessage: `⬆️ Starting Storybook`,
errorMessage: `🚨 Starting Storybook failed`,
debug: true,
}
);
} else {
await executeCLIStep(steps.build, { cwd, dryRun });
await executeCLIStep(steps.build, { cwd, dryRun, debug });
// TODO serve
}

Expand Down
2 changes: 2 additions & 0 deletions scripts/utils/cli-step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export async function executeCLIStep<TOptions extends OptionSpecifier>(
optionValues?: Partial<OptionValues<TOptions>>;
cwd: string;
dryRun?: boolean;
debug: boolean;
}
) {
if (cliStep.hasArgument && !options.argument)
Expand All @@ -38,6 +39,7 @@ export async function executeCLIStep<TOptions extends OptionSpecifier>(
startMessage: `${cliStep.icon} ${cliStep.description}`,
errorMessage: `🚨 ${cliStep.description} failed`,
dryRun: options.dryRun,
debug: options.debug,
}
);
}
53 changes: 23 additions & 30 deletions scripts/utils/exec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import shell, { ExecOptions } from 'shelljs';
import execa, { Options } from 'execa';
import chalk from 'chalk';

const logger = console;

type StepOptions = {
startMessage?: string;
errorMessage?: string;
dryRun?: boolean;
debug?: boolean;
};

export const exec = async (
command: string,
options: ExecOptions = {},
{
startMessage,
errorMessage,
dryRun,
}: { startMessage?: string; errorMessage?: string; dryRun?: boolean } = {}
) => {
options: Options = {},
{ startMessage, errorMessage, dryRun, debug }: StepOptions = {}
): Promise<void> => {
logger.info();
if (startMessage) logger.info(startMessage);

if (dryRun) {
Expand All @@ -20,27 +24,16 @@ export const exec = async (
}

logger.debug(command);
return new Promise((resolve, reject) => {
const defaultOptions: ExecOptions = {
silent: false,
};
const child = shell.exec(command, {
...defaultOptions,
...options,
async: true,
silent: false,
});

child.stderr.pipe(process.stderr);
const defaultOptions: Options = {
stdout: debug ? 'inherit' : 'ignore',
};
try {
await execa.command(command, { ...defaultOptions, ...options });
} catch (err) {
logger.error(chalk.red(`An error occurred while executing: \`${command}\``));
logger.log(errorMessage);
throw err;
}

child.on('exit', (code) => {
if (code === 0) {
resolve(undefined);
} else {
logger.error(chalk.red(`An error occurred while executing: \`${command}\``));
logger.log(errorMessage);
reject(new Error(`command exited with code: ${code}: `));
}
});
});
return undefined;
};
12 changes: 10 additions & 2 deletions scripts/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import prompts, { Falsy, PrevCaller, PromptType } from 'prompts';
import type { PromptObject } from 'prompts';
import program from 'commander';
import dedent from 'ts-dedent';
import chalk from 'chalk';
import kebabCase from 'lodash/kebabCase';

// Option types
Expand Down Expand Up @@ -125,8 +127,14 @@ export function getOptions<TOptions extends OptionSpecifier>(
if (isBooleanOption(option)) return acc.option(flags, option.description, !!option.inverse);

const checkStringValue = (raw: string) => {
if (!option.values.includes(raw))
throw new Error(`Unexpected value '${raw}' for option '${key}'`);
if (!option.values.includes(raw)) {
const possibleOptions = chalk.cyan(option.values.join(', '));
throw new Error(
dedent`Unexpected value '${chalk.yellow(raw)}' for option '${chalk.magenta(key)}'.
These are the possible options: ${possibleOptions}\n\n`
);
}
return raw;
};

Expand Down

0 comments on commit 308de76

Please sign in to comment.