Make sure the background image is committed, too
[git/dscho.git] / upload.sh
blob1b3ec07b3428e811410e4a0059b31ea3ce5c959e
1 #!/bin/sh
3 # This is a simple script that will produce my blog on repo.or.cz
5 # The idea is to have source-<timestamp>.txt files as input, having the
6 # stories, and this script turning them into nice HTML, committing
7 # everything, and then pushing it to my repository.
9 # The blog will then be served using gitweb.
11 # To make it easier on me, if a file "new" exists, it is automatically
12 # renamed using the current timestamp.
14 # make sure we're in the correct working directory
15 cd "$(dirname "$0")"
17 GITWEBURL="$(git config gitweb.url)"
18 test -z "$GITWEBURL" && {
19 echo "Please set gitweb.url in the Git config first!" >&2
20 exit 1
23 get_config () {
24 value=$(git config blog.$1)
25 test -z "$value" && value=$2
26 echo $value
29 BACKGROUNDIMG=$(get_config background paper.jpg)
30 TITLE=$(get_config title "Dscho's blog")
31 MAXENTRIES=$(get_config maxPostsPerPage 10)
32 BRANCH=$(get_config branch blog)
34 URLPREFIX="$(dirname "$GITWEBURL")"/
35 REMOTEREPOSITORY="$(basename "$GITWEBURL")"
36 URL="$REMOTEREPOSITORY?a=blob_plain;hb=$BRANCH;f="
37 ORIGURL=$URL
38 NEW=new
39 OUTPUT=index.html
40 RSS=blog.rss
41 TEST=test.html
42 THIS=$0
44 LC_ALL=C
45 export LC_ALL
47 move_new_entry_back () {
48 test -f source-$now.txt &&
49 mv source-$now.txt $NEW &&
50 git rm --cached -f source-$now.txt
53 die () {
54 move_new_entry_back
55 echo "$*" >&2
56 exit 1
59 nth () {
60 # add illogical suffix
61 case "$1" in
62 *1?|*[04-9]) echo "$1th";;
63 *1) echo "$1st";;
64 *2) echo "$1nd";;
65 *3) echo "$1rd";;
66 esac
69 make_chinese_hour () {
70 case $1 in
71 23|00) echo Rat;;
72 01|02) echo Buffalo;;
73 03|04) echo Tiger;;
74 05|06) echo Rabbit;;
75 07|08) echo Dragon;;
76 09|10) echo Snake;;
77 11|12) echo Horse;;
78 13|14) echo Goat;;
79 15|16) echo Monkey;;
80 17|18) echo Rooster;;
81 19|20) echo Dog;;
82 21|22) echo Pig;;
83 esac
86 digit_to_roman () {
87 case $1 in
88 1) echo $2;;
89 2) echo $2$2;;
90 3) echo $2$2$2;;
91 4) echo $2$3;;
92 5) echo $3;;
93 6) echo $3$2;;
94 7) echo $3$2$2;;
95 8) echo $3$2$2$2;;
96 9) echo $2$4;;
97 esac
100 make_roman_number () {
101 case $1 in
102 '') ;;
103 ?) digit_to_roman $1 I V X;;
104 ??) echo $(digit_to_roman ${1%?} X L C)$(make_roman_number ${1#?});;
105 ???) echo $(digit_to_roman ${1%??} C D M)$(make_roman_number ${1#?});;
106 ????) echo $(digit_to_roman ${1%???} M)$(make_roman_number ${1#?});;
107 esac
110 make_date () {
111 printf "%s, %s of %s, Anno Domini %s, at the hour of the %s\n" \
112 $(date +%A -d @$1) \
113 $(nth $(date +%e -d @$1)) \
114 $(date +%B -d @$1) \
115 $(make_roman_number $(date +%Y -d @$1)) \
116 $(make_chinese_hour $(date +%H -d @$1))
119 # make an argument for sed, to replace $1..$1 by <$2>..</$2>
120 markup_substitution () {
121 case "$1" in
122 ?) echo "s/$1\\([^$1]*\\)$1/<$2>\\\\1<\/$2>/g";;
124 tmp="[^${1%?}]*"
125 tmp2="\\|${1%?}[^${1#?}]$tmp"
126 tmp3="\\($tmp\\($tmp2\\($tmp2\\($tmp2\\)\\)\\)\\)"
127 echo "s/$1$tmp3$1/<$2>\\\\1<\/$2>/g"
129 esac
132 # transform markup in stdin to HTML
133 markup () {
134 image_pattern="\\[\\[Image:\([^]]*\)"
135 image_pattern2="$image_pattern\(\\|[^\]]*\)\?\]\]"
136 case "$*" in
137 *invert-bash*) bash_bg=white; bash_fg=black;;
138 *) bash_bg=black; bash_fg=white;;
139 esac
140 sed -e 's!^$!</p><p>!' \
141 -e "$(markup_substitution "''" i)" \
142 -e "$(markup_substitution "_" u)" \
143 -e 's!IMHO!in my humble opinion!g' \
144 -e 's!BTW!By the way,!g' \
145 -e 's!repo.or.cz!<a href=http://&>&</a>!g' \
146 -e 's!:-)!\&#x263a;!g' \
147 -e "s!$image_pattern2!<center><img src=$URL\1></center>!g" \
148 -e "s!\\[\\[SVG:\\([^]]*\\)\]\]!$THIS handle &!e" \
149 -e 's!<bash>!<table\
150 border=1 bgcolor='$bash_bg'>\
151 <tr><td bgcolor=lightblue colspan=3>\
152 \&nbsp;\
153 </td></tr>\
154 <tr><td>\
155 <table cellspacing=5 border=0\
156 style="color:'$bash_fg';">\
157 <tr><td>\
158 <pre>!' \
159 -e 's!</bash>! </pre>\
160 </td></tr>\
161 </table>\
162 </td></tr>\
163 </table>!' \
167 # output lines containing <timestamp> <filename> <title>
168 get_blog_entries () {
169 for file in $(ls -r source-*.txt)
171 basename=${file%.txt}
172 timestamp=${basename#source-}
173 title="$(sed 1q < $file | markup)"
174 echo "$timestamp $file $title"
175 done
178 get_last_removed_entry () {
179 git log --pretty=format: --name-only --diff-filter=D HEAD |
180 while read line
182 case "$line" in
183 source-*.txt) file=$line;;
184 '') test -z "$file" || {
185 echo "$file"
186 break
188 esac
189 done
192 # make HTML page
193 make_html () {
194 body_style="width:800px"
195 body_style="$body_style;background-image:url($URL$BACKGROUNDIMG)"
196 body_style="$body_style;background-repeat:repeat-y"
197 body_style="$body_style;background-attachment:scroll"
198 body_style="$body_style;padding:0px;"
199 text_style="width:610px"
200 text_style="$text_style;margin-left:120px"
201 text_style="$text_style;margin-top:50px"
202 text_style="$text_style;align:left"
203 text_style="$text_style;vertical-align:top;"
204 cat << EOF
205 <html>
206 <head>
207 <title>$TITLE</title>
208 <meta http-equiv="Content-Type"
209 content="text/html; charset=UTF-8"/>
210 </head>
211 <body style="$body_style">
212 <div style="$text_style">
213 <h1>$TITLE</h1>
215 indent='\t\t\t'
217 # make toc
218 toc_width=400px
219 toc_style="position:absolute;top:50px;left:810px;width=$toc_width"
221 echo "<div style=\"$toc_style\">"
222 echo "<table width=$toc_width bgcolor=#e0e0e0 border=1>"
223 echo "<tr><th>Table of contents:</th></tr>"
224 echo "<tr><td>"
225 echo '<p><ul>'
226 get_blog_entries |
227 while read timestamp filename title
229 date="$(date +"%d %b %Y" -d @$timestamp)"
230 echo "<li><a href=#$timestamp>$date $title</a>"
231 done
232 echo '</ul></p>'
233 file=
234 last_removed_entry=$(get_last_removed_entry)
235 test -z "$last_removed_entry" || {
236 commit=$(git log --pretty=format:%H --diff-filter=AM \
237 -- $last_removed_entry |
238 head -n 1)
239 previous="$REMOTEREPOSITORY?a=blob_plain;hb=$commit"
240 echo "<a href=$previous;f=index.html>Older posts</a>"
242 echo '</td></tr></table>'
244 # RSS feed
245 rss_style="background-color:orange;text-decoration:none"
246 rss_style="$rss_style;color:white;font-family:sans-serif;"
247 echo '<br>'
248 echo '<div style="text-align:right;">'
249 echo "<a href=\"$ORIGURL$RSS\""
250 echo ' title="Subscribe to my RSS feed"'
251 echo ' class="rss" rel="nofollow"'
252 echo " style=\"$rss_style\">RSS</a>"
253 echo '</div>'
255 # Links
256 test -f links.html && {
257 echo "<br>"
258 echo "<table width=$toc_width bgcolor=#e0e0e0 border=1>"
259 echo "<tr><th>Links:</th></tr>"
260 echo "<tr><td>"
261 cat links.html
262 echo "</td></tr></table>"
265 # Google AdSense
266 test -z "$DRYRUN" && test -f google.adsense && {
267 echo "<br>"
268 echo "<table width=$toc_width bgcolor=#e0e0e0 border=1>"
269 echo "<tr><th>Google Ads:</th></tr>"
270 echo "<tr><td align=center>"
271 cat google.adsense
272 echo "</td></tr></table>"
275 echo '</div>'
276 } | sed -s "s/^/$indent/"
279 # timestamps will not need padding to sort correctly, for some time...
280 get_blog_entries |
281 while read timestamp filename title
283 echo "<h6>$(make_date $timestamp)</h6>"
284 echo "<a name=$timestamp>"
285 echo "<h2>$title</h2>"
286 echo ""
287 echo "<p>"
288 sed 1d < $filename | markup
289 echo "</p>"
290 done |
291 sed -e "s/^./$indent&/" \
292 -e "/<pre>/,/<\/pre>/s/^$indent//"
294 cat << EOF
295 </div>
296 </body>
297 </html>
301 generate_rss () {
302 echo '<?xml version="1.0" encoding="utf-8"?>'
303 echo '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">'
304 echo '<channel>'
305 echo "<title>Dscho's blog</title>"
306 echo "<link>$URLPREFIX${URL}index.html</link>"
307 self="$URLPREFIX$ORIGURL$RSS"
308 selfattribs='rel="self" type="application/rss+xml"'
309 echo "<atom:link href=\"$self\" $selfattribs/>"
310 echo '<description>A few stories told by Dscho</description>'
311 echo "<lastBuildDate>$(date --rfc-2822)</lastBuildDate>"
312 echo '<language>en-us</language>'
314 get_blog_entries |
315 while read timestamp filename title
317 echo '<item>'
318 echo "<title>$title</title>"
319 echo "<link>$URLPREFIX${URL}index.html#$timestamp</link>"
320 echo "<guid>$URLPREFIX${URL}index.html#$timestamp</guid>"
321 echo "<pubDate>$(date --rfc-2822 -d @$timestamp)</pubDate>"
322 description="$(cat < $filename | markup invert-bash)"
323 echo "<description><![CDATA[$description]]></description>"
324 echo "</item>"
325 done
327 echo '</channel>'
328 echo '</rss>'
331 get_image_files () {
332 git ls-files |
333 grep -v '\.\(rss\|html\|gitignore\|in\|sh\|txt\|adsense\)$'
336 remove_old_entries () {
337 count=$(ls source-*.txt | wc -l)
338 test $MAXENTRIES -ge $count && return
340 for file in source-*.txt
342 test $MAXENTRIES -lt $count || break
343 git rm $file > /dev/null || return 1
344 count=$(($count-1))
345 echo $file
346 done
348 # remove no-longer referenced images
349 image_files=$(get_image_files)
350 referenced_files="$(cat source-*.txt |
351 tr ']|' '\n' |
352 sed -ne 's/\[\[\(Image\|SVG\)://p') $BACKGROUNDIMG"
353 for file in $(echo $image_files $referenced_files $referenced_files |
354 tr ' ' '\n' | sort | uniq -u)
356 git rm $file > /dev/null || return 1
357 echo $file
358 done
361 # never, ever have spaces in the file names
362 commit_new_images () {
363 files="$(remove_old_entries) $RSS $BACKGROUNDIMG" ||
364 die "Could not remove old entries"
367 generate_rss > $RSS &&
368 git add $RSS ||
369 die "Could not generate $RSS"
371 for image in $(cat source-* |
372 tr ' ]|' '\n' |
373 sed -n 's/.*\[\[\(Image\|SVG\)://p' |
374 sort |
375 uniq)
377 git add $image || die "Could not git add image $image"
378 files="$files $image"
379 done
381 git update-index --refresh &&
382 git diff-files --quiet -- $files &&
383 git diff --cached --quiet HEAD -- $files ||
384 git commit -s -m "Commit some images on $(make_date $now)" $files
387 handle_svg_file () {
388 # for some reason, Firefox adds scrollbars, so nudge the width a bit
389 width=$(sed -ne 's/.* width="\([^"]*\).*/\1/p' -e '/<metadata/q' < "$1")
390 test -z "$width" || width=" width=$(($width+5))"
391 URL=
392 if test -z "$DRYRUN"
393 then
394 REV=$(git rev-list -1 HEAD -- $1)
395 test -z "$REV" ||
396 URL="$REMOTEREPOSITORY?a=blob_plain;hb=$REV;f="
398 cat << EOF
399 <center>
400 <table border=0>
401 <tr>
402 <td align=center>
403 <embed type="image/svg+xml"
404 src="$URL$1"$width />
405 </td>
406 </tr>
407 <tr>
408 <td align=center>
409 <a href=$URL$1>$1</a>
410 </td>
411 </tr>
412 </table>
413 </center>
419 # parse command line option
420 case "$1" in
421 *dry*) DRYRUN=1; export DRYRUN; shift;;
422 *show*) firefox "$(pwd)"/$TEST; exit;;
423 *remote*) firefox $URLPREFIX$URL$OUTPUT; exit;;
424 handle)
425 case "$2" in
426 "[[SVG:"*) file=${2#??SVG:}; file=${file%??}; handle_svg_file "$file";;
427 esac
428 exit
430 '') ;;
431 *) die "Unknown command: $1";;
432 esac
434 test "$#" = 0 ||
435 die "Usage: $0 [--dry-run]"
437 # make sure we're on the correct branch
438 test refs/heads/$BRANCH = $(git symbolic-ref HEAD) ||
439 die "Not on branch $BRANCH"
441 # make sure there are no uncommitted changes
442 git update-index --refresh &&
443 git diff-files --quiet ||
444 die "Have unstaged changes!"
446 # rename the new blog entry if it exists
447 now=$(date +%s)
448 test ! -f $NEW || {
449 mv -i $NEW source-$now.txt &&
450 git add source-$now.txt
451 } ||
452 die "Could not rename source.txt"
454 # commit the images that are referenced and not yet committed
455 test ! -z "$DRYRUN" ||
456 commit_new_images ||
457 die "Could not commit new images"
459 # to find the images reliably, we have to use the commit name, not the branch
460 # we use the latest commit touching an image file.
461 IMAGEFILES="$(get_image_files)"
462 REV=$(git rev-list -1 HEAD -- $IMAGEFILES)
463 test -z "$REV" && REV=$BRANCH
464 URL="$REMOTEREPOSITORY?a=blob_plain;hb=$REV;f="
466 if test ! -z "$DRYRUN"
467 then
468 # Output to test.html and have local links into the current directory
469 OUTPUT=$TEST
470 URL=
473 make_html > $OUTPUT || die "Could not write $OUTPUT"
475 test ! -z "$DRYRUN" && {
476 move_new_entry_back
477 exit
480 git add $OUTPUT &&
481 git commit -s -m "Update $(make_date $now)" &&
482 git push origin +$BRANCH