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*
#
# 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 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 (but see also its 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...
#=====================================================================



[Home page] Books Code Blog Python Author Train Find ©M.Lutz