UNPKG

11.1 kBapplication/x-shView Raw
1#!/bin/bash
2#
3# Script to perform a release of matrix-js-sdk and downstream projects.
4#
5# Requires:
6# github-changelog-generator; install via:
7# pip install git+https://github.com/matrix-org/github-changelog-generator.git
8# jq; install from your distribution's package manager (https://stedolan.github.io/jq/)
9# hub; install via brew (macOS) or source/pre-compiled binaries (debian) (https://github.com/github/hub) - Tested on v2.2.9
10# npm; typically installed by Node.js
11# yarn; install via brew (macOS) or similar (https://yarnpkg.com/docs/install/)
12#
13# Note: this script is also used to release matrix-react-sdk and riot-web.
14
15set -e
16
17jq --version > /dev/null || (echo "jq is required: please install it"; kill $$)
18if [[ `command -v hub` ]] && [[ `hub --version` =~ hub[[:space:]]version[[:space:]]([0-9]*).([0-9]*) ]]; then
19 HUB_VERSION_MAJOR=${BASH_REMATCH[1]}
20 HUB_VERSION_MINOR=${BASH_REMATCH[2]}
21 if [[ $HUB_VERSION_MAJOR -lt 2 ]] || [[ $HUB_VERSION_MAJOR -eq 2 && $HUB_VERSION_MINOR -lt 5 ]]; then
22 echo "hub version 2.5 is required, you have $HUB_VERSION_MAJOR.$HUB_VERSION_MINOR installed"
23 exit
24 fi
25else
26 echo "hub is required: please install it"
27 exit
28fi
29npm --version > /dev/null || (echo "npm is required: please install it"; kill $$)
30yarn --version > /dev/null || (echo "yarn is required: please install it"; kill $$)
31
32USAGE="$0 [-xz] [-c changelog_file] vX.Y.Z"
33
34help() {
35 cat <<EOF
36$USAGE
37
38 -c changelog_file: specify name of file containing changelog
39 -x: skip updating the changelog
40 -z: skip generating the jsdoc
41EOF
42}
43
44ret=0
45cat package.json | jq '.dependencies[]' | grep -q '#develop' || ret=$?
46if [ "$ret" -eq 0 ]; then
47 echo "package.json contains develop dependencies. Refusing to release."
48 exit
49fi
50
51if ! git diff-index --quiet --cached HEAD; then
52 echo "this git checkout has staged (uncommitted) changes. Refusing to release."
53 exit
54fi
55
56if ! git diff-files --quiet; then
57 echo "this git checkout has uncommitted changes. Refusing to release."
58 exit
59fi
60
61skip_changelog=
62skip_jsdoc=
63changelog_file="CHANGELOG.md"
64expected_npm_user="matrixdotorg"
65while getopts hc:u:xz f; do
66 case $f in
67 h)
68 help
69 exit 0
70 ;;
71 c)
72 changelog_file="$OPTARG"
73 ;;
74 x)
75 skip_changelog=1
76 ;;
77 z)
78 skip_jsdoc=1
79 ;;
80 u)
81 expected_npm_user="$OPTARG"
82 ;;
83 esac
84done
85shift `expr $OPTIND - 1`
86
87if [ $# -ne 1 ]; then
88 echo "Usage: $USAGE" >&2
89 exit 1
90fi
91
92if [ -z "$skip_changelog" ]; then
93 # update_changelog doesn't have a --version flag
94 update_changelog -h > /dev/null || (echo "github-changelog-generator is required: please install it"; exit)
95fi
96
97# Login and publish continues to use `npm`, as it seems to have more clearly
98# defined options and semantics than `yarn` for writing to the registry.
99actual_npm_user=`npm whoami`;
100if [ $expected_npm_user != $actual_npm_user ]; then
101 echo "you need to be logged into npm as $expected_npm_user, but you are logged in as $actual_npm_user" >&2
102 exit 1
103fi
104
105# ignore leading v on release
106release="${1#v}"
107tag="v${release}"
108rel_branch="release-$tag"
109
110prerelease=0
111# We check if this build is a prerelease by looking to
112# see if the version has a hyphen in it. Crude,
113# but semver doesn't support postreleases so anything
114# with a hyphen is a prerelease.
115echo $release | grep -q '-' && prerelease=1
116
117if [ $prerelease -eq 1 ]; then
118 echo Making a PRE-RELEASE
119fi
120
121if [ -z "$skip_changelog" ]; then
122 if ! command -v update_changelog >/dev/null 2>&1; then
123 echo "release.sh requires github-changelog-generator. Try:" >&2
124 echo " pip install git+https://github.com/matrix-org/github-changelog-generator.git" >&2
125 exit 1
126 fi
127fi
128
129# we might already be on the release branch, in which case, yay
130# If we're on any branch starting with 'release', we don't create
131# a separate release branch (this allows us to use the same
132# release branch for releases and release candidates).
133curbranch=$(git symbolic-ref --short HEAD)
134if [[ "$curbranch" != release* ]]; then
135 echo "Creating release branch"
136 git checkout -b "$rel_branch"
137else
138 echo "Using current branch ($curbranch) for release"
139 rel_branch=$curbranch
140fi
141
142if [ -z "$skip_changelog" ]; then
143 echo "Generating changelog"
144 update_changelog -f "$changelog_file" "$release"
145 read -p "Edit $changelog_file manually, or press enter to continue " REPLY
146
147 if [ -n "$(git ls-files --modified $changelog_file)" ]; then
148 echo "Committing updated changelog"
149 git commit "$changelog_file" -m "Prepare changelog for $tag"
150 fi
151fi
152latest_changes=`mktemp`
153cat "${changelog_file}" | `dirname $0`/scripts/changelog_head.py > "${latest_changes}"
154
155set -x
156
157# Bump package.json and build the dist
158echo "yarn version"
159# yarn version will automatically commit its modification
160# and make a release tag. We don't want it to create the tag
161# because it can only sign with the default key, but we can
162# only turn off both of these behaviours, so we have to
163# manually commit the result.
164yarn version --no-git-tag-version --new-version "$release"
165
166# commit yarn.lock if it exists, is versioned, and is modified
167if [[ -f yarn.lock && `git status --porcelain yarn.lock | grep '^ M'` ]];
168then
169 pkglock='yarn.lock'
170else
171 pkglock=''
172fi
173git commit package.json $pkglock -m "$tag"
174
175
176# figure out if we should be signing this release
177signing_id=
178if [ -f release_config.yaml ]; then
179 signing_id=`cat release_config.yaml | python -c "import yaml; import sys; print yaml.load(sys.stdin)['signing_id']"`
180fi
181
182
183# If there is a 'dist' script in the package.json,
184# run it in a separate checkout of the project, then
185# upload any files in the 'dist' directory as release
186# assets.
187# We make a completely separate checkout to be sure
188# we're using released versions of the dependencies
189# (rather than whatever we're pulling in from yarn link)
190assets=''
191dodist=0
192jq -e .scripts.dist package.json 2> /dev/null || dodist=$?
193if [ $dodist -eq 0 ]; then
194 projdir=`pwd`
195 builddir=`mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir'`
196 echo "Building distribution copy in $builddir"
197 pushd "$builddir"
198 git clone "$projdir" .
199 git checkout "$rel_branch"
200 # We use Git branch / commit dependencies for some packages, and Yarn seems
201 # to have a hard time getting that right. See also
202 # https://github.com/yarnpkg/yarn/issues/4734. As a workaround, we clean the
203 # global cache here to ensure we get the right thing.
204 yarn cache clean
205 yarn install
206 # We haven't tagged yet, so tell the dist script what version
207 # it's building
208 DIST_VERSION="$tag" yarn dist
209
210 popd
211
212 for i in "$builddir"/dist/*; do
213 assets="$assets -a $i"
214 if [ -n "$signing_id" ]
215 then
216 gpg -u "$signing_id" --armor --output "$i".asc --detach-sig "$i"
217 assets="$assets -a $i.asc"
218 fi
219 done
220fi
221
222if [ -n "$signing_id" ]; then
223 # make a signed tag
224 # gnupg seems to fail to get the right tty device unless we set it here
225 GIT_COMMITTER_EMAIL="$signing_id" GPG_TTY=`tty` git tag -u "$signing_id" -F "${latest_changes}" "$tag"
226else
227 git tag -a -F "${latest_changes}" "$tag"
228fi
229
230# push the tag and the release branch
231git push origin "$rel_branch" "$tag"
232
233if [ -n "$signing_id" ]; then
234 # make a signature for the source tarball.
235 #
236 # github will make us a tarball from the tag - we want to create a
237 # signature for it, which means that first of all we need to check that
238 # it's correct.
239 #
240 # we can't deterministically build exactly the same tarball, due to
241 # differences in gzip implementation - but we *can* build the same tar - so
242 # the easiest way to check the validity of the tarball from git is to unzip
243 # it and compare it with our own idea of what the tar should look like.
244
245 # the name of the sig file we want to create
246 source_sigfile="${tag}-src.tar.gz.asc"
247
248 tarfile="$tag.tar.gz"
249 gh_project_url=$(git remote get-url origin |
250 sed -e 's#^git@github\.com:#https://github.com/#' \
251 -e 's#^git\+ssh://git@github\.com/#https://github.com/#' \
252 -e 's/\.git$//')
253 project_name="${gh_project_url##*/}"
254 curl -L "${gh_project_url}/archive/${tarfile}" -o "${tarfile}"
255
256 # unzip it and compare it with the tar we would generate
257 if ! cmp --silent <(gunzip -c $tarfile) \
258 <(git archive --format tar --prefix="${project_name}-${release}/" "$tag"); then
259
260 # we don't bail out here, because really it's more likely that our comparison
261 # screwed up and it's super annoying to abort the script at this point.
262 cat >&2 <<EOF
263!!!!!!!!!!!!!!!!!
264!!!! WARNING !!!!
265
266Mismatch between our own tarfile and that generated by github: not signing
267source tarball.
268
269To resolve, determine if $tarfile is correct, and if so sign it with gpg and
270attach it to the release as $source_sigfile.
271
272!!!!!!!!!!!!!!!!!
273EOF
274 else
275 gpg -u "$signing_id" --armor --output "$source_sigfile" --detach-sig "$tarfile"
276 assets="$assets -a $source_sigfile"
277 fi
278fi
279
280hubflags=''
281if [ $prerelease -eq 1 ]; then
282 hubflags='-p'
283fi
284
285release_text=`mktemp`
286echo "$tag" > "${release_text}"
287echo >> "${release_text}"
288cat "${latest_changes}" >> "${release_text}"
289hub release create $hubflags $assets -F "${release_text}" "$tag"
290
291if [ $dodist -eq 0 ]; then
292 rm -rf "$builddir"
293fi
294rm "${release_text}"
295rm "${latest_changes}"
296
297# Login and publish continues to use `npm`, as it seems to have more clearly
298# defined options and semantics than `yarn` for writing to the registry.
299# Tag both releases and prereleases as `next` so the last stable release remains
300# the default.
301npm publish --tag next
302if [ $prerelease -eq 0 ]; then
303 # For a release, also add the default `latest` tag.
304 package=$(cat package.json | jq -er .name)
305 npm dist-tag add "$package@$release" latest
306fi
307
308if [ -z "$skip_jsdoc" ]; then
309 echo "generating jsdocs"
310 yarn gendoc
311
312 echo "copying jsdocs to gh-pages branch"
313 git checkout gh-pages
314 git pull
315 cp -a ".jsdoc/matrix-js-sdk/$release" .
316 perl -i -pe 'BEGIN {$rel=shift} $_ =~ /^<\/ul>/ && print
317 "<li><a href=\"${rel}/index.html\">Version ${rel}</a></li>\n"' \
318 $release index.html
319 git add "$release"
320 git commit --no-verify -m "Add jsdoc for $release" index.html "$release"
321fi
322
323# if it is a pre-release, leave it on the release branch for now.
324if [ $prerelease -eq 1 ]; then
325 git checkout "$rel_branch"
326 exit 0
327fi
328
329# merge release branch to master
330echo "updating master branch"
331git checkout master
332git pull
333git merge "$rel_branch"
334
335# push master and docs (if generated) to github
336git push origin master
337if [ -z "$skip_jsdoc" ]; then
338 git push origin gh-pages
339fi
340
341# finally, merge master back onto develop
342git checkout develop
343git pull
344git merge master
345git push origin develop