diff --git a/packages/cli/package.json b/packages/cli/package.json index 8a330d861b..89c0520b59 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -108,6 +108,13 @@ "yn": "^4.0.0" }, "devDependencies": { + "@backstage/backend-common": "^0.1.1-alpha.25", + "@backstage/cli": "^0.1.1-alpha.25", + "@backstage/config": "^0.1.1-alpha.25", + "@backstage/core": "^0.1.1-alpha.25", + "@backstage/dev-utils": "^0.1.1-alpha.25", + "@backstage/test-utils": "^0.1.1-alpha.25", + "@backstage/theme": "^0.1.1-alpha.25", "@types/diff": "^4.0.2", "@types/fs-extra": "^9.0.1", "@types/html-webpack-plugin": "^3.2.2", diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 057e36ceb9..b603e732b8 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -28,8 +28,8 @@ import { getCodeownersFilePath, } from '../../lib/codeowners'; import { paths } from '../../lib/paths'; +import { versions } from '../../lib/version'; import { Task, templatingTask } from '../../lib/tasks'; -import { version as backstageVersion } from '../../lib/version'; const exec = promisify(execCb); @@ -243,7 +243,7 @@ export default async (cmd: Command) => { ? paths.resolveTargetRoot('plugins', pluginId) : paths.resolveTargetRoot(pluginId); const ownerIds = parseOwnerIds(answers.owner); - const { version } = isMonoRepo + const { version: pluginVersion } = isMonoRepo ? await fs.readJson(paths.resolveTargetRoot('lerna.json')) : { version: '0.1.0' }; @@ -259,14 +259,18 @@ export default async (cmd: Command) => { Task.section('Preparing files'); - await templatingTask(templateDir, tempDir, { - ...answers, - version, - backstageVersion, - name, - privatePackage, - npmRegistry, - }); + await templatingTask( + templateDir, + tempDir, + { + ...answers, + pluginVersion, + name, + privatePackage, + npmRegistry, + }, + versions, + ); Task.section('Moving to final location'); await movePlugin(tempDir, pluginDir, pluginId); @@ -276,7 +280,7 @@ export default async (cmd: Command) => { if ((await fs.pathExists(appPackage)) && !cmd.backend) { Task.section('Adding plugin as dependency in app'); - await addPluginDependencyToApp(paths.targetRoot, name, version); + await addPluginDependencyToApp(paths.targetRoot, name, pluginVersion); Task.section('Import plugin in app'); await addPluginToApp(paths.targetRoot, pluginId, name); diff --git a/packages/cli/src/commands/plugin/diff.ts b/packages/cli/src/commands/plugin/diff.ts index 012c061f74..416d67869b 100644 --- a/packages/cli/src/commands/plugin/diff.ts +++ b/packages/cli/src/commands/plugin/diff.ts @@ -25,13 +25,12 @@ import { yesPromptFunc, } from '../../lib/diff'; import { paths } from '../../lib/paths'; -import { version as backstageVersion } from '../../lib/version'; export type PluginData = { id: string; name: string; privatePackage: string; - version: string; + pluginVersion: string; npmRegistry: string; }; @@ -62,10 +61,7 @@ export default async (cmd: Command) => { } const data = await readPluginData(); - const templateFiles = await diffTemplateFiles('default-plugin', { - backstageVersion, - ...data, - }); + const templateFiles = await diffTemplateFiles('default-plugin', data); await handleAllFiles(fileHandlers, templateFiles, promptFunc); await finalize(); }; @@ -74,13 +70,13 @@ export default async (cmd: Command) => { async function readPluginData(): Promise { let name: string; let privatePackage: string; - let version: string; + let pluginVersion: string; let npmRegistry: string; try { const pkg = require(paths.resolveTarget('package.json')); name = pkg.name; privatePackage = pkg.private; - version = pkg.version; + pluginVersion = pkg.version; const scope = name.split('/')[0]; if (`${scope}:registry` in pkg.publishConfig) { const registryURL = pkg.publishConfig[`${scope}:registry`]; @@ -102,5 +98,5 @@ async function readPluginData(): Promise { const id = pluginIdMatch[1]; - return { id, name, privatePackage, version, npmRegistry }; + return { id, name, privatePackage, pluginVersion, npmRegistry }; } diff --git a/packages/cli/src/lib/diff/read.ts b/packages/cli/src/lib/diff/read.ts index 75e1b86d24..a4a7355d2d 100644 --- a/packages/cli/src/lib/diff/read.ts +++ b/packages/cli/src/lib/diff/read.ts @@ -24,6 +24,7 @@ import handlebars from 'handlebars'; import recursiveReadDir from 'recursive-readdir'; import { paths } from '../paths'; import { FileDiff } from './types'; +import { versions } from '../../lib/version'; export type TemplatedFile = { path: string; @@ -40,7 +41,16 @@ async function readTemplateFile( return contents; } - return handlebars.compile(contents)(templateVars); + return handlebars.compile(contents)(templateVars, { + helpers: { + version(name: string) { + if (versions[name]) { + return versions[name]; + } + throw new Error(`No version available for package ${name}`); + }, + }, + }); } async function readTemplate( diff --git a/packages/cli/src/lib/tasks.test.ts b/packages/cli/src/lib/tasks.test.ts index d80fcbe9b0..a42902c60b 100644 --- a/packages/cli/src/lib/tasks.test.ts +++ b/packages/cli/src/lib/tasks.test.ts @@ -33,7 +33,8 @@ describe('templatingTask', () => { // Files content const testFileContent = 'testing'; - const testVersionFileContent = 'version: {{version}}'; + const testVersionFileContent = + "version: {{pluginVersion}} {{version 'mock-pkg'}}"; mockFs({ [tmplDir]: { @@ -45,15 +46,20 @@ describe('templatingTask', () => { [destDir]: {}, }); - await templatingTask(tmplDir, destDir, { - version: '0.0.0', - }); + await templatingTask( + tmplDir, + destDir, + { + version: '0.0.0', + }, + { 'mock-pkg': '0.1.2' }, + ); await expect( fs.readFile(resolvePath(destDir, 'test.txt'), 'utf8'), ).resolves.toBe(testFileContent); await expect( fs.readFile(resolvePath(destDir, 'sub/version.txt'), 'utf8'), - ).resolves.toBe('version: 0.0.0'); + ).resolves.toBe('version: 0.0.0 0.1.2'); }); }); diff --git a/packages/cli/src/lib/tasks.ts b/packages/cli/src/lib/tasks.ts index 80dc1a5ed7..4b92c6b9cb 100644 --- a/packages/cli/src/lib/tasks.ts +++ b/packages/cli/src/lib/tasks.ts @@ -69,6 +69,7 @@ export async function templatingTask( templateDir: string, destinationDir: string, context: any, + versions: { [name: string]: string }, ) { const files = await recursive(templateDir).catch(error => { throw new Error(`Failed to read template directory: ${error.message}`); @@ -85,7 +86,19 @@ export async function templatingTask( const template = await fs.readFile(file); const compiled = handlebars.compile(template.toString()); - const contents = compiled({ name: basename(destination), ...context }); + const contents = compiled( + { name: basename(destination), ...context }, + { + helpers: { + version(name: string) { + if (versions[name]) { + return versions[name]; + } + throw new Error(`No version available for package ${name}`); + }, + }, + }, + ); await fs.writeFile(destination, contents).catch(error => { throw new Error( diff --git a/packages/cli/src/lib/version.ts b/packages/cli/src/lib/version.ts index 24734b87cc..5cc3eb7fbb 100644 --- a/packages/cli/src/lib/version.ts +++ b/packages/cli/src/lib/version.ts @@ -17,6 +17,38 @@ import fs from 'fs-extra'; import { paths } from './paths'; +/* eslint-disable import/no-extraneous-dependencies,monorepo/no-internal-import */ +/* +This is a list of all packages used by the templates. If dependencies are added or removed, +this list should be updated as well. + +The list, and the accompanying devDependencies entries, are here to ensure correct versioning +and bumping of this package. Without this list the version would not be bumped unless we +manually trigger a release. + +This does not create an actual dependency on these packages and does not bring in any code. +Rollup will extract the value of the version field in each package at build time without +leaving any imports in place. +*/ + +import { version as backendCommon } from '@backstage/backend-common/package.json'; +import { version as cli } from '@backstage/cli/package.json'; +import { version as config } from '@backstage/config/package.json'; +import { version as core } from '@backstage/core/package.json'; +import { version as devUtils } from '@backstage/dev-utils/package.json'; +import { version as testUtils } from '@backstage/test-utils/package.json'; +import { version as theme } from '@backstage/theme/package.json'; + +export const versions: { [name: string]: string } = { + '@backstage/backend-common': backendCommon, + '@backstage/cli': cli, + '@backstage/config': config, + '@backstage/core': core, + '@backstage/dev-utils': devUtils, + '@backstage/test-utils': testUtils, + '@backstage/theme': theme, +}; + export function findVersion() { const pkgContent = fs.readFileSync(paths.resolveOwn('package.json'), 'utf8'); return JSON.parse(pkgContent).version; diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index 1a73515622..b762bb9645 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -1,6 +1,6 @@ { "name": "{{name}}", - "version": "{{version}}", + "version": "{{pluginVersion}}", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,8 +23,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^{{backstageVersion}}", - "@backstage/config": "^{{backstageVersion}}", + "@backstage/backend-common": "^{{version '@backstage/backend-common'}}", + "@backstage/config": "^{{version '@backstage/config'}}", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -33,7 +33,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^{{backstageVersion}}", + "@backstage/cli": "^{{version '@backstage/cli'}}", "@types/supertest": "^2.0.8", "supertest": "^4.0.2", "msw": "^0.21.2" diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 41f942fb79..a3f924408a 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -1,6 +1,6 @@ { "name": "{{name}}", - "version": "{{version}}", + "version": "{{pluginVersion}}", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,8 +24,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^{{backstageVersion}}", - "@backstage/theme": "^{{backstageVersion}}", + "@backstage/core": "^{{version '@backstage/core'}}", + "@backstage/theme": "^{{version '@backstage/theme'}}", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -34,9 +34,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^{{backstageVersion}}", - "@backstage/dev-utils": "^{{backstageVersion}}", - "@backstage/test-utils": "^{{backstageVersion}}", + "@backstage/cli": "^{{version '@backstage/cli'}}", + "@backstage/dev-utils": "^{{version '@backstage/dev-utils'}}", + "@backstage/test-utils": "^{{version '@backstage/test-utils'}}", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7",