From 4ebc9fd277fad6f14ada6d2570d2cdbf017778e6 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 20 Oct 2021 14:12:02 +0200 Subject: [PATCH 1/4] Create .backstage-version file Signed-off-by: Vincenzo Scamporlino --- .changeset/healthy-kids-mate.md | 7 +++++++ packages/create-app/src/createApp.ts | 6 +++--- packages/create-app/src/index.ts | 2 +- packages/create-app/src/lib/tasks.ts | 7 ++++++- 4 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 .changeset/healthy-kids-mate.md diff --git a/.changeset/healthy-kids-mate.md b/.changeset/healthy-kids-mate.md new file mode 100644 index 0000000000..b9acfc24b0 --- /dev/null +++ b/.changeset/healthy-kids-mate.md @@ -0,0 +1,7 @@ +--- +'@backstage/create-app': patch +--- + +Add .bsversion file + +`@backstage/create-app` will create a new `.bsversion` file containing the version of `@backstage/create-app` that has been used for creating the application. diff --git a/packages/create-app/src/createApp.ts b/packages/create-app/src/createApp.ts index 8823e6c858..d301792380 100644 --- a/packages/create-app/src/createApp.ts +++ b/packages/create-app/src/createApp.ts @@ -30,7 +30,7 @@ import { templatingTask, } from './lib/tasks'; -export default async (cmd: Command): Promise => { +export default async (cmd: Command, version: string): Promise => { /* eslint-disable-next-line no-restricted-syntax */ const paths = findPaths(__dirname); @@ -82,7 +82,7 @@ export default async (cmd: Command): Promise => { await checkPathExistsTask(appDir); Task.section('Preparing files'); - await templatingTask(templateDir, cmd.path, answers); + await templatingTask(templateDir, cmd.path, answers, version); } else { // Template to temporary location, and then move files @@ -93,7 +93,7 @@ export default async (cmd: Command): Promise => { await createTemporaryAppFolderTask(tempDir); Task.section('Preparing files'); - await templatingTask(templateDir, tempDir, answers); + await templatingTask(templateDir, tempDir, answers, version); Task.section('Moving to final location'); await moveAppTask(tempDir, appDir, answers.name); diff --git a/packages/create-app/src/index.ts b/packages/create-app/src/index.ts index 1f37be56d2..8a2da2fad8 100644 --- a/packages/create-app/src/index.ts +++ b/packages/create-app/src/index.ts @@ -38,7 +38,7 @@ const main = (argv: string[]) => { '--skip-install', 'Skip the install and builds steps after creating the app', ) - .action(createApp); + .action(cmd => createApp(cmd, version)); program.parse(argv); }; diff --git a/packages/create-app/src/lib/tasks.ts b/packages/create-app/src/lib/tasks.ts index 0b3ca63728..aefff2c9ee 100644 --- a/packages/create-app/src/lib/tasks.ts +++ b/packages/create-app/src/lib/tasks.ts @@ -19,7 +19,7 @@ import fs from 'fs-extra'; import handlebars from 'handlebars'; import ora from 'ora'; import recursive from 'recursive-readdir'; -import { basename, dirname, resolve as resolvePath } from 'path'; +import { basename, dirname, join, resolve as resolvePath } from 'path'; import { exec as execCb } from 'child_process'; import { packageVersions } from './versions'; import { promisify } from 'util'; @@ -79,6 +79,7 @@ export async function templatingTask( templateDir: string, destinationDir: string, context: any, + version: string, ) { const files = await recursive(templateDir).catch(error => { throw new Error(`Failed to read template directory: ${error.message}`); @@ -125,6 +126,10 @@ export async function templatingTask( }); } } + const bsVersionFileName = '.bsversion'; + await Task.forItem('creating', bsVersionFileName, () => + fs.writeFile(join(destinationDir, bsVersionFileName), version), + ); } /** From 085edcfc5fa3756eb2a6d6744ea1276ee4c770cb Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 20 Oct 2021 20:36:35 +0200 Subject: [PATCH 2/4] fix comments Signed-off-by: Vincenzo Scamporlino --- .changeset/healthy-kids-mate.md | 4 ++-- packages/create-app/src/lib/tasks.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.changeset/healthy-kids-mate.md b/.changeset/healthy-kids-mate.md index b9acfc24b0..d94e380ee5 100644 --- a/.changeset/healthy-kids-mate.md +++ b/.changeset/healthy-kids-mate.md @@ -2,6 +2,6 @@ '@backstage/create-app': patch --- -Add .bsversion file +Add .backstage-version file -`@backstage/create-app` will create a new `.bsversion` file containing the version of `@backstage/create-app` that has been used for creating the application. +`@backstage/create-app` will create a new `.backstage-version` file containing the version of `@backstage/create-app` that has been used for creating the application. diff --git a/packages/create-app/src/lib/tasks.ts b/packages/create-app/src/lib/tasks.ts index aefff2c9ee..c8209435bf 100644 --- a/packages/create-app/src/lib/tasks.ts +++ b/packages/create-app/src/lib/tasks.ts @@ -126,9 +126,12 @@ export async function templatingTask( }); } } - const bsVersionFileName = '.bsversion'; - await Task.forItem('creating', bsVersionFileName, () => - fs.writeFile(join(destinationDir, bsVersionFileName), version), + const backstageVersionFileName = '.backstage-version'; + await Task.forItem('creating', backstageVersionFileName, () => + fs.writeFile( + join(destinationDir, backstageVersionFileName), + `${version}\n`, + ), ); } From b6b4d4b73fdefde1ddf3dc3acb1f204e677b7a60 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 10 Nov 2021 23:51:42 +0100 Subject: [PATCH 3/4] Bump .backstage.json version Signed-off-by: Vincenzo Scamporlino --- .changeset/healthy-kids-mate.md | 4 +- packages/cli-common/api-report.md | 5 ++ packages/cli-common/src/index.ts | 2 +- packages/cli-common/src/paths.ts | 5 ++ .../cli/src/commands/versions/bump.test.ts | 65 ++++++++++++++++++- packages/cli/src/commands/versions/bump.ts | 40 ++++++++++++ packages/create-app/src/lib/tasks.ts | 8 +-- 7 files changed, 121 insertions(+), 8 deletions(-) diff --git a/.changeset/healthy-kids-mate.md b/.changeset/healthy-kids-mate.md index d94e380ee5..3ed8172199 100644 --- a/.changeset/healthy-kids-mate.md +++ b/.changeset/healthy-kids-mate.md @@ -1,7 +1,9 @@ --- +'@backstage/cli': patch +'@backstage/cli-common': patch '@backstage/create-app': patch --- Add .backstage-version file -`@backstage/create-app` will create a new `.backstage-version` file containing the version of `@backstage/create-app` that has been used for creating the application. +`@backstage/create-app` will create a new `.backstage.json` file containing the version of `@backstage/create-app` used for creating the application. diff --git a/packages/cli-common/api-report.md b/packages/cli-common/api-report.md index 4b61cfedb8..e100b40cc2 100644 --- a/packages/cli-common/api-report.md +++ b/packages/cli-common/api-report.md @@ -3,6 +3,11 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +// Warning: (ae-missing-release-tag) "BACKSTAGE_JSON" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const BACKSTAGE_JSON = '.backstage.json'; + // @public export function findPaths(searchDir: string): Paths; diff --git a/packages/cli-common/src/index.ts b/packages/cli-common/src/index.ts index e7cda19514..8809764b43 100644 --- a/packages/cli-common/src/index.ts +++ b/packages/cli-common/src/index.ts @@ -20,6 +20,6 @@ * @packageDocumentation */ -export { findPaths } from './paths'; +export { findPaths, BACKSTAGE_JSON } from './paths'; export { isChildPath } from './isChildPath'; export type { Paths, ResolveFunc } from './paths'; diff --git a/packages/cli-common/src/paths.ts b/packages/cli-common/src/paths.ts index c9f486752f..2f46fee27a 100644 --- a/packages/cli-common/src/paths.ts +++ b/packages/cli-common/src/paths.ts @@ -167,3 +167,8 @@ export function findPaths(searchDir: string): Paths { resolveTargetRoot: (...paths) => resolvePath(getTargetRoot(), ...paths), }; } + +/** + * The name of the backstage's config file + */ +export const BACKSTAGE_JSON = '.backstage.json'; diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts index 96bd1ab6fd..6759928458 100644 --- a/packages/cli/src/commands/versions/bump.test.ts +++ b/packages/cli/src/commands/versions/bump.test.ts @@ -20,7 +20,7 @@ import { resolve as resolvePath } from 'path'; import { paths } from '../../lib/paths'; import { mapDependencies } from '../../lib/versioning'; import * as runObj from '../../lib/run'; -import bump from './bump'; +import bump, { bumpBackstageJsonVersion } from './bump'; import { withLogCollector } from '@backstage/test-utils'; // Remove log coloring to simplify log matching @@ -141,7 +141,7 @@ describe('bump', () => { 'Version bump complete!', ]); - expect(runObj.runPlain).toHaveBeenCalledTimes(3); + expect(runObj.runPlain).toHaveBeenCalledTimes(4); expect(runObj.runPlain).toHaveBeenCalledWith( 'yarn', 'info', @@ -245,3 +245,64 @@ describe('bump', () => { }); }); }); + +describe('bumpBackstageJsonVersion', () => { + afterEach(() => { + mockFs.restore(); + jest.resetAllMocks(); + }); + + it('should bump version in .backstage.json', async () => { + mockFs({ + '/.backstage.json': JSON.stringify({ version: '0.0.1' }), + }); + paths.targetDir = '/'; + const latest = '1.4.1'; + jest + .spyOn(paths, 'resolveTargetRoot') + .mockImplementation((...path) => resolvePath('/', ...path)); + jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) => + JSON.stringify({ + type: 'inspect', + data: { + name, + 'dist-tags': { + latest, + }, + }, + }), + ); + jest.spyOn(runObj, 'run').mockResolvedValue(undefined); + + await bumpBackstageJsonVersion(); + + const json = await fs.readJson('/.backstage.json'); + expect(json).toEqual({ version: '1.4.1' }); + }); + + it("should create .backstage.json if doesn't exist", async () => { + mockFs({}); + paths.targetDir = '/'; + const latest = '1.4.1'; + jest + .spyOn(paths, 'resolveTargetRoot') + .mockImplementation((...path) => resolvePath('/', ...path)); + jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) => + JSON.stringify({ + type: 'inspect', + data: { + name, + 'dist-tags': { + latest, + }, + }, + }), + ); + jest.spyOn(runObj, 'run').mockResolvedValue(undefined); + + await bumpBackstageJsonVersion(); + + const json = await fs.readJson('/.backstage.json'); + expect(json).toEqual({ version: '1.4.1' }); + }); +}); diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index 7b967839b2..0e37895cad 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -27,6 +27,7 @@ import { Lockfile, } from '../../lib/versioning'; import { includedFilter, forbiddenDuplicatesFilter } from './lint'; +import { BACKSTAGE_JSON } from '@backstage/cli-common'; const DEP_TYPES = [ 'dependencies', @@ -182,6 +183,10 @@ export default async () => { await fs.writeJson(pkgPath, pkgJson, { spaces: 2 }); }); + console.log(); + + await bumpBackstageJsonVersion(); + console.log(); console.log( `Running ${chalk.blue('yarn install')} to install new versions`, @@ -271,6 +276,41 @@ function createVersionFinder() { }; } +export async function bumpBackstageJsonVersion() { + const backstageJsonPath = paths.resolveTargetRoot(BACKSTAGE_JSON); + const backstageJson = await fs.readJSON(backstageJsonPath).catch(e => { + if (e.code === 'ENOENT') { + // gracefully continue in case the file doesn't exist + return; + } + throw e; + }); + + const info = await fetchPackageInfo('@backstage/create-app'); + const { latest } = info['dist-tags']; + + if (backstageJson?.version === latest) { + return; + } + + console.log( + chalk.yellow( + typeof backstageJson === 'undefined' + ? 'Creating .backstage.json' + : 'Bumping version in .backstage.json', + ), + ); + + await fs.writeJson( + backstageJsonPath, + { ...backstageJson, version: latest }, + { + spaces: 2, + encoding: 'utf8', + }, + ); +} + async function workerThreads( count: number, items: IterableIterator, diff --git a/packages/create-app/src/lib/tasks.ts b/packages/create-app/src/lib/tasks.ts index c8209435bf..86e178de52 100644 --- a/packages/create-app/src/lib/tasks.ts +++ b/packages/create-app/src/lib/tasks.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { BACKSTAGE_JSON } from '@backstage/cli-common'; import chalk from 'chalk'; import fs from 'fs-extra'; import handlebars from 'handlebars'; @@ -126,11 +127,10 @@ export async function templatingTask( }); } } - const backstageVersionFileName = '.backstage-version'; - await Task.forItem('creating', backstageVersionFileName, () => + await Task.forItem('creating', BACKSTAGE_JSON, () => fs.writeFile( - join(destinationDir, backstageVersionFileName), - `${version}\n`, + join(destinationDir, BACKSTAGE_JSON), + `{\n "version": ${JSON.stringify(version)}\n}\n`, ), ); } From 677bfc2dd00ef382abb8025d3e3632aaa08615f8 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 15 Nov 2021 16:32:46 +0100 Subject: [PATCH 4/4] fix comments Signed-off-by: Vincenzo Scamporlino --- .changeset/cyan-bottles-hug.md | 8 ++++++++ .changeset/healthy-kids-mate.md | 10 ++++++---- packages/cli-common/api-report.md | 4 +--- packages/cli-common/src/paths.ts | 4 +++- packages/cli/src/commands/versions/bump.test.ts | 10 +++++----- packages/cli/src/commands/versions/bump.ts | 4 ++-- packages/create-app/src/createApp.test.ts | 6 +++--- packages/create-app/src/lib/tasks.test.ts | 5 ++++- 8 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 .changeset/cyan-bottles-hug.md diff --git a/.changeset/cyan-bottles-hug.md b/.changeset/cyan-bottles-hug.md new file mode 100644 index 0000000000..7c2083c4ff --- /dev/null +++ b/.changeset/cyan-bottles-hug.md @@ -0,0 +1,8 @@ +--- +'@backstage/cli': patch +'@backstage/cli-common': patch +--- + +Keep backstage.json in sync + +The `versions:bump` script now takes care about updating the `version` property inside `backstage.json` file. The file is created if is not present. diff --git a/.changeset/healthy-kids-mate.md b/.changeset/healthy-kids-mate.md index 3ed8172199..10e8222a15 100644 --- a/.changeset/healthy-kids-mate.md +++ b/.changeset/healthy-kids-mate.md @@ -1,9 +1,11 @@ --- -'@backstage/cli': patch -'@backstage/cli-common': patch '@backstage/create-app': patch --- -Add .backstage-version file +Create backstage.json file -`@backstage/create-app` will create a new `.backstage.json` file containing the version of `@backstage/create-app` used for creating the application. +`@backstage/create-app` will create a new `backstage.json` file. At this point, the file will contain a `version` property, representing the version of `@backstage/create-app` used for creating the application. If the backstage's application has been bootstrapped using an older version of `@backstage/create-app`, the `backstage.json` file can be created and kept in sync, together with all the changes of the latest version of backstage, by running the following script: + +```bash +yarn backstage-cli versions:bump +``` diff --git a/packages/cli-common/api-report.md b/packages/cli-common/api-report.md index e100b40cc2..34f56c3f19 100644 --- a/packages/cli-common/api-report.md +++ b/packages/cli-common/api-report.md @@ -3,10 +3,8 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -// Warning: (ae-missing-release-tag) "BACKSTAGE_JSON" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public -export const BACKSTAGE_JSON = '.backstage.json'; +export const BACKSTAGE_JSON = 'backstage.json'; // @public export function findPaths(searchDir: string): Paths; diff --git a/packages/cli-common/src/paths.ts b/packages/cli-common/src/paths.ts index 2f46fee27a..2b1ca34ca5 100644 --- a/packages/cli-common/src/paths.ts +++ b/packages/cli-common/src/paths.ts @@ -170,5 +170,7 @@ export function findPaths(searchDir: string): Paths { /** * The name of the backstage's config file + * + * @public */ -export const BACKSTAGE_JSON = '.backstage.json'; +export const BACKSTAGE_JSON = 'backstage.json'; diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts index 6759928458..bc4049f1f4 100644 --- a/packages/cli/src/commands/versions/bump.test.ts +++ b/packages/cli/src/commands/versions/bump.test.ts @@ -252,9 +252,9 @@ describe('bumpBackstageJsonVersion', () => { jest.resetAllMocks(); }); - it('should bump version in .backstage.json', async () => { + it('should bump version in backstage.json', async () => { mockFs({ - '/.backstage.json': JSON.stringify({ version: '0.0.1' }), + '/backstage.json': JSON.stringify({ version: '0.0.1' }), }); paths.targetDir = '/'; const latest = '1.4.1'; @@ -276,11 +276,11 @@ describe('bumpBackstageJsonVersion', () => { await bumpBackstageJsonVersion(); - const json = await fs.readJson('/.backstage.json'); + const json = await fs.readJson('/backstage.json'); expect(json).toEqual({ version: '1.4.1' }); }); - it("should create .backstage.json if doesn't exist", async () => { + it("should create backstage.json if doesn't exist", async () => { mockFs({}); paths.targetDir = '/'; const latest = '1.4.1'; @@ -302,7 +302,7 @@ describe('bumpBackstageJsonVersion', () => { await bumpBackstageJsonVersion(); - const json = await fs.readJson('/.backstage.json'); + const json = await fs.readJson('/backstage.json'); expect(json).toEqual({ version: '1.4.1' }); }); }); diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index 0e37895cad..3898c1654a 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -296,8 +296,8 @@ export async function bumpBackstageJsonVersion() { console.log( chalk.yellow( typeof backstageJson === 'undefined' - ? 'Creating .backstage.json' - : 'Bumping version in .backstage.json', + ? `Creating ${BACKSTAGE_JSON}` + : `Bumping version in ${BACKSTAGE_JSON}`, ), ); diff --git a/packages/create-app/src/createApp.test.ts b/packages/create-app/src/createApp.test.ts index 7b3b8f124c..5e54fac31e 100644 --- a/packages/create-app/src/createApp.test.ts +++ b/packages/create-app/src/createApp.test.ts @@ -59,7 +59,7 @@ describe('command entrypoint', () => { test('should call expected tasks with no path option', async () => { const cmd = {} as unknown as Command; - await createApp(cmd); + await createApp(cmd, '1.0.0'); expect(checkAppExistsMock).toHaveBeenCalled(); expect(createTemporaryAppFolderMock).toHaveBeenCalled(); expect(templatingMock).toHaveBeenCalled(); @@ -69,7 +69,7 @@ describe('command entrypoint', () => { it('should call expected tasks with path option', async () => { const cmd = { path: 'myDirectory' } as unknown as Command; - await createApp(cmd); + await createApp(cmd, '1.0.0'); expect(checkPathExistsMock).toHaveBeenCalled(); expect(templatingMock).toHaveBeenCalled(); expect(buildAppMock).toHaveBeenCalled(); @@ -77,7 +77,7 @@ describe('command entrypoint', () => { it('should not call `buildAppTask` when `skipInstall` is supplied', async () => { const cmd = { skipInstall: true } as unknown as Command; - await createApp(cmd); + await createApp(cmd, '1.0.0'); expect(buildAppMock).not.toHaveBeenCalled(); }); }); diff --git a/packages/create-app/src/lib/tasks.test.ts b/packages/create-app/src/lib/tasks.test.ts index d69526cb49..24df97c694 100644 --- a/packages/create-app/src/lib/tasks.test.ts +++ b/packages/create-app/src/lib/tasks.test.ts @@ -195,9 +195,12 @@ describe('templatingTask', () => { name: 'SuperCoolBackstageInstance', dbTypeSqlite: true, }; - await templatingTask(templateDir, destinationDir, context); + await templatingTask(templateDir, destinationDir, context, '1.0.0'); expect(fs.existsSync('templatedApp/package.json')).toBe(true); expect(fs.existsSync('templatedApp/.dockerignore')).toBe(true); + await expect(fs.readJson('templatedApp/backstage.json')).resolves.toEqual({ + version: '1.0.0', + }); // catalog was populated with `context.name` expect( fs.readFileSync('templatedApp/catalog-info.yaml', 'utf-8'),