Fix #338: re.sub() flag argument at wrong position.
[docutils.git] / sandbox / infrastructure / release.sh
blob1e0f3003a1029e22a99d145ff1f656eb60013cd4
1 #!/bin/bash
3 # Author: Lea Wiemann
4 # Contact: grubert@users.sourceforge.net
5 # Revision: $Revision$
6 # Date: $Date$
7 # Copyright: This script has been placed in the public domain.
9 # USAGE see: docutils/docs/dev/release.txt
11 # must be run from docutils trunk/docutils,
12 # because HISTORY and RELEASE_NOTES.txt are modified.
14 set -e
16 function print_command()
18 # Print "$@", quoting parameters containing spaces.
19 echo -n $
20 for param in "$@"; do
21 echo "$param" | grep -Fq ' ' && echo -n " '$param'" || echo -n " $param"
22 done
25 function run()
27 # Print and run "$@".
28 print_command "$@"
29 echo
30 "$@"
33 function confirm()
35 # Print, let the user confirm and run "$@".
36 echo 'Press enter to run (or enter anything to skip):'
37 print_command "$@"
38 read
39 test "$REPLY" && echo Skipped. || "$@"
42 function svn_up()
44 if test $svn == svk; then
45 confirm svk sync "$depot"
47 confirm $svn up
50 function checkin()
52 # Parameters: log_message, file, file, file ...
53 log_message="$1"
54 shift
55 confirm $svn diff "$@"
56 confirm $svn ci -m "$log_prefix $log_message" "$@"
59 function set_ver()
61 # Parameters: old_version new_version
62 shopt -s extglob
63 echo Determining list of files to be changed...
64 # BUG ls lists directories but does not descend
65 files="docutils/__init__.py setup.py `$svn ls test/functional/expected/ | sed 's|^|test/functional/expected/|'`"
66 echo "Now I'll change the version number to $2 in the following files:"
67 echo $files
68 echo
69 echo 'Press enter to proceed (or enter anything to skip)...'
70 read
71 if [ ! "$REPLY" ]; then
72 echo 'Modifying files with ed...'
73 old_ver_regex="`echo "$1" | sed 's/\./\\\\./g'`"
74 # "ed" returns an error code if there has been no substitution, so
75 # we temporarily deactivate exit-on-error.
76 set +e
77 for F in $files; do
78 (echo ",s/$old_ver_regex/$2/g"; echo 'wq') | ed "$F"
79 done
80 set -e
82 echo
83 echo 'CAUTION: please look at the diffs carefully, for wrongly'
84 echo ' replaced embedded numbers.'
85 checkin "set version number to $2" $files
88 function usage()
90 echo 'Usage:'
91 echo
92 echo ' release.sh new_version svn_version[:branch_version] [stage]'
93 echo
94 echo 'The following things will be done:'
95 echo
96 echo '* Change version number to new_version. (stage 1)'
97 echo '* SVN-export, test, and release Docutils version new_version. (stage 2)'
98 echo '* Change version number to svn_version. (stage 3)'
99 echo
100 echo 'If stage is supplied (1, 2 or 3), only the specified stage will'
101 echo 'be executed. Otherwise, it defaults to executing all stages.'
102 echo
103 echo 'Before doing dangerous things, you will be asked to press enter.'
104 echo
105 echo 'A maintenance branch called docutils-new_version will be created'
106 echo 'if branch_version is given. The version number inside the'
107 echo 'maintenance branch will be set to branch_version.'
108 echo
109 echo 'Access ssh,scp and sftp access to shell/frs.sourceforge.net'
110 echo 'must be configured.'
111 exit 1
114 function initialize()
116 if [ "$#" -lt 2 ]; then
117 usage
119 echo 'Initializing...'
120 python_versions='2.4 2.5 2.6 2.7'
121 for py_ver in $python_versions; do
122 echo -n "Checking for Python $py_ver (python$py_ver)... "
123 if ! echo 'print "OK"' | python$py_ver; then
124 echo "WARN: Python $py_ver (python$py_ver) not found."
126 done
127 echo -n 'Clearing $PYTHONPATH... '
128 export PYTHONPATH=
129 echo 'done'
130 echo -n 'Checking whether we are in a working copy... '
131 if [ -f HISTORY.txt ]; then
132 echo yes
133 else
134 echo "no (HISTORY.txt doesn't exist)"
135 echo 'Aborting.'
136 echo 'Please cd to a working copy before running this script.'
137 exit 1
139 echo -n 'Subversion binary to use: '
140 # TODO breaks for newer svn, .svn is in upper. is this for svk
141 svn=svn
142 echo $svn
143 working_copy="`pwd -P`"
144 echo "Working copy: $working_copy"
145 svnurl="`$svn info . | grep ^URL: | sed 's/URL: //'`"
146 if test -z "$svnurl"; then
147 echo 'Unable to detect Subversion URL. Aborting.'
148 exit 1
150 echo "Subversion URL: $svnurl"
151 if ! echo "$svnurl" | grep -q 'branches\|trunk'; then
152 echo 'Subversion URL contains neither "branches" nor "trunk".'
153 echo 'Aborting.'
154 exit 1
156 if test `uname` = "Darwin" ; then
157 svnroot="`echo "$svnurl" | sed -E 's/\/(branches|trunk).*//'`"
158 else
159 svnroot="`echo "$svnurl" | sed 's/\/\(branches\|trunk\).*//'`"
161 echo "Subversion root URL: $svnroot"
162 if test "$svnurl" = "$svnroot"; then
163 echo 'Error: Subversion URL and Subversion root URL are the same.'
164 echo ' probably a MacOSX sed problem.'
165 exit 1
167 echo -n 'Detecting current Docutils version... '
168 old_ver="`python -c 'import docutils; print docutils.__version__'`"
169 echo "$old_ver"
170 new_ver="$1"
171 # log_prefix is for SVN logs.
172 log_prefix="Release $new_ver:"
173 echo "New version number (for releasing): $new_ver"
174 svn_ver="$2"
175 if echo "$svn_ver" | grep -q :; then
176 # Split at colon: svn_ver:branch_ver
177 branch_ver="${svn_ver#*:}"
178 svn_ver="${svn_ver%:*}"
179 else
180 branch_ver=
182 echo "New Subversion version number (after releasing): $svn_ver"
183 echo -n 'Create maintenance branch: '
184 if test "$branch_ver"; then
185 echo yes
186 echo "New version number on maintenance branch: $branch_ver"
187 else
188 echo no
190 if test "$branch_ver"; then
191 echo -n 'Checking that we have a full checkout... '
192 if echo "$working_copy" | grep -q 'branches\|trunk'; then
193 echo OK
194 else
195 echo 'no'
196 echo 'Working copy path contains neither "branches" nor "trunk".'
197 echo 'You need a full checkout in order to branch.'
198 echo 'Aborting.'
199 exit 1
201 wcroot="`echo "$working_copy" | sed 's/\/\(branches\|trunk\).*//'`"
202 echo "Working copy root: $wcroot"
204 tarball=docutils-"$new_ver".tar.gz
205 echo "Tarball name: $tarball"
206 echo 'Initialization completed.'
207 echo
210 function test_tarball()
212 # Assume we have the tarball in the current directory.
213 # Pass test number as first parameter.
214 echo 'Testing the release tarball.'
215 run mkdir tarball_test/
216 run cd tarball_test/
217 confirm tar xzvf "../$tarball"
218 echo
219 run cd docutils-"$new_ver"
220 echo 'Deleteing old installations. Installing the distribution.'
221 echo "WARN: might not find installation."
222 for py_ver in '"$python_versions"'; do
223 echo "python$py_ver install/update and test."
224 bash release-test.sh
225 echo "Enter to test next."
226 read
227 done
230 function upload_tarball()
232 # Assume we have the tarball in the working area.
233 run cd "$working_area"
234 mkdir $new_ver
235 cp docutils-$new_ver.tar.gz $new_ver
236 cp docutils/RELEASE-NOTES.txt $new_ver
237 # README.txt would be displayed automatically on sf.
238 # BUG user grubert hardcoded
239 # short path "/home/frs/project/docutils/docutils/" also exists
240 scp -r $new_ver grubert,docutils@frs.sourceforge.net:/home/frs/project/d/do/docutils/docutils/
241 echo 'Upload completed.'
244 function upload_htdocs()
246 # Assume we have the tarball in the working area.
247 run cd "$working_area"
248 echo "Upload htdocs for $new_ver"
249 run mkdir htdocs
250 run cd htdocs
251 confirm tar xzvf "../$tarball"
252 run cd docutils-"$new_ver"/tools/
253 echo "BUG no docutils installation left."
254 echo "DO NOT let call but manually in $(pwd)"
255 confirm ./buildhtml.py --local ..
256 confirm ./buildhtml.py ../docs
257 run cd ..
258 echo '$ find . -name test -type d -prune -o -name \*.css -print0 \
259 -o -name \*.html -print0 -o -name \*.txt -print0 \
260 | tar -cjvf docutils-docs.tar.bz2 -T - --null'
261 find . -name test -type d -prune -o -name \*.css -print0 \
262 -o -name \*.html -print0 -o -name \*.txt -print0 \
263 | tar -cjvf docutils-docs.tar.bz2 -T - --null
264 echo 'Upload docs to SF.net...'
265 echo 'Press enter (or enter anything to skip).'
266 read
267 if [ ! "$REPLY" ]; then
268 mkdir $new_ver
269 cd $new_ver
270 tar xjvf ../docutils-docs.tar.bz2
271 cd ..
272 chmod -R g+rw $new_ver
273 scp -r -p -C $new_ver web.sourceforge.net:/home/groups/d/do/docutils/htdocs
277 function create_maintenance_branch()
279 echo 'Creating maintenance branch.'
280 branch_name="docutils-$new_ver"
281 echo "Branch name: $branch_name"
282 branch_url="$svnroot/branches/$branch_name"
283 echo "Branch URL: $branch_url"
284 confirm svn cp "$svnurl" "$branch_url" -m \
285 "$log_prefix created maintenance branch for version $new_ver"
286 cd "$wcroot"
287 svn_up
288 cd branches/"$branch_name"
289 set_ver "$new_ver" "$branch_ver"
292 function run_stage()
294 if [ ! "$1" ]; then
295 run_stage 1
296 echo
297 run_stage 2
298 echo
299 run_stage 3
300 else
301 echo "Press enter to run stage $1 (or enter anything to skip this stage)."
302 read
303 if [ ! "$REPLY" ]; then
304 cd "$working_copy"
305 if [ "$1" == 1 ]; then
306 stage_1
307 elif [ "$1" == 2 ]; then
308 stage_2
309 elif [ "$1" == 3 ]; then
310 stage_3
311 else
312 echo 'Invalid stage.'
313 echo
314 usage
316 echo
317 echo "Stage $1 completed."
318 else
319 echo "Skipped stage $1."
324 function stage_1()
326 svn_up
327 echo
328 # update __version_details__ string
329 (echo ",s/^__version_details__ = .*\$/__version_details__ = 'release'/";
330 echo wq) | ed docutils/__init__.py 2> /dev/null
331 set_ver "$old_ver" "$new_ver"
332 echo
333 history_files='HISTORY.txt RELEASE-NOTES.txt'
334 echo "Now updating the following files: $history_files"
335 old_string="Changes Since [0-9.]+"
336 new_string="Release $new_ver (`date --utc --iso-8601`)"
337 echo 'Press enter to replace "'"$old_string"'" with "'"$new_string"\",
338 echo 'or enter anything to skip.'
339 read
340 test "$REPLY" || python -c "for filename in '$history_files'.split():
341 import re
342 h = file(filename).read()
343 h = re.sub('$old_string\\n=+', '$new_string\\n' + '=' * len('$new_string'), h, count=1)
344 file(filename, 'w').write(h)"
345 checkin 'closed "Changes Since ..." section' $history_files
348 function stage_2()
350 echo 'Creating working area...'
351 working_area="`echo ~/tmp/docutils-release.$$`"
352 run mkdir -p "$working_area"
353 echo
354 echo 'Getting a fresh export.'
355 echo 'Press enter to proceed (or enter anything to skip)...'
356 read
357 if [ ! "$REPLY" ]; then
358 run cd "$working_area"
359 confirm svn export "$svnurl"
360 echo
361 echo 'Building the release tarball.'
362 run cd docutils
363 confirm ./setup.py sdist
364 run cd ..
365 echo 'Tarball built.'
366 run cp docutils/dist/"$tarball" .
367 echo 'BETTER run release-test.sh manually for each installed python version.'
368 echo "DO: cd $working_area and call sandbox/infrastructure/release-test.sh py_ver docutils_ver"
369 confirm test_tarball
370 echo "Testing documentation and uploading htdocs of version $new_ver..."
371 confirm upload_htdocs
372 echo "Tagging current revision..."
373 confirm svn cp "$svnurl" "$svnroot/tags/docutils-$new_ver/" -m "$log_prefix tagging released revision"
374 echo "Uploading $tarball to SF.net."
375 confirm upload_tarball
376 echo 'Now go to https://sourceforge.net/projects/docutils/files/docutils'
377 echo 'and follow the instructions at'
378 echo 'http://docutils.sf.net/docs/dev/release.html#file-release-system'
379 echo
380 echo 'Then press enter.'
381 read
383 run cd $working_area
384 echo 'Downloading the tarball to verify its integrity.'
385 while true; do
386 # BUG path is wrong. project admin filemanager shows md5sum
387 confirm wget http://sourceforge.net/projects/docutils/files/"$tarball"
388 echo 'Was the download successful?'
389 echo 'If yes, press enter to continue, otherwise enter anything to repeat'
390 echo '(it is possible that the file will show up in a few minutes).'
391 read
392 test "$REPLY" || break
393 done
394 confirm test_tarball
395 echo 'we are registered with PyPI...'
396 echo 'upload to pypi with twine or set download url for this release'
398 echo 'Press enter to proceed (or enter anything to skip)...'
399 read
400 if [ ! "$REPLY" ]; then
401 echo "Unpacking tarball..."
402 ls -l
404 run twine upload "$tarball"
405 echo "verify on PyPI. hide older releases."
406 echo "TODO TEST build wheels and upload"
407 run pip wheel docutils
408 run twine upload docutils-*-py2-none-any.whl
409 run pip3 wheel docutils
410 run twine upload wheelhouse/docutils-${new_ver}-py3-none-any.whl
414 function stage_3()
416 svn_up
417 echo
418 # update __version_details__ string
419 (echo ",s/^__version_details__ = .*\$/__version_details__ = 'repository'/";
420 echo wq) | ed docutils/__init__.py 2> /dev/null
421 checkin 'set __version_details__ to "repository"'
422 echo
423 history_files='HISTORY.txt RELEASE-NOTES.txt'
424 echo "Now updating the following files: $history_files"
425 add_string="Changes Since $new_ver"
426 before="Release "
427 echo 'Press enter to add "'"$add_string"'" section,'
428 echo 'or enter anything to skip.'
429 read
430 test "$REPLY" || python -c "for filename in '$history_files'.split():
431 import re
432 h = file(filename).read()
433 h = re.sub('\n$before', '\\n$add_string\\n' + '=' * len('$add_string') +
434 '\\n\\n\\n$before', h, count=1)
435 file(filename, 'w').write(h)"
436 checkin "added empty \"Changes Since $new_ver\" section" $history_files
437 echo
438 if test "$branch_ver"; then
439 create_maintenance_branch
440 cd "$working_copy"
442 set_ver "$new_ver" "$svn_ver"
443 echo 'Please change version number in README.txt'
444 echo
445 echo 'Please update the web page now (web/index.txt).'
446 echo 'cd into sandbox/infrastructure'
447 echo 'and call docutils-update.local (requires linux, macosx cp misses something)'
448 echo "Press enter when you're done."
449 read
452 initialize "$@"
453 run_stage "$3"
454 echo
455 echo 'Finished.'
456 echo 'Run alltests.py on svn version now.'
458 # Local Variables:
459 # indent-tabs-mode: nil
460 # End: