collect(): ~[FfMmUu]: should default to the "dot" (Andrew Gee)
[s-mailx.git] / make-release.inc
blob1499bd33e168665c7d1899ea2d1f6aae798a0456
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.
5 : ${PROGRAM:?"Need \$PROGRAM"}
6 : ${UPROGRAM:?"Need \$UPROGRAM"}
7 : ${MANUAL:?"May need \$MANUAL for announcement references"}
9 : ${UPLOAD:?"Need \$UPLOAD URL for scp(1)"}
11 : ${MAILX:=mailx}
12 : ${ACCOUNT:?"May need mailx(1) -A \$ACCOUNT"}
13 : ${MAILTO:?"May need \$MAILTO for announcement"}
14 : ${MAILBCC:?"May need \$MAILBCC for announcement"}
16 : ${awk:=awk}
18 ##  --  >8  --  8<  --  ##
20 ORIG_LC_ALL=${LC_ALL} LC_ALL=C
21 export LC_ALL
23 DATE_MAN=`date -u +'%B %d, %Y'`
24 DATE_ISO=`date -u +%Y-%m-%d`
26 yesno() {
27    while [ 1 ]; do
28       [ ${#} -gt 0 ] && printf '%s ' "${@}"
29       printf '[y/n] '
30       read i
31       case ${i} in
32       [Yy]*) return 0;;
33       [Nn]*) return 1;;
34       *) ;;
35       esac
36    done
39 headref="`git rev-parse --verify HEAD`"
40 brref=
41 for i in `git rev-parse --branches=stable master^{commit}`; do
42    if [ ${headref} = ${i} ]; then
43       brref=${headref}
44       break
45    fi
46 done
47 if [ -z "${brref}" ]; then
48    echo >&2 'Not on the [master] or a [stable/*] branch'
49    exit 1
51 if [ "`git status --porcelain --ignored |
52       ${awk} 'BEGIN{no=0}{++no}END{print no}'`" -ne 0 ]; then
53    echo >&2 'Directory not clean, see git status --ignored'
54    exit 2
57 echo 'Preparing a release on commit '"${headref}"
58 #brname="`git branch | sed -e '/^* /b X' -e d -e :X -e 's/^* //'`"
59 brname=`git symbolic-ref --short HEAD`
60 echo '  That is '"${brname}"
61 printf '  Name of release tag: '
62 read REL
63 VERSION=${REL}
64 vmaj=`{ echo ${VERSION}; } | sed -e 's/^\([^.]\{1,\}\).*/\1/'`
65 vmin=`{ echo ${VERSION}; } | sed -e 's/^[^.]\{1,\}\.\([^.]\{1,\}\).*/\1/'`
66 [ ${vmin} = ${VERSION} ] && VERSION=${VERSION}.0 vmin=0
67 vupd=`{ echo ${VERSION}; } |
68       sed -e 's/^[^.]\{1,\}\.[^.]\{1,\}\.\([^.-]\{1,\}\).*/\1/'`
69 [ ${vupd} = ${VERSION} ] && VERSION=${VERSION}.0 vupd=0
70 REL=${VERSION}
71 export VERSION
72 if yesno 'Is '${PROGRAM}' <v'${REL}'> correct?'; then :; else
73    echo >&2 'Bailing out'
74    exit 3
77 stblbrname=stable/v${vmaj}.${vmin} need_stblbrname=
78 brref=`git rev-parse --verify ${stblbrname} 2>/dev/null`
79 if [ -z "${brref}" ]; then
80    if yesno 'Create new branch '"${stblbrname}"' after release tag'; then
81       need_stblbrname=1
82    fi
83 elif [ ${brref} != ${headref} ] || [ ${brname} != ${stblbrname} ]; then
84    echo >&2 "For ${REL} we should be on ${stblbrname}, not ${brname}"
85    echo >&2 'Bailing out'
86    exit 4
89 relbrname=release/v${VERSION}
90 brref=`git rev-parse --verify ${relbrname} 2>/dev/null`
91 if [ -z "${brref}" ]; then :; else
92    echo >&2 "The ${relbrname} already exists"
93    echo >&2 'Bailing out'
94    exit 5
97 RELSYM=
98 stblmsg= relmsg=
99 if yesno 'Shall '${PROGRAM}' v'${REL}' have a symbolic name?'; then
100    printf '  ..and it shall be known as: '
101    read RELSYM
102    if yesno 'Is '"${RELSYM}"' correct?'; then :; else
103       echo >&2 'Bailing out'
104       exit 3
105    fi
106    stblmsg="Bump ${UPROGRAM} v${REL} (\"${RELSYM}\"), ${DATE_ISO}"
107    relmsg="Bump ${UPROGRAM} v${REL}.ar (\"${RELSYM}\"), ${DATE_ISO}"
108    RELSYM=" (\"${RELSYM}\")"
109 else
110    stblmsg="Bump ${UPROGRAM} v${REL}, ${DATE_ISO}"
111    relmsg="Bump ${UPROGRAM} v${REL}.ar, ${DATE_ISO}"
115 echo 'Updating stable/ files to match the release'
117 if [ -f gen-version.h ] && [ -f make-config.in ]; then
118    grep=grep sed=sed cmp=cmp mv=mv make -f make-config.in _update-version
119    git add gen-version.h
121 update_stable_hook
123 LC_ALL=${ORIG_LC_ALL} git commit -S -n -m "${stblmsg}"
124 LC_ALL=${ORIG_LC_ALL} git tag -s -f -m "${stblmsg}" v${REL}
126 if [ -n "${need_stblbrname}" ]; then
127    git checkout -b ${stblbrname}
129 # Normally done in post-commit hook, but not once initially created
130 if yesno 'Shall i update stable/latest "symlink"?'; then
131    git update-ref refs/heads/stable/latest ${stblbrname}
133 if yesno 'Shall i update stable/stable "symlink"?'; then
134    git update-ref refs/heads/stable/stable ${stblbrname}
139 if yesno 'Create release/ branch?'; then
140    git checkout -b ${relbrname}
142    git rm -f .gitignore .mailmap TODO make-release.*
143    update_release_hook
145    LC_ALL=${ORIG_LC_ALL} git commit -S -n -m "${relmsg}"
146    LC_ALL=${ORIG_LC_ALL} git tag -s -f -m "${relmsg}" v${REL}.ar
148    if yesno 'Shall i update release/latest "symlink"?'; then
149       git update-ref refs/heads/release/latest ${relbrname}
150    fi
151    if yesno 'Shall i update release/stable "symlink"?'; then
152       git update-ref refs/heads/release/stable ${relbrname}
153    fi
154 else
155    relbrname=${stblbrname}
159 # [timeline]
161 if [ ${relbrname} != ${stblbrname} ] &&
162       `git rev-parse --verify timeline^{commit} >/dev/null 2>&1` &&
163       yesno 'Shall i update [timeline]?'; then
164    git checkout timeline
165    git rm -rf '*'
166    git archive --format=tar "v${REL}.ar" | tar -x -f -
167    git add .
168    LC_ALL=${ORIG_LC_ALL} git commit -S -n -m "${relmsg}"
172 # repo push
174 [ ${relbrname} != ${stblbrname} ] && git checkout ${stblbrname}
175 git log --no-walk --decorate --oneline --branches --remotes
176 yesno 'Push git(1) repo?' && git push
179 # Big balls
181 if [ ${relbrname} != ${stblbrname} ] && yesno 'Create tarballs?'; then
182    bigballs=y
183    (
184    umask 0022
186    # Repack with standard tar(1) to avoid new-style headers
187    git archive --format=tar --prefix="${PROGRAM}-${REL}/" v${REL}.ar |
188       ( cd "${TMPDIR}" && tar -x -f - )
189    cd "${TMPDIR}"
191    tar -c -f "${PROGRAM}-${REL}.tar" "${PROGRAM}-${REL}"
192    < "${PROGRAM}-${REL}.tar" xz -e -C sha256 > "${PROGRAM}-${REL}.tar.xz"
193    < "${PROGRAM}-${REL}.tar" gzip > "${PROGRAM}-${REL}.tar.gz"
194    rm "${PROGRAM}-${REL}.tar"
196    : > "${PROGRAM}-${REL}.cksum"
197    openssl sha1 "${PROGRAM}-${REL}.tar.xz" >> "${PROGRAM}-${REL}.cksum"
198    openssl sha256 "${PROGRAM}-${REL}.tar.xz" >> "${PROGRAM}-${REL}.cksum"
199    openssl sha512 "${PROGRAM}-${REL}.tar.xz" >> "${PROGRAM}-${REL}.cksum"
200    openssl sha1 "${PROGRAM}-${REL}.tar.gz" >> "${PROGRAM}-${REL}.cksum"
201    openssl sha256 "${PROGRAM}-${REL}.tar.gz" >> "${PROGRAM}-${REL}.cksum"
202    openssl sha512 "${PROGRAM}-${REL}.tar.gz" >> "${PROGRAM}-${REL}.cksum"
204    echo >> "${PROGRAM}-${REL}.cksum"
205    gpg --detach-sign --armor "${PROGRAM}-${REL}.tar.xz"
206    cat "${PROGRAM}-${REL}.tar.xz.asc" >> "${PROGRAM}-${REL}.cksum"
207    gpg --detach-sign --armor "${PROGRAM}-${REL}.tar.gz"
208    cat "${PROGRAM}-${REL}.tar.gz.asc" >> "${PROGRAM}-${REL}.cksum"
209    )
210 else
211    bigballs=
215 # Announcement .txt and .html
217 if yesno 'Prepare announcement?'; then
218    anntxt=y
220    if `git cat-file -e ${relbr}:NEWS 2>/dev/null`; then
221       git show ${relbr}:NEWS > "${TMPDIR}/.${PROGRAM}-${REL}.news"
222    else
223       : > "${TMPDIR}/.${PROGRAM}-${REL}.news"
224    fi
226    { echo "${relmsg}"; echo; } > "${TMPDIR}/${PROGRAM}-${REL}.txt"
227    if [ -f .git/make-release.txt ]; then
228       # For the checksums
229       if [ -n "${bigballs}" ] && [ -f "${TMPDIR}/${PROGRAM}-${REL}.cksum" ]
230       then
231          cks=`< "${TMPDIR}/${PROGRAM}-${REL}.cksum" sed -e 's/ //' -e '/^$/,$d'`
232          < "${TMPDIR}/${PROGRAM}-${REL}.cksum" sed '1,/^$/d' \
233             > "${TMPDIR}/.${PROGRAM}-${REL}.sigs"
234          < .git/make-release.txt ${awk} \
235                -v INS="${cks}" -v SIGS="${TMPDIR}/.${PROGRAM}-${REL}.sigs" \
236                -v NEWS="${TMPDIR}/.${PROGRAM}-${REL}.news" '
237             /-----CHECKSUMS-----/{
238                atop = split(INS, a)
239                fn = ""
240                for(i = 1; i <= atop; ++i){
241                   match(a[i], /(\(.+\))/)
242                   tfn = substr(a[i], RSTART + 1, RLENGTH - 2)
243                   tpre = substr(a[i], 1, RSTART - 1)
244                   tsuf = substr(a[i], RSTART + RLENGTH + 1)
245                   if(fn == "" || fn != tfn)
246                      printf "%s:\n", (fn = tfn)
247                   printf "  %6s %s\n", tpre, tsuf
248                }
249                next
250             }
251             /-----SIGNATURES-----/{
252                while(getline sl < SIGS)
253                   print sl
254                next
255             }
256             /-----NEWS-----/{
257                while(getline sl < NEWS)
258                   print sl
259                next
260             }
261             {print}
262          ' >> "${TMPDIR}/${PROGRAM}-${REL}.txt"
263          rm -f "${TMPDIR}/.${PROGRAM}-${REL}.sigs"
264       else
265          < .git/make-release.txt ${awk} \
266                -v NEWS="${TMPDIR}/.${PROGRAM}-${REL}.news" '
267             /-----NEWS-----/{
268                while(getline sl < NEWS)
269                   print sl
270                next
271             }
272             {print}
273          ' >> "${TMPDIR}/${PROGRAM}-${REL}.txt"
274       fi
275    elif [ -f "${TMPDIR}/.${PROGRAM}-${REL}.news" ]; then
276       cat "${TMPDIR}/.${PROGRAM}-${REL}.news" >> \
277          "${TMPDIR}/${PROGRAM}-${REL}.txt"
278    fi
280    rm -f "${TMPDIR}/.${PROGRAM}-${REL}.news"
282    LC_ALL=${ORIG_LC_ALL} ${EDITOR} "${TMPDIR}/${PROGRAM}-${REL}.txt"
284    # HTML convert ready for S-Web42
285    APO=\'
286    < "${TMPDIR}/${PROGRAM}-${REL}.txt" ${awk} -v manual="${MANUAL}" '
287    BEGIN{
288       hot = 0
289       print "<?begin?><?mode icewatsm?><pre>"
290    }
291    function strips(){
292       gsub("&", "\\&amp;")
293       gsub("<", "\\&lt;")
294       gsub(">", "\\&gt;")
295    }
296    function urls(){
297       any = 0
298       res = ""
299       s = $0
301       while(match(s, /(\\?https?\??:\/\/[^ ]*)/)){
302          pre = substr(s, 1, RSTART - 1)
303          mat = substr(s, RSTART, RLENGTH)
304          s = substr(s, RSTART + RLENGTH)
305          if("\\" == substr(mat, 1, 1))
306             mat = substr(mat, 2)
307          else{
308             xt = 0
309             if(match(mat, /^https\?/))
310                mat = "https" substr(xt = mat, RSTART + 6)
311             if(match(mat, /sdaoden\.eu/))
312                mat = "<?lref" (xt ? "t " : " ") mat (xt ? "<>" xt : "") "?>"
313             else
314                mat = "<?href" (xt ? "t " : " ") mat (xt ? "<>" xt : "") "?>"
315          }
316          res = res pre mat
317          any = 1
318       }
319       if(any && length(s))
320          res = res s
321       $0 = any ? res : s
322    }
323    /^[[:space:]]*s-.*-mode[[:space:]]*$/{
324       exit 0
325    }
326    /^(NOTES|ChangeLog)/{
327       hot = 1
328       strips()
329       print
330       next
331    }
332    /^(Appendix|git\(1\) shortlog)/{
333       hot = -1
334       strips()
335       print
336       next
337    }
338    {
339       strips()
340       urls()
341       if(hot <= 0){
342          print
343          next
344       }
345       any = 0
346       res = ""
347       s = $0
348       # Create S-Web42 local references for the possible anchors:
349       #     *XY*# / $XY# / -XY# / `XY${APO}# / `~XY${APO}# / "XY"#
350       # (where the mdocmx(7) anchor follows the number sign).
351       # Ideally the anchors have been automatically expanded by
352       # make-news-anchors.sh before.
353       while(match(s,
354             /(^|\(|[[:space:]]+)("[^"]+"|\*[^\*]+\*|`[^'${APO}']+'${APO}'|[-~][-#\/:_.[:alnum:]]+|\$[_[:alnum:]]+)#[0-9]+/))
355       {
356          pre = (RSTART > 1) ? substr(s, 1, RSTART - 1) : ""
357          mat = substr(s, RSTART, RLENGTH)
358          s = substr(s, RSTART + RLENGTH)
360          # Unfortunately groups are not supported
361          if(match(mat, /^(\(|[[:space:]]+)/) != 0 && RLENGTH > 0){
362             pre = pre substr(mat, 1, RLENGTH)
363             mat = substr(mat, RSTART + RLENGTH)
364          }
366          match(mat, /#[0-9]+/)
367          targ = substr(mat, RSTART + 1, RLENGTH)
368          mat = substr(mat, 1, RSTART - 1)
369          res = res pre "<?lreft " manual "#" targ "<>" mat "?>"
370          any = 1
371       }
372       if(any && length(s))
373          res = res s
374       print any ? res : s
375    }
376    END{
377       print "</pre><?end?>"
378    }
379    ' > "${TMPDIR}/.${PROGRAM}-ann.html"
380 else
381    anntxt=
385 # Upload
387 if [ -n "${bigballs}" ] && yesno 'Upload archives'; then
388    (
389    cd "${TMPDIR}"
391    {
392       echo "-put ${PROGRAM}-${REL}.tar.xz"
393       echo "-rm ${PROGRAM}-latest.tar.xz"
394       echo "-ln -s ${PROGRAM}-${REL}.tar.xz ${PROGRAM}-latest.tar.xz"
396       echo "-put ${PROGRAM}-${REL}.tar.xz.asc"
397       echo "-rm ${PROGRAM}-latest.tar.xz.asc"
398       echo "-ln -s ${PROGRAM}-${REL}.tar.xz.asc ${PROGRAM}-latest.tar.xz.asc"
400       echo "-put ${PROGRAM}-${REL}.tar.gz"
401       echo "-rm ${PROGRAM}-latest.tar.gz"
402       echo "-ln -s ${PROGRAM}-${REL}.tar.gz ${PROGRAM}-latest.tar.gz"
404       echo "-put ${PROGRAM}-${REL}.tar.gz.asc"
405       echo "-rm ${PROGRAM}-latest.tar.gz.asc"
406       echo "-ln -s ${PROGRAM}-${REL}.tar.gz.asc ${PROGRAM}-latest.tar.gz.asc"
408       if [ -n "${anntxt}" ]; then
409          echo "-put ${PROGRAM}-${REL}.txt"
410          echo "-rm ${PROGRAM}-latest.txt"
411          echo "-ln -s ${PROGRAM}-${REL}.txt ${PROGRAM}-latest.txt"
412       fi
414       echo "-chmod 0644 ${PROGRAM}-${REL}.*"
415    } |
416    sftp -b - ${UPLOAD}
417    )
421 # Announcement mail
423 if [ -n "${anntxt}" ] && yesno 'Send announcement mail?'; then
424    LC_ALL=${ORIG_LC_ALL} ${MAILX} -A ${ACCOUNT} \
425       -s "[ANN]ounce of ${UPROGRAM} v${REL}${RELSYM}" \
426       -q "${TMPDIR}/${PROGRAM}-${REL}.txt" \
427       -b ${MAILBCC} ${MAILTO}
430 # Finally remove the temporary instances than ran this
431 rm -f .git/make-release.*
432 echo 'Done'
433 exit
434 # s-sh-mode