From 63310e3987af133a8d4c248f5ea46adf03dbfcb7 Mon Sep 17 00:00:00 2001 From: Aramis Sennyey Date: Tue, 22 Nov 2022 14:42:57 -0500 Subject: [PATCH] Remove CLI-related changes. Signed-off-by: Aramis Sennyey --- .changeset/metal-fishes-grow.md | 2 +- .changeset/short-games-march.md | 5 - .changeset/young-chairs-check.md | 5 + packages/cli/package.json | 3 +- .../__fixtures__/test-project/app-config.yaml | 6 - .../__fixtures__/test-project/backstage.json | 3 - .../__fixtures__/test-project/package.json | 19 -- .../test-project/packages/app/package.json | 12 -- .../packages/app/public/index.html | 11 -- .../test-project/packages/app/src/index.tsx | 17 -- .../src/__fixtures__/test-project/yarn.lock | 0 packages/cli/src/commands/build/command.ts | 1 - packages/cli/src/commands/index.ts | 8 - packages/cli/src/commands/repo/build.ts | 1 - packages/cli/src/lib/bundler/config.ts | 6 +- packages/cli/src/lib/config.test.ts | 106 ------------ packages/cli/src/lib/config.ts | 49 +----- packages/cli/src/serve.test.ts | 163 ------------------ 18 files changed, 11 insertions(+), 406 deletions(-) delete mode 100644 .changeset/short-games-march.md create mode 100644 .changeset/young-chairs-check.md delete mode 100644 packages/cli/src/__fixtures__/test-project/app-config.yaml delete mode 100644 packages/cli/src/__fixtures__/test-project/backstage.json delete mode 100644 packages/cli/src/__fixtures__/test-project/package.json delete mode 100644 packages/cli/src/__fixtures__/test-project/packages/app/package.json delete mode 100644 packages/cli/src/__fixtures__/test-project/packages/app/public/index.html delete mode 100644 packages/cli/src/__fixtures__/test-project/packages/app/src/index.tsx delete mode 100644 packages/cli/src/__fixtures__/test-project/yarn.lock delete mode 100644 packages/cli/src/lib/config.test.ts delete mode 100644 packages/cli/src/serve.test.ts diff --git a/.changeset/metal-fishes-grow.md b/.changeset/metal-fishes-grow.md index 7067a881bb..885bcecd1b 100644 --- a/.changeset/metal-fishes-grow.md +++ b/.changeset/metal-fishes-grow.md @@ -2,4 +2,4 @@ '@backstage/core-app-api': patch --- -Apps will now detect when a relative `backend.baseUrl` is provided and update the config accordingly. +Apps will now detect when a relative `backend.baseUrl` or `app.baseUrl` is provided and update the config to produce a full URL. diff --git a/.changeset/short-games-march.md b/.changeset/short-games-march.md deleted file mode 100644 index 54c40b943e..0000000000 --- a/.changeset/short-games-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/app-defaults': minor ---- - -Added support for parsing relative backend urls to the api factory. Allows relative backend urls to be specified in config options. diff --git a/.changeset/young-chairs-check.md b/.changeset/young-chairs-check.md new file mode 100644 index 0000000000..b0611af148 --- /dev/null +++ b/.changeset/young-chairs-check.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +Apps will now detect when a relative `backend.baseUrl` or `app.baseUrl` is provided and update it to be a full URL. This means that you can provide relative URLs and they will be resolved as expected across the application. diff --git a/packages/cli/package.json b/packages/cli/package.json index 41b37fc7b0..661d53d897 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -154,7 +154,6 @@ "@types/terser-webpack-plugin": "^5.0.4", "@types/yarnpkg__lockfile": "^1.1.4", "del": "^6.0.0", - "get-port": "^6.1.2", "mock-fs": "^5.1.0", "msw": "^0.49.0", "nodemon": "^2.0.2", @@ -273,4 +272,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/cli/src/__fixtures__/test-project/app-config.yaml b/packages/cli/src/__fixtures__/test-project/app-config.yaml deleted file mode 100644 index 69bb98f0c7..0000000000 --- a/packages/cli/src/__fixtures__/test-project/app-config.yaml +++ /dev/null @@ -1,6 +0,0 @@ -app: - baseUrl: http://localhost:${PORT}/test - title: test - -backend: - baseUrl: http://localhost:${BACKEND_PORT} diff --git a/packages/cli/src/__fixtures__/test-project/backstage.json b/packages/cli/src/__fixtures__/test-project/backstage.json deleted file mode 100644 index 1587a66968..0000000000 --- a/packages/cli/src/__fixtures__/test-project/backstage.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "1.0.0" -} diff --git a/packages/cli/src/__fixtures__/test-project/package.json b/packages/cli/src/__fixtures__/test-project/package.json deleted file mode 100644 index e1f6b310f3..0000000000 --- a/packages/cli/src/__fixtures__/test-project/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "root", - "version": "1.0.0", - "private": true, - "engines": { - "node": "14 || 16" - }, - "workspaces": { - "packages": [ - "packages/*" - ] - }, - "devDependencies": { - "@backstage/cli": "^0.20.0-next.0", - "lerna": "^4.0.0", - "node-gyp": "^9.0.0", - "typescript": "~4.6.4" - } -} diff --git a/packages/cli/src/__fixtures__/test-project/packages/app/package.json b/packages/cli/src/__fixtures__/test-project/packages/app/package.json deleted file mode 100644 index 95e50a6774..0000000000 --- a/packages/cli/src/__fixtures__/test-project/packages/app/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "app", - "version": "0.0.0", - "private": true, - "bundled": true, - "backstage": { - "role": "frontend" - }, - "files": [ - "dist" - ] -} diff --git a/packages/cli/src/__fixtures__/test-project/packages/app/public/index.html b/packages/cli/src/__fixtures__/test-project/packages/app/public/index.html deleted file mode 100644 index 93da08ba81..0000000000 --- a/packages/cli/src/__fixtures__/test-project/packages/app/public/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - -
- - diff --git a/packages/cli/src/__fixtures__/test-project/packages/app/src/index.tsx b/packages/cli/src/__fixtures__/test-project/packages/app/src/index.tsx deleted file mode 100644 index 03a9b39841..0000000000 --- a/packages/cli/src/__fixtures__/test-project/packages/app/src/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export {}; -console.log('Hello World.'); diff --git a/packages/cli/src/__fixtures__/test-project/yarn.lock b/packages/cli/src/__fixtures__/test-project/yarn.lock deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/commands/build/command.ts b/packages/cli/src/commands/build/command.ts index b5120f7ae0..fcaf58e1ee 100644 --- a/packages/cli/src/commands/build/command.ts +++ b/packages/cli/src/commands/build/command.ts @@ -29,7 +29,6 @@ export async function command(opts: OptionValues): Promise { targetDir: paths.targetDir, configPaths: opts.config as string[], writeStats: Boolean(opts.stats), - cliOptions: opts, }); } if (role === 'backend') { diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 2dd2973de1..82cb60e62f 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -126,14 +126,6 @@ export function registerScriptCommand(program: Command) { '--stats', 'If bundle stats are available, write them to the output directory. Applies to app packages only.', ) - .option( - '--public-path ', - 'Public path for hosting the website on, can be relative.', - ) - .option( - '--backend-url ', - 'Backend url, expects just the origin or sub-route. Do not include /api. Can be relative.', - ) .option( '--config ', 'Config files to load instead of app-config.yaml. Applies to app packages only.', diff --git a/packages/cli/src/commands/repo/build.ts b/packages/cli/src/commands/repo/build.ts index 55574a53b0..21b1580cb9 100644 --- a/packages/cli/src/commands/repo/build.ts +++ b/packages/cli/src/commands/repo/build.ts @@ -153,7 +153,6 @@ export async function command(opts: OptionValues, cmd: Command): Promise { return; } await buildFrontend({ - cliOptions: opts, targetDir: pkg.dir, configPaths: (buildOptions.config as string[]) ?? [], writeStats: Boolean(buildOptions.stats), diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index bc0ff2db3b..ace60883a4 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -36,12 +36,12 @@ import { runPlain } from '../run'; import ESLintPlugin from 'eslint-webpack-plugin'; import pickBy from 'lodash/pickBy'; -const DUMMY_URL = 'http://dummyurl.org'; +const NO_OP_URL = 'http://test-site-asdf.org'; export function resolveBaseUrl(config: Config): URL { const baseUrl = config.getString('app.baseUrl'); try { - return new URL(baseUrl, DUMMY_URL); + return new URL(baseUrl, NO_OP_URL); } catch (error) { throw new Error(`Invalid app.baseUrl, ${error}`); } @@ -90,7 +90,7 @@ export async function createConfig( const externalPkgs = packages.filter(p => !isChildPath(paths.root, p.dir)); const baseUrl = frontendConfig.getString('app.baseUrl'); - const validBaseUrl = new URL(baseUrl, DUMMY_URL); + const validBaseUrl = new URL(baseUrl, NO_OP_URL); const publicPath = validBaseUrl.pathname.replace(/\/$/, ''); if (checksEnabled) { plugins.push( diff --git a/packages/cli/src/lib/config.test.ts b/packages/cli/src/lib/config.test.ts deleted file mode 100644 index 9db076e8e4..0000000000 --- a/packages/cli/src/lib/config.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { readCliConfig } from './config'; - -describe('readCliConfig', () => { - it('should return empty config for empty cli', () => { - expect(readCliConfig({})).toEqual([]); - }); - - it('should return empty config for no matching keys', () => { - expect( - readCliConfig({ - test: '123', - } as any), - ).toEqual([]); - }); - - it('should return backend.baseUrl when backendUrl present in cli options', () => { - expect( - readCliConfig({ - backendUrl: '/', - }), - ).toEqual([ - { - data: { - backend: { - baseUrl: '/', - }, - }, - context: 'cli', - }, - ]); - }); - - it('should return app.baseUrl when publicPath present in cli options', () => { - expect( - readCliConfig({ - publicPath: '/', - }), - ).toEqual([ - { - data: { - app: { - baseUrl: '/', - }, - }, - context: 'cli', - }, - ]); - }); - - it('should return app.baseUrl and backend.baseUrl when publicPath and backendUrl present in cli options', () => { - expect( - readCliConfig({ - publicPath: '/', - backendUrl: '/api', - }), - ).toEqual([ - { - data: { - app: { - baseUrl: '/', - }, - backend: { - baseUrl: '/api', - }, - }, - context: 'cli', - }, - ]); - }); - - it('should throw for public paths that do NOT start with /', () => { - ['http://localhost:3000', './', '../..'].forEach(publicPath => - expect(() => { - readCliConfig({ - publicPath, - }); - }).toThrow('Public path must be relative'), - ); - }); - - it('should throw for backend urls that do NOT start with /', () => { - ['http://localhost:3000', './', '../..'].forEach(backendUrl => - expect(() => { - readCliConfig({ - backendUrl, - }); - }).toThrow('Backend URL must be relative'), - ); - }); -}); diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 9084f72057..a76571c3f6 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -19,17 +19,11 @@ import { loadConfig, loadConfigSchema, } from '@backstage/config-loader'; -import { AppConfig, ConfigReader } from '@backstage/config'; +import { ConfigReader } from '@backstage/config'; import { paths } from './paths'; import { isValidUrl } from './urls'; import { getPackages } from '@manypkg/get-packages'; import { PackageGraph } from './monorepo'; -import { JsonObject } from '@backstage/types'; - -export type CliConfigOptions = { - publicPath?: string; - backendUrl?: string; -}; type Options = { args: string[]; @@ -38,44 +32,8 @@ type Options = { withFilteredKeys?: boolean; withDeprecatedKeys?: boolean; fullVisibility?: boolean; - cliOptions?: CliConfigOptions; }; -/** - * Read specific parameters from the CLI and add them to the build config. - * @param opts CLI passed parameters. - * @returns Array of config, empty if there is no relevant passed in cli options. - * - * @public - */ -export function readCliConfig(opts?: CliConfigOptions): AppConfig[] { - if (!opts || Object.keys(opts).length === 0) return []; - const data: JsonObject = {}; - - if (opts.publicPath?.startsWith('/')) { - data.app = { - baseUrl: opts.publicPath, - }; - } else if (opts.publicPath) { - throw new Error( - 'Public path must be relative and start with "/" when specified through CLI options. Path traversals like "./" and assumed relative endpoints like "backstage" are not supported.', - ); - } - if (opts.backendUrl?.startsWith('/')) { - data.backend = { - baseUrl: opts.backendUrl, - }; - } else if (opts.backendUrl) { - throw new Error( - 'Backend URL must be relative and start with "/" when specified through CLI options. Path traversals like "./" and assumed relative endpoint like "api" are not supported.', - ); - } - - if (Object.keys(data).length === 0) return []; - - return [{ data, context: 'cli' }]; -} - export async function loadCliConfig(options: Options) { const configTargets: ConfigTarget[] = []; options.args.forEach(arg => { @@ -114,8 +72,6 @@ export async function loadCliConfig(options: Options) { packagePaths: [paths.resolveTargetRoot('package.json')], }); - const cliConfigs = readCliConfig(options.cliOptions); - const { appConfigs } = await loadConfig({ experimentalEnvFunc: options.mockEnv ? async name => process.env[name] || 'x' @@ -124,9 +80,6 @@ export async function loadCliConfig(options: Options) { configTargets: configTargets, }); - // Add the cliConfigs to the end of the appConfigs array for final overriding. - appConfigs.push(...cliConfigs); - // printing to stderr to not clobber stdout in case the cli command // outputs structured data (e.g. as config:schema does) process.stderr.write( diff --git a/packages/cli/src/serve.test.ts b/packages/cli/src/serve.test.ts deleted file mode 100644 index 7256e7c4ad..0000000000 --- a/packages/cli/src/serve.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { spawn, SpawnOptionsWithoutStdio } from 'child_process'; -import EventEmitter from 'events'; -import fetch from 'node-fetch'; -import path from 'path'; -import getPort from 'get-port'; - -jest.setTimeout(150000); - -const executeCommand = ( - command: string, - args: string[], - options?: SpawnOptionsWithoutStdio, - events?: EventEmitter, - eventConfig?: { - killRegex?: RegExp; - signalRegex?: RegExp[]; - }, -): Promise<{ - exit: number; - stdout: string; - stderr: string; -}> => { - return new Promise((resolve, reject) => { - const stdout: Buffer[] = []; - const stderr: Buffer[] = []; - - const shell = process.platform === 'win32'; - const proc = spawn(command, args, { ...options, shell }); - - proc.stdout?.on('data', data => { - stdout.push(Buffer.from(data)); - }); - - proc.stderr?.on('data', data => { - stderr.push(Buffer.from(data)); - }); - - /** - * Set an interval to check if we should kill the process. - * This was the easiest way I could think of of testing across two processes. - */ - let intervalId: NodeJS.Timer | undefined = undefined; - if (eventConfig) { - intervalId = setInterval(() => { - const stdoutStr = Buffer.concat(stdout).toString('utf8'); - if (eventConfig.killRegex?.test(stdoutStr)) { - proc.kill('SIGINT'); - } else if ( - eventConfig.signalRegex?.some(regex => regex.test(stdoutStr)) - ) { - events?.emit('hit'); - } - }, 1000); - } - - const clearEventInterval = () => { - if (intervalId) { - try { - clearInterval(intervalId); - } catch (err) { - console.error(err); - } - } - }; - - /** - * Need a way to kill the process from another process. - */ - events?.on('stop', signal => { - clearEventInterval(); - proc.kill(signal); - }); - - proc.on('error', (...errorArgs) => { - clearEventInterval(); - reject(errorArgs); - }); - proc.on('exit', code => { - clearEventInterval(); - resolve({ - exit: code ?? 0, - stdout: Buffer.concat(stdout).toString('utf8'), - stderr: Buffer.concat(stderr).toString('utf8'), - }); - }); - }); -}; - -const testProjectDir = path.resolve( - __dirname, - '__fixtures__/test-project/packages/app', -); - -describe('end-to-end', () => { - const entryPoint = path.resolve(__dirname, '../bin/backstage-cli'); - - it('builds frontend with correct url overrides', async () => { - const buildProc = await executeCommand( - entryPoint, - ['package', 'build', '--public-path', '/test', '--backend-url', '/api'], - { - cwd: testProjectDir, - }, - ); - expect(buildProc.stderr).toContain( - 'Loaded config from app-config.yaml, cli', - ); - - expect(buildProc.exit).toEqual(0); - }); - - it('starts frontend on correct url', async () => { - expect.assertions(4); - - const startEmitter = new EventEmitter(); - const frontendPort = await getPort(); - startEmitter.on('hit', async () => { - const response = await fetch( - `http://localhost:${frontendPort}/test/catalog`, - ); - const text = await response.text(); - startEmitter.emit('stop', 'SIGINT'); - expect(response.status).toBe(200); - expect(text.length).toBeGreaterThan(0); - expect(text).toContain('id="root"'); - }); - const startProc = await executeCommand( - entryPoint, - ['package', 'start'], - { - cwd: testProjectDir, - env: { - ...process.env, - PORT: `${frontendPort}`, - BACKEND_PORT: `${await getPort()}`, - }, - }, - startEmitter, - { - // Need to match console colors as well, so use .* instead of just ' '. - signalRegex: [/compiled.*successfully/], - }, - ); - - expect(startProc.exit).toEqual(0); - }); -});