New 2-step release process: script/{prep,push}-release.sh
Steps:
1. On a clean tree on `master`, add a new entry to `CHANGELOG.md` in the
same format as the other entries, then run `script/prep-release.sh`
to do everything that can be done locally: bump version, build,
package as tarball + zipfile, commit, tag.
2. After double-checking the build/package/commit/tag, run
`script/push-release.sh` (with a GitHub access token env variable)
to automatically publish to NPM, push to GitHub, create a new GitHub
Release, and cleanup the tarballs etc.
Notes:
- `CHANGELOG.md`: I changed the format to resemble [the GitHub Releases
page] more. In particular, each entry has a one-line summary on the
GitHub Releases page that was the commit message for the version bump/
changelog addition, but didn't show up in the changelog at all.
- `Makefile`: since these scripts create and cleanup the tarballs and
zipfiles, there's no need for `make dist` anymore, and `make clean` is
now, well, cleaner.
- Creating GitHub Releases: this was loosely inspired by the
[gh-release Bash script], but was mostly based on the [GitHub Releases
API docs].
[the GitHub Releases page] https://github.com/mathquill/mathquill/releases
[gh-release Bash script]: https://github.com/progrium/gh-release/blob/master/bash/gh-release.bash
[itHub Releases API docs]: https://developer.github.com/v3/repos/releases/#create-a-release
--
I designed this release process to have 2 steps:
1. Do everything that can be done locally: build, changelogs, bump
version, commit, tag, create tarballs and zipfiles
2. Only after getting a chance to double-check the stuff done locally do
we publish on public servers like NPM and GitHub
Not coincidentally, `npm` has two relevant commands, [`npm version`]
and [`npm publish`]. They seem like they'd correspond nicely with my
2 steps, so I tried implementing the release process as hooks into those
commands, but it was too annoying:
- [`npm version`] ensures that the working tree is clean even before
calling the `preversion` hook, and then automatically commits, so
the releaser would have to edit `CHANGELOG.md` during one of those
hook calls (like it opens a shell or editor or something).
- [`npm publish`] is not as well-documented as `npm version`, but I
[stumbled on] a sub-step: [`npm pack`], which figures out the files
to be included and packs them into a tarball to be uploaded to the
NPM registry. However, we actually want that to happen during the
Step 1, so we can double-check it before uploading, not this step.
So using NPM hook scripts, the workflow would have to be:
1. On a clean tree, run `npm version patch`, which opens an editor on
`CHANGELOG.md`, then builds, commits, tags, and in particular
packages a tarball like `mathquill-0.10.2.tgz`.
2. After double-checking the build/package/commit/tag, run
`npm publish mathquill-0.10.2.tgz`. Note that plain `npm publish`
would re-run `npm pack` instead of using the tarball from Step 1.
(I guess that would be fine, just inefficient?)
Rather than work within these inconveniences, I figured it'd be simpler
to just use "lower-level" tools, or at least, the same tools but in a
lower-level capacity. Specifically, `prep-release.sh` calls
`npm version --no-git-tag-version`, which just bumps the version in
package.json and doesn't do anything with Git, and then calls
`npm pack` to package the tarball; and `push-release.sh` calls
`npm publish <tarball>`, skipping the internal call to `npm pack` simply
using `npm publish` to upload to the NPM registry.
[`npm version`]: https://docs.npmjs.com/cli/version
[`npm publish`]: https://docs.npmjs.com/cli/publish
[`npm pack`]: https://docs.npmjs.com/cli/pack
[stumbled on]: https://github.com/npm/npm/pull/13080
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a787ed..e48a512 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,6 @@
-## v0.10.1: 2016-03-21
+## v0.10.1: Fix `font-size: 0` typing problems and more
+
+_2016-03-21_
Important fix: remove `font-size: 0` on textarea (#585), fixing typing
in Chrome Canary (#540) as well as the Enter key not triggering the
@@ -35,7 +37,9 @@
**build system fixes:**
- (#532) add console output to show URL of local test pages
-## v0.10.0: 2016-02-20
+## v0.10.0: Total API overhaul, new features galore
+
+_2016-02-20_
Many major changes including a total overhaul of the API (no more
auto-MathQuill-ifying of `.mathquill-editable` etc, and no more jQuery
@@ -137,7 +141,9 @@
- (#117, #142, #186, #287) massive refactor of cursor methods to not
assume the edit tree is double-layered
-## v0.9.4: 2014-1-22
+## v0.9.4: URGENT HOTFIX for cursor showing up as an ugly box in Chrome 40
+
+_2014-1-22_
URGENT HOTFIX for cursor showing up as an ugly box in Chrome 40 (#371)
@@ -155,7 +161,9 @@
**docs:**
- (#283) change license from LGPL to Mozilla Public License
-## v0.9.3: 2013-11-11
+## v0.9.3: Fix `NZQRC` appearing double-struck/blackboard bold
+
+_2013-11-11_
**new features:**
- (#185) add `\vec`
@@ -174,7 +182,9 @@
- (#189) replace Connect with tiny handwritten static server
- upgrade to uglifyjs2
-## v0.9.2: 2013-04-02
+## v0.9.2: Fix bug in hotfix for typing over selections in Safari 5.1
+
+_2013-04-02_
NOTE: The hotfix for typing over selections in Safari 5.1 (#135) from
v0.9.1 had a huge bug, fixed as #166.
@@ -199,7 +209,9 @@
- New site-building system
- no more submodules, `npm` only
-## v0.9.1: 2012-12-19
+## v0.9.1: Hotfix for typing over selections in Safari 5.1
+
+_2012-12-19_
* Started the changelog
* Added a `make publish` script
diff --git a/Makefile b/Makefile
index a238724..102f909 100644
--- a/Makefile
+++ b/Makefile
@@ -80,12 +80,6 @@
BUILD_TEST = $(BUILD_DIR)/mathquill.test.js
UGLY_JS = $(BUILD_DIR)/mathquill.min.js
UGLY_BASIC_JS = $(BUILD_DIR)/mathquill-basic.min.js
-CLEAN += $(BUILD_DIR)/*
-
-DISTDIR = ./mathquill-$(VERSION)
-DISTTAR = $(DISTDIR).tgz
-DISTZIP = $(DISTDIR).zip
-CLEAN += $(DISTTAR) $(DISTZIP)
# programs and flags
UGLIFY ?= ./node_modules/.bin/uglifyjs
@@ -111,7 +105,7 @@
# -*- Build tasks -*-
#
-.PHONY: all basic dev js uglify css font dist clean
+.PHONY: all basic dev js uglify css font clean
all: font css uglify
basic: $(UGLY_BASIC_JS) $(BASIC_CSS)
# dev is like all, but without minification
@@ -121,7 +115,7 @@
css: $(BUILD_CSS)
font: $(FONT_TARGET)
clean:
- rm -rf $(CLEAN)
+ rm -rf $(BUILD_DIR)
$(PJS_SRC): $(NODE_MODULES_INSTALLED)
@@ -159,13 +153,6 @@
rm -rf $@
cp -r $< $@
-dist: $(UGLY_JS) $(BUILD_JS) $(BUILD_CSS) $(FONT_TARGET)
- rm -rf $(DISTDIR)
- cp -r $(BUILD_DIR) $(DISTDIR)
- zip -r -X $(DISTZIP) $(DISTDIR)
- tar -czf $(DISTTAR) $(DISTDIR)
- rm -r $(DISTDIR)
-
#
# -*- Test tasks -*-
#
diff --git a/script/prep-release.sh b/script/prep-release.sh
new file mode 100755
index 0000000..1f0d2bf
--- /dev/null
+++ b/script/prep-release.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+set -e -o pipefail
+die () { printf '\n\tERROR: %s\n\n' "$*"; exit 1; }
+
+#
+# -1. Old versions of npm omit random files due to race condition https://git.io/vooV3
+#
+equalOrNewer () { # inspired by http://stackoverflow.com/a/25731924/362030
+ printf '%s\n%s\n' "$@" | sort -cnrt . -k 1,1 -k 2,2 -k 3,3 2>/dev/null
+}
+npm_v="$(npm -v)"
+if echo "$npm_v" | grep -q '^2\.'; then
+ equalOrNewer "$npm_v" 2.15.8 \
+ || die 'Your npm@2 version must be >=2.15.8, see https://git.io/vooV3'
+else
+ equalOrNewer "$npm_v" 3.10.1 \
+ || die 'Your npm@3 version must be >=3.10.1, see https://git.io/vooV3'
+fi
+
+#
+# 0. Clean tree & repo state except for CHANGELOG
+#
+files="$(git diff --name-only HEAD)"
+test "$files" \
+ || { echo 'First, you must add an entry to CHANGELOG.md'; exit 1; }
+test "$files" = CHANGELOG.md \
+ || die 'You have uncommitted changes other than to CHANGELOG.md'
+test "$(git rev-parse --abbrev-ref HEAD)" = master \
+ || die 'You must be on master'
+test "$(git rev-list --count @{upstream}..)" = 0 \
+ || test "$1" = --allow-unpushed-commits \
+ || die "You have unpushed commits (do $0 --allow-unpushed-commits to continue anyway)"
+
+#
+# 1. Bump package.json version
+#
+change_summary="$(git diff HEAD | grep '^+' | sed -n '2 s/^+## // p')"
+version="$(echo "$change_summary" | sed 's/:.*//')"
+git cat-file -e "$version" 2>/dev/null \
+ && die "$version already exists"
+npm version "$version" --no-git-tag-version >/dev/null
+echo "1. Bumped package.json version to \""$(node -p 'require("./package.json").version')"\""
+
+#
+# 2. Build
+#
+echo '2. make:'
+make 2>&1 | sed 's/^/ /'
+
+#
+# 3. Package as tarball + zipfile
+#
+tarball=$(npm pack) # create tarball
+tar -xzf $tarball # extract tarball as package/
+zipfile=${tarball%.tgz}.zip
+zip -qrX $zipfile package # create zipfile from package/
+echo "3. Collected release files into package/, packed as $tarball and $zipfile"
+
+#
+# 4. Commit
+#
+git add CHANGELOG.md package.json
+git commit -m "$change_summary" | sed '1 s/^/4. Committed: /; 2,$ s/^/ /'
+
+#
+# 5. Record shrinkwrap
+#
+npm shrinkwrap --dev | sed 's/^/5. /'
+shrinkwrap="$(<npm-shrinkwrap.json)"
+rm npm-shrinkwrap.json
+
+#
+# 6. Tag
+#
+{
+ echo "$change_summary"
+ echo
+ echo Created automatically by: $0
+ echo
+ echo npm-shrinkwrap.json:
+ echo "$shrinkwrap"
+} | git tag -F - $version
+echo "6. Tagged $version"
+
+#
+# Done!
+#
+echo
+git status -sb
+echo After double-checking the build/package/commit/tag, run script/push-release.sh to publish the release
diff --git a/script/push-release.sh b/script/push-release.sh
new file mode 100755
index 0000000..f588fc3
--- /dev/null
+++ b/script/push-release.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+set -e -o pipefail
+die () { printf '\n\tERROR: %s\n\n' "$*"; exit 1; }
+
+#
+# 0. Precheck that a release has been prepped (and we have an access token)
+#
+test "$(git rev-list --count @{upstream}..)" != 0 \
+ || die 'No unpushed commits, first run script/prep-release.sh'
+
+tagname="$(git describe --candidates=0 --match 'v*.*.*')"
+test "$tagname" \
+ || die 'No version tag for HEAD, first run script/prep-release.sh'
+
+tarball="mathquill-${tagname#v}.tgz"
+zipfile="mathquill-${tagname#v}.zip"
+ls "$tarball" "$zipfile" >/dev/null \
+ || die 'No tarball or zipfile, first run script/prep-release.sh'
+
+test "$GITHUB_ACCESS_TOKEN" || {
+ echo
+ echo ' ERROR: No $GITHUB_ACCESS_TOKEN defined.'
+ echo
+ echo 'This script needs an access token to create GitHub Releases.'
+ echo 'Follow these instructions to create a token authorized for the "repo" scope:'
+ echo ' https://help.github.com/articles/creating-an-access-token-for-command-line-use/'
+ echo 'Then do:'
+ echo " GITHUB_ACCESS_TOKEN=<token> $0"
+ exit 1
+}
+
+#
+# 1. npm publish
+#
+npm publish $tarball
+
+#
+# 2. git push, with tag
+#
+git push origin master tag $tagname
+
+#
+# 3. Create GitHub Release
+#
+changelog_entry="$(git show CHANGELOG.md | grep '^+' | sed -n '2,$ s/^+// p')"
+json="$(
+ tagname=$tagname \
+ summary="$(echo "$changelog_entry" | sed -n '1 s/^## // p')" \
+ body="$(echo "$changelog_entry" | tail +5)" \
+ node -p 'JSON.stringify({
+ tag_name: process.env.tagname,
+ name: process.env.summary,
+ body: process.env.body
+ })'
+)"
+
+endpoint='https://api.github.com/repos/mathquill/mathquill/releases'
+release_response="$(curl -s "$endpoint" -d "$json" \
+ -H "Authorization: token $GITHUB_ACCESS_TOKEN")"
+upload_url="$(response="$release_response" \
+ node -p 'JSON.parse(process.env.response).upload_url' | sed 's/{.*}$//')"
+
+cat $tarball | curl "$upload_url?name=$tarball" --data-binary @- \
+ -H 'Content-Type: application/x-gzip' -H "Authorization: token $GITHUB_ACCESS_TOKEN"
+cat $zipfile | curl "$upload_url?name=$zipfile" --data-binary @- \
+ -H 'Content-Type: application/zip' -H "Authorization: token $GITHUB_ACCESS_TOKEN"
+
+#
+# 4. Cleanup
+#
+rm -rf package $tarball $zipfile