From 80724f644479fec3e3239e7b96de85a1486c1e58 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 11 Jan 2022 14:40:36 +0100 Subject: [PATCH 1/6] Initial prerelease work Signed-off-by: Johan Haals --- .changeset/patched.json | 3 + .github/workflows/changeset.yml | 2 + .github/workflows/master.yml | 2 +- SECURITY.md | 17 +++++ scripts/pre-release.js | 124 ++++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 .changeset/patched.json create mode 100644 scripts/pre-release.js diff --git a/.changeset/patched.json b/.changeset/patched.json new file mode 100644 index 0000000000..1029c8f106 --- /dev/null +++ b/.changeset/patched.json @@ -0,0 +1,3 @@ +{ + "currentReleaseVersion": {} +} diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index a649904a01..e960d45744 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -13,6 +13,8 @@ jobs: - uses: actions/checkout@v2 - name: Install Dependencies run: yarn --frozen-lockfile + - name: Run pre-release preparations + run: node scripts/pre-release.js - name: Create Release Pull Request uses: changesets/action@master with: diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 8106dd1198..2a6e3dc6f9 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -3,7 +3,7 @@ name: Main Master Build on: workflow_dispatch: push: - branches: [master] + branches: [master, release-*-patch] jobs: build: diff --git a/SECURITY.md b/SECURITY.md index ba96c694ee..c825cdd97e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -36,3 +36,20 @@ There are many situations where a vulnerability does not affect a particular dep To work around this and other similar issues, Snyk provides a method to ignore vulnerabilities. In order to provide the best visibility and most utility to adopters of Backstage, we store these ignore rules in `.snyk` policy files. This allows adopters to rely on our ignore policies if they wish to do so. Adding a new ignore policy is done by creating or modifying an existing `.snyk` file within a package root. See the [Snyk Documentation](https://support.snyk.io/hc/en-us/articles/360007487097-The-snyk-file) for details on the syntax. Always include a description, full path, and time limit of the ignore policy. + +TODO: MOVE TO SEPARATE FILE + +## Emergency Release Process + +This emergency release process is intended only for the Backstage maintainers. + +For this example we will be using the `@backstage/core` package which is currently version `1.5.0` in the master branch. + +In the event of a severe bug or security vulnerability detected in `1.1.1` which was released in `2048-01-01` please proceed using this process. + +- [ ] Create new base branch using `git checkout 2048-01-01 && git checkout -b release-2048-01-01-patch && git push --set-upstream origin release-2048-01-01-patch` +- [ ] Create new branch of the base branch(`release-2048-01-01-patch`) that will contain the emergency fix. `git checkout -b release-2048-01-01-emergency-fix` +- [ ] Apply fixes and generate new patch changeset for the effected package. +- [ ] Create PR towards the base branch(`release-2048-01-01-patch`) with the emergency fix. +- [ ] Review/Merge PR towards `release-2048-01-01-patch`. This will automatically trigger a release. +- [ ] Apply security fix towards master. diff --git a/scripts/pre-release.js b/scripts/pre-release.js new file mode 100644 index 0000000000..c9b004ba60 --- /dev/null +++ b/scripts/pre-release.js @@ -0,0 +1,124 @@ +#!/usr/bin/env node +/* eslint-disable import/no-extraneous-dependencies */ +/* + * 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. + */ + +const fs = require('fs-extra'); +const semver = require('semver'); +const { getPackages } = require('@manypkg/get-packages'); +const path = require('path'); + +const DEPENDENCY_TYPES = [ + 'dependencies', + 'devDependencies', + 'optionalDependencies', + 'peerDependencies', +]; + +/* + +Shipping to main TODO: + +- [] Document emergency release flow +- [x] Workflow that triggers releases of `release-*-patch` branches +- [x] Branch protection of `release-*-patch` branches +- [x] Bring release.js + patched.json into the main repo +- [] profit + + +*/ + +async function main() { + const patchedJsonPath = path.resolve('.changeset', 'patched.json'); + const { currentReleaseVersion } = await fs.readJSON(patchedJsonPath); + if (Object.keys(currentReleaseVersion).length === 0) { + console.log('No currentReleaseVersion overrides found, skipping.'); + return; + } + + const { packages } = await getPackages(path.resolve('.')); + console.log('packages', packages); + + const pendingVersionBumps = new Map(); + + for (const [name, version] of Object.entries(currentReleaseVersion)) { + const pkg = packages.find(p => p.packageJson.name === name); + if (!pkg) { + throw new Error(`Package ${name} not found`); + } + + if (!semver.valid(version)) { + throw new Error(`Invalid base version ${version} for package ${name}`); + } + + const currentVersion = semver.parse(pkg.packageJson.version); + const targetVersion = semver.parse(version).inc('patch'); + targetVersion.prerelease = currentVersion.prerelease; + + const targetVersionString = targetVersion.format(); + pendingVersionBumps.set(name, { + targetVersion: targetVersionString, + targetRange: `^${targetVersionString}`, + }); + } + + console.log('DEBUG: pendingVersionBumps =', pendingVersionBumps); + + for (const { dir, packageJson } of packages) { + let hasChanges = false; + + if (pendingVersionBumps.has(packageJson.name)) { + packageJson.version = pendingVersionBumps.get( + packageJson.name, + ).targetVersion; + hasChanges = true; + } + + for (const depType of DEPENDENCY_TYPES) { + const deps = packageJson[depType]; + for (const depName of Object.keys(deps ?? {})) { + const currentRange = deps[depName]; + + if (pendingVersionBumps.has(depName)) { + const pendingBump = pendingVersionBumps.get(depName); + console.log( + `DEBUG: replacing ${depName} ${currentRange} with ${pendingBump.targetRange} in ${depType} of ${packageJson.name}`, + ); + deps[depName] = pendingBump.targetRange; + hasChanges = true; + } + } + } + + if (hasChanges) { + console.log('writing package', packageJson.name); + await fs.writeJSON(path.resolve(dir, 'package.json'), packageJson, { + spaces: 2, + }); + } + } + + await fs.writeJSON( + patchedJsonPath, + { currentReleaseVersion: {} }, + { spaces: 2 }, + ); +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +}); From e6bef72496c85c8381f82deb8b3ac1610c88e784 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 12 Jan 2022 11:28:10 +0100 Subject: [PATCH 2/6] scripts: rename pre-release to prepare-release + cleanup Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- .../{pre-release.js => prepare-release.js} | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) rename scripts/{pre-release.js => prepare-release.js} (84%) mode change 100644 => 100755 diff --git a/scripts/pre-release.js b/scripts/prepare-release.js old mode 100644 new mode 100755 similarity index 84% rename from scripts/pre-release.js rename to scripts/prepare-release.js index c9b004ba60..3d4f142a8d --- a/scripts/pre-release.js +++ b/scripts/prepare-release.js @@ -28,19 +28,6 @@ const DEPENDENCY_TYPES = [ 'peerDependencies', ]; -/* - -Shipping to main TODO: - -- [] Document emergency release flow -- [x] Workflow that triggers releases of `release-*-patch` branches -- [x] Branch protection of `release-*-patch` branches -- [x] Bring release.js + patched.json into the main repo -- [] profit - - -*/ - async function main() { const patchedJsonPath = path.resolve('.changeset', 'patched.json'); const { currentReleaseVersion } = await fs.readJSON(patchedJsonPath); @@ -50,7 +37,6 @@ async function main() { } const { packages } = await getPackages(path.resolve('.')); - console.log('packages', packages); const pendingVersionBumps = new Map(); @@ -75,8 +61,6 @@ async function main() { }); } - console.log('DEBUG: pendingVersionBumps =', pendingVersionBumps); - for (const { dir, packageJson } of packages) { let hasChanges = false; @@ -91,11 +75,14 @@ async function main() { const deps = packageJson[depType]; for (const depName of Object.keys(deps ?? {})) { const currentRange = deps[depName]; + if (currentRange === '*') { + continue; + } if (pendingVersionBumps.has(depName)) { const pendingBump = pendingVersionBumps.get(depName); console.log( - `DEBUG: replacing ${depName} ${currentRange} with ${pendingBump.targetRange} in ${depType} of ${packageJson.name}`, + `Replacing ${depName} ${currentRange} with ${pendingBump.targetRange} in ${depType} of ${packageJson.name}`, ); deps[depName] = pendingBump.targetRange; hasChanges = true; @@ -104,7 +91,6 @@ async function main() { } if (hasChanges) { - console.log('writing package', packageJson.name); await fs.writeJSON(path.resolve(dir, 'package.json'), packageJson, { spaces: 2, }); From 34d5570c78a768db85a91355484a97448775f13a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 12 Jan 2022 11:32:19 +0100 Subject: [PATCH 3/6] SECURITY,docs: move emergency release process docs to publishing.md + complete Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- SECURITY.md | 17 -------- docs/plugins/publishing.md | 81 +++++++++++++++++++++++--------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index c825cdd97e..ba96c694ee 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -36,20 +36,3 @@ There are many situations where a vulnerability does not affect a particular dep To work around this and other similar issues, Snyk provides a method to ignore vulnerabilities. In order to provide the best visibility and most utility to adopters of Backstage, we store these ignore rules in `.snyk` policy files. This allows adopters to rely on our ignore policies if they wish to do so. Adding a new ignore policy is done by creating or modifying an existing `.snyk` file within a package root. See the [Snyk Documentation](https://support.snyk.io/hc/en-us/articles/360007487097-The-snyk-file) for details on the syntax. Always include a description, full path, and time limit of the ignore policy. - -TODO: MOVE TO SEPARATE FILE - -## Emergency Release Process - -This emergency release process is intended only for the Backstage maintainers. - -For this example we will be using the `@backstage/core` package which is currently version `1.5.0` in the master branch. - -In the event of a severe bug or security vulnerability detected in `1.1.1` which was released in `2048-01-01` please proceed using this process. - -- [ ] Create new base branch using `git checkout 2048-01-01 && git checkout -b release-2048-01-01-patch && git push --set-upstream origin release-2048-01-01-patch` -- [ ] Create new branch of the base branch(`release-2048-01-01-patch`) that will contain the emergency fix. `git checkout -b release-2048-01-01-emergency-fix` -- [ ] Apply fixes and generate new patch changeset for the effected package. -- [ ] Create PR towards the base branch(`release-2048-01-01-patch`) with the emergency fix. -- [ ] Review/Merge PR towards `release-2048-01-01-patch`. This will automatically trigger a release. -- [ ] Apply security fix towards master. diff --git a/docs/plugins/publishing.md b/docs/plugins/publishing.md index 06fae0533e..4f74877cb2 100644 --- a/docs/plugins/publishing.md +++ b/docs/plugins/publishing.md @@ -14,45 +14,62 @@ npm. ### Creating a new release -Version bumps are made through release PRs. To create a new release, checkout -out a new branch that you will use for the release, e.g. +Releases are handled by changesets and trigger whenever the "Version Packages" +PR is merged. This is typically done every Thursday around noon CET. -```sh -$ git checkout -b new-release -``` +## Emergency Release Process -First bump the `CHANGELOG.md` in the root of the repo and commit. You bump it by -adding a header for the new version just below the `## Next Release` one. +**This emergency release process is intended only for the Backstage +maintainers.** -Then, from the root of the repo, run +For this example we will be using the `@backstage/plugin-foo` package as an +example and assume that it is currently version `1.5.0` in the master branch. -```sh -$ yarn release -``` +In the event of a severe bug being introduced in version `1.5.0` of the +`@backstage/plugin-foo` released in the `2048-01-01` release, the following +processed is used to release an emergency fix as `1.5.1`: -This will bring up the lerna release CLI where you choose what type of version -bump you want to make, (major/minor/patch/prerelease). The CLI will take you -through choosing a version, previewing all changes, and then approving the -release. Once the release is approved, a new commit is created that you can -submit as a PR. Push the branch to GitHub: +- [ ] Identify the release that needs to be patched, in this case we're fixing a + broken release, so it would be the most recent one, `2048-01-01`. In the + event of a backported security fix, the release that has the last + published version of each major version of the package should be the one + patched. +- [ ] Make sure a patch branch exists for the release that is being patched. If + a patch already exists, reuse the existing branch. -```sh -$ git push origin -u new-release -``` + ```bash + git checkout release-2048-01-01 + git checkout -b release-2048-01-01-patch + git push --set-upstream origin release-2048-01-01-patch + ``` -And then create a PR. Once the PR is approved and merged into master, the master -build will publish new versions of all bumped packages. +- [ ] With the `release-2048-01-01-patch` branch as a base, create a new branch + for your fix: -### Include new changes in existing release PR + ```bash + git checkout -b ${USER}/release-2048-01-01-emergency-fix + ``` -If you want to include some last minute changes to an existing release PR, -follow these instructions: +- [ ] Apply fixes and create a new patch changeset for the effected package, + then commit these changes. +- [ ] Run `yarn release` in the root of the repo in order to convert your + changeset into package version bumps and changelog entries. Commit these + changes as a second `"Generated release"` commit. +- [ ] Create PR towards the base branch(`release-2048-01-01-patch`) containing + the two commits. +- [ ] Review/Merge PR into `release-2048-01-01-patch`. This will automatically + trigger a release. +- [ ] In the master branch, add a `.changeset/patched.json` to make sure that + future releases of the packages are bumped accordingly: -```sh -$ git checkout master -$ git pull -$ git checkout new-release -$ git reset --hard master -$ yarn release -$ git push --force -``` + ```json + { + "currentReleaseVersion": { + "@backstage/plugin-foo": "1.5.1" + } + } + ``` + +- [ ] Apply the same fix towards master if needed and create appropriate + changeset. In the changeset towards master you should refer back to all + patch releases that also received the same fix. From 874bdb3febaa2bb927d278b955a46a030bbe7964 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 12 Jan 2022 11:33:44 +0100 Subject: [PATCH 4/6] docs: remove publishing.md from microsite and move to docs root Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- docs/{plugins => }/publishing.md | 6 +----- microsite/sidebars.json | 1 - mkdocs.yml | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) rename docs/{plugins => }/publishing.md (96%) diff --git a/docs/plugins/publishing.md b/docs/publishing.md similarity index 96% rename from docs/plugins/publishing.md rename to docs/publishing.md index 4f74877cb2..1189aa7e47 100644 --- a/docs/plugins/publishing.md +++ b/docs/publishing.md @@ -1,8 +1,4 @@ ---- -id: publishing -title: Publishing -description: Documentation on Publishing npm packages ---- + ## npm diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 6c4c5261d9..c0a0b9b24f 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -202,7 +202,6 @@ "type": "subcategory", "label": "Publishing", "ids": [ - "plugins/publishing", "plugins/publish-private", "plugins/add-to-marketplace", "plugins/observability" diff --git a/mkdocs.yml b/mkdocs.yml index 81dad55b84..398863a3c0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -126,7 +126,6 @@ nav: - Testing: - Testing with Jest: 'plugins/testing.md' - Publishing: - - Publishing: 'plugins/publishing.md' - Publish private: 'plugins/publish-private.md' - Add to Marketplace: 'plugins/add-to-marketplace.md' - Observability: 'plugins/observability.md' From 7c04bfb8284d31ab701ac30e114ae10a7da313c3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 12 Jan 2022 11:34:44 +0100 Subject: [PATCH 5/6] vocab.txt: added "backported" Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- .github/styles/vocab.txt | 1 + .github/workflows/changeset.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index d3ea8c1c27..71babd3604 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -16,6 +16,7 @@ Autoscaling autoselect Avro backrub +backported Bigtable Billett Bitbucket diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index e960d45744..44ddbe3334 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -13,8 +13,8 @@ jobs: - uses: actions/checkout@v2 - name: Install Dependencies run: yarn --frozen-lockfile - - name: Run pre-release preparations - run: node scripts/pre-release.js + - name: Run release preparations + run: node scripts/prepare-release.js - name: Create Release Pull Request uses: changesets/action@master with: From 163b4d2d33a0c6dda8cb78b355ddc16556aad15f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 12 Jan 2022 13:30:43 +0100 Subject: [PATCH 6/6] docs/publishing.md: review fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- docs/publishing.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/publishing.md b/docs/publishing.md index 1189aa7e47..e5642a3467 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -23,7 +23,7 @@ example and assume that it is currently version `1.5.0` in the master branch. In the event of a severe bug being introduced in version `1.5.0` of the `@backstage/plugin-foo` released in the `2048-01-01` release, the following -processed is used to release an emergency fix as `1.5.1`: +process is used to release an emergency fix as `1.5.1`: - [ ] Identify the release that needs to be patched, in this case we're fixing a broken release, so it would be the most recent one, `2048-01-01`. In the @@ -31,7 +31,8 @@ processed is used to release an emergency fix as `1.5.1`: published version of each major version of the package should be the one patched. - [ ] Make sure a patch branch exists for the release that is being patched. If - a patch already exists, reuse the existing branch. + a patch already exists, reuse the existing branch. The branch **must + always** be named exactly `release--patch`. ```bash git checkout release-2048-01-01 @@ -40,23 +41,27 @@ processed is used to release an emergency fix as `1.5.1`: ``` - [ ] With the `release-2048-01-01-patch` branch as a base, create a new branch - for your fix: + for your fix. This branch can be named anything, but the following naming + pattern may be suitable: ```bash git checkout -b ${USER}/release-2048-01-01-emergency-fix ``` -- [ ] Apply fixes and create a new patch changeset for the effected package, +- [ ] Apply fixes and create a new patch changeset for the affected package, then commit these changes. - [ ] Run `yarn release` in the root of the repo in order to convert your changeset into package version bumps and changelog entries. Commit these changes as a second `"Generated release"` commit. -- [ ] Create PR towards the base branch(`release-2048-01-01-patch`) containing +- [ ] Create PR towards the base branch (`release-2048-01-01-patch`) containing the two commits. -- [ ] Review/Merge PR into `release-2048-01-01-patch`. This will automatically - trigger a release. -- [ ] In the master branch, add a `.changeset/patched.json` to make sure that - future releases of the packages are bumped accordingly: +- [ ] Review/Merge the PR into `release-2048-01-01-patch`. This will + automatically trigger a release. +- [ ] Make sure the same fix is applied in master before the next release, and + create an appropriate changeset for that as well. In the changeset towards + master you should refer back to all patch releases that also received the + same fix. Also be sure to update `.changeset/patched.json` in the same PR + to make sure that future releases of the packages are bumped accordingly: ```json { @@ -65,7 +70,3 @@ processed is used to release an emergency fix as `1.5.1`: } } ``` - -- [ ] Apply the same fix towards master if needed and create appropriate - changeset. In the changeset towards master you should refer back to all - patch releases that also received the same fix.