Bump S-nail v14.9.16 ("Message of Winter, your hopes shall be crushed"), 2019-12-29
[s-mailx.git] / mk / make-release.inc
blobc66df0d2c1d84ddcd6c72f6e9a0581cc45efe432
1 #@ Include file for the make-release.sh generic release builder.
2 #@ It also needs two hooks: update_stable_hook(), update_release_hook(),
3 #@ which need to "git add" what they have modified.
4 #@ The "grappa" mode needs a current_version() hook, which has to set $VERSION
5 #@ to the current program version, expected as MAJOR.MINOR.UPDATE[-whatever]
7 : ${PROGRAM:?"Need \$PROGRAM"}
8 : ${UPROGRAM:?"Need \$UPROGRAM"}
9 # For announcement only.
10 : ${MANUAL:?"May need \$MANUAL for announcement references"}
12 # When we upload balls only.
13 : ${UPLOAD:?"Need \$UPLOAD URL for scp(1)"}
15 # For announcement mail only.
16 : ${MAILX:=mailx}
17 : ${ACCOUNT:?"May need mailx(1) -A \$ACCOUNT"}
18 : ${MAILTO:?"May need \$MAILTO for announcement"}
19 : ${MAILBCC:?"May need \$MAILBCC for announcement"}
21 # Program stuff
22 : ${awk:=awk}
23 : ${cat:=cat}
24 : ${cmp:=cmp}
25 : ${date:=date}
26 : ${git:=git}
27 : ${grep:=grep}
28 : ${make:=make}
29 : ${mv:=mv}
30 : ${SHELL:=sh}
31 : ${sed:=sed}
33 # non-grappa only
34 : ${gzip:=gzip}
35 : ${openssl:=openssl}
36 : ${gpg:=gpg}
37 : ${rm:=rm}
38 : ${roff:=s-roff} # optional (command(1) tested)
39 : ${sftp:=sftp}
40 : ${tar:=tar}
41 : ${xz:=xz}
43 ##  --  >8  --  8<  --  ##
45 ORIG_LC_ALL=${LC_ALL} LC_ALL=C
46 export LC_ALL
48 DATE_MAN=`${date} -u +'%B %d, %Y'`
49 DATE_ISO=`${date} -u +%Y-%m-%d`
51 yesno() {
52    while [ 1 ]; do
53       [ ${#} -gt 0 ] && printf '%s ' "${@}"
54       printf '[y/n] '
55       read i
56       case ${i} in
57       [Yy]*) return 0;;
58       [Nn]*) return 1;;
59       *) ;;
60       esac
61    done
64 ref_status() {
65    headref="`${git} rev-parse --verify HEAD`"
66    brref=
67    for i in `${git} rev-parse --branches=stable master^{commit} \
68          2>/dev/null`; do
69       if [ ${headref} = ${i} ]; then
70          brref=${headref}
71          break
72       fi
73    done
74    if [ -z "${brref}" ]; then
75       echo >&2 'Not on the [master] or a [stable/*] branch'
76       [ -z "${grappa}" ] && exit 1
77       if yesno 'Are you sure you want grappa from '${headref}'?'; then :; else
78          echo >&2 'Bailing out'
79          exit 3
80       fi
81    fi
82    if [ "`${git} status --porcelain --ignored |
83          ${awk} 'BEGIN{no=0}{++no}END{print no}'`" -ne 0 ]; then
84       echo >&2 'Directory not clean, see git status --ignored'
85       exit 2
86    fi
87    #brname="`git branch | sed -e '/^* /b X' -e d -e :X -e 's/^* //'`"
88    brname=`${git} symbolic-ref --short HEAD`
91 release_version() {
92    vmaj=`{ echo ${VERSION}; } | ${sed} -e 's/^\([^.]\{1,\}\).*/\1/'`
93    vmin=`{ echo ${VERSION}; } |
94       ${sed} -e 's/^[^.]\{1,\}\.\([^.]\{1,\}\).*/\1/'`
95    [ ${vmin} = ${VERSION} ] && VERSION=${VERSION}.0 vmin=0
96    vupd=`{ echo ${VERSION}; } |
97          ${sed} -e 's/^[^.]\{1,\}\.[^.]\{1,\}\.\([^.-]\{1,\}\).*/\1/'`
98    [ ${vupd} = ${VERSION} ] && VERSION=${VERSION}.0 vupd=0
99    REL=${VERSION}
100    export VERSION
101    if yesno 'Is '${PROGRAM}' <v'${REL}'> correct?'; then :; else
102       echo >&2 'Bailing out'
103       exit 3
104    fi
107 release_brcheck() {
108    stblbrname=stable/v${vmaj}.${vmin} need_stblbrname=
109    brref=`${git} rev-parse --verify ${stblbrname} 2>/dev/null`
110    if [ -z "${brref}" ]; then
111       if yesno 'Create new branch '"${stblbrname}"' after release tag'; then
112          need_stblbrname=1
113       fi
114    elif [ ${brref} != ${headref} ] || [ ${brname} != ${stblbrname} ]; then
115       echo >&2 "For ${REL} we should be on ${stblbrname}, not ${brname}"
116       echo >&2 'Bailing out'
117       exit 4
118    fi
120    relbrname=release/v${VERSION}
121    brref=`${git} rev-parse --verify ${relbrname} 2>/dev/null`
122    if [ -z "${brref}" ]; then :; else
123       echo >&2 "The ${relbrname} already exists"
124       echo >&2 'Bailing out'
125       exit 5
126    fi
129 release_symname() {
130    RELSYM=
131    stblmsg= relmsg=
132    if yesno 'Shall '${PROGRAM}' v'${REL}' have a symbolic name?'; then
133       printf '  ..and it shall be known as: '
134       read RELSYM
135       if yesno 'Is '"${RELSYM}"' correct?'; then :; else
136          echo >&2 'Bailing out'
137          exit 3
138       fi
139       stblmsg="Bump ${UPROGRAM} v${REL} (\"${RELSYM}\"), ${DATE_ISO}"
140       relmsg="Bump ${UPROGRAM} v${REL}.ar (\"${RELSYM}\"), ${DATE_ISO}"
141       RELSYM=" (\"${RELSYM}\")"
142    else
143       stblmsg="Bump ${UPROGRAM} v${REL}, ${DATE_ISO}"
144       relmsg="Bump ${UPROGRAM} v${REL}.ar, ${DATE_ISO}"
145    fi
148 update_stable_branch() {
149    LC_ALL=${ORIG_LC_ALL} ${git} commit -S -n -m "${stblmsg}"
150    LC_ALL=${ORIG_LC_ALL} ${git} tag -s -f -m "${stblmsg}" v${REL}
152    if [ -n "${need_stblbrname}" ]; then
153       ${git} checkout -b ${stblbrname}
154    fi
155    # Normally done in post-commit hook, but not once initially created
156    if yesno 'Shall i update stable/latest "symlink"?'; then
157       ${git} update-ref refs/heads/stable/latest ${stblbrname}
158    fi
159    if yesno 'Shall i update stable/stable "symlink"?'; then
160       ${git} update-ref refs/heads/stable/stable ${stblbrname}
161    fi
164 create_release_branch() {
165    if yesno 'Create release/ branch?'; then
166       ${git} checkout -b ${relbrname}
168       echo 'Updating files: calling update_release_hook'
169       update_release_hook
171       LC_ALL=${ORIG_LC_ALL} ${git} commit -S -n -m "${relmsg}"
172       LC_ALL=${ORIG_LC_ALL} ${git} tag -s -f -m "${relmsg}" v${REL}.ar
174       if yesno 'Shall i update release/latest "symlink"?'; then
175          ${git} update-ref refs/heads/release/latest ${relbrname}
176       fi
177       if yesno 'Shall i update release/stable "symlink"?'; then
178          ${git} update-ref refs/heads/release/stable ${relbrname}
179       fi
180    else
181       relbrname=${stblbrname}
182    fi
185 check_timeline_branch() {
186    if [ ${relbrname} != ${stblbrname} ] &&
187          `${git} rev-parse --verify timeline^{commit} >/dev/null 2>&1` &&
188          yesno 'Shall i update [timeline]?'; then
189       ${git} checkout timeline
190       ${git} rm -rf '*'
191       ${git} archive --format=tar "v${REL}.ar" | ${tar} -x -f -
192       ${git} add .
193       LC_ALL=${ORIG_LC_ALL} ${git} commit -S -n -m "${relmsg}"
194    fi
197 repo_push() {
198    [ ${relbrname} != ${stblbrname} ] && ${git} checkout ${stblbrname}
199    ${git} log --no-walk --decorate --oneline --branches --remotes
200    yesno 'Push git(1) repo?' && ${git} push
203 big_balls() {
204    if [ ${relbrname} != ${stblbrname} ] && yesno 'Create tarballs?'; then
205       bigballs=y
206       (
207       umask 0022
209       # Repack with standard tar(1) to avoid new-style headers
210       ${git} archive --format=tar --prefix="${PROGRAM}-${REL}/" v${REL}.ar |
211          ( cd "${TMPDIR}" && ${tar} -x -f - )
212       cd "${TMPDIR}"
214       ${tar} -c -f "${PROGRAM}-${REL}.tar" "${PROGRAM}-${REL}"
215       < "${PROGRAM}-${REL}.tar" ${xz} -e -C sha256 > "${PROGRAM}-${REL}.tar.xz"
216       < "${PROGRAM}-${REL}.tar" ${gzip} -9 > "${PROGRAM}-${REL}.tar.gz"
217       ${rm} "${PROGRAM}-${REL}.tar"
219       printf '' > "${PROGRAM}-${REL}.cksum"
220       ${openssl} sha1 "${PROGRAM}-${REL}.tar.xz" >> "${PROGRAM}-${REL}.cksum"
221       ${openssl} sha256 "${PROGRAM}-${REL}.tar.xz" >> "${PROGRAM}-${REL}.cksum"
222       ${openssl} sha512 "${PROGRAM}-${REL}.tar.xz" >> "${PROGRAM}-${REL}.cksum"
223       ${openssl} sha1 "${PROGRAM}-${REL}.tar.gz" >> "${PROGRAM}-${REL}.cksum"
224       ${openssl} sha256 "${PROGRAM}-${REL}.tar.gz" >> "${PROGRAM}-${REL}.cksum"
225       ${openssl} sha512 "${PROGRAM}-${REL}.tar.gz" >> "${PROGRAM}-${REL}.cksum"
227       echo >> "${PROGRAM}-${REL}.cksum"
228       ${gpg} --detach-sign --armor "${PROGRAM}-${REL}.tar.xz"
229       ${cat} "${PROGRAM}-${REL}.tar.xz.asc" >> "${PROGRAM}-${REL}.cksum"
230       ${gpg} --detach-sign --armor "${PROGRAM}-${REL}.tar.gz"
231       ${cat} "${PROGRAM}-${REL}.tar.gz.asc" >> "${PROGRAM}-${REL}.cksum"
232       )
233    else
234       bigballs=
235    fi
238 announcement_prepare() {
239    anntxt=
240    if yesno 'Prepare announcement?'; then :; else
241       return
242    fi
243    anntxt=y
245    if `${git} cat-file -e ${relbr}:NEWS 2>/dev/null`; then
246       ${git} show ${relbr}:NEWS > "${TMPDIR}/.${PROGRAM}-${REL}.news"
247    else
248       printf '' > "${TMPDIR}/.${PROGRAM}-${REL}.news"
249    fi
251    { echo "${relmsg}"; echo; } > "${TMPDIR}/${PROGRAM}-${REL}.txt"
252    if [ -f .git/make-release.txt ]; then
253       # For the checksums
254       if [ -n "${bigballs}" ] && [ -f "${TMPDIR}/${PROGRAM}-${REL}.cksum" ]
255       then
256          cks=`< "${TMPDIR}/${PROGRAM}-${REL}.cksum" \
257                ${sed} -e 's/ //' -e '/^$/,$d'`
258          < "${TMPDIR}/${PROGRAM}-${REL}.cksum" ${sed} '1,/^$/d' \
259             > "${TMPDIR}/.${PROGRAM}-${REL}.sigs"
260          < .git/make-release.txt ${awk} \
261                -v INS="${cks}" -v SIGS="${TMPDIR}/.${PROGRAM}-${REL}.sigs" \
262                -v NEWS="${TMPDIR}/.${PROGRAM}-${REL}.news" '
263             /-----CHECKSUMS-----/{
264                atop = split(INS, a)
265                fn = ""
266                for(i = 1; i <= atop; ++i){
267                   match(a[i], /(\(.+\))/)
268                   tfn = substr(a[i], RSTART + 1, RLENGTH - 2)
269                   tpre = substr(a[i], 1, RSTART - 1)
270                   tsuf = substr(a[i], RSTART + RLENGTH + 1)
271                   if(fn == "" || fn != tfn)
272                      printf "%s:\n", (fn = tfn)
273                   printf "  %6s %s\n", tpre, tsuf
274                }
275                next
276             }
277             /-----SIGNATURES-----/{
278                while(getline sl < SIGS)
279                   print sl
280                next
281             }
282             /-----NEWS-----/{
283                while(getline sl < NEWS)
284                   print sl
285                next
286             }
287             {print}
288          ' >> "${TMPDIR}/${PROGRAM}-${REL}.txt"
289          ${rm} -f "${TMPDIR}/.${PROGRAM}-${REL}.sigs"
290       else
291          < .git/make-release.txt ${awk} \
292                -v NEWS="${TMPDIR}/.${PROGRAM}-${REL}.news" '
293             /-----NEWS-----/{
294                while(getline sl < NEWS)
295                   print sl
296                next
297             }
298             {print}
299          ' >> "${TMPDIR}/${PROGRAM}-${REL}.txt"
300       fi
301    elif [ -f "${TMPDIR}/.${PROGRAM}-${REL}.news" ]; then
302       ${cat} "${TMPDIR}/.${PROGRAM}-${REL}.news" >> \
303          "${TMPDIR}/${PROGRAM}-${REL}.txt"
304    fi
306    ${rm} -f "${TMPDIR}/.${PROGRAM}-${REL}.news"
308    LC_ALL=${ORIG_LC_ALL} ${EDITOR} "${TMPDIR}/${PROGRAM}-${REL}.txt"
310    # HTML convert ready for S-Web42
311    APO=\'
312    < "${TMPDIR}/${PROGRAM}-${REL}.txt" ${awk} -v manual="${MANUAL}" '
313    BEGIN{
314       hot = 0
315       print "<?begin?><?mode icewatsm?><pre>"
316    }
317    function strips(){
318       gsub("&", "\\&amp;")
319       gsub("<", "\\&lt;")
320       gsub(">", "\\&gt;")
321    }
322    function urls(){
323       any = 0
324       res = ""
325       s = $0
327       while(match(s, /(\\?https?\??:\/\/[^ ]*)/)){
328          pre = substr(s, 1, RSTART - 1)
329          mat = substr(s, RSTART, RLENGTH)
330          s = substr(s, RSTART + RLENGTH)
331          if("\\" == substr(mat, 1, 1))
332             mat = substr(mat, 2)
333          else{
334             xt = 0
335             if(match(mat, /^https\?/))
336                mat = "https" substr(xt = mat, RSTART + 6)
337             if(match(mat, /sdaoden\.eu/))
338                mat = "<?lref" (xt ? "t " : " ") mat (xt ? "<>" xt : "") "?>"
339             else
340                mat = "<?href" (xt ? "t " : " ") mat (xt ? "<>" xt : "") "?>"
341          }
342          res = res pre mat
343          any = 1
344       }
345       if(any && length(s))
346          res = res s
347       $0 = any ? res : s
348    }
349    /^[[:space:]]*s-.*-mode[[:space:]]*$/{
350       exit 0
351    }
352    /^(NOTES|ChangeLog)/{
353       hot = 1
354       strips()
355       print
356       next
357    }
358    /^(Appendix|git\(1\) shortlog)/{
359       hot = -1
360       strips()
361       print
362       next
363    }
364    {
365       strips()
366       urls()
367       if(hot <= 0){
368          print
369          next
370       }
371       any = 0
372       res = ""
373       s = $0
374       # Create S-Web42 local references for the possible anchors:
375       #     *XY*# / $XY# / -XY# / `XY${APO}# / `~XY${APO}# / "XY"#
376       # (where the mdocmx(7) anchor follows the number sign).
377       # Ideally the anchors have been automatically expanded by
378       # make-news-anchors.sh before.
379       while(match(s,
380             /(^|\(|[[:space:]]+)("[^"]+"|\*[^\*]+\*|`[^'${APO}']+'${APO}'|[-~][-#\/:_.[:alnum:]]+|\$[_[:alnum:]]+)#[0-9]+/))
381       {
382          pre = (RSTART > 1) ? substr(s, 1, RSTART - 1) : ""
383          mat = substr(s, RSTART, RLENGTH)
384          s = substr(s, RSTART + RLENGTH)
386          # Unfortunately groups are not supported
387          if(match(mat, /^(\(|[[:space:]]+)/) != 0 && RLENGTH > 0){
388             pre = pre substr(mat, 1, RLENGTH)
389             mat = substr(mat, RSTART + RLENGTH)
390          }
392          match(mat, /#[0-9]+/)
393          targ = substr(mat, RSTART + 1, RLENGTH)
394          mat = substr(mat, 1, RSTART - 1)
395          res = res pre "<?lreft " manual "#" targ "<>" mat "?>"
396          any = 1
397       }
398       if(any && length(s))
399          res = res s
400       print any ? res : s
401    }
402    END{
403       print "</pre><?end?>"
404    }
405    ' > "${TMPDIR}/.${PROGRAM}-ann.html"
408 upload() (
409    if [ -n "${bigballs}" ] && yesno 'Upload archives'; then :; else
410       return
411    fi
412    cd "${TMPDIR}"
414    {
415       echo "-put ${PROGRAM}-${REL}.tar.xz"
416       echo "-rm ${PROGRAM}-latest.tar.xz"
417       echo "-ln -s ${PROGRAM}-${REL}.tar.xz ${PROGRAM}-latest.tar.xz"
419       echo "-put ${PROGRAM}-${REL}.tar.xz.asc"
420       echo "-rm ${PROGRAM}-latest.tar.xz.asc"
421       echo "-ln -s ${PROGRAM}-${REL}.tar.xz.asc ${PROGRAM}-latest.tar.xz.asc"
423       echo "-put ${PROGRAM}-${REL}.tar.gz"
424       echo "-rm ${PROGRAM}-latest.tar.gz"
425       echo "-ln -s ${PROGRAM}-${REL}.tar.gz ${PROGRAM}-latest.tar.gz"
427       echo "-put ${PROGRAM}-${REL}.tar.gz.asc"
428       echo "-rm ${PROGRAM}-latest.tar.gz.asc"
429       echo "-ln -s ${PROGRAM}-${REL}.tar.gz.asc ${PROGRAM}-latest.tar.gz.asc"
431       if [ -n "${anntxt}" ]; then
432          echo "-put ${PROGRAM}-${REL}.txt"
433          echo "-rm ${PROGRAM}-latest.txt"
434          echo "-ln -s ${PROGRAM}-${REL}.txt ${PROGRAM}-latest.txt"
435       fi
437       echo "-chmod 0644 ${PROGRAM}-${REL}.*"
438    } |
439    ${sftp} -b - ${UPLOAD}
442 announcement_send() {
443    if [ -n "${anntxt}" ] && yesno 'Send announcement mail?'; then
444       LC_ALL=${ORIG_LC_ALL} ${MAILX} -A ${ACCOUNT} \
445          -s "[ANN]ounce of ${UPROGRAM} v${REL}${RELSYM}" \
446          -q "${TMPDIR}/${PROGRAM}-${REL}.txt" \
447          -b ${MAILBCC} ${MAILTO}
448    fi
451 create_grappa_env() {
452    echo 'Updating files: calling update_release_hook'
453    update_release_hook
454    echo 'E allora io quasi quasi prendo il treno'
457 grappa=
458 if [ ${#} -ne 0 ]; then
459    if [ ${#} != 2 ] || [ "${1}" != grappa ] || [ -z "${2}" ]; then
460       echo >&2 'You have a hell of a lot to learn about Rock'"'"'n Roll'
461       exit 55
462    fi
463    grappa=${2}
466 ref_status
467 echo 'Preparing a release on commit '"${headref}"
468 if [ -z "${grappa}" ]; then
469    printf '  The HEAD is %s\nName of release tag: ' "${brname}"
470    read REL
471    VERSION=${REL}
472    release_version
473    release_brcheck
474    release_symname
475 else
476    echo 'Grappa to be brought from '"${brname}"' to '"${grappa}"
477    current_version
478    printf 'Program version is %s, packager release addition shall be: ' \
479       "${VERSION}"
480    read REL
481    VERSION="${VERSION}-${REL}"
482    release_version
484    i=
485    if ${git} rev-parse --verify ${grappa} >/dev/null 2>/dev/null; then :; else
486       i=-B
487    fi
488    ${git} checkout ${i} ${grappa}
489    ${git} rm -f '*'
490    ${git} archive --format=tar ${headref} | ${tar} -x -f -
491    ${git} add .
494 echo 'Updating files: calling update_stable_hook'
495 update_stable_hook
497 if [ -z "${grappa}" ]; then
498    update_stable_branch
499    create_release_branch
500    check_timeline_branch
501    repo_push
502    big_balls
503    announcement_prepare
504    upload
505    announcement_send
506 else
507    create_grappa_env
511 # Finally remove the temporary instances than ran this
512 ${rm} -f .git/make-release.*
513 echo 'Done'
514 exit
515 # s-sh-mode