swdb: Rename some tags.
[gnupg-doc.git] / tools / build-website.sh
1 #!/bin/sh
2 # Build the gnupg.org website from a git working directory.
3 #
4 # This script requires two users
5 #
6 #   webbuilder - the user to run this script
7 #   webbuild-x - the user used by this script to run emacs
8 #   webbuild-y - the user used by this script to run emacs (for preview)
9 #
10 # A certain directory layout is required with permissions setup
11 # so that the webbuild-x has only write access to the stage area
12 # and to its own home directory.  The script checks the permissions.
13 #
14 # The trigger-website-build scripts is expected to be installed
15 # as git post-merge hook.
16 #
17 # These cronjobs are required for user webbuilder:
18 # --8<---------------cut here---------------start------------->8---
19 # # Pull the master branch of the web pages
20 # */20  * * * * cd /home/webbuilder/gnupg-doc && git pull -q origin master
21 # */18  * * * * cd /home/webbuilder/gnupg-doc-preview && git pull -q origin preview
22 #
23 # # In case of race conditions we try to build every few ours again.
24 # 35  */7 * * * /home/webbuilder/bin/build-website.sh --cron
25 # --8<---------------cut here---------------end--------------->8---
26 #
27 # /etc/sudoers needs this:
28 # --8<---------------cut here---------------start------------->8---
29 # # Let webbuilder run any command as user webbuild-x
30 # webbuilder         ALL = (webbuild-x,webbuild-y) NOPASSWD: ALL
31 # --8<---------------cut here---------------end--------------->8---
32 #
33
34 set -e
35
36 pgm=build-website.sh
37 mainuser=webbuilder
38 workuser=webbuild-x
39 workuser_pv=webbuild-y
40
41 # We use a fixed HOME so that this script can be run here from other
42 # accounts.
43 HOME=$(awk </etc/passwd -F: '$1=="'$mainuser'" {print $6;exit}')
44 if [ ! -d "$HOME" ]; then
45    echo "$pgm: directory '${HOME}' missing" >&2;
46    exit 1
47 fi
48
49 reponame=gnupg-doc
50 htdocs_web="/var/www/www/www.gnupg.org/htdocs"
51 htdocs_preview="/var/www/www/preview.gnupg.org/htdocs"
52 htdocs_blog="/var/www/www/www.gnupg.org/misc/blog"
53
54 workuser_dir=$HOME/${workuser}
55 workuser_pv_dir=$HOME/${workuser_pv}
56 log_dir="$HOME/log"
57 root_dir="$HOME/${reponame}"
58 root_dir_pv="$HOME/${reponame}-preview"
59 stage_dir="$HOME/${reponame}-stage"
60 stage_dir_pv="$HOME/${reponame}-preview-stage"
61 LOCKFILE="${log_dir}/${reponame}.lock"
62
63 if [ x"$1" = x"--git" ]; then
64   shift
65   exec  >>${log_dir}/"$reponame".log 2>&1
66   echo "$(date -u -Iseconds) gpgweb site build was git triggered"
67 elif [ x"$1" = x"--cron" ]; then
68   shift
69   exec  >>${log_dir}/"$reponame".log 2>&1
70   echo "$(date -u -Iseconds) gpgweb site build was cron triggered"
71 fi
72
73 if ! id $workuser >/dev/null 2>&1 ; then
74    echo "$pgm: sudo user '${workuser}' not available" >&2;
75    exit 1
76 fi
77
78 if ! id $workuser_pv >/dev/null 2>&1 ; then
79    echo "$pgm: sudo user '${workuser_pv}' not available" >&2;
80    exit 1
81 fi
82
83 # Check directories
84 for f in "${workuser_dir}" "${root_dir}" "${stage_dir}" \
85          "${workuser_pv_dir}" "${root_dir_pv}" "${stage_dir_pv}"; do
86   if [ ! -d "$f" ]; then
87      echo "$pgm: directory '$f' missing" >&2;
88      exit 1
89   fi
90 done
91 want="2775:${workuser}:${mainuser}"
92 for f in "${workuser_dir}" "${stage_dir}"; do
93   x=$(stat -c '%a:%U:%G' "$f")
94   if [ x"$x" != x"$want" ]; then
95     echo "$pgm: directory '$f' has wrong permissions" >&2
96     echo "$pgm:   want: $want" >&2
97     echo "$pgm:   have: $x" >&2
98     exit 1
99   fi
100 done
101 want="2775:${workuser_pv}:${mainuser}"
102 for f in "${workuser_pv_dir}" "${stage_dir_pv}"; do
103   x=$(stat -c '%a:%U:%G' "$f")
104   if [ x"$x" != x"$want" ]; then
105     echo "$pgm: directory '$f' has wrong permissions" >&2
106     echo "$pgm:   want: $want" >&2
107     echo "$pgm:   have: $x" >&2
108     exit 1
109   fi
110 done
111
112 cd "${root_dir}"
113
114 #
115 # Take a lock so that only one instance of this script runs.
116 #
117 if ! lockfile -l 7200 -r 2 $LOCKFILE; then
118     echo "$pgm: another instance is still running" >&2
119     exit 0
120 fi
121 trap "rm -f $LOCKFILE" 0
122
123
124 # These flags are set to the stage directory if a sync is required
125 sync_web=
126 sync_blog=
127 sync_preview=
128
129
130 #
131 # Build main part
132 #
133 subdir=web
134
135 revlastfile="${log_dir}/${reponame}.$(echo $subdir | tr / _).revlast"
136 buildlog="${log_dir}/${reponame}.$(echo $subdir | tr / _).log"
137 rev="$(git rev-parse --verify HEAD:$subdir)"
138 if [ -z "$rev" ]; then
139    echo "$pgm: No git revision found" >&2;
140    exit 1
141 fi
142 revlast="$(head -1 ${revlastfile} 2>/dev/null || true)"
143 if [ x"$rev" = x"$revlast" ]; then
144    echo "$pgm: No need to build $subdir" >&2;
145 else
146
147   echo "$(date -u -Iseconds) build started for $subdir" | tee ${buildlog}
148
149   if [ ! -d ${stage_dir}/${subdir} ]; then
150       sudo -u webbuild-x mkdir ${stage_dir}/${subdir}
151   fi
152
153   sudo 2>>${buildlog} -u webbuild-x emacs24 -q --batch  \
154   --eval "(require 'assoc)" \
155   --eval "(require 'org)" \
156   --eval "(setq make-backup-files nil)" \
157   --eval "(setq gpgweb-root-dir  \"${root_dir}/${subdir}/\")" \
158   --eval "(setq gpgweb-stage-dir \"${stage_dir}/${subdir}/\")" \
159   --eval "(require 'gpgweb (concat gpgweb-root-dir \"share/gpgweb.el\"))" \
160   --eval "(setq org-publish-use-timestamps-flag nil)" \
161   --eval "(setq org-export-html-toplevel-hlevel 1)" \
162   --eval "(setq org-export-html-coding-system 'utf-8)" \
163   --eval "(gpgweb-setup-project)" \
164   --eval "(org-publish-initialize-cache \"gpgweb\")" \
165   --eval "(message \"root=(%s)\" gpgweb-root-dir)" \
166   --eval "(org-publish \"gpgweb\" t nil)"
167
168   echo "$rev" > ${revlastfile}
169   sync_web=${stage_dir}/${subdir}
170   echo "$(date -u -Iseconds) build finished for $subdir" | tee -a ${buildlog}
171 fi
172
173
174 #
175 # Build blogs
176 #
177 subdir=misc/blog.gnupg.org
178
179 revlastfile="${log_dir}/${reponame}.$(echo $subdir | tr / _).revlast"
180 buildlog="${log_dir}/${reponame}.$(echo $subdir | tr / _).log"
181 rev="$(git rev-parse --verify HEAD:$subdir)"
182 if [ -z "$rev" ]; then
183    echo "$pgm: No git revision found" >&2;
184    exit 1
185 fi
186 revlast="$(head -1 ${revlastfile} 2>/dev/null || true)"
187 if [ x"$rev" = x"$revlast" ]; then
188    echo "$pgm: No need to build $subdir" >&2;
189 else
190
191   echo "$(date -u -Iseconds) build started for $subdir" | tee ${buildlog}
192
193   if [ ! -d ${stage_dir}/${subdir} ]; then
194       sudo -u webbuild-x mkdir -p ${stage_dir}/${subdir}
195   fi
196   cd ${stage_dir}/${subdir}
197
198   # We need to initialize that org cache to use our own publish function
199   # despite that we do not use any org-publish feature
200   echo "$pgm: Rendering blogs" >&2
201   sudo 2>>${buildlog} -u webbuild-x emacs24 -q --batch \
202   --eval "(require 'assoc)" \
203   --eval "(require 'org)" \
204   --eval "(setq gpgweb-root-dir \"${root_dir}/web/\")" \
205   --eval "(setq gpgweb-blog-dir \"${root_dir}/${subdir}/\")" \
206   --eval "(setq gpgweb-stage-dir \"${stage_dir}/${subdir}/\")" \
207   --eval "(require 'gpgweb (concat gpgweb-root-dir \"share/gpgweb.el\"))" \
208   --eval "(setq org-publish-use-timestamps-flag nil)" \
209   --eval "(setq org-export-html-toplevel-hlevel 1)" \
210   --eval "(setq org-export-html-coding-system 'utf-8)" \
211   --eval "(gpgweb-setup-project)" \
212   --eval "(org-publish-initialize-cache \"gpgweb\")" \
213   --eval "(message \"root=(%s)\" gpgweb-root-dir)" \
214   --eval "(gpgweb-publish-blogs)"
215
216   echo "$pgm: Updating blog index" >&2
217   indexcreator="${root_dir}/${subdir}/update-index.sh"
218   if [ ! -f $indexcreator ]; then
219     echo "$pgm: $indexcreator not found" >&2
220     exit 1
221   fi
222   sudo -u webbuild-x ${indexcreator}
223
224   echo "$rev" > ${revlastfile}
225   sync_blog=${stage_dir}/${subdir}
226   echo "$(date -u -Iseconds) build finished for $subdir" | tee -a ${buildlog}
227
228 fi
229
230
231 #
232 # Build the preview site (w/o blogs)
233 #
234 branch=preview
235 subdir=web
236
237 cd "${root_dir_pv}"
238
239 revlastfile="${log_dir}/${reponame}.$branch.$(echo $subdir | tr / _).revlast"
240 buildlog="${log_dir}/${reponame}.$branch.$(echo $subdir | tr / _).log"
241 rev="$(git rev-parse --verify $branch:$subdir)"
242 if [ -z "$rev" ]; then
243    echo "$pgm: No git revision found" >&2;
244    exit 1
245 fi
246 revlast="$(head -1 ${revlastfile} 2>/dev/null || true)"
247 if [ x"$rev" = x"$revlast" ]; then
248    echo "$pgm: No need to build $branch:$subdir" >&2;
249 else
250
251   echo "$(date -u -Iseconds) build started for $branch:$subdir" | tee ${buildlog}
252
253   if [ ! -d ${stage_dir_pv}/${subdir} ]; then
254       sudo -u webbuild-y mkdir ${stage_dir_pv}/${subdir}
255   fi
256
257   sudo 2>>${buildlog} -u webbuild-y emacs24 -q --batch  \
258   --eval "(require 'assoc)" \
259   --eval "(require 'org)" \
260   --eval "(setq make-backup-files nil)" \
261   --eval "(setq gpgweb-root-dir  \"${root_dir_pv}/${subdir}/\")" \
262   --eval "(setq gpgweb-stage-dir \"${stage_dir_pv}/${subdir}/\")" \
263   --eval "(require 'gpgweb (concat gpgweb-root-dir \"share/gpgweb.el\"))" \
264   --eval "(setq org-publish-use-timestamps-flag nil)" \
265   --eval "(setq org-export-html-toplevel-hlevel 1)" \
266   --eval "(setq org-export-html-coding-system 'utf-8)" \
267   --eval "(gpgweb-setup-project)" \
268   --eval "(org-publish-initialize-cache \"gpgweb\")" \
269   --eval "(message \"root=(%s)\" gpgweb-root-dir)" \
270   --eval "(org-publish \"gpgweb\" t nil)"
271
272   echo "$rev" > ${revlastfile}
273   sync_preview=${stage_dir_pv}/${subdir}
274   echo "$(date -u -Iseconds) build finished for $branch:$subdir" | tee -a ${buildlog}
275 fi
276 cd "${root_dir}"
277
278
279 #
280 # Sync to the webspace
281 #
282 cd "${root_dir}"
283 any_sync=
284
285 if [ -n "$sync_web" ]; then
286   cd "$sync_web"
287   rsync -rlt --exclude '*~' --exclude '*.tmp' \
288         . ${htdocs_web}/
289   touch ${htdocs_web}/donate/donors.dat
290   cd ${htdocs_web}
291   ln -sf ../../howtos.gnupg.org/htdocs howtos
292   ln -sf software related_software
293   ln -sf software/index.html features.html
294   cd "$sync_web"
295   any_sync=yes
296 fi
297
298 if [ -n "$sync_blog" ]; then
299   cd "$sync_blog"
300   rsync -rt --links --exclude '*~' --exclude '*.sh' \
301         --exclude '*tmp' --exclude '*.org' . ${htdocs_blog}/
302   cd "$root_dir/misc/blog.gnupg.org"
303   rsync -rt --links --exclude '*~' --exclude '*.sh' \
304         --exclude '*tmp' --exclude '*.org' img data  ${htdocs_blog}/
305   any_sync=yes
306 fi
307
308 if [ -n "$sync_preview" ]; then
309   cd "$sync_preview"
310   rsync -rlt --exclude '*~' --exclude '*.tmp' \
311         . ${htdocs_preview}/
312   $HOME/bin/mkkudos.sh --verbose --force --test
313 fi
314
315
316 cd "${root_dir}"
317
318 if [ "$any_sync" = yes ]; then
319   $HOME/bin/mkkudos.sh --verbose --force
320 fi
321
322
323 #
324 # Print warnings when the scripts are out of date
325 # (For security reasons the scripts need to be installed manually.)
326 #
327 for f in trigger-website-build build-website.sh mkkudos.sh \
328          append-to-donors.sh ; do
329   if ! cmp -s ${HOME}/bin/$f tools/$f ; then
330     echo "$pgm: Warning: A newer version of $f is available" >&2;
331   fi
332 done
333
334 exit 0