console/packages/libraries/cli/src/base-command.ts

176 lines
4.8 KiB
TypeScript
Raw Normal View History

2022-05-18 07:26:57 +00:00
import colors from 'colors';
2022-12-28 19:22:54 +00:00
import { ClientError, GraphQLClient } from 'graphql-request';
2022-05-18 07:26:57 +00:00
import symbols from 'log-symbols';
import { Command, Errors, Config as OclifConfig } from '@oclif/core';
import { Config, GetConfigurationValueType, ValidConfigurationKeys } from './helpers/config';
2022-05-18 07:26:57 +00:00
import { getSdk } from './sdk';
type OmitNever<T> = { [K in keyof T as T[K] extends never ? never : K]: T[K] };
2022-05-18 07:26:57 +00:00
export default abstract class extends Command {
protected _userConfig: Config;
protected constructor(argv: string[], config: OclifConfig) {
2022-05-18 07:26:57 +00:00
super(argv, config);
this._userConfig = new Config({
feat: clean app env (#422) * feat: document environment variables * more env documentation * wip * add noop for backend env * typo * feat: embed environment validation/parsing in built app * fix the sentry integration 😌 * feat: use env * feat: decode the usage service environment * feat: decode the webhooks service environment * feat: disallow process.env * feat: decode the tokens service environment * feat: decode the stripe-billing service environment * feat: decode server service environment * feat: decode schema service environment * feat: decode rate-limit service environment * feat: decode usage-estimator service environment * feat: decode emails service environment * adjust env * remove commented out code * adjust env variable name * use separate env variables * env fixes * more environmental stuff :) * ... * replace POSTGRES_CONNECTION_STRING with specific environment variables * handle optional clickhouse (for now :) * add missing POSTGRES_DB environment variable * make ENVIRONMENT optional * the other matters lol * feat: support delivering mails via SMTP (#412) * feat: optional CDN (#410) * feat: optional CDN * enable CDN in deployment * enable the CDN in integration tests * add sendmail provider * remove unused env variables from the deployment * only show login alert when legacy auth0 migration is enabled * feat: make TOKENS_ENDPOINT mandatory and RATE_LIMIT_ENDPOINT optional for usage service * feat: upgrade supertokens and enable server side email confirmation (#423) * feat: upgrade supertokens and enable server side email confirmation * feat: opt into required email verification * docs: self hosting (#428) * docs: self-hosting quick start * Update packages/web/docs/pages/self-hosting/get-started.mdx Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> * feat: optional GitHub and Slack environment variables (#440) * feat: optional github integration environment variables * feat: optional slack integration (#441) * use latest stable supertokens Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com>
2022-10-04 12:30:21 +00:00
// eslint-disable-next-line no-process-env
2022-05-18 07:26:57 +00:00
filepath: process.env.HIVE_CONFIG,
rootDir: process.cwd(),
});
}
success(...args: any[]) {
this.log(colors.green(symbols.success), ...args);
}
fail(...args: any[]) {
this.log(colors.red(symbols.error), ...args);
}
info(...args: any[]) {
this.log(colors.yellow(symbols.info), ...args);
}
bolderize(msg: string) {
const findSingleQuotes = /'([^']+)'/gim;
const findDoubleQuotes = /"([^"]+)"/gim;
return msg
.replace(findSingleQuotes, (_: string, value: string) => colors.bold(value))
.replace(findDoubleQuotes, (_: string, value: string) => colors.bold(value));
2022-05-18 07:26:57 +00:00
}
/**
* Get a value from arguments or flags first, then from env variables,
* then fallback to config.
* Throw when there's no value.
2022-05-18 07:26:57 +00:00
*
* @param key
* @param args all arguments or flags
* @param defaultValue default value
* @param message custom error message in case of no value
* @param env an env var name
2022-05-18 07:26:57 +00:00
*/
ensure<
TKey extends ValidConfigurationKeys,
2022-05-18 07:26:57 +00:00
TArgs extends {
[key in TKey]: GetConfigurationValueType<TKey>;
2022-05-18 07:26:57 +00:00
},
>({
key,
args,
legacyFlagName,
2022-05-18 07:26:57 +00:00
defaultValue,
message,
env,
}: {
args: TArgs;
key: TKey;
/** By default we try to match config names with flag names, but for legacy compatibility we need to provide the old flag name. */
legacyFlagName?: keyof OmitNever<{
// Symbol.asyncIterator to discriminate against any lol
[TArgKey in keyof TArgs]: typeof Symbol.asyncIterator extends TArgs[TArgKey]
? never
: string extends TArgs[TArgKey]
? TArgKey
: never;
}>;
defaultValue?: TArgs[keyof TArgs] | null;
2022-05-18 07:26:57 +00:00
message?: string;
env?: string;
}): NonNullable<GetConfigurationValueType<TKey>> | never {
if (args[key] != null) {
return args[key] as NonNullable<GetConfigurationValueType<TKey>>;
}
if (legacyFlagName && (args as any)[legacyFlagName] != null) {
return args[legacyFlagName] as any as NonNullable<GetConfigurationValueType<TKey>>;
2022-05-18 07:26:57 +00:00
}
feat: clean app env (#422) * feat: document environment variables * more env documentation * wip * add noop for backend env * typo * feat: embed environment validation/parsing in built app * fix the sentry integration 😌 * feat: use env * feat: decode the usage service environment * feat: decode the webhooks service environment * feat: disallow process.env * feat: decode the tokens service environment * feat: decode the stripe-billing service environment * feat: decode server service environment * feat: decode schema service environment * feat: decode rate-limit service environment * feat: decode usage-estimator service environment * feat: decode emails service environment * adjust env * remove commented out code * adjust env variable name * use separate env variables * env fixes * more environmental stuff :) * ... * replace POSTGRES_CONNECTION_STRING with specific environment variables * handle optional clickhouse (for now :) * add missing POSTGRES_DB environment variable * make ENVIRONMENT optional * the other matters lol * feat: support delivering mails via SMTP (#412) * feat: optional CDN (#410) * feat: optional CDN * enable CDN in deployment * enable the CDN in integration tests * add sendmail provider * remove unused env variables from the deployment * only show login alert when legacy auth0 migration is enabled * feat: make TOKENS_ENDPOINT mandatory and RATE_LIMIT_ENDPOINT optional for usage service * feat: upgrade supertokens and enable server side email confirmation (#423) * feat: upgrade supertokens and enable server side email confirmation * feat: opt into required email verification * docs: self hosting (#428) * docs: self-hosting quick start * Update packages/web/docs/pages/self-hosting/get-started.mdx Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> * feat: optional GitHub and Slack environment variables (#440) * feat: optional github integration environment variables * feat: optional slack integration (#441) * use latest stable supertokens Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com>
2022-10-04 12:30:21 +00:00
// eslint-disable-next-line no-process-env
2022-05-18 07:26:57 +00:00
if (env && process.env[env]) {
feat: clean app env (#422) * feat: document environment variables * more env documentation * wip * add noop for backend env * typo * feat: embed environment validation/parsing in built app * fix the sentry integration 😌 * feat: use env * feat: decode the usage service environment * feat: decode the webhooks service environment * feat: disallow process.env * feat: decode the tokens service environment * feat: decode the stripe-billing service environment * feat: decode server service environment * feat: decode schema service environment * feat: decode rate-limit service environment * feat: decode usage-estimator service environment * feat: decode emails service environment * adjust env * remove commented out code * adjust env variable name * use separate env variables * env fixes * more environmental stuff :) * ... * replace POSTGRES_CONNECTION_STRING with specific environment variables * handle optional clickhouse (for now :) * add missing POSTGRES_DB environment variable * make ENVIRONMENT optional * the other matters lol * feat: support delivering mails via SMTP (#412) * feat: optional CDN (#410) * feat: optional CDN * enable CDN in deployment * enable the CDN in integration tests * add sendmail provider * remove unused env variables from the deployment * only show login alert when legacy auth0 migration is enabled * feat: make TOKENS_ENDPOINT mandatory and RATE_LIMIT_ENDPOINT optional for usage service * feat: upgrade supertokens and enable server side email confirmation (#423) * feat: upgrade supertokens and enable server side email confirmation * feat: opt into required email verification * docs: self hosting (#428) * docs: self-hosting quick start * Update packages/web/docs/pages/self-hosting/get-started.mdx Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> * feat: optional GitHub and Slack environment variables (#440) * feat: optional github integration environment variables * feat: optional slack integration (#441) * use latest stable supertokens Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com>
2022-10-04 12:30:21 +00:00
// eslint-disable-next-line no-process-env
return process.env[env] as TArgs[keyof TArgs] as NonNullable<GetConfigurationValueType<TKey>>;
2022-05-18 07:26:57 +00:00
}
const userConfigValue = this._userConfig.get(key);
if (userConfigValue != null) {
return userConfigValue;
2022-05-18 07:26:57 +00:00
}
if (defaultValue) {
return defaultValue;
}
if (message) {
throw new Errors.CLIError(message);
}
throw new Errors.CLIError(`Missing "${String(key)}"`);
2022-05-18 07:26:57 +00:00
}
cleanRequestId(requestId?: string | null) {
return requestId ? requestId.split(',')[0].trim() : undefined;
}
registryApi(registry: string, token: string) {
return getSdk(
new GraphQLClient(registry, {
headers: {
Accept: 'application/json',
'User-Agent': `hive-cli/${this.config.version}`,
Authorization: `Bearer ${token}`,
2022-05-18 07:26:57 +00:00
'graphql-client-name': 'Hive CLI',
'graphql-client-version': this.config.version,
},
}),
2022-05-18 07:26:57 +00:00
);
}
handleFetchError(error: unknown): never {
if (typeof error === 'string') {
return this.error(error);
}
if (error instanceof Error) {
if (isClientError(error)) {
const errors = error.response?.errors;
if (Array.isArray(errors) && errors.length > 0) {
return this.error(errors[0].message, {
ref: this.cleanRequestId(error.response?.headers?.get('x-request-id')),
});
}
return this.error(error.message, {
ref: this.cleanRequestId(error.response?.headers?.get('x-request-id')),
});
}
return this.error(error);
}
return this.error(JSON.stringify(error));
}
2022-05-18 07:26:57 +00:00
async require<
TFlags extends {
require: string[];
[key: string]: any;
},
2022-05-18 07:26:57 +00:00
>(flags: TFlags) {
if (flags.require && flags.require.length > 0) {
await Promise.all(
flags.require.map(mod => import(require.resolve(mod, { paths: [process.cwd()] }))),
);
2022-05-18 07:26:57 +00:00
}
}
}
function isClientError(error: Error): error is ClientError {
return 'response' in error;
}