Skip to content

Commit

Permalink
New 2-step release process: script/{prep,push}-release.sh
Browse files Browse the repository at this point in the history
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]: npm/npm#13080
  • Loading branch information
laughinghan committed Jun 23, 2016
1 parent 99c0a34 commit 44ab36c
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 21 deletions.
24 changes: 18 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -35,7 +37,9 @@ see the [v0.9.x → v0.10.0 Migration Guide][].)
**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
Expand Down Expand Up @@ -137,7 +141,9 @@ itself): See the [v0.9.x → v0.10.0 Migration Guide]
- (#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)

Expand All @@ -155,7 +161,9 @@ URGENT HOTFIX for cursor showing up as an ugly box in Chrome 40 (#371)
**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`
Expand All @@ -174,7 +182,9 @@ URGENT HOTFIX for cursor showing up as an ugly box in Chrome 40 (#371)
- (#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.
Expand All @@ -199,7 +209,9 @@ v0.9.1 had a huge bug, fixed as #166.
- 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
Expand Down
17 changes: 2 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,6 @@ BASIC_CSS = $(BUILD_DIR)/mathquill-basic.css
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
Expand All @@ -111,7 +105,7 @@ BUILD_DIR_EXISTS = $(BUILD_DIR)/.exists--used_by_Makefile
# -*- 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
Expand All @@ -121,7 +115,7 @@ uglify: $(UGLY_JS)
css: $(BUILD_CSS)
font: $(FONT_TARGET)
clean:
rm -rf $(CLEAN)
rm -rf $(BUILD_DIR)

$(PJS_SRC): $(NODE_MODULES_INSTALLED)

Expand Down Expand Up @@ -159,13 +153,6 @@ $(FONT_TARGET): $(FONT_SOURCE) $(BUILD_DIR_EXISTS)
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 -*-
#
Expand Down
90 changes: 90 additions & 0 deletions script/prep-release.sh
Original file line number Diff line number Diff line change
@@ -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
71 changes: 71 additions & 0 deletions script/push-release.sh
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 44ab36c

Please sign in to comment.