diff --git a/packages/cli/src/commands/database/seed.ts b/packages/cli/src/commands/database/seed.ts index 26bd5411d..7f7611a9d 100644 --- a/packages/cli/src/commands/database/seed.ts +++ b/packages/cli/src/commands/database/seed.ts @@ -3,21 +3,16 @@ import path from 'path'; import { seeds } from '@logto/schemas'; import chalk from 'chalk'; -import ora from 'ora'; import { DatabasePool, DatabaseTransactionConnection, sql } from 'slonik'; import { raw } from 'slonik-sql-tag-raw'; import { CommandModule } from 'yargs'; import { createPoolAndDatabaseIfNeeded, insertInto } from '../../database'; import { updateDatabaseTimestamp } from '../../queries/logto-config'; -import { buildApplicationSecret, getPathInModule, log } from '../../utilities'; +import { buildApplicationSecret, getPathInModule, log, oraPromise } from '../../utilities'; import { getLatestAlterationTimestamp } from './alteration'; const createTables = async (connection: DatabaseTransactionConnection) => { - const spinner = ora({ - text: 'Create tables', - prefixText: chalk.blue('[info]'), - }).start(); const tableDirectory = getPathInModule('@logto/schemas', 'tables'); const directoryFiles = await readdir(tableDirectory); const tableFiles = directoryFiles.filter((file) => file.endsWith('.sql')); @@ -28,17 +23,11 @@ const createTables = async (connection: DatabaseTransactionConnection) => { ]) ); - // Disable for spinner - /* eslint-disable @silverhand/fp/no-mutation */ // Await in loop is intended for better error handling - for (const [file, query] of queries) { + for (const [, query] of queries) { // eslint-disable-next-line no-await-in-loop await connection.query(sql`${raw(query)}`); - spinner.text = `Run ${file} succeeded`; } - - spinner.succeed(`Created ${queries.length} tables`); - /* eslint-enable @silverhand/fp/no-mutation */ }; const seedTables = async (connection: DatabaseTransactionConnection) => { @@ -50,11 +39,6 @@ const seedTables = async (connection: DatabaseTransactionConnection) => { defaultRole, } = seeds; - const spinner = ora({ - text: 'Seed data', - prefixText: chalk.blue('[info]'), - }).start(); - await Promise.all([ connection.query(insertInto(managementResource, 'resources')), connection.query(insertInto(createDefaultSetting(), 'settings')), @@ -65,14 +49,18 @@ const seedTables = async (connection: DatabaseTransactionConnection) => { connection.query(insertInto(defaultRole, 'roles')), updateDatabaseTimestamp(connection, await getLatestAlterationTimestamp()), ]); - - spinner.succeed(); }; export const seedByPool = async (pool: DatabasePool) => { await pool.transaction(async (connection) => { - await createTables(connection); - await seedTables(connection); + await oraPromise(createTables(connection), { + text: 'Create tables', + prefixText: chalk.blue('[info]'), + }); + await oraPromise(seedTables(connection), { + text: 'Seed data', + prefixText: chalk.blue('[info]'), + }); }); }; diff --git a/packages/cli/src/commands/install.ts b/packages/cli/src/commands/install.ts index 9382becfd..67ca4b6e1 100644 --- a/packages/cli/src/commands/install.ts +++ b/packages/cli/src/commands/install.ts @@ -8,13 +8,12 @@ import { conditional } from '@silverhand/essentials'; import chalk from 'chalk'; import { remove, writeFile } from 'fs-extra'; import inquirer from 'inquirer'; -import ora from 'ora'; import * as semver from 'semver'; import tar from 'tar'; import { CommandModule } from 'yargs'; import { createPoolAndDatabaseIfNeeded, getDatabaseUrlFromConfig } from '../database'; -import { downloadFile, log, safeExecSync } from '../utilities'; +import { downloadFile, log, oraPromise, safeExecSync } from '../utilities'; import { seedByPool } from './database/seed'; export type InstallArgs = { @@ -92,20 +91,12 @@ const downloadRelease = async () => { }; const decompress = async (toPath: string, tarPath: string) => { - const decompressSpinner = ora({ - text: `Decompress to ${toPath}`, - prefixText: chalk.blue('[info]'), - }).start(); - try { await mkdir(toPath); await tar.extract({ file: tarPath, cwd: toPath, strip: 1 }); } catch (error: unknown) { - decompressSpinner.fail(); log.error(error); } - - decompressSpinner.succeed(); }; const installLogto = async ({ path: pathArgument = defaultPath, silent = false }: InstallArgs) => { @@ -119,7 +110,14 @@ const installLogto = async ({ path: pathArgument = defaultPath, silent = false } // Download and decompress const tarPath = await downloadRelease(); - await decompress(instancePath, tarPath); + await oraPromise( + decompress(instancePath, tarPath), + { + text: `Decompress to ${instancePath}`, + prefixText: chalk.blue('[info]'), + }, + true + ); try { // Seed database @@ -138,13 +136,11 @@ const installLogto = async ({ path: pathArgument = defaultPath, silent = false } }); if (!value) { - const spinner = ora({ + await oraPromise(remove(instancePath), { text: 'Clean up', prefixText: chalk.blue('[info]'), - }).start(); + }); - await remove(instancePath); - spinner.succeed(); // eslint-disable-next-line unicorn/no-process-exit process.exit(1); } diff --git a/packages/cli/src/utilities.ts b/packages/cli/src/utilities.ts index df0d71640..72f6257f0 100644 --- a/packages/cli/src/utilities.ts +++ b/packages/cli/src/utilities.ts @@ -80,6 +80,29 @@ export const getPathInModule = (moduleName: string, relativePath = '/') => relativePath ); +export const oraPromise = async ( + promise: PromiseLike, + options?: ora.Options, + exitOnError = false +) => { + const spinner = ora(options).start(); + + try { + const result = await promise; + spinner.succeed(); + + return result; + } catch (error: unknown) { + spinner.fail(); + + if (exitOnError) { + log.error(error); + } + + throw error; + } +}; + // TODO: Move to `@silverhand/essentials` // Intended // eslint-disable-next-line @typescript-eslint/no-empty-function