-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
42 changed files
with
521 additions
and
415 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
import { launchScaffold } from '@bluecadet/launchpad-scaffold'; | ||
import { LogManager } from '@bluecadet/launchpad-utils'; | ||
|
||
/** | ||
* @param {import("../cli.js").LaunchpadArgv} argv | ||
*/ | ||
export async function scaffold(argv) { | ||
await launchScaffold(); | ||
const rootLogger = LogManager.configureRootLogger(); | ||
await launchScaffold(rootLogger); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,39 @@ | ||
import { loadConfigAndEnv } from '../utils/load-config-and-env.js'; | ||
import { handleFatalError, initializeLogger, loadConfigAndEnv } from '../utils/command-utils.js'; | ||
import { importLaunchpadMonitor } from './monitor.js'; | ||
import { importLaunchpadContent } from './content.js'; | ||
import { ResultAsync } from 'neverthrow'; | ||
import { err, ok, ResultAsync } from 'neverthrow'; | ||
import { MonitorError } from '../errors.js'; | ||
|
||
/** | ||
* @param {import("../cli.js").LaunchpadArgv} argv | ||
*/ | ||
export async function start(argv) { | ||
return loadConfigAndEnv(argv).andThen(config => { | ||
return importLaunchpadContent().map(({ LaunchpadContent }) => { | ||
const contentInstance = new LaunchpadContent(config.content); | ||
return contentInstance.start(); | ||
}).andThen(() => importLaunchpadMonitor()) | ||
.map(({ LaunchpadMonitor }) => { | ||
return new LaunchpadMonitor(config.monitor); | ||
}) | ||
.andThrough((monitorInstance) => { | ||
return ResultAsync.fromPromise(monitorInstance.connect(), () => new MonitorError('Failed to connect to monitor')); | ||
}) | ||
.andThrough((monitorInstance) => { | ||
return ResultAsync.fromPromise(monitorInstance.start(), () => new MonitorError('Failed to start monitor')); | ||
}); | ||
}).mapErr(error => { | ||
console.error('Launchpad failed to start.'); | ||
console.error(error.message); | ||
process.exit(1); | ||
}); | ||
return loadConfigAndEnv(argv) | ||
.andThen(initializeLogger) | ||
.andThen(({ config, rootLogger }) => { | ||
return importLaunchpadContent() | ||
.andThen(({ LaunchpadContent }) => { | ||
if (!config.content) { | ||
return err(new Error('No content config found in your config file.')); | ||
} | ||
|
||
const contentInstance = new LaunchpadContent(config.content, rootLogger); | ||
return contentInstance.start(); | ||
}) | ||
.andThen(() => importLaunchpadMonitor()) | ||
.andThen(({ LaunchpadMonitor }) => { | ||
if (!config.monitor) { | ||
return err(new Error('No monitor config found in your config file.')); | ||
} | ||
|
||
const monitorInstance = new LaunchpadMonitor(config.monitor, rootLogger); | ||
return ok(monitorInstance); | ||
}) | ||
.andThrough((monitorInstance) => { | ||
return ResultAsync.fromPromise(monitorInstance.connect(), () => new MonitorError('Failed to connect to monitor')); | ||
}) | ||
.andThrough((monitorInstance) => { | ||
return ResultAsync.fromPromise(monitorInstance.start(), () => new MonitorError('Failed to start monitor')); | ||
}).orElse(error => handleFatalError(error, rootLogger)); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { err, errAsync, ok, ResultAsync } from 'neverthrow'; | ||
import { findConfig, loadConfigFromFile } from './config.js'; | ||
import { ConfigError } from '../errors.js'; | ||
import path from 'path'; | ||
import { resolveEnv } from './env.js'; | ||
import { resolveLaunchpadOptions } from '../launchpad-options.js'; | ||
import chalk from 'chalk'; | ||
import { LogManager } from '@bluecadet/launchpad-utils'; | ||
|
||
/** | ||
* @param {import("../cli.js").LaunchpadArgv} argv | ||
* @returns {import('neverthrow').ResultAsync<import('../launchpad-options.js').ResolvedLaunchpadOptions, ConfigError>} | ||
*/ | ||
export function loadConfigAndEnv(argv) { | ||
const configPath = argv.config ?? findConfig(); | ||
|
||
if (!configPath) { | ||
return errAsync(new ConfigError('No config file found.')); | ||
} | ||
|
||
const configDir = path.dirname(configPath); | ||
|
||
if (argv.env) { | ||
// if env arg is passed, resolve paths relative to the CWD | ||
const rootDir = process.env.INIT_CWD ?? ''; | ||
resolveEnv( | ||
argv.env.map(p => path.resolve(rootDir, p.toString())) | ||
); | ||
} else if (argv.envCascade) { | ||
// if env-cascade arg is passed, resolve paths relative to the config file | ||
|
||
// Load order: .env < .env.local < .env.[override] < .env.[override].local | ||
resolveEnv([ | ||
path.resolve(configDir, '.env'), | ||
path.resolve(configDir, '.env.local'), | ||
path.resolve(configDir, `.env.${argv.envCascade}`), | ||
path.resolve(configDir, `.env.${argv.envCascade}.local`) | ||
]); | ||
} else { | ||
// default to loading .env and .env.local in the config dir | ||
resolveEnv([ | ||
path.resolve(configDir, '.env'), | ||
path.resolve(configDir, '.env.local') | ||
]); | ||
} | ||
|
||
return ResultAsync.fromPromise(loadConfigFromFile(configPath), (e) => new ConfigError(`Failed to load config file at path: ${chalk.white(configPath)}`)) | ||
.map(config => resolveLaunchpadOptions(config)); | ||
} | ||
|
||
/** | ||
* | ||
* @param {import('../launchpad-options.js').LaunchpadOptions} config | ||
*/ | ||
export function initializeLogger(config) { | ||
const rootLogger = LogManager.configureRootLogger(config.logging); | ||
|
||
return ok({ config, rootLogger }); | ||
} | ||
|
||
/** | ||
* | ||
* @param {Error} error | ||
* @param {import('@bluecadet/launchpad-utils').Logger} rootLogger | ||
* @returns {never} | ||
*/ | ||
export function handleFatalError(error, rootLogger) { | ||
rootLogger.error('Content failed to download.'); | ||
logFullErrorChain(rootLogger, error); | ||
process.exit(1); | ||
} | ||
|
||
/** | ||
* @param {import('@bluecadet/launchpad-utils').Logger} logger | ||
* @param {Error} error | ||
*/ | ||
export function logFullErrorChain(logger, error) { | ||
/** @type {Error | undefined} */ | ||
let currentError = error; | ||
while (currentError) { | ||
logger.error(`${chalk.red('ββ')} ${chalk.red.bold(currentError.name)}: ${chalk.red(currentError.message)}`); | ||
const callstack = currentError.stack; | ||
// logger.error(`${chalk.red(callstack ? 'β' : 'β')} `); | ||
if (callstack) { | ||
const lines = callstack.split('\n').slice(1); | ||
// log up to 3 lines of the callstack | ||
let loggedLines = 0; | ||
for (const line of lines) { | ||
const isLastLine = loggedLines === lines.length - 1 || loggedLines > 2; | ||
logger.error(`${chalk.red('β')} ${chalk.red.dim((isLastLine && lines.length > 3) ? '...' : line.trim())}`); | ||
if (isLastLine) { | ||
logger.error(`${chalk.red('βββββββββββββββββββ')}`); | ||
} | ||
loggedLines++; | ||
|
||
if (loggedLines > 3) { | ||
break; | ||
} | ||
} | ||
} | ||
if (currentError.cause && currentError.cause instanceof Error) { | ||
currentError = currentError.cause; | ||
logger.error(` ${chalk.red.dim('β')} ${chalk.red.dim('Caused by:')}`); | ||
logger.error(` ${chalk.red.dim('β')}`); | ||
} else { | ||
currentError = undefined; | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.