diff --git a/packages/cli/package.json b/packages/cli/package.json index 317e78ca16..66c56dd8cc 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -76,6 +76,7 @@ "jest": "^26.0.1", "jest-css-modules": "^2.1.0", "jest-esm-transformer": "^1.0.0", + "lodash": "^4.17.19", "mini-css-extract-plugin": "^0.9.0", "ora": "^4.0.3", "raw-loader": "^4.0.1", diff --git a/packages/cli/src/commands/config/print.ts b/packages/cli/src/commands/config/print.ts index eb814a5bee..ce0d9976b1 100644 --- a/packages/cli/src/commands/config/print.ts +++ b/packages/cli/src/commands/config/print.ts @@ -16,16 +16,43 @@ import { Command } from 'commander'; import { stringify as stringifyYaml } from 'yaml'; +import { ConfigReader } from '@backstage/config'; import { loadCliConfig } from '../../lib/config'; +import cloneDeepWith from 'lodash/cloneDeepWith'; export default async (cmd: Command) => { - const { config } = await loadCliConfig(cmd.config, cmd.withSecrets ?? false); + const { schema, appConfigs } = await loadCliConfig(cmd.config); + let data; - const flatConfig = config.get(); + if (cmd.frontend) { + const frontendConfigs = schema.process(appConfigs, { + visibilities: ['frontend'], + }); + data = ConfigReader.fromConfigs(frontendConfigs).get(); + } else if (cmd.withSecrets) { + data = ConfigReader.fromConfigs(appConfigs).get(); + } else { + let secretConfigs = schema.process(appConfigs, { + visibilities: ['secret'], + }); + + secretConfigs = secretConfigs.map(entry => ({ + context: entry.context, + data: cloneDeepWith(entry.data, value => { + console.log('DEBUG: value =', value); + if (typeof value !== 'object') { + return ''; + } + return undefined; + }), + })); + + data = ConfigReader.fromConfigs(appConfigs.concat(secretConfigs)).get(); + } if (cmd.format === 'json') { - process.stdout.write(`${JSON.stringify(flatConfig, null, 2)}\n`); + process.stdout.write(`${JSON.stringify(data, null, 2)}\n`); } else { - process.stdout.write(`${stringifyYaml(flatConfig)}\n`); + process.stdout.write(`${stringifyYaml(data)}\n`); } }; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 5b0dae6244..bcbde20d84 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -136,6 +136,7 @@ export function registerCommands(program: CommanderStatic) { program .command('config:print') + .option('--frontend', 'Print only the frontend configuration') .option('--with-secrets', 'Include secrets in the printed configuration') .option( '--format ', diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index 8746c807e7..a5d581c60a 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -40,7 +40,7 @@ export async function buildBundle(options: BuildOptions) { ...options, checksEnabled: false, isDev: false, - baseUrl: resolveBaseUrl(options.config), + baseUrl: resolveBaseUrl(options.frontendConfig), }); const compiler = webpack(config); diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 34b9f498ad..9e150e0ccb 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -74,11 +74,11 @@ export async function createConfig( paths: BundlingPaths, options: BundlingOptions, ): Promise { - const { checksEnabled, isDev } = options; + const { checksEnabled, isDev, frontendConfig } = options; const { plugins, loaders } = transforms(options); - const baseUrl = options.config.getString('app.baseUrl'); + const baseUrl = frontendConfig.getString('app.baseUrl'); const validBaseUrl = new URL(baseUrl); if (checksEnabled) { @@ -99,7 +99,7 @@ export async function createConfig( plugins.push( new webpack.EnvironmentPlugin({ - APP_CONFIG: options.appConfigs, + APP_CONFIG: options.frontendAppConfigs, }), ); @@ -109,9 +109,9 @@ export async function createConfig( templateParameters: { publicPath: validBaseUrl.pathname.replace(/\/$/, ''), app: { - title: options.config.getString('app.title'), + title: frontendConfig.getString('app.title'), baseUrl: validBaseUrl.href, - googleAnalyticsTrackingId: options.config.getOptionalString( + googleAnalyticsTrackingId: frontendConfig.getOptionalString( 'app.googleAnalyticsTrackingId', ), }, diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 2710145d83..f540e01084 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -23,7 +23,7 @@ import { ServeOptions } from './types'; import { resolveBundlingPaths } from './paths'; export async function serveBundle(options: ServeOptions) { - const url = resolveBaseUrl(options.config); + const url = resolveBaseUrl(options.frontendConfig); const host = options.config.getOptionalString('app.listen.host') || url.hostname; diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 00f1895725..d0af0a9937 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -21,23 +21,23 @@ import { ParallelOption } from '../parallel'; export type BundlingOptions = { checksEnabled: boolean; isDev: boolean; - config: Config; - appConfigs: AppConfig[]; + frontendConfig: Config; + frontendAppConfigs: AppConfig[]; baseUrl: URL; parallel?: ParallelOption; }; export type ServeOptions = BundlingPathsOptions & { checksEnabled: boolean; - config: Config; - appConfigs: AppConfig[]; + frontendConfig: Config; + frontendAppConfigs: AppConfig[]; }; export type BuildOptions = BundlingPathsOptions & { statsJsonEnabled: boolean; parallel?: ParallelOption; - config: Config; - appConfigs: AppConfig[]; + frontendConfig: Config; + frontendAppConfigs: AppConfig[]; }; export type BackendBundlingOptions = { diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 0ce19c80f5..a282bbf88c 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -14,13 +14,22 @@ * limitations under the License. */ -import { loadConfig } from '@backstage/config-loader'; +import { loadConfig, loadConfigSchema } from '@backstage/config-loader'; import { ConfigReader } from '@backstage/config'; import { paths } from './paths'; export async function loadCliConfig(configArgs: string[]) { const configPaths = configArgs.map(arg => paths.resolveTarget(arg)); + // Consider all packages in the monorepo when loading in config + const LernaProject = require('@lerna/project'); + const project = new LernaProject(paths.targetDir); + const packages = await project.getPackages(); + const localPackageNames = packages.map((p: any) => p.name); + const schema = await loadConfigSchema({ + dependencies: localPackageNames, + }); + const appConfigs = await loadConfig({ env: process.env.APP_ENV ?? process.env.NODE_ENV ?? 'production', configRoot: paths.targetRoot, @@ -31,8 +40,15 @@ export async function loadCliConfig(configArgs: string[]) { `Loaded config from ${appConfigs.map(c => c.context).join(', ')}`, ); + const frontendAppConfigs = schema.process(appConfigs, { + visibilities: ['frontend'], + }); + const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs); + return { + schema, appConfigs, - config: ConfigReader.fromConfigs(appConfigs), + frontendConfig, + frontendAppConfigs, }; }