chiark / gitweb /
make-release: Poll crates.io in the hope of making release reliable
[otter.git] / make-release
1 #!/bin/bash
2 #
3 # usage:
4 #    ./make-release --dry-run|--real <branch>
5 # eg
6 #    ./make-release --dry-run main
7
8 #---------- argument parsing and options ----------
9
10 set -e
11 set -o pipefail
12
13 fail () { echo >&2 "${0##*/}: error: $*"; exit 12; }
14
15 dryrun=x-dry-run-unset
16 cargo_dryrun=--not-a-cargo-option-please-crash
17
18 case "$#.$1" in
19 2.--real)    dryrun=x     ; cargo_dryrun=''         ; ;;
20 2.--dry-run) dryrun=dryrun; cargo_dryrun='--dry-run'; ;;
21 *)           fail "bad usage" ;;
22 esac
23
24 keyid=0x559AE46C2D6B6D3265E7CBA1E3E3392348B50D39
25
26 cratesio_raw_url=\
27 https://raw.githubusercontent.com/rust-lang/crates.io-index/master
28
29 branch="$2"
30
31 dryrun () { echo "WOULD  $*"; }
32 x () { echo >&2 "+ $*"; "$@"; }
33
34 trouble=false
35 trouble () { echo >&2 "***TROUBLE***: $*"; trouble=true; }
36
37 #---------- checks ----------
38
39 version=$(perl <Cargo.toml -ne '
40     next unless m{^version\s*=\s*\"([0-9.]+)\"\s*$};
41     print "$1\n" or die $!;
42     exit 0;
43 ')
44
45 case "$version" in
46 '') fail "no version?" ;;
47 esac
48
49 echo "version $version"
50
51 equals () {
52     diff <(git rev-parse refs/heads/$1) <(git rev-parse HEAD) \
53         || trouble "HEAD not equal to $1"
54 }
55
56 equals $branch
57 equals tested
58
59 bad=$(git status --porcelain)
60 if [ "x$bad" != x ]; then
61     printf >&2 '%s\n' "$bad"
62     trouble 'tree is dirty'
63 fi
64
65 tag="otter-$version"
66 tag_exists=$(git for-each-ref "[r]efs/tags/$tag")
67 if [ "x$tag_exists" != x ]; then trouble "tag $tag already exists"; fi
68
69 head -1 CHANGELOG.md | grep "^Version $version" \
70 || trouble "CHANGELOG.md not updated"
71
72 cargo_order='base . cli daemon wasm apitest wdriver'
73 missing=(git ls-files :\*/Cargo.toml :Cargo.toml)
74 for x in $cargo_order; do missing+=(:!$x/Cargo.toml); done
75 missing=$( "${missing[@]}" )
76 if [ "x$missing" != x ]; then trouble "missing cargo package(s) $missing"; fi
77
78 #---------- end of checks ----------
79
80 if $trouble; then
81     $dryrun fail "trouble! checks failed!"
82 else
83     echo 'checks passed'
84 fi
85
86 #---------- actually do the work ----------
87
88 $dryrun git push chiark $branch
89 $dryrun git push origin $branch
90
91 #---------- non-idempotent things ----------
92
93 $dryrun make -j12 publish
94
95 $dryrun git tag -s -u "$keyid" -m "Otter v$version" $tag
96 $dryrun git push chiark $tag
97 $dryrun git push origin $tag
98
99 #---------- oh woe cargo ----------
100
101 # https://github.com/rust-lang/cargo/issues/9507
102 wait_for_crates_io () {
103     local p=$1
104     local delay=1
105     local url="$cratesio_raw_url/${p:0:2}/${p:2:2}/$p"
106     printf >&2 "waiting for upload of %s to take effect" "$p"
107     while sleep $delay; do
108         printf >&2 .
109         local got=$(
110             curl -sS "$url" | jq '.vers | select(. == "'"$version"'")'
111         )
112         if [ "x$got" != x ]; then break; fi
113         delay=$(( $delay * 11 / 10 + 1 ))
114     done
115     echo >&2 'done\n'
116 }
117
118 for cargo_dir in $cargo_order; do
119     $dryrun_no_more_cargo \
120     nailing-cargo --no-nail --linkfarm=git --- \
121         sh -xec "
122           find . ! -type l ! -type d ! -path './target/*' -print0 \
123               | xargs -0r rm --
124           cd $cargo_dir; cargo publish $cargo_dryrun
125         "
126
127     cargo_package=$(
128         sed -n '/^name *=/ { s/^name *= *"\(.*\)" *$/\1/; p; q }' \
129             <$cargo_dir/Cargo.toml
130     )
131
132     wait_for_crates_io "$cargo_package"
133
134     dryrun_no_more_cargo=$dryrun
135
136 done
137
138 #---------- finish ----------
139
140 $dryrun cat <<END
141
142
143 Successfully released to
144   - git tags
145   - all git branches
146   - cargo publish
147
148 Consider
149   - make deploy
150
151 You need to write release announcements
152   - sgo-software-announce
153   - blog
154
155 END