|
| 1 | +#!/bin/bash |
| 2 | +# This script is normally invoked from build.sh, but can be run |
| 3 | +# interactively from anywhere in the CodeWorld source tree. |
| 4 | +# |
| 5 | +# It mirrors static content from various places on the Web and puts it |
| 6 | +# in subdirectories of "${BUILD}/mirrored", which is in turn linked |
| 7 | +# into the CodeWorld "web" hierarchy, where the mirrored files are |
| 8 | +# available to be loaded from our own Web server instead of having |
| 9 | +# pages retrieve a lot of static content from "remote" sources. |
| 10 | +# |
| 11 | +# The files are checksummed to make sure the content matches |
| 12 | +# what's expected. |
| 13 | +# |
| 14 | + |
| 15 | +## Configuration variables |
| 16 | + |
| 17 | +# These are the URLS to be copied in. |
| 18 | +TO_MIRROR=( |
| 19 | + 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css' |
| 20 | + 'http://code.jquery.com/jquery-2.2.4.min.js' |
| 21 | + 'http://code.jquery.com/jquery-1.12.4.min.js' |
| 22 | + 'http://code.jquery.com/jquery-1.12.4.js' |
| 23 | + 'http://code.jquery.com/ui/1.12.0/jquery-ui.min.js' |
| 24 | + 'http://cdn.materialdesignicons.com/3.6.95/css/materialdesignicons.min.css' |
| 25 | + 'http://cdn.materialdesignicons.com/3.6.95/fonts/materialdesignicons-webfont.ttf' |
| 26 | + 'http://cdn.materialdesignicons.com/3.6.95/fonts/materialdesignicons-webfont.woff2' |
| 27 | + 'http://casual-effects.com/markdeep/latest/markdeep.min.js' |
| 28 | + 'http://cdnjs.cloudflare.com/ajax/libs/jquery-layout/1.4.3/jquery.layout_and_plugins.min.js' |
| 29 | + 'http://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/7.19.2/sweetalert2.all.min.js' |
| 30 | + 'http://cdnjs.cloudflare.com/ajax/libs/jqtree/1.4.10/tree.jquery.js' |
| 31 | + 'http://cdn.jsdelivr.net/npm/[email protected]/dist/promise.min.js' |
| 32 | +) |
| 33 | + |
| 34 | +# Files that are already in the build tree aren't normally |
| 35 | +# re-downloaded... except that files older than "MAX_DAYS" are |
| 36 | +# deleted and treated as if they hadn't existed. If the original |
| 37 | +# source has withdrawn or modifieda file, there'll be a download error |
| 38 | +# a checksum error, which may alert a maintainer or admin to a change |
| 39 | +# that needs to be dealt with. |
| 40 | +MAX_DAYS=30 |
| 41 | + |
| 42 | +# The "frozen" hash file may be checked into git to force the checksums expected |
| 43 | +# for mirrored files starting from the very first download of each file. If a file |
| 44 | +# isn't listed, any checksum will be accepted on the first download, but any subsequent |
| 45 | +# downloads must have the same checksum (until the file is removed from the list of |
| 46 | +# cached hashes). |
| 47 | +# |
| 48 | +FROZEN_HASHES="sums.txt.frozen" # In the script directory, if it exists |
| 49 | +CACHED_HASHES="sums.txt" # In the mirrorored directory (location computed below) |
| 50 | + |
| 51 | +## Global shell behavior |
| 52 | +shopt -s extglob |
| 53 | + |
| 54 | +## Prelimininaries, mostly to make it convenient to run this script |
| 55 | +## interactively for administrative or debugging purproses. Also |
| 56 | +## does a couple of minor sanity checks on the build tree. |
| 57 | + |
| 58 | +# Where is this script? |
| 59 | +script_dir="${0%/*}" |
| 60 | + |
| 61 | +# We may be run interactively from unpredictable places, so have a way |
| 62 | +# of guessing the root of the CodeWorld source tree if we need it |
| 63 | +cw_base() { |
| 64 | + if [ -z "${cw_base}" ]; then |
| 65 | + if [ -f "base.sh" ]; then |
| 66 | + cw_base="$(pwd)" |
| 67 | + elif cw_base="$(cd "$(git rev-parse --show-cdup)"; pwd)" && [ -f "${cw_base}/base.sh" ]; then |
| 68 | + : |
| 69 | + else |
| 70 | + echo "Can't find root of Codeworld source tree" 1>&2 |
| 71 | + exit 1 |
| 72 | + fi |
| 73 | + fi |
| 74 | + echo "${cw_base}" |
| 75 | + return |
| 76 | +} |
| 77 | + |
| 78 | +# Find the build directory. Normally this will be passed to us from |
| 79 | +# build.sh. |
| 80 | +if [ -z "${BUILD}" ]; then |
| 81 | + pushd "$(cw_base)" > /dev/null |
| 82 | + . base.sh |
| 83 | + popd > /dev/null |
| 84 | + if [ -z "${BUILD}" ]; then |
| 85 | + BUILD="$(cw_base)/build" |
| 86 | + if [ ! -d "${BUILD}" ]; then |
| 87 | + echo "Won't create build directory in a guessed location (${BUILD})" 1>&2 |
| 88 | + exit 1 |
| 89 | + fi |
| 90 | + fi |
| 91 | +fi |
| 92 | + |
| 93 | +## The meat of the script |
| 94 | + |
| 95 | +mirror_dir="${BUILD}/mirrored" |
| 96 | +mkdir -p "${mirror_dir}" || exit 1 |
| 97 | + |
| 98 | +cached_hash_file="${mirror_dir}/${CACHED_HASHES}" |
| 99 | +frozen_hash_file="${script_dir}/${FROZEN_HASHES}" |
| 100 | +if [ ! -e "${cached_hash_file}" ]; then |
| 101 | + if [ -f "${frozen_hash_file}" ]; then |
| 102 | + cp "${frozen_hash_file}" "${cached_hash_file}" || exit 1 |
| 103 | + else |
| 104 | + touch "${cached_hash_file}" |
| 105 | + fi |
| 106 | +fi |
| 107 | + |
| 108 | +# Now do the actual work |
| 109 | +exitstat=0 |
| 110 | + |
| 111 | +for url in "${TO_MIRROR[@]}"; do |
| 112 | + file_relative=".${url#http?(s):/}" |
| 113 | + file="${mirror_dir}/${file_relative}" |
| 114 | + [ -e "${file}" ] && (find "${file}" -type f -mtime "+${MAX_DAYS}" -print0 | xargs -0 -r rm) || exitstat=1 |
| 115 | + [ -f "${file}" ] || \ |
| 116 | + wget --no-verbose --no-use-server-timestamps \ |
| 117 | + --directory-prefix="${mirror_dir}" --force-directories "${url}" || \ |
| 118 | + exitstat=1 |
| 119 | + if ! (sed 's/[^ ]* *//' "${cached_hash_file}" | grep -F -x -q "${file_relative}"); then |
| 120 | + echo "Warning: First download of ${url}; adding hash to ${cached_hash_file}" |
| 121 | + (cd "${mirror_dir}"; sha256sum "${file_relative}") >> "${cached_hash_file}" || exitstat=1 |
| 122 | + fi |
| 123 | +done |
| 124 | + |
| 125 | +if ! (cd "${mirror_dir}"; sha256sum --quiet --check -) < "${cached_hash_file}"; then |
| 126 | + exitstat="$?" |
| 127 | + echo "DANGER: Validation failed for downloaded third-party code; tampered with?" 1>&2 |
| 128 | +fi |
| 129 | + |
| 130 | +exit "${exitstat}" |
0 commit comments