File: thumbspage/build/_PUBLISH.sh
#!/bin/bash
#========================================================================
# Package and publish thumbspage: the main, top-level script [2.1].
# Part of the thumbspage program (learning-python.com/thumbspage.html).
#
# USAGE
# -----
# Run me anywhere: automatically cds to target folders along the way.
# Also runs examples generator, user-guide fixer, analytics inserter.
#
# To publish thumbspage, simply do this on a Unix platform:
# bash build/_PUBLISH.sh
#
# Which in turn runs:
# python3 build/generate-examples.py
# python3 build/userguide-online-links.py
# python3 build/insert-analytics.py
#
# The first of which runs:
# _generate.sh in examples/* subfolders, plus make-narrow-labels.py
#
# Then, if galleries have changed:
# Update external clients in website: see x-publish-external-demos*
# Especially republish the hosting site's trnpix/ gallery, a demo.
#
# All told, this script:
# - Generates example galleries
# - Makes release zips
# - Copies zips and content to the local site dir
# - Builds the site
# - Zips the upload package
# - Uploads and unzips on the server
# - Downloads and verifies its results
#
# And runs the other .py scripts listed above in the first three steps
# (e.g., examples' _generate.sh may use 'here documents', sed, and more).
# The generate-examples.py run verifies thumbspage before uploading it.
#
# This script also uses these Python scripts: ziptools create/extract
# (learning-python.com/ziptools.html); diffall.py, a part of Mergeall
# (learning-python.com/mergeall.html), and the larger website's main
# _PUBLISH.py script (learning-python.com/genhtml/__docs__/PUBLISH.py).
#
# NOTES
# -----
# This could be coded in Python, but it's largely shell commands which
# are marginally easier in Bash. OTOH, Bash syntax is arguably odd,
# and Python may be better for portability to Windows/etc. For more
# on Bash syntax and usage, see also the "Bash Rant" at end of file.
#
# Config edits, zip uploads, unzips on the server, and downloads are
# coded with Unix shell tools sed, scp, ssh, and curl, respectively.
# There are equivalents on Windows (see also its WSL Linux subsystem),
# and Python options exists for all these steps too (e.g., urllib).
#
# In the end, this script fully automates manual work that used to
# take hours (or days) every time thumbspage was changed. Its run
# output must still be inspected for diffs and errors, and its new
# combined zip makes for a long upload (5~10 minutes). But there are
# no manual steps left after starting this script; this is a huge win.
#
# OTHER
# -----
# See also ./x-publish-external-demos.sh for a script that automates
# builds of other site packages that depend on thumbspage. Naturally,
# this is specific to the hosting site, but it may serve as an example.
# Folder x-publish-external-demos-prod/ has bits of the live version.
#
# Update: build logs are no longer updated for each release, and the
# sizes of zipfiles are current only in the main web page, not in
# UserGuide.html. Else, each release must be run multiple times.
#========================================================================
#------------------------------------------------------------------------
# Setup: globals, work dir
#------------------------------------------------------------------------
# verify temp results with long-running cp+diffall?
Verify=y
# download and validate content/zips with diffall at end?
Validate=y
# stop for enter or ctrl-c between steps (y or not)?
Pause=y
# a conditional 'read -p', () optional
function announce() {
echo
echo STARTING: $1
if [ $Pause == 'y' ]; then
read -p 'Press enter/return to continue'
fi
}
# all exits happen here
function shutdown() {
echo
echo 'Build stopping at:' $(date)
echo 'thumbspage' $1
echo 'Bye.'
exit
}
echo 'Build starting at:' $(date)
# testing
#Z=~MY-STUFF/Code/ziptools/link
#C=$temp/test-tp-build/C
#W=$temp/test-tp-build/W
#P=$W/P
# live
C=~/MY-STUFF/Code
Z=$C/ziptools/link
W=~/MY-STUFF/Websites
P=$W/Programs/Current/Complete
# ensure scratch folder ({}; optional, ; or \n, -d=nonexist||nondir)
temp=~/Desktop/temp
if [ ! -d $temp ]; then { rm -f $temp; mkdir $temp; } fi
#------------------------------------------------------------------------
# Interact: make galleries?, verify run
#------------------------------------------------------------------------
read -p 'Generate examples galleries first (y or n)? ' userreply
if [[ -n $userreply && $userreply == 'y' ]]
then
##bash $C/thumbspage/build/generate-examples.sh
python3 $C/thumbspage/build/generate-examples.py
echo
echo '***All galleries built***'
fi
read -p 'About to bundle thumbspage; continue (y or n)? ' userreply
if [[ -z $userreply || $userreply != 'y' ]]; then shutdown 'bundle aborted'; fi
#------------------------------------------------------------------------
# Make three release zips in Code/
#------------------------------------------------------------------------
cd $C
announce 'ZIPS IN CODE'
# save original to verify $C/tp reset later?
if [ $Verify == 'y' ]
then
rm -rf $temp/thumbspage
cp -Rp $C/thumbspage $temp
fi
# make a backup copy of configs for users
cp -p thumbspage/user_configs.py thumbspage/docetc/user_configs.py.bkp
#------
# Zip 1: program + ug (local links) + examples [also for online copy]
mv thumbspage/_private $temp
mv thumbspage/__pycache__ $temp
echo '...Making full-package zip...'
python3 $Z/zip-create.py thumbspage-full-package.zip thumbspage \
-skipcruft | tail -n 25
#------
# Zip 2: program + ug (online links)
mv thumbspage/.htaccess $temp
mv thumbspage/examples $temp
echo '...Changing examples/ links in user guide...'
python3 thumbspage/build/userguide-online-links.py
mv thumbspage/UserGuide.html $temp
mv thumbspage/UserGuideLive.html thumbspage/UserGuide.html
echo '...Making program-only zip...'
python3 $Z/zip-create.py thumbspage-program-only.zip thumbspage \
-skipcruft | tail -n 25
rm thumbspage/UserGuide.html
mv $temp/UserGuide.html thumbspage
#------
# Zip 3: examples + ug (local links)
mv $temp/examples thumbspage
# unzips to folder; don't include user guide: requires other top-level items
echo '...Making examples-only zip...'
python3 $Z/zip-create.py thumbspage-examples-only.zip thumbspage/examples \
-skipcruft | tail -n 25
mv $temp/_private thumbspage
mv $temp/__pycache__ thumbspage
mv $temp/.htaccess thumbspage
# $C/tp is back to original, 3 new zips are in $C itself
if [ $Verify == 'y' ]
then
echo '** Diffall: Code/ to original: there should be no diffs (but docetc/user_configs if changed)'
python3 $C/mergeall/diffall.py $C/thumbspage $temp/thumbspage -skipcruft | tail -n 10
rm -rf $temp/thumbspage
fi
#------------------------------------------------------------------------
# Copy zips+folder to Websites/ subdir
#------------------------------------------------------------------------
cd $P
announce 'COPY TO SITE FOLDER'
# the zip here makes ./thumbspage/ (finder may make macos subfolder)
# remake ./thumbspage from $C zip
rm -rf thumbspage/
cp -p $C/thumbspage-full-package.zip .
python3 $Z/zip-extract.py thumbspage-full-package.zip . | tail -n 25
# embed zips in ./thumbspage
mv thumbspage-full-package.zip thumbspage/
cp -p $C/thumbspage-program-only.zip thumbspage/
cp -p $C/thumbspage-examples-only.zip thumbspage/
# move zips to private copies with date suffix
private=$C/thumbspage/_private
stamp=$(date +%b-%d-%y)
mv $C/thumbspage-full-package.zip $private/thumbspage-full-package--${stamp}.zip
mv $C/thumbspage-program-only.zip $private/thumbspage-program-only--${stamp}.zip
mv $C/thumbspage-examples-only.zip $private/thumbspage-examples-only--${stamp}.zip
# $P/tp is same as $C/tp, except for 3 added zips and no _private/__pycache
if [ $Verify == 'y' ]
then
echo '** Diffall: Websites/ to Code/: there should be +3 zips and -1 _file unique'
python3 $C/mergeall/diffall.py $P/thumbspage $C/thumbspage -skipcruft > $temp/diffall
grep --context=8 '*UNIQUE' $temp/diffall
tail -n 10 $temp/diffall
fi
#------------------------------------------------------------------------
# Zip folder with nested zips for upload
#------------------------------------------------------------------------
cd $W
announce 'ZIP FOR UPLOAD'
# add analytics to online resources
echo '...Adding analytics...'
python3 $C/thumbspage/build/insert-analytics.py
# build/copy site to local UNION; runs ip-anon, fix-readmes, genhtml
echo '...Building site...'
python3 _PUBLISH.py | tail -n 25
cd UNION
# combined upload zip
echo '...Making upload zip...'
python3 $Z/zip-create.py thumbspage.zip thumbspage -skipcruft | tail -n 25
# Zip embeds folder + 3 nested zips
# Nested zips' content has no analytics
# Folder has user guide (local links) + examples/ + .htaccess with analytics/ip-anon
# Folder also has top-level _README.txt, displayed by .htaccess
#------------------------------------------------------------------------
# Upload, unzip on website server
#------------------------------------------------------------------------
# still in UNION
announce 'UPLOAD TO SITE'
# site credentials
sshusr=bitnami@44.232.137.139
sshkey=$W/_admin/SSH-private-key-download/LightsailDefaultKey-us-west-2.pem
# upload combined zip (now automated, was FileZilla, see also curl scp://)
echo '...Uploading to server root (long)...'
scp -i $sshkey thumbspage.zip $sshusr:htdocs
# unzip on web server (now automated, was copy/paste, run remote commands)
echo '...Unzipping on server...'
ssh -i $sshkey $sshusr <<-EOF
cd htdocs
rm -rf thumbspage
unzip -d . thumbspage.zip | tail -n 25
rm thumbspage.zip
exit
EOF
# from local UNION
rm thumbspage.zip
#------------------------------------------------------------------------
# Fetch and validate online results
#------------------------------------------------------------------------
if [ $Validate != 'y' ]; then shutdown 'validate skipped'; fi
# _really_ make sure - this is a very long task
if [ $Validate == 'y' ]
then
echo
read -p 'Upload complete; fetch and validate results (y or n)? ' Validate
if [ -z $Validate ] || [ $Validate != 'y' ]; then
shutdown 'validate aborted'
fi
fi
cd $temp
#------
announce 'VALIDATE ONLINE CONTENT'
curl -O https://learning-python.com/thumbspage/UserGuide.html
echo '** Diff: there should be just 1 diff for UG: added analytics'
diff UserGuide.html $C/thumbspage/UserGuide.html
# site: rewrites .py to a display script, rawmode sends just text
# bash: quote for '&', need hack to break the string across lines
# curl: need '-o file' or '> file' else -O names with query param
url='https://learning-python.com/cgi/showcode.py'
url+='?name=thumbspage/thumbspage.py&rawmode=view'
curl $url -o thumbspage.py
echo '** Diff: there should be no diffs for code'
diff thumbspage.py $C/thumbspage/thumbspage.py
rm UserGuide.html thumbspage.py
#------
announce 'VALIDATE ZIPS'
curl -O https://learning-python.com/thumbspage/thumbspage-full-package.zip
curl -O https://learning-python.com/thumbspage/thumbspage-program-only.zip
curl -O https://learning-python.com/thumbspage/thumbspage-examples-only.zip
# bash: {}s optional here
echo '** Cmps: there should be no cmp output for zips'
cmp -bl thumbspage-full-package.zip $private/thumbspage-full-package--${stamp}.zip
cmp -bl thumbspage-program-only.zip $private/thumbspage-program-only--${stamp}.zip
cmp -bl thumbspage-examples-only.zip $private/thumbspage-examples-only--${stamp}.zip
# each of the following makes a ./thumbspage folder
#------
announce 'VALIDATE UNZIP ALL'
python3 $Z/zip-extract.py thumbspage-full-package.zip . | tail -n 25
python3 $C/mergeall/diffall.py thumbspage $C/thumbspage -skipcruft > $temp/diffall
echo '** Diffall: fullpkg download to Code/: there should be -1: _file'
grep --context=8 '*UNIQUE' $temp/diffall
tail -n 10 $temp/diffall
rm -rf thumbspage/
#------
announce 'VALIDATE UNZIP PROGRAM'
python3 $Z/zip-extract.py thumbspage-program-only.zip . | tail -n 25
python3 $C/mergeall/diffall.py thumbspage $C/thumbspage -skipcruft > $temp/diffall
echo '** Diffall: program download to Code/: there should be UG + -3=[1 _file, examples/, .htaccess']
grep --context=8 '*UNIQUE' $temp/diffall
tail -n 10 $temp/diffall
# >200 links differ: tail this
diff thumbspage/UserGuide.html $C/thumbspage/UserGuide.html | tail -n 25
rm -rf thumbspage/
#------
announce 'VALIDATE UNZIP EXAMPLES'
python3 $Z/zip-extract.py thumbspage-examples-only.zip . | tail -n 25
python3 $C/mergeall/diffall.py thumbspage $C/thumbspage -skipcruft > $temp/diffall
echo '** Diffall: examples download to Code/: there should be -N: all other top-level items'
grep --context=20 '*UNIQUE' $temp/diffall
tail -n 10 $temp/diffall
rm -rf thumbspage/
rm thumbspage-full-package.zip thumbspage-program-only.zip thumbspage-examples-only.zip
shutdown '_PUBLISH complete'
#=====================================================================
# The Bash Rant - a few notes on the wildly arcane syntax used above:
#
# - '{ ; }' around a statement block is optional
#
# - $x is required to eval vars, even in expression contexts
#
# - Linebreaks can be used instead of--or in addition to--';'
# statement delimiters
#
# - The quotes around 'y' strings above are optional (well, often),
# and " " evals nested $ vars but ' ' does not
#
# - Spaces are required around operators and within [ ] tests
# and { } compounds, but cannot be used around '=' in assignments
#
# - A nested ' within a ' ' requires either $' \' ' or " ' ",
# but " " has extra semantics (see above)
#
# - There seems no good way to make strings span lines in '=': see
# the url= and url+= work-around above ('a''b' and $a$b iff no space)
#
# - 'a b c' auto-splits to a list, but ('a' 'b' 'c') is an array
# which requires bizarre reference syntax like ${examples[@]}
#
# - Function defs can code 'function', '()', or both - so there
# are already 3 wtdi; and params are '$N', not useful names
#
# - Tests must be coded in '[]' or '[[]]' depending on content -
# '[[ a && b ]]' is just '[ a ] && [ b ]', iff '[[]]' is supported
#
# - Plus more bracket hell: '()', '{}', '(())',...
#
# And all these work the same in macOS's Bash (but why?):
#
# if test 's'; then echo 'yes'; fi
# if [ 's' ]; then echo yes; fi
#
# if [ 's' ]
# then
# echo yes
# fi
#
# if [[ 's' ]]; then
# echo yes; fi
#
# if { test 's'; } then { echo yes; } fi
# if { test 's'; }; then { echo yes; }; fi;
#
# Which is to subjectively say: yuck. Bash seems ad hock (and
# plausibly odd hack). Python is a lot more coherent, but Bash is
# okay for command-line centric scripts like this one. But when
# build/generate-examples.sh was no longer in that category, it
# was converted to Python, the better option for program logic.
#
# And the closer: editing a Bash script's source file like this one
# while it is running can cause the script to fail on nonsensical
# syntax errors, because Bash reads the script's code as it goes.
# No, really. So don't do that. And OMG...
#=====================================================================