mirror of
https://github.com/logto-io/logto.git
synced 2025-01-27 21:39:16 -05:00
refactor(cli): reorg options and logic
This commit is contained in:
parent
a5cd73d961
commit
fa195f4aa5
10 changed files with 283 additions and 242 deletions
|
@ -61,7 +61,6 @@
|
|||
"@silverhand/eslint-config": "1.0.0",
|
||||
"@silverhand/jest-config": "1.0.0",
|
||||
"@silverhand/ts-config": "1.0.0",
|
||||
"@types/decompress": "^4.2.4",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/inquirer": "^8.2.1",
|
||||
"@types/jest": "^28.1.6",
|
||||
|
|
|
@ -8,7 +8,7 @@ import { copy, existsSync, remove, readdir } from 'fs-extra';
|
|||
import { DatabasePool } from 'slonik';
|
||||
import { CommandModule } from 'yargs';
|
||||
|
||||
import { createPoolFromEnv } from '../../database';
|
||||
import { createPoolFromConfig } from '../../database';
|
||||
import {
|
||||
getCurrentDatabaseAlterationTimestamp,
|
||||
updateDatabaseTimestamp,
|
||||
|
@ -140,7 +140,7 @@ const alteration: CommandModule<unknown, { action: string }> = {
|
|||
log.error('Unsupported action');
|
||||
}
|
||||
|
||||
const pool = await createPoolFromEnv();
|
||||
const pool = await createPoolFromConfig();
|
||||
const alterations = await getUndeployedAlterations(pool);
|
||||
|
||||
log.info(
|
||||
|
|
|
@ -3,7 +3,7 @@ import { deduplicate } from '@silverhand/essentials';
|
|||
import chalk from 'chalk';
|
||||
import { CommandModule } from 'yargs';
|
||||
|
||||
import { createPoolFromEnv } from '../../database';
|
||||
import { createPoolFromConfig } from '../../database';
|
||||
import { getRowsByKeys, updateValueByKey } from '../../queries/logto-config';
|
||||
import { log } from '../../utilities';
|
||||
|
||||
|
@ -48,7 +48,7 @@ export const getConfig: CommandModule<unknown, { key: string; keys: string[] }>
|
|||
const queryKeys = deduplicate([key, ...keys]);
|
||||
validateKeys(queryKeys);
|
||||
|
||||
const pool = await createPoolFromEnv();
|
||||
const pool = await createPoolFromConfig();
|
||||
const { rows } = await getRowsByKeys(pool, queryKeys);
|
||||
await pool.end();
|
||||
|
||||
|
@ -88,7 +88,7 @@ export const setConfig: CommandModule<unknown, { key: string; value: string }> =
|
|||
|
||||
const guarded = logtoConfigGuards[key].parse(JSON.parse(value));
|
||||
|
||||
const pool = await createPoolFromEnv();
|
||||
const pool = await createPoolFromConfig();
|
||||
await updateValueByKey(pool, key, guarded);
|
||||
await pool.end();
|
||||
|
||||
|
|
|
@ -1,188 +0,0 @@
|
|||
import { execSync } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { mkdir } from 'fs/promises';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import chalk from 'chalk';
|
||||
import { remove, writeFile } from 'fs-extra';
|
||||
import inquirer from 'inquirer';
|
||||
import * as semver from 'semver';
|
||||
import tar from 'tar';
|
||||
import { CommandModule } from 'yargs';
|
||||
|
||||
import { createPoolAndDatabaseIfNeeded, getDatabaseUrlFromEnv } from '../database';
|
||||
import { downloadFile, log, oraPromise, safeExecSync } from '../utilities';
|
||||
import { seedByPool } from './database/seed';
|
||||
|
||||
export type InstallArgs = {
|
||||
path?: string;
|
||||
silent?: boolean;
|
||||
};
|
||||
|
||||
const defaultPath = path.join(os.homedir(), 'logto');
|
||||
const pgRequired = new semver.SemVer('14.0.0');
|
||||
|
||||
const validateNodeVersion = () => {
|
||||
const required = new semver.SemVer('16.0.0');
|
||||
const current = new semver.SemVer(execSync('node -v', { encoding: 'utf8', stdio: 'pipe' }));
|
||||
|
||||
if (required.compare(current) > 0) {
|
||||
log.error(`Logto requires NodeJS >=${required.version}, but ${current.version} found.`);
|
||||
}
|
||||
|
||||
if (current.major > required.major) {
|
||||
log.warn(
|
||||
`Logto is tested under NodeJS ^${required.version}, but version ${current.version} found.`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const inquireInstancePath = async (initialPath?: string) => {
|
||||
const { instancePath } = await inquirer.prompt<{ instancePath: string }>(
|
||||
{
|
||||
name: 'instancePath',
|
||||
message: 'Where should we create your Logto instance?',
|
||||
type: 'input',
|
||||
default: defaultPath,
|
||||
filter: (value: string) => value.trim(),
|
||||
validate: (value: string) =>
|
||||
existsSync(path.resolve(value))
|
||||
? `The path ${chalk.green(value)} already exists, please try another.`
|
||||
: true,
|
||||
},
|
||||
{ instancePath: initialPath }
|
||||
);
|
||||
|
||||
return instancePath;
|
||||
};
|
||||
|
||||
const validateDatabase = async () => {
|
||||
const { hasPostgresUrl } = await inquirer.prompt<{ hasPostgresUrl?: boolean }>({
|
||||
name: 'hasPostgresUrl',
|
||||
message: `Logto requires PostgreSQL >=${pgRequired.version} but cannot find in the current environment.\n Do you have a remote PostgreSQL instance ready?`,
|
||||
type: 'confirm',
|
||||
when: () => {
|
||||
const pgOutput = safeExecSync('postgres --version') ?? '';
|
||||
// Filter out all brackets in the output since Homebrew will append `(Homebrew)`.
|
||||
const pgArray = pgOutput.split(' ').filter((value) => !value.startsWith('('));
|
||||
const pgCurrent = semver.coerce(pgArray[pgArray.length - 1]);
|
||||
|
||||
return !pgCurrent || pgCurrent.compare(pgRequired) < 0;
|
||||
},
|
||||
});
|
||||
|
||||
if (hasPostgresUrl === false) {
|
||||
log.error('Logto requires a Postgres instance to run.');
|
||||
}
|
||||
};
|
||||
|
||||
const downloadRelease = async () => {
|
||||
const tarFilePath = path.resolve(os.tmpdir(), './logto.tar.gz');
|
||||
|
||||
log.info(`Download Logto to ${tarFilePath}`);
|
||||
await downloadFile(
|
||||
'https://github.com/logto-io/logto/releases/latest/download/logto.tar.gz',
|
||||
tarFilePath
|
||||
);
|
||||
|
||||
return tarFilePath;
|
||||
};
|
||||
|
||||
const decompress = async (toPath: string, tarPath: string) => {
|
||||
try {
|
||||
await mkdir(toPath);
|
||||
await tar.extract({ file: tarPath, cwd: toPath, strip: 1 });
|
||||
} catch (error: unknown) {
|
||||
log.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const installLogto = async ({ path: pathArgument = defaultPath, silent = false }: InstallArgs) => {
|
||||
validateNodeVersion();
|
||||
|
||||
// Get instance path
|
||||
const instancePath = await inquireInstancePath(conditional(silent && pathArgument));
|
||||
|
||||
// Validate database URL
|
||||
await validateDatabase();
|
||||
|
||||
// Download and decompress
|
||||
const tarPath = await downloadRelease();
|
||||
await oraPromise(
|
||||
decompress(instancePath, tarPath),
|
||||
{
|
||||
text: `Decompress to ${instancePath}`,
|
||||
prefixText: chalk.blue('[info]'),
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
try {
|
||||
// Seed database
|
||||
const pool = await createPoolAndDatabaseIfNeeded(); // It will ask for database URL and save to config
|
||||
await seedByPool(pool, 'all');
|
||||
await pool.end();
|
||||
} catch (error: unknown) {
|
||||
console.error(error);
|
||||
|
||||
const { value } = await inquirer.prompt<{ value: boolean }>({
|
||||
name: 'value',
|
||||
type: 'confirm',
|
||||
message:
|
||||
'Error occurred during seeding your Logto database. Nothing has changed since the seeding process was in a transaction.\n' +
|
||||
' Would you like to continue without seed?',
|
||||
default: false,
|
||||
});
|
||||
|
||||
if (!value) {
|
||||
await oraPromise(remove(instancePath), {
|
||||
text: 'Clean up',
|
||||
prefixText: chalk.blue('[info]'),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line unicorn/no-process-exit
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log.info(`You can use ${chalk.green('db seed')} command to seed when ready.`);
|
||||
}
|
||||
|
||||
// Save to dot env
|
||||
const databaseUrl = await getDatabaseUrlFromEnv();
|
||||
const dotEnvPath = path.resolve(instancePath, '.env');
|
||||
await writeFile(dotEnvPath, `DB_URL=${databaseUrl}`, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
log.info(`Saved database URL to ${chalk.blue(dotEnvPath)}`);
|
||||
|
||||
// Finale
|
||||
const startCommand = `cd ${instancePath} && npm start`;
|
||||
log.info(
|
||||
`Use the command below to start Logto. Happy hacking!\n\n ${chalk.green(startCommand)}`
|
||||
);
|
||||
};
|
||||
|
||||
const install: CommandModule<unknown, { path?: string; silent?: boolean }> = {
|
||||
command: ['init', 'i', 'install'],
|
||||
describe: 'Download and run the latest Logto release',
|
||||
builder: (yargs) =>
|
||||
yargs.options({
|
||||
path: {
|
||||
alias: 'p',
|
||||
describe: 'Path of Logto, must be a non-existing path',
|
||||
type: 'string',
|
||||
},
|
||||
silent: {
|
||||
alias: 's',
|
||||
describe: 'Entering non-interactive mode',
|
||||
type: 'boolean',
|
||||
},
|
||||
}),
|
||||
handler: async ({ path, silent }) => {
|
||||
await installLogto({ path, silent });
|
||||
},
|
||||
};
|
||||
|
||||
export default install;
|
71
packages/cli/src/commands/install/index.ts
Normal file
71
packages/cli/src/commands/install/index.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import chalk from 'chalk';
|
||||
import { CommandModule } from 'yargs';
|
||||
|
||||
import { getDatabaseUrlFromConfig } from '../../database';
|
||||
import { log } from '../../utilities';
|
||||
import {
|
||||
validateNodeVersion,
|
||||
inquireInstancePath,
|
||||
validateDatabase,
|
||||
downloadRelease,
|
||||
seedDatabase,
|
||||
createEnv,
|
||||
logFinale,
|
||||
decompress,
|
||||
} from './utils';
|
||||
|
||||
export type InstallArgs = {
|
||||
path?: string;
|
||||
skipSeed: boolean;
|
||||
};
|
||||
|
||||
const installLogto = async ({ path, skipSeed }: InstallArgs) => {
|
||||
validateNodeVersion();
|
||||
|
||||
// Get instance path
|
||||
const instancePath = await inquireInstancePath(path);
|
||||
|
||||
// Validate if user has a valid database
|
||||
await validateDatabase();
|
||||
|
||||
// Download and decompress
|
||||
const tarPath = await downloadRelease();
|
||||
await decompress(instancePath, tarPath);
|
||||
|
||||
// Seed database
|
||||
if (skipSeed) {
|
||||
log.info(`You can use ${chalk.green('db seed')} command to seed database when ready.`);
|
||||
} else {
|
||||
await seedDatabase(instancePath);
|
||||
}
|
||||
|
||||
// Save to dot env
|
||||
await createEnv(instancePath, await getDatabaseUrlFromConfig());
|
||||
|
||||
// Finale
|
||||
logFinale(instancePath);
|
||||
};
|
||||
|
||||
const install: CommandModule<unknown, { path?: string; skipSeed: boolean }> = {
|
||||
command: ['init', 'i', 'install'],
|
||||
describe: 'Download and run the latest Logto release',
|
||||
builder: (yargs) =>
|
||||
yargs.options({
|
||||
path: {
|
||||
alias: 'p',
|
||||
describe: 'Path of Logto, must be a non-existing path',
|
||||
type: 'string',
|
||||
},
|
||||
skipSeed: {
|
||||
alias: 'ss',
|
||||
describe: 'Skip Logto database seeding',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
}),
|
||||
handler: async ({ path, skipSeed }) => {
|
||||
await installLogto({ path, skipSeed });
|
||||
},
|
||||
};
|
||||
|
||||
export default install;
|
154
packages/cli/src/commands/install/utils.ts
Normal file
154
packages/cli/src/commands/install/utils.ts
Normal file
|
@ -0,0 +1,154 @@
|
|||
import { execSync } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { mkdir } from 'fs/promises';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { remove, writeFile } from 'fs-extra';
|
||||
import inquirer from 'inquirer';
|
||||
import * as semver from 'semver';
|
||||
import tar from 'tar';
|
||||
|
||||
import { createPoolAndDatabaseIfNeeded } from '../../database';
|
||||
import { cliConfig, ConfigKey, downloadFile, log, oraPromise, safeExecSync } from '../../utilities';
|
||||
import { seedByPool } from '../database/seed';
|
||||
|
||||
export const defaultPath = path.join(os.homedir(), 'logto');
|
||||
const pgRequired = new semver.SemVer('14.0.0');
|
||||
|
||||
export const validateNodeVersion = () => {
|
||||
const required = new semver.SemVer('16.0.0');
|
||||
const current = new semver.SemVer(execSync('node -v', { encoding: 'utf8', stdio: 'pipe' }));
|
||||
|
||||
if (required.compare(current) > 0) {
|
||||
log.error(`Logto requires NodeJS >=${required.version}, but ${current.version} found.`);
|
||||
}
|
||||
|
||||
if (current.major > required.major) {
|
||||
log.warn(
|
||||
`Logto is tested under NodeJS ^${required.version}, but version ${current.version} found.`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const validatePath = (value: string) =>
|
||||
existsSync(path.resolve(value))
|
||||
? `The path ${chalk.green(value)} already exists, please try another.`
|
||||
: true;
|
||||
|
||||
export const inquireInstancePath = async (initialPath?: string) => {
|
||||
const { instancePath } = await inquirer.prompt<{ instancePath: string }>(
|
||||
{
|
||||
name: 'instancePath',
|
||||
message: 'Where should we create your Logto instance?',
|
||||
type: 'input',
|
||||
default: defaultPath,
|
||||
filter: (value: string) => value.trim(),
|
||||
validate: validatePath,
|
||||
},
|
||||
{ instancePath: initialPath }
|
||||
);
|
||||
|
||||
// Validate for initialPath
|
||||
const validated = validatePath(instancePath);
|
||||
|
||||
if (validated !== true) {
|
||||
log.error(validated);
|
||||
}
|
||||
|
||||
return instancePath;
|
||||
};
|
||||
|
||||
export const validateDatabase = async () => {
|
||||
if (cliConfig.has(ConfigKey.DatabaseUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { hasPostgresUrl } = await inquirer.prompt<{ hasPostgresUrl?: boolean }>({
|
||||
name: 'hasPostgresUrl',
|
||||
message: `Logto requires PostgreSQL >=${pgRequired.version} but cannot find in the current environment.\n Do you have a remote PostgreSQL instance ready?`,
|
||||
type: 'confirm',
|
||||
when: () => {
|
||||
const pgOutput = safeExecSync('postgres --version') ?? '';
|
||||
// Filter out all brackets in the output since Homebrew will append `(Homebrew)`.
|
||||
const pgArray = pgOutput.split(' ').filter((value) => !value.startsWith('('));
|
||||
const pgCurrent = semver.coerce(pgArray[pgArray.length - 1]);
|
||||
|
||||
return !pgCurrent || pgCurrent.compare(pgRequired) < 0;
|
||||
},
|
||||
});
|
||||
|
||||
if (hasPostgresUrl === false) {
|
||||
log.error('Logto requires a Postgres instance to run.');
|
||||
}
|
||||
};
|
||||
|
||||
export const downloadRelease = async () => {
|
||||
const tarFilePath = path.resolve(os.tmpdir(), './logto.tar.gz');
|
||||
|
||||
log.info(`Download Logto to ${tarFilePath}`);
|
||||
await downloadFile(
|
||||
'https://github.com/logto-io/logto/releases/latest/download/logto.tar.gz',
|
||||
tarFilePath
|
||||
);
|
||||
|
||||
return tarFilePath;
|
||||
};
|
||||
|
||||
export const decompress = async (toPath: string, tarPath: string) => {
|
||||
const run = async () => {
|
||||
try {
|
||||
await mkdir(toPath);
|
||||
await tar.extract({ file: tarPath, cwd: toPath, strip: 1 });
|
||||
} catch (error: unknown) {
|
||||
log.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
return oraPromise(
|
||||
run(),
|
||||
{
|
||||
text: `Decompress to ${toPath}`,
|
||||
prefixText: chalk.blue('[info]'),
|
||||
},
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
export const seedDatabase = async (instancePath: string) => {
|
||||
try {
|
||||
const pool = await createPoolAndDatabaseIfNeeded();
|
||||
await seedByPool(pool, 'all');
|
||||
await pool.end();
|
||||
} catch (error: unknown) {
|
||||
console.error(error);
|
||||
|
||||
await oraPromise(remove(instancePath), {
|
||||
text: 'Clean up',
|
||||
prefixText: chalk.blue('[info]'),
|
||||
});
|
||||
|
||||
log.error(
|
||||
'Error occurred during seeding your Logto database. Nothing has changed since the seeding process was in a transaction.\n\n' +
|
||||
` To skip the database seeding, append ${chalk.green(
|
||||
'--skip-seed'
|
||||
)} to the command options.`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const createEnv = async (instancePath: string, databaseUrl: string) => {
|
||||
const dotEnvPath = path.resolve(instancePath, '.env');
|
||||
await writeFile(dotEnvPath, `DB_URL=${databaseUrl}`, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
log.info(`Saved database URL to ${chalk.blue(dotEnvPath)}`);
|
||||
};
|
||||
|
||||
export const logFinale = (instancePath: string) => {
|
||||
const startCommand = `cd ${instancePath} && npm start`;
|
||||
log.info(
|
||||
`Use the command below to start Logto. Happy hacking!\n\n ${chalk.green(startCommand)}`
|
||||
);
|
||||
};
|
|
@ -5,19 +5,19 @@ import { createPool, parseDsn, sql, stringifyDsn } from 'slonik';
|
|||
import { createInterceptors } from 'slonik-interceptor-preset';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { getCliConfig, log } from './utilities';
|
||||
import { ConfigKey, getCliConfigWithPrompt, log } from './utilities';
|
||||
|
||||
export const defaultDatabaseUrl = 'postgresql://localhost:5432/logto';
|
||||
|
||||
export const getDatabaseUrlFromEnv = async () =>
|
||||
(await getCliConfig({
|
||||
key: 'DB_URL',
|
||||
export const getDatabaseUrlFromConfig = async () =>
|
||||
(await getCliConfigWithPrompt({
|
||||
key: ConfigKey.DatabaseUrl,
|
||||
readableKey: 'Logto database URL',
|
||||
defaultValue: defaultDatabaseUrl,
|
||||
})) ?? '';
|
||||
|
||||
export const createPoolFromEnv = async () => {
|
||||
const databaseUrl = await getDatabaseUrlFromEnv();
|
||||
export const createPoolFromConfig = async () => {
|
||||
const databaseUrl = await getDatabaseUrlFromConfig();
|
||||
|
||||
return createPool(databaseUrl, {
|
||||
interceptors: createInterceptors(),
|
||||
|
@ -25,14 +25,14 @@ export const createPoolFromEnv = async () => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Create a database pool with the database URL in config.
|
||||
* Create a database pool with the URL in CLI config; if no URL found, prompt to input.
|
||||
* If the given database does not exists, it will try to create a new database by connecting to the maintenance database `postgres`.
|
||||
*
|
||||
* @returns A new database pool with the database URL in config.
|
||||
*/
|
||||
export const createPoolAndDatabaseIfNeeded = async () => {
|
||||
try {
|
||||
return await createPoolFromEnv();
|
||||
return await createPoolFromConfig();
|
||||
} catch (error: unknown) {
|
||||
const result = z.object({ code: z.string() }).safeParse(error);
|
||||
|
||||
|
@ -42,7 +42,7 @@ export const createPoolAndDatabaseIfNeeded = async () => {
|
|||
log.error(error);
|
||||
}
|
||||
|
||||
const databaseUrl = await getDatabaseUrlFromEnv();
|
||||
const databaseUrl = await getDatabaseUrlFromConfig();
|
||||
const dsn = parseDsn(databaseUrl);
|
||||
// It's ok to fall back to '?' since:
|
||||
// - Database name is required to connect in the previous pool
|
||||
|
@ -59,7 +59,7 @@ export const createPoolAndDatabaseIfNeeded = async () => {
|
|||
|
||||
log.succeed(`Created database ${databaseName}`);
|
||||
|
||||
return createPoolFromEnv();
|
||||
return createPoolFromConfig();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import { hideBin } from 'yargs/helpers';
|
|||
|
||||
import database from './commands/database';
|
||||
import install from './commands/install';
|
||||
import { cliConfig, ConfigKey } from './utilities';
|
||||
|
||||
void yargs(hideBin(process.argv))
|
||||
.option('env', {
|
||||
|
@ -12,8 +13,19 @@ void yargs(hideBin(process.argv))
|
|||
describe: 'The path to your `.env` file',
|
||||
type: 'string',
|
||||
})
|
||||
.middleware(({ env }) => {
|
||||
.option('databaseUrl', {
|
||||
alias: ['db-url'],
|
||||
describe: 'The Postgres URL to Logto database',
|
||||
type: 'string',
|
||||
})
|
||||
.middleware(({ env, databaseUrl }) => {
|
||||
dotenv.config({ path: env });
|
||||
|
||||
const initialDatabaseUrl = databaseUrl ?? process.env[ConfigKey.DatabaseUrl];
|
||||
|
||||
if (initialDatabaseUrl) {
|
||||
cliConfig.set(ConfigKey.DatabaseUrl, initialDatabaseUrl);
|
||||
}
|
||||
})
|
||||
.command(install)
|
||||
.command(database)
|
||||
|
|
|
@ -108,46 +108,47 @@ export const oraPromise = async <T>(
|
|||
}
|
||||
};
|
||||
|
||||
const cliConfig = new Map<string, Optional<string>>();
|
||||
export enum ConfigKey {
|
||||
DatabaseUrl = 'DB_URL',
|
||||
}
|
||||
|
||||
export type GetCliConfig = {
|
||||
key: string;
|
||||
export const cliConfig = new Map<ConfigKey, Optional<string>>();
|
||||
|
||||
export type GetCliConfigWithPrompt = {
|
||||
key: ConfigKey;
|
||||
readableKey: string;
|
||||
comments?: string;
|
||||
defaultValue?: string;
|
||||
};
|
||||
|
||||
export const getCliConfig = async ({ key, readableKey, comments, defaultValue }: GetCliConfig) => {
|
||||
export const getCliConfigWithPrompt = async ({
|
||||
key,
|
||||
readableKey,
|
||||
comments,
|
||||
defaultValue,
|
||||
}: GetCliConfigWithPrompt) => {
|
||||
if (cliConfig.has(key)) {
|
||||
return cliConfig.get(key);
|
||||
}
|
||||
|
||||
const { [key]: value } = process.env;
|
||||
const { input } = await inquirer
|
||||
.prompt<{ input?: string }>({
|
||||
type: 'input',
|
||||
name: 'input',
|
||||
message: `Enter your ${readableKey}${conditionalString(comments && ' ' + comments)}`,
|
||||
default: defaultValue,
|
||||
})
|
||||
.catch(async (error) => {
|
||||
if (error.isTtyError) {
|
||||
log.error(`No ${readableKey} (${chalk.green(key)}) configured in option nor env`);
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
const { input } = await inquirer
|
||||
.prompt<{ input?: string }>({
|
||||
type: 'input',
|
||||
name: 'input',
|
||||
message: `Enter your ${readableKey}${conditionalString(comments && ' ' + comments)}`,
|
||||
default: defaultValue,
|
||||
})
|
||||
.catch(async (error) => {
|
||||
if (error.isTtyError) {
|
||||
log.error(`No ${readableKey} (${chalk.green(key)}) configured in env`);
|
||||
}
|
||||
// The type definition does not give us type except `any`, throw it directly will honor the original behavior.
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw error;
|
||||
});
|
||||
|
||||
// The type definition does not give us type except `any`, throw it directly will honor the original behavior.
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw error;
|
||||
});
|
||||
cliConfig.set(key, input);
|
||||
|
||||
cliConfig.set(key, input);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
cliConfig.set(key, value);
|
||||
|
||||
return value;
|
||||
return input;
|
||||
};
|
||||
|
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
|
@ -26,7 +26,6 @@ importers:
|
|||
'@silverhand/essentials': ^1.3.0
|
||||
'@silverhand/jest-config': 1.0.0
|
||||
'@silverhand/ts-config': 1.0.0
|
||||
'@types/decompress': ^4.2.4
|
||||
'@types/fs-extra': ^9.0.13
|
||||
'@types/inquirer': ^8.2.1
|
||||
'@types/jest': ^28.1.6
|
||||
|
@ -85,7 +84,6 @@ importers:
|
|||
'@silverhand/eslint-config': 1.0.0_swk2g7ygmfleszo5c33j4vooni
|
||||
'@silverhand/jest-config': 1.0.0_bi2kohzqnxavgozw3csgny5hju
|
||||
'@silverhand/ts-config': 1.0.0_typescript@4.7.4
|
||||
'@types/decompress': 4.2.4
|
||||
'@types/fs-extra': 9.0.13
|
||||
'@types/inquirer': 8.2.1
|
||||
'@types/jest': 28.1.6
|
||||
|
@ -4415,12 +4413,6 @@ packages:
|
|||
'@types/ms': 0.7.31
|
||||
dev: true
|
||||
|
||||
/@types/decompress/4.2.4:
|
||||
resolution: {integrity: sha512-/C8kTMRTNiNuWGl5nEyKbPiMv6HA+0RbEXzFhFBEzASM6+oa4tJro9b8nj7eRlOFfuLdzUU+DS/GPDlvvzMOhA==}
|
||||
dependencies:
|
||||
'@types/node': 17.0.23
|
||||
dev: true
|
||||
|
||||
/@types/etag/1.8.1:
|
||||
resolution: {integrity: sha512-bsKkeSqN7HYyYntFRAmzcwx/dKW4Wa+KVMTInANlI72PWLQmOpZu96j0OqHZGArW4VQwCmJPteQlXaUDeOB0WQ==}
|
||||
dependencies:
|
||||
|
|
Loading…
Add table
Reference in a new issue