Merge pull request #8883 from backstage/mob/prerelease
Prepare for prerelease flow and introduce documentation and tooling for emergency releases
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"currentReleaseVersion": {}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ Autoscaling
|
||||
autoselect
|
||||
Avro
|
||||
backrub
|
||||
backported
|
||||
Bigtable
|
||||
Billett
|
||||
Bitbucket
|
||||
|
||||
@@ -13,6 +13,8 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
- name: Run release preparations
|
||||
run: node scripts/prepare-release.js
|
||||
- name: Create Release Pull Request
|
||||
uses: changesets/action@master
|
||||
with:
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Main Master Build
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [master]
|
||||
branches: [master, release-*-patch]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
id: publishing
|
||||
title: Publishing
|
||||
description: Documentation on Publishing npm packages
|
||||
---
|
||||
|
||||
## npm
|
||||
|
||||
npm packages are published through CI/CD in the
|
||||
[`.github/workflows/master.yml`](https://github.com/backstage/backstage/blob/master/.github/workflows/master.yml)
|
||||
workflow. Every commit that is merged to master will be checked for new versions
|
||||
of all public packages, and any new versions will automatically be published to
|
||||
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.
|
||||
|
||||
```sh
|
||||
$ git checkout -b new-release
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
Then, from the root of the repo, run
|
||||
|
||||
```sh
|
||||
$ yarn release
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```sh
|
||||
$ git push origin -u new-release
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Include new changes in existing release PR
|
||||
|
||||
If you want to include some last minute changes to an existing release PR,
|
||||
follow these instructions:
|
||||
|
||||
```sh
|
||||
$ git checkout master
|
||||
$ git pull
|
||||
$ git checkout new-release
|
||||
$ git reset --hard master
|
||||
$ yarn release
|
||||
$ git push --force
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
<!-- This is intentionally left out of the microsite, since it only applies to the main repo -->
|
||||
|
||||
## npm
|
||||
|
||||
npm packages are published through CI/CD in the
|
||||
[`.github/workflows/master.yml`](https://github.com/backstage/backstage/blob/master/.github/workflows/master.yml)
|
||||
workflow. Every commit that is merged to master will be checked for new versions
|
||||
of all public packages, and any new versions will automatically be published to
|
||||
npm.
|
||||
|
||||
### Creating a new release
|
||||
|
||||
Releases are handled by changesets and trigger whenever the "Version Packages"
|
||||
PR is merged. This is typically done every Thursday around noon CET.
|
||||
|
||||
## Emergency Release Process
|
||||
|
||||
**This emergency release process is intended only for the Backstage
|
||||
maintainers.**
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
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. The branch **must
|
||||
always** be named exactly `release-<release>-patch`.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
- [ ] With the `release-2048-01-01-patch` branch as a base, create a new branch
|
||||
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 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
|
||||
the two commits.
|
||||
- [ ] 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
|
||||
{
|
||||
"currentReleaseVersion": {
|
||||
"@backstage/plugin-foo": "1.5.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -202,7 +202,6 @@
|
||||
"type": "subcategory",
|
||||
"label": "Publishing",
|
||||
"ids": [
|
||||
"plugins/publishing",
|
||||
"plugins/publish-private",
|
||||
"plugins/add-to-marketplace",
|
||||
"plugins/observability"
|
||||
|
||||
@@ -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'
|
||||
|
||||
Executable
+110
@@ -0,0 +1,110 @@
|
||||
#!/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',
|
||||
];
|
||||
|
||||
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('.'));
|
||||
|
||||
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}`,
|
||||
});
|
||||
}
|
||||
|
||||
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 (currentRange === '*') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pendingVersionBumps.has(depName)) {
|
||||
const pendingBump = pendingVersionBumps.get(depName);
|
||||
console.log(
|
||||
`Replacing ${depName} ${currentRange} with ${pendingBump.targetRange} in ${depType} of ${packageJson.name}`,
|
||||
);
|
||||
deps[depName] = pendingBump.targetRange;
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChanges) {
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user