diff --git a/packages/cli/src/commands/create-app/createApp.ts b/packages/cli/src/commands/create-app/createApp.ts index 219406704d..cb34dc0686 100644 --- a/packages/cli/src/commands/create-app/createApp.ts +++ b/packages/cli/src/commands/create-app/createApp.ts @@ -86,26 +86,22 @@ export async function moveApp( }); } -async function addPackageResolutions(rootDir: string, appDir: string) { - process.chdir(appDir); - - const packageFileContent = await fs.readFile('package.json', 'utf-8'); +async function addPackageResolutions(appDir: string) { + const pkgJsonPath = resolvePath(appDir, 'package.json'); + const packageFileContent = await fs.readFile(pkgJsonPath, 'utf-8'); const packageFileJson = JSON.parse(packageFileContent); - if (packageFileJson.resolutions) { - throw new Error('package.json already contains resolutions'); - } - packageFileJson.resolutions = {}; + packageFileJson.resolutions = packageFileJson.resolutions || {}; const packages = ['cli', 'core', 'test-utils', 'test-utils-core', 'theme']; for (const pkg of packages) { await Task.forItem('adding', `${pkg} link to package.json`, async () => { - const pkgPath = require('path').join(rootDir, 'packages', pkg); + const pkgPath = paths.resolveOwnRoot('packages', pkg); packageFileJson.resolutions[`@backstage/${pkg}`] = `file:${pkgPath}`; const newContents = `${JSON.stringify(packageFileJson, null, 2)}\n`; - await fs.writeFile('package.json', newContents, 'utf-8').catch(error => { + await fs.writeFile(pkgJsonPath, newContents, 'utf-8').catch(error => { throw new Error( `Failed to add resolutions to package.json: ${error.message}`, ); @@ -157,10 +153,7 @@ export default async () => { // e2e testing needs special treatment if (process.env.BACKSTAGE_E2E_CLI_TEST) { Task.section('Linking packages locally for e2e tests'); - const rootDir = process.env.CI - ? resolvePath(process.env.GITHUB_WORKSPACE!) - : resolvePath(__dirname, '..', '..', '..'); - await addPackageResolutions(rootDir, appDir); + await addPackageResolutions(appDir); } Task.section('Building the app'); diff --git a/packages/cli/src/helpers/paths.ts b/packages/cli/src/helpers/paths.ts index 173e77e1c5..e7b351ec16 100644 --- a/packages/cli/src/helpers/paths.ts +++ b/packages/cli/src/helpers/paths.ts @@ -25,6 +25,9 @@ export type Paths = { // Root dir of the cli itself, containing package.json ownDir: string; + // Monorepo root dir of the cli itself. Only accessible when running inside Backstage repo. + ownRoot: string; + // The location of the app that the cli is being executed in targetDir: string; @@ -34,6 +37,9 @@ export type Paths = { // Resolve a path relative to own repo resolveOwn: ResolveFunc; + // Resolve a path relative to own monorepo root. Only accessible when running inside Backstage repo. + resolveOwnRoot: ResolveFunc; + // Resolve a path relative to the app resolveTarget: ResolveFunc; @@ -91,10 +97,31 @@ export function findOwnDir() { return resolvePath(__dirname, path); } +// Finds the root of the monorepo that the cli exists in. Only accessible when running inside Backstage repo. +export function findOwnRootPath(ownDir: string) { + const isLocal = fs.pathExistsSync(resolvePath(ownDir, 'src')); + if (!isLocal) { + throw new Error( + 'Tried to access monorepo package root dir outside of Backstage repository', + ); + } + + return resolvePath(ownDir, '../..'); +} + export function findPaths(): Paths { const ownDir = findOwnDir(); const targetDir = fs.realpathSync(process.cwd()); + // Lazy load this as it will throw an error if we're not inside the Backstage repo. + let ownRoot = ''; + const getOwnRoot = () => { + if (!ownRoot) { + ownRoot = findOwnRootPath(ownDir); + } + return ownRoot; + }; + // We're not always running in a monorepo, so we lazy init this to only crash commands // that require a monorepo when we're not in one. let targetRoot = ''; @@ -107,11 +134,15 @@ export function findPaths(): Paths { return { ownDir, + get ownRoot() { + return getOwnRoot(); + }, targetDir, get targetRoot() { return getTargetRoot(); }, resolveOwn: (...paths) => resolvePath(ownDir, ...paths), + resolveOwnRoot: (...paths) => resolvePath(getOwnRoot(), ...paths), resolveTarget: (...paths) => resolvePath(targetDir, ...paths), resolveTargetRoot: (...paths) => resolvePath(getTargetRoot(), ...paths), };