script/release.sh: This is a new script to do releases
[Samba.git] / script / release.sh
blob9f7379ede23d81f1538a0a78c4dcf9224e0429f6
1 #!/bin/bash
2 # make a release of Samba or a library
4 LC_ALL=C
5 export LC_ALL
6 LANG=C
7 export LANG
8 LANGUAGE=C
9 export LANGUAGE
11 set -u
12 set -e
13 umask 0022
15 CONF_REPO_URL="ssh://git.samba.org/data/git/samba.git"
16 CONF_UPLOAD_URL="samba-bugs@download-master.samba.org:/home/data/ftp/pub"
17 CONF_DOWNLOAD_URL="https://download.samba.org/pub"
19 test -d ".git" || {
20 echo "Run this script from the top-level directory in the"
21 echo "repository"
22 exit 1
25 usage() {
26 echo "Usage: release.sh <PRODUCT> <COMMAND>"
27 echo ""
28 echo "PRODUCT: ldb, talloc, tevent, tdb, samba-rc"
29 echo "COMMAND: fullrelease, create, push, upload, announce"
30 echo ""
31 return 0
34 check_args() {
35 local cmd="$1"
36 local got_args="$2"
37 local take_args="$3"
39 test x"${got_args}" = x"${take_args}" || {
40 usage
41 echo "cmd[${cmd}] takes ${take_args} instead of ${got_args}"
42 return 1
45 return 0
48 min_args() {
49 local cmd="$1"
50 local got_args="$2"
51 local min_args="$3"
53 test "${got_args}" -ge "${min_args}" || {
54 usage
55 echo "cmd[${cmd}] takes at least ${min_args} instead of ${got_args}"
56 return 1
59 return 0
62 min_args "$0" "$#" "2"
64 product="$1"
65 globalcmd="$2"
66 shift 2
67 tagname=""
68 cmds=""
69 next_cmd=""
71 require_tagname() {
72 min_args "${FUNCNAME}" "$#" "1" || return 1
73 local cmd="$1"
75 test -n "${tagname}" || {
76 echo "cmd[${cmd}] requires '\${tagname}' variable to be set"
77 return 1
80 local name=$(echo "${tagname}" | cut -d '-' -f1)
81 test x"${name}" = x"${productbase}" || {
82 echo "Invalid tagname[${tgzname}]"
83 return 1
86 return 0
89 cmd_allowed() {
90 min_args "${FUNCNAME}" "$#" "2" || return 1
91 local cmd="$1"
92 shift 1
94 echo "$@" | grep -q "\<${cmd}\>" || {
95 return 1
98 return 0
101 verify_samba_rc() {
102 check_args "${FUNCNAME}" "$#" "0" || return 1
104 test -f VERSION || {
105 echo "VERSION doesn't exist"
106 return 1
109 grep -q 'SAMBA_VERSION_IS_GIT_SNAPSHOT=no' VERSION || {
110 echo "SAMBA_VERSION_IS_GIT_SNAPSHOT is not 'no'"
111 return 1
114 grep -q '^SAMBA_VERSION_RC_RELEASE=' VERSION || {
115 echo "SAMBA_VERSION_RC_RELEASE= missing"
116 return 1
119 grep -q '^SAMBA_VERSION_RC_RELEASE=$' VERSION && {
120 echo "SAMBA_VERSION_RC_RELEASE= missing the rc version"
121 return 1
124 return 0
127 verify_release() {
128 check_args "${FUNCNAME}" "$#" "0" || return 1
130 test -n "${verify_fn}" || {
131 echo "verify_fn variable empty"
132 return 1
135 echo "Running ${verify_fn}"
136 ${verify_fn}
139 create_release() {
140 check_args "${FUNCNAME}" "$#" "0" || return 1
142 echo "Releasing product ${product}"
144 echo "Building release tarball"
145 local tgzname=$(make dist 2>&1 | grep ^Created | cut -d' ' -f2)
146 test -f "${tgzname}" || {
147 echo "Failed to create tarball"
148 return 1
150 CLEANUP_FILES="${CLEANUP_FILES} ${tgzname}"
152 local name=$(echo "${tgzname}" | cut -d '-' -f1)
153 test x"${name}" = x"${productbase}" || {
154 echo "Invalid tgzname[${tgzname}]"
155 return 1
158 local tarname=$(basename ${tgzname} .gz)
159 echo "Tarball: ${tarname}"
160 gunzip -f ${tgzname} || exit 1
161 test -f "${tarname}" || {
162 echo "Failed to decompress tarball ${tarname}"
163 return 1
165 CLEANUP_FILES="${CLEANUP_FILES} ${tarname}"
167 # tagname is global
168 tagname=$(basename ${tarname} .tar)
169 echo "Tagging as ${tagname}"
170 git tag -u ${GPG_KEYID} -s "${tagname}" -m "${productbase}: tag release ${tagname}" || {
171 return 1
173 CLEANUP_TAGS="${CLEANUP_TAGS} ${tagname}"
175 echo "signing"
176 rm -f "${tarname}.asc"
177 gpg -u "${GPG_USER}" --detach-sign --armor ${tarname} || {
178 return 1
180 test -f "${tarname}.asc" || {
181 echo "Failed to create signature ${tarname}.asc"
182 return 1
184 CLEANUP_FILES="${CLEANUP_FILES} ${tarname}.asc"
185 echo "compressing"
186 gzip -f -9 ${tarname}
187 test -f "${tgzname}" || {
188 echo "Failed to compress ${tgzname}"
189 return 1
192 return 0
195 whatsnew_release() {
196 check_args "${FUNCNAME}" "$#" "0" || return 1
197 require_tagname "${FUNCNAME}"
199 echo "extract ${tagname}.WHATSNEW.txt"
200 tar xf ${tagname}.tar.gz --to-stdout ${tagname}/WHATSNEW.txt > ${tagname}.WHATSNEW.txt
201 CLEANUP_FILES="${CLEANUP_FILES} ${tagname}.WHATSNEW.txt"
203 return 0
206 check_nopatch() {
207 check_args "${FUNCNAME}" "$#" "0" || return 1
208 require_tagname "${FUNCNAME}"
210 git tag -v "${tagname}" || {
211 echo "failed to verify tag[${tagname}]"
212 return 1
215 test -f "${tagname}.tar.gz" || {
216 echo "${tagname}.tar.gz does not exist"
217 return 1
220 test -f "${tagname}.tar.asc" || {
221 echo "${tagname}.tar.asc does not exist"
222 return 1
225 ls -la ${tagname}.*
227 return 0
230 check_release() {
231 check_args "${FUNCNAME}" "$#" "0" || return 1
233 test -n "${check_fn}" || {
234 echo "check_fn variable empty"
235 return 1
238 echo "Running ${check_fn}"
239 ${check_fn}
242 push_release() {
243 check_args "${FUNCNAME}" "$#" "0" || return 1
244 require_tagname "${FUNCNAME}"
246 echo "Push git tag ${tagname} to '${repo_url}'"
247 git push "${repo_url}" "refs/tags/${tagname}:refs/tags/${tagname}" || {
248 return 1
251 return 0
254 upload_release() {
255 check_args "${FUNCNAME}" "$#" "0" || return 1
256 require_tagname "${FUNCNAME}"
258 echo "Upload ${tagname}.* to '${upload_url}'"
259 rsync -Pav ${tagname}.* "${upload_url}/" || {
260 return 1
262 rsync ${upload_url}/${tagname}.*
264 return 0
267 announce_samba_rc() {
268 check_args "${FUNCNAME}" "$#" "0" || return 1
269 require_tagname "${FUNCNAME}"
271 test -f "${tagname}.WHATSNEW.txt" || {
272 echo "${tagname}.WHATSNEW.txt does not exist"
273 return 1
276 local t=""
277 local utcdate=$(date --utc +"%d %B %Y")
278 local utctime=$(date --utc +"%Y%m%d-%H%M%S")
279 local version=$(echo "${tagname}" | sed -e 's!^samba-!!')
280 local href="#${version}"
281 local series=$(echo "${version}" | cut -d '.' -f1-2)
282 local rc=$(echo "${version}" | sed -e 's!.*rc\([0-9][0-9]*\)!\1!')
283 local rcname="${rc}th"
284 case "${rc}" in
286 rcname="first"
289 rcname="second"
292 rcname="third"
295 rcname="fourth"
298 rcname="fifth"
300 esac
303 echo "samba-announce@lists.samba.org, samba@lists.samba.org, samba-technical@lists.samba.org"
304 } > announce.${tagname}.to.txt
305 CLEANUP_FILES="${CLEANUP_FILES} announce.${tagname}.to.txt"
308 echo "[Announce] Samba ${version} Available for Download"
309 } > announce.${tagname}.subject.txt
310 CLEANUP_FILES="${CLEANUP_FILES} announce.${tagname}.subject.txt"
313 cat ${tagname}.WHATSNEW.txt
314 echo ""
315 echo "================"
316 echo "Download Details"
317 echo "================"
318 echo ""
319 echo "The uncompressed tarballs and patch files have been signed"
320 echo "using GnuPG (ID 6568B7EA). The source code can be downloaded"
321 echo "from:"
322 echo ""
323 echo " ${download_url}"
324 echo ""
325 echo "The release notes are available online at:"
326 echo ""
327 echo " ${download_url}${tagname}.WHATSNEW.txt"
328 echo ""
329 echo "Our Code, Our Bugs, Our Responsibility."
330 echo "(https://bugzilla.samba.org/)"
331 echo ""
332 echo " --Enjoy"
333 echo " The Samba Team"
334 } > announce.${tagname}.mail.txt
335 CLEANUP_FILES="${CLEANUP_FILES} announce.${tagname}.mail.txt"
338 echo -n "-i announce.${tagname}.mail.txt "
339 echo -n "-s \"$(cat announce.${tagname}.subject.txt | xargs)\" "
340 echo -n "$(cat announce.${tagname}.to.txt | xargs)"
341 } > announce.${tagname}.mutt-arguments.txt
342 CLEANUP_FILES="${CLEANUP_FILES} announce.${tagname}.mutt-arguments.txt"
344 local headlinefile="${utctime}.${version}.headline.html"
346 echo "<!-- BEGIN: posted_news/${headlinefile} -->"
347 echo "<li> ${utcdate} <a href=\"${href}\">Samba ${version} Available for Download</a></li>"
348 echo "<!-- END: posted_news/${headlinefile} -->"
349 } > ${headlinefile}
350 CLEANUP_FILES="${CLEANUP_FILES} ${headlinefile}"
352 local bodyfile="${utctime}.${version}.body.html"
354 echo "<!-- BEGIN: posted_news/${bodyfile} -->"
355 echo "<h5><a name=\"${version}\">${utcdate}</a></h5>"
356 echo "<p class="headline">Samba ${version} Available for Download</p>"
357 echo "<p>"
358 echo "This is the ${rcname} release candidate of the upcoming Samba ${series} release series."
359 echo "</p>"
360 echo "<p>"
361 echo "The uncompressed tarball has been signed using GnuPG (ID ${GPG_KEYID})."
362 echo "The source code can be <a href=\"${download_url}${tagname}.tar.gz\">downloaded now</a>."
363 echo "See <a href=\"${download_url}${tagname}.WHATSNEW.txt\">the release notes for more info</a>."
364 echo "</p>"
365 echo "<!-- END: posted_news/${bodyfile} -->"
366 echo ""
367 } > ${bodyfile}
368 CLEANUP_FILES="${CLEANUP_FILES} ${bodyfile}"
371 ls -lart announce.${tagname}.* ${headlinefile} ${bodyfile}
372 echo ""
373 echo "NOTICE:"
374 echo "You need to do the following manual steps in order"
375 echo "to finish the announcement of ${tagname}!"
376 echo ""
377 echo "Copy the following files into the posted_news/"
378 echo "subdirectory of the samba-web.git repository and commit them:"
379 echo " ${headlinefile}"
380 echo " ${bodyfile}"
381 echo ""
382 echo " cp -a ${utctime}.${version}.*.html /path/to/samba-web/posted_news/"
383 echo " pushd /path/to/samba-web"
384 echo " git add posted_news/${utctime}.${version}.*.html"
385 echo " git commit --signoff --message \"NEWS[${version}]: Samba ${version} Available for Download\""
386 echo " git show -p --stat HEAD"
387 echo " git push ..."
388 echo " popd"
389 echo ""
390 echo "Once the resulting commit is pushed a cron job will update "
391 echo "the content exported by the webserver every 5mins."
392 echo ""
393 echo "If the web content is updated, you need to send the announce mail (gpg signed)."
394 echo "- announce.${tagname}.to.txt contains the mail's recipients for the To: header."
395 echo "- announce.${tagname}.subject.txt contains the mail's subject line."
396 echo "- announce.${tagname}.mail.txt contains the content of the mail body."
397 echo "In case your're using mutt, you can use the following shortcut:"
398 echo " eval mutt \$(cat announce.${tagname}.mutt-arguments.txt)"
399 echo ""
400 echo "NOTICE: you're not done yet! Read the above instructions carefully!"
401 echo "See: announce.${tagname}.todo.txt"
402 echo ""
403 } > announce.${tagname}.todo.txt
404 CLEANUP_FILES="${CLEANUP_FILES} announce.${tagname}.todo.txt"
406 cat announce.${tagname}.todo.txt
408 return 0
411 announce_release() {
412 check_args "${FUNCNAME}" "$#" "0" || return 1
414 test -n "${announce_fn}" || {
415 echo "announce_fn variable empty"
416 return 1
419 echo "Running ${announce_fn}"
420 ${announce_fn}
423 case "${product}" in
424 talloc | tdb | tevent | ldb)
425 test -z "${GPG_USER-}" && {
426 GPG_USER='Samba Library Distribution Key <samba-bugs@samba.org>'
429 test -z "${GPG_KEYID-}" && {
430 GPG_KEYID='13084025'
433 productbase="${product}"
434 srcdir="lib/${product}"
435 repo_url="${CONF_REPO_URL}"
436 upload_url="${CONF_UPLOAD_URL}/${product}/"
437 download_url="${CONF_DOWNLOAD_URL}/${product}/"
439 check_fn="check_nopatch"
440 fullcmds="create check push upload"
442 samba-rc)
443 test -z "${GPG_USER-}" && {
444 GPG_USER='Samba Distribution Verification Key <samba-bugs@samba.org>'
447 test -z "${GPG_KEYID-}" && {
448 GPG_KEYID='6568B7EA'
451 productbase="samba"
452 srcdir="."
453 repo_url="${CONF_REPO_URL}"
454 upload_url="${CONF_UPLOAD_URL}/samba/rc/"
455 download_url="${CONF_DOWNLOAD_URL}/samba/rc/"
457 verify_fn="verify_samba_rc"
458 check_fn="check_nopatch"
459 announce_fn="announce_samba_rc"
460 fullcmds="verify create check whatsnew push upload announce"
462 TODO-samba-stable)
463 test -z "${GPG_USER-}" && {
464 GPG_USER='Samba Distribution Verification Key <samba-bugs@samba.org>'
467 test -z "${GPG_KEYID-}" && {
468 GPG_KEYID='6568B7EA'
471 productbase="samba"
472 srcdir="."
473 repo_url="${CONF_REPO_URL}"
474 upload_url="${CONF_UPLOAD_URL}/samba/stable/"
475 download_url="${CONF_DOWNLOAD_URL}/samba/stable/"
477 verify_fn="verify_samba_stable"
478 check_fn="check_withpatch"
479 announce_fn="announce_samba_stable"
480 fullcmds="verify create patch check push upload announce"
482 TODO-samba-security)
483 test -z "${GPG_USER-}" && {
484 GPG_USER='Samba Distribution Verification Key <samba-bugs@samba.org>'
487 test -z "${GPG_KEYID-}" && {
488 GPG_KEYID='6568B7EA'
491 productbase="samba"
492 srcdir="."
493 repo_url="${CONF_REPO_URL}"
494 upload_url="${CONF_UPLOAD_URL}/samba/stable/"
495 download_url="${CONF_DOWNLOAD_URL}/samba/stable/"
497 verify_fn="verify_samba_stable"
498 check_fn="check_samba_stable"
499 announce_fn="announce_samba_security"
500 fullcmds="verify create patch check"
501 next_cmd="push"
504 usage
505 echo "Unknown product ${product}"
506 exit 1
507 esac
509 pushd ${srcdir} || {
510 echo "srcdir[${srcdir}] does not exist"
511 exit 1
514 trap_handler() {
515 echo ""
516 echo "ERROR: cleaning up"
517 echo ""
519 for t in ${CLEANUP_TAGS}; do
520 echo "Removing tag[${t}]"
521 git tag -v "${t}" && {
522 git tag -d "${t}" || {
523 echo "failed to remove tag ${t}"
526 done
528 for f in ${CLEANUP_FILES}; do
529 echo "Removing file[${f}]"
530 test -f "${f}" && {
531 rm "${f}" || {
532 echo "failed to remove ${f}"
535 done
538 CLEANUP_TAGS=""
539 CLEANUP_FILES=""
540 trap trap_handler INT QUIT TERM EXIT
542 cmd_allowed "${globalcmd}" fullrelease ${fullcmds} || {
543 usage
544 echo "command[${globalcmd}] not supported for product[${product}]"
545 exit 1
548 case "${globalcmd}" in
549 fullrelease)
550 check_args "${globalcmd}" "$#" "0" || exit 1
551 cmds="${fullcmds}"
553 create)
554 check_args "${globalcmd}" "$#" "0" || exit 1
555 check_args "create" "$#" "0" || exit 1
557 cmds=""
558 cmd_allowed "verify" ${fullcmds} && {
559 cmds="${cmds} verify"
561 cmds="${cmds} create"
562 cmd_allowed "whatsnew" ${fullcmds} && {
563 cmds="${cmds} whatsnew"
565 cmd_allowed "patch" ${fullcmds} && {
566 cmds="${cmds} patch"
568 cmds="${cmds} check"
569 next_cmd="push"
571 push)
572 check_args "${globalcmd}" "$#" "1" || exit 1
573 tagname="$1"
574 cmds="check push"
575 next_cmd="upload"
577 upload)
578 check_args "${globalcmd}" "$#" "1" || exit 1
579 tagname="$1"
580 cmds="check upload"
581 cmd_allowed "symlinks" ${fullcmds} && {
582 cmds="${cmds} symlinks"
584 cmd_allowed "announce" ${fullcmds} && {
585 next_cmd="announce"
588 announce)
589 check_args "${globalcmd}" "$#" "1" || exit 1
590 tagname="$1"
591 cmds="check announce"
594 usage
595 echo "Unknown command ${globalcmd}"
596 exit 1
598 esac
600 for cmd in ${cmds}; do
601 echo "Starting subcommand[${cmd}]"
602 ${cmd}_release || {
603 echo "Failed subcommand[${cmd}]"
604 exit 1
606 echo "Finished subcommand[${cmd}]"
607 done
609 test -n "${next_cmd}" && {
610 echo "Continue with '$0 ${product} ${next_cmd} ${tagname}'."
613 trap - INT QUIT TERM EXIT
615 exit 0