Update release docs
This commit is contained in:
parent
931e475de2
commit
f3c59665b1
2 changed files with 22 additions and 364 deletions
|
|
@ -4,102 +4,40 @@ Describes how we do releases of Mithril.js
|
||||||
|
|
||||||
# Mithril.js Release Processes
|
# Mithril.js Release Processes
|
||||||
|
|
||||||
**Note** These steps all assume that `MithrilJS/mithril.js` is a git remote named `mithriljs`, adjust accordingly if that doesn't match your setup.
|
Mithril's release process is automated by [pr-release]. pr-release is maintained by a long time mithril.js community member [@JAForbes](https://github.com/JAForbes).
|
||||||
|
|
||||||
- [Releasing a new Mithril.js version](#releasing-a-new-mithriljs-version)
|
pr-release handles the following:
|
||||||
- [Updating mithril.js.org](#updating-mithriljsorg)
|
|
||||||
|
|
||||||
## Releasing a new Mithril.js version
|
- Generating changelog entries
|
||||||
|
- Automating the semver version
|
||||||
|
- Publishing releases and pre-releases to npm
|
||||||
|
- Creating github releases
|
||||||
|
- Rollbacks
|
||||||
|
|
||||||
### Prepare the release
|
## For contributors
|
||||||
|
|
||||||
1. Ensure your local branch is up to date
|
Contributors should create their feature branch targetting the default branch `next`. When this branch is merged `pr-release` will either generate or update a release PR from `next` to `main`.
|
||||||
|
|
||||||
```bash
|
The description and title will be managed by [pr-release], including the semver version.
|
||||||
$ git checkout next
|
|
||||||
$ git pull --rebase mithriljs next
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Determine patch level of the change
|
Contributors who have permissions should add the correct semver label to their PR (`major` | `minor` | `patch`). If no label is set, `patch` is assumed.
|
||||||
3. Update information in `docs/changelog.md` to match reality of the new version being prepared for release.
|
|
||||||
- Don't forget to add today's date under the version heading!
|
|
||||||
4. Replace all existing references to `mithril@next` to `mithril` if moving from a release candidate to stable.
|
|
||||||
- Note: if making an initial release candidate, don't forget to move all the playground snippets to pull from `mithril@next`!
|
|
||||||
5. Commit changes to `next`
|
|
||||||
|
|
||||||
```
|
If you do not have permissions, the maintainer will set the label on your behalf.
|
||||||
$ git add .
|
|
||||||
$ git commit -m "Preparing for release"
|
|
||||||
|
|
||||||
# Push to your branch
|
## Changelog
|
||||||
$ git push
|
|
||||||
|
|
||||||
# Push to MithrilJS/mithril.js
|
There are two changelogs in the mithril project
|
||||||
$ git push mithriljs next
|
|
||||||
```
|
|
||||||
|
|
||||||
### Merge from `next` to `master`
|
- `docs/changelog.md` a hand written curated reflection of changes to the codebase
|
||||||
|
- `docs/release.md` an automatically prepended log of changes, managed by pr-release
|
||||||
|
|
||||||
6. Switch to `master` and make sure it's up to date
|
In future we may collapse these into a single file, the separation is due to the fact the `changelog.md` predates the `release.md` file.
|
||||||
|
|
||||||
```bash
|
## For maintainers
|
||||||
$ git checkout master
|
|
||||||
$ git pull --rebase mithriljs master
|
|
||||||
```
|
|
||||||
|
|
||||||
7. merge `next` on top of it
|
Whenever a new feature branch is opened, a reviewing maintainer should add the correct semver label to their PR (`major` | `minor` | `patch`). If no label is set, `patch` is assumed.
|
||||||
|
|
||||||
```bash
|
If a `major` or `minor` feature branch is merge, but no labels were set, you can go back and apply the labels and then re-run the `pr` workflow in github actions, this will recalculate the semver version.
|
||||||
$ git merge next
|
|
||||||
```
|
|
||||||
|
|
||||||
8. Clean & update npm dependencies and ensure the tests are passing.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ npm prune
|
|
||||||
$ npm i
|
|
||||||
$ npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Publish the release
|
|
||||||
|
|
||||||
9. `npm run release <major|minor|patch|semver>`, see the docs for [`npm version`](https://docs.npmjs.com/cli/version)
|
|
||||||
10. The changes will be automatically pushed to your fork
|
|
||||||
11. Push the changes to `MithrilJS/mithril.js`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git push mithriljs master
|
|
||||||
```
|
|
||||||
|
|
||||||
12. Travis will push the new release to npm & create a GitHub release
|
|
||||||
|
|
||||||
### Merge `master` back into `next`
|
|
||||||
|
|
||||||
This helps to ensure that the `version` field of `package.json` doesn't get out of date.
|
|
||||||
|
|
||||||
13. Switch to `next` and make sure it's up to date
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git checkout next
|
|
||||||
$ git pull --rebase mithriljs next
|
|
||||||
```
|
|
||||||
|
|
||||||
14. Merge `master` back onto `next`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git merge master
|
|
||||||
```
|
|
||||||
|
|
||||||
15. Push the changes to your fork & `MithrilJS/mithril.js`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git push
|
|
||||||
$ git push mithriljs next
|
|
||||||
```
|
|
||||||
|
|
||||||
### Update the GitHub release
|
|
||||||
|
|
||||||
16. The GitHub Release will require a manual description & title to be added. I suggest coming up with a fun title & then copying the `docs/changelog.md` entry for the build.
|
|
||||||
|
|
||||||
## Updating mithril.js.org
|
## Updating mithril.js.org
|
||||||
|
|
||||||
|
|
@ -124,3 +62,5 @@ $ node scripts/update-docs
|
||||||
After the docs build completes, the updated docs should appear on https://mithril.js.org in a few minutes.
|
After the docs build completes, the updated docs should appear on https://mithril.js.org in a few minutes.
|
||||||
|
|
||||||
**Note:** When updating the stable version with a release candidate out, ***make sure to update the index + navigation to point to the new stable version!!!***
|
**Note:** When updating the stable version with a release candidate out, ***make sure to update the index + navigation to point to the new stable version!!!***
|
||||||
|
|
||||||
|
[pr-release]: https://pr-release.org/
|
||||||
|
|
@ -1,282 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
"use strict"
|
|
||||||
|
|
||||||
// This is my temporary hack to simplify deployment until I fix the underlying
|
|
||||||
// problems in these bugs:
|
|
||||||
// - https://github.com/MithrilJS/mithril.js/issues/2417
|
|
||||||
// - https://github.com/MithrilJS/mithril.js/pull/2422
|
|
||||||
//
|
|
||||||
// Depending on the complexity, it might become permanent. It really isn't that
|
|
||||||
// helpful to create a release on Travis vs locally, aside from a couple extra
|
|
||||||
// potential 2FA prompts by npm during login and publish.
|
|
||||||
|
|
||||||
const path = require("path")
|
|
||||||
const {promises: fsp} = require("fs")
|
|
||||||
const readline = require("readline")
|
|
||||||
const {execFileSync} = require("child_process")
|
|
||||||
const {promisify} = require("util")
|
|
||||||
const rimraf = promisify(require("rimraf"))
|
|
||||||
const semver = require("semver")
|
|
||||||
const upstream = require("./_upstream")
|
|
||||||
const updateDocs = require("./update-docs")
|
|
||||||
|
|
||||||
// Fake it until it works with this.
|
|
||||||
upstream.fetch.remote = "origin"
|
|
||||||
|
|
||||||
function showHelp() {
|
|
||||||
console.error(`
|
|
||||||
node scripts/release increment [ --preid id ] [ --publish ]
|
|
||||||
|
|
||||||
Invoke as 'scripts/release.sh' to invoke the release sequence, specifying the
|
|
||||||
version increment via 'increment' (required). Pass '--publish' to push the
|
|
||||||
change and publish it, instead of just logging the commands used to push the
|
|
||||||
release.
|
|
||||||
|
|
||||||
Here's how each increment type works:
|
|
||||||
|
|
||||||
- 'major' increments from 1.0.0 or 2.0.0-beta.0 to 2.0.0
|
|
||||||
- 'minor' increments from 1.0.0 to 1.1.0
|
|
||||||
- 'patch' increments from 1.0.0 to 1.0.1
|
|
||||||
- 'premajor' increments from 1.0.0 to 2.0.0-beta.0
|
|
||||||
- 'preminor' increments from 1.0.0 to 1.1.0-beta.0
|
|
||||||
- 'prepatch' increments from 1.0.0 to 1.0.1-beta.0
|
|
||||||
- 'prerelease' increments from 2.0.0-beta.0 to 2.0.0-beta.1
|
|
||||||
|
|
||||||
'--preid beta' specifies the 'beta' part above (default). It's required for all
|
|
||||||
'pre*' increment types except 'prerelease'.
|
|
||||||
|
|
||||||
See the docs for 'npm version' <https://docs.npmjs.com/cli/version> for details
|
|
||||||
on the 'increment' parameter.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const rootDir = path.dirname(__dirname)
|
|
||||||
const p = (...args) => path.resolve(rootDir, ...args)
|
|
||||||
|
|
||||||
function fail(...args) {
|
|
||||||
console.error(...args)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function execCommand(cmd, args, opts) {
|
|
||||||
console.error()
|
|
||||||
console.error(["executing:", cmd, ...args].join(" "))
|
|
||||||
return execFileSync(cmd, args, {
|
|
||||||
windowsHide: true,
|
|
||||||
stdio: "inherit",
|
|
||||||
encoding: "utf-8",
|
|
||||||
...opts,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function git(...cmd) { return execCommand("git", cmd) }
|
|
||||||
function npm(...cmd) { return execCommand("npm", cmd) }
|
|
||||||
function npmConfig(key) { return npm("config", "get", key).trim() }
|
|
||||||
|
|
||||||
function getChanges() {
|
|
||||||
return execCommand("git", ["status", "-z"], {
|
|
||||||
stdio: ["inherit", "pipe", "inherit"],
|
|
||||||
})
|
|
||||||
.split(/\0/g)
|
|
||||||
.filter((l) => (/\S/).test(l))
|
|
||||||
}
|
|
||||||
|
|
||||||
async function release({increment, preid, publish}) {
|
|
||||||
if (!(/^prerelease$|^(pre)?(major|minor|patch)$/).test(increment)) {
|
|
||||||
return fail(`Invalid increment: ${increment}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((/^pre(major|minor|patch)/).test(increment) && preid == null) {
|
|
||||||
return fail(`'${increment}' must include a '--preid'`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getChanges().length) {
|
|
||||||
return fail("Tree must be clean to start!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (upstream.push == null) {
|
|
||||||
return fail("You must have an upstream to push to!")
|
|
||||||
}
|
|
||||||
|
|
||||||
const rl = readline.createInterface({
|
|
||||||
input: process.stdin,
|
|
||||||
output: process.stdout,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Update local `master` and `next`.
|
|
||||||
git("fetch", upstream.fetch.remote, "master", "next")
|
|
||||||
|
|
||||||
// Make sure we're on the current `next` and merge any docs fixes and
|
|
||||||
// similar that have landed in upstream `master`.
|
|
||||||
git("checkout", "next")
|
|
||||||
git("pull", "--rebase", upstream.fetch.remote, "next")
|
|
||||||
git(
|
|
||||||
"pull", "--allow-unrelated-histories",
|
|
||||||
upstream.fetch.remote, "master"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Note: we're doing our own semver incrementing.
|
|
||||||
const packageJson = JSON.parse(
|
|
||||||
await fsp.readFile(p("package.json"), "utf-8")
|
|
||||||
)
|
|
||||||
const version = semver.inc(packageJson.version, increment, preid)
|
|
||||||
|
|
||||||
console.error(`
|
|
||||||
Copy the parts listed in "Upcoming" to a new section "### v${version}" in
|
|
||||||
docs/changelog.md and clear that section out. Also, add today's date under the
|
|
||||||
new section's heading to match the others and don't forget to update the table
|
|
||||||
of contents accordingly.
|
|
||||||
`)
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
await new Promise((resolve) => rl.question(
|
|
||||||
"Press <Enter> once ready to continue or Ctrl+C to abort.",
|
|
||||||
// Ignore any input.
|
|
||||||
() => resolve(),
|
|
||||||
))
|
|
||||||
|
|
||||||
// Verify the changelog was updated, and give a chance to retry if it's
|
|
||||||
// prematurely continued.
|
|
||||||
const changes = getChanges()
|
|
||||||
const isChangelog = /^[ M][ M] docs\/changelog\.md$/
|
|
||||||
const errors = []
|
|
||||||
|
|
||||||
console.log("changes", changes)
|
|
||||||
|
|
||||||
if (!changes.some((l) => isChangelog.test(l))) {
|
|
||||||
errors.push("Changelog must be updated!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.some((l) => !isChangelog.test(l))) {
|
|
||||||
errors.push("Tree must not be otherwise dirty!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errors.length) break
|
|
||||||
console.error(errors.join("\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
await rimraf(p("node_modules"))
|
|
||||||
npm("install-test")
|
|
||||||
npm("run", "build")
|
|
||||||
console.log("*** Build done ***")
|
|
||||||
|
|
||||||
// Update the package file.
|
|
||||||
packageJson.version = version
|
|
||||||
await fsp.writeFile(p("package.json"), "utf-8",
|
|
||||||
JSON.stringify(packageJson, null, 2)
|
|
||||||
)
|
|
||||||
// Commit and tag the new release, with the appropriate CLI flag if the
|
|
||||||
// commit needs signed.
|
|
||||||
git("add", ".")
|
|
||||||
git(
|
|
||||||
"commit",
|
|
||||||
...npmConfig("sign-git-tag") === "true" ? ["--gpg-sign"] : [],
|
|
||||||
"--message", `v${version}`,
|
|
||||||
)
|
|
||||||
git("tag", `v${version}`)
|
|
||||||
|
|
||||||
// Update `master` to reflect the current state of `next`.
|
|
||||||
git("checkout", "master")
|
|
||||||
git("reset", "--hard", "next")
|
|
||||||
git("checkout", "next")
|
|
||||||
|
|
||||||
if (publish) {
|
|
||||||
// TODO: switch this to just do the push, and use the following Travis
|
|
||||||
// config. This also conveniently keeps private stuff out of the build
|
|
||||||
// scripts and just in build config, avoiding the grief that led to this
|
|
||||||
// file's existence.
|
|
||||||
//
|
|
||||||
// ```yml
|
|
||||||
// # See https://docs.travis-ci.com/user/deployment/npm/ for details on
|
|
||||||
// # `api_key:` for the npm provider.
|
|
||||||
// # See https://docs.travis-ci.com/user/deployment/pages/ for details
|
|
||||||
// # on `github_token:` for the pages provider.
|
|
||||||
// after_success: >
|
|
||||||
// [ "$TRAVIS_BRANCH" == "master" ] && node scripts/generate-docs
|
|
||||||
//
|
|
||||||
// deploy:
|
|
||||||
// - provider: npm
|
|
||||||
// skip_cleanup: true
|
|
||||||
// email: 'contact@claudiameadows.dev'
|
|
||||||
// api_key:
|
|
||||||
// secure: 'output of `travis encrypt NPM_AUTH_TOKEN`'
|
|
||||||
// on:
|
|
||||||
// tags: true
|
|
||||||
// condition: "$TRAVIS_TAG != *-*"
|
|
||||||
// - provider: npm
|
|
||||||
// skip_cleanup: true
|
|
||||||
// tag: next
|
|
||||||
// email: 'contact@claudiameadows.dev'
|
|
||||||
// api_key:
|
|
||||||
// secure: 'output of `travis encrypt NPM_AUTH_TOKEN`'
|
|
||||||
// on:
|
|
||||||
// tags: true
|
|
||||||
// condition: "$TRAVIS_TAG == *-*"
|
|
||||||
// - provider: pages
|
|
||||||
// skip_cleanup: true
|
|
||||||
// github_token:
|
|
||||||
// secure: 'output of `travis encrypt GITHUB_AUTH_TOKEN`'
|
|
||||||
// local_dir: dist
|
|
||||||
// fqdn: mithril.js.org
|
|
||||||
// committer_from_gh: true
|
|
||||||
// on:
|
|
||||||
// tags: false
|
|
||||||
// branch: master
|
|
||||||
// ```
|
|
||||||
npm("login")
|
|
||||||
if (increment.startsWith("pre")) {
|
|
||||||
npm("publish", "--tag", "next")
|
|
||||||
} else {
|
|
||||||
npm("publish")
|
|
||||||
}
|
|
||||||
npm("logout")
|
|
||||||
|
|
||||||
// Only push after successful publish
|
|
||||||
git(
|
|
||||||
"push", "--atomic", "origin",
|
|
||||||
"+next:master", "next:next", `next:refs/tags/v${version}`,
|
|
||||||
)
|
|
||||||
git(
|
|
||||||
"push", "--atomic", upstream.push.remote,
|
|
||||||
"+next:master", "next:next", `next:refs/tags/v${version}`,
|
|
||||||
)
|
|
||||||
await updateDocs()
|
|
||||||
} else {
|
|
||||||
const remote = upstream.push.remote
|
|
||||||
console.error(`
|
|
||||||
npm login
|
|
||||||
npm publish${increment.startsWith("pre") ? " --tag next" : ""}
|
|
||||||
npm logout
|
|
||||||
git push --atomic origin +next:master next:next next:refs/tags/v${version}
|
|
||||||
git push --atomic ${remote} +next:master next:next next:refs/tags/v${version}
|
|
||||||
npm run release:docs
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error(`
|
|
||||||
Don't forget to update the latest release! You can find it here:
|
|
||||||
https://github.com/MithrilJS/mithril.js/releases/tag/v${version}
|
|
||||||
`)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eslint-disable global-require */
|
|
||||||
if (require.main === module) {
|
|
||||||
require("./_command")({async exec() {
|
|
||||||
const parsed = require("minimist")(process.argv.slice(2), {
|
|
||||||
boolean: ["help", "publish"],
|
|
||||||
alias: {help: ["h", "?"]},
|
|
||||||
string: ["preid"],
|
|
||||||
})
|
|
||||||
|
|
||||||
if (parsed.help || !parsed._.length) showHelp()
|
|
||||||
else {
|
|
||||||
await release({
|
|
||||||
increment: parsed._[0],
|
|
||||||
preid: parsed.preid,
|
|
||||||
publish: parsed.publish,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}})
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue