blob: 3a6cd4435ab8bf1b6f7b7f7846451a47f8fa7cc6 [file]
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Fail on error
set -e
#set -x
# Base image and packages are defined in Dockerfile (digest-pinned, tracked by Dependabot).
# To regenerate requirements.txt after changing requirements.in, see README.md#updating-the-dependency-lockfile
SOLR_LOCAL_PELICAN_IMAGE="solr-pelican-image"
DOCKER_CMD="docker run --rm -ti -w /work -p 8000:8000 -v $(pwd):/work $SOLR_LOCAL_PELICAN_IMAGE"
unset SERVE
unset BUILD
unset LOCK
PELICAN_CMD="pelican content -o output"
export SITEURL="https://solr.apache.org/"
# Option definitions: "short_flag:long_flag:description"
# Omit short_flag (leave empty before first colon) for long-only options.
# Usage text and getopt spec are both derived from this array.
OPTIONS=(
"l:live:Live build and reload source changes on localhost:8000"
"b:build:Force rebuild of the local Docker image"
":lock:Regenerate requirements.txt from requirements.in (use with -b to also rebuild image)"
"h:help:Show this help message"
":pelican-help:Show all options accepted by Pelican"
)
function usage {
echo "Usage: ./build.sh [OPTIONS] [-- <pelican arguments>]"
echo ""
echo "Options:"
for opt_def in "${OPTIONS[@]}"; do
short="${opt_def%%:*}"
rest="${opt_def#*:}"
long="${rest%%:*}"
desc="${rest#*:}"
if [[ -n "$short" ]]; then
printf " -%s, --%-16s %s\n" "$short" "$long" "$desc"
else
printf " --%-16s %s\n" "$long" "$desc"
fi
done
echo ""
echo "Any extra arguments after -- are passed directly to Pelican."
}
# Build short and long getopt spec strings from OPTIONS array.
function _getopt_specs {
local short="" long=""
for opt_def in "${OPTIONS[@]}"; do
s="${opt_def%%:*}"
l="${opt_def#*:}"; l="${l%%:*}"
[[ -n "$s" ]] && short+="$s"
long+="${long:+,}${l}"
done
echo "${short}|${long}"
}
function build_image {
echo "Building local Docker image for Pelican, called $SOLR_LOCAL_PELICAN_IMAGE."
docker build --no-cache -t $SOLR_LOCAL_PELICAN_IMAGE .
}
function regen_lockfile {
ensure_image
echo "Regenerating requirements.txt from requirements.in inside Docker..."
docker run --rm -w /work -v "$(pwd):/work" $SOLR_LOCAL_PELICAN_IMAGE \
pip-compile --quiet --strip-extras --allow-unsafe --generate-hashes --output-file=requirements.txt requirements.in
echo "requirements.txt updated."
}
function ensure_image {
if ! docker inspect $SOLR_LOCAL_PELICAN_IMAGE >/dev/null 2>&1
then
build_image
fi
}
function check_requirements_update {
# Check requirements.txt — that is what is COPY'd into the Dockerfile
local req_mod_time
if [[ $(uname) == "Darwin" ]]; then
req_mod_time=$(stat -f "%m" requirements.txt)
else
req_mod_time=$(stat -c "%Y" requirements.txt)
fi
# Get the build timestamp of the docker image
local image_build_time
image_build_time=$(docker inspect --format='{{.Created}}' $SOLR_LOCAL_PELICAN_IMAGE)
# Parse the timestamp into seconds since epoch in UTC
if [[ $(uname) == "Darwin" ]]; then
# macOS date command workaround
image_build_time=$(echo "$image_build_time" | awk -F '.' '{print $1}')
image_build_time=$(date -ju -f "%Y-%m-%dT%H:%M:%S" "$image_build_time" "+%s")
else
# Linux date command
image_build_time=$(date -d "$(echo "$image_build_time" | cut -d'.' -f1 | sed 's/T/ /; s/Z//')" --utc "+%s")
fi
# Compare the timestamps and rebuild if requirements.txt is newer than the image
if [[ $req_mod_time -gt $image_build_time ]]; then
echo "requirements.txt has been updated since the last build, rebuilding image!"
build_image
fi
}
if ! docker -v >/dev/null 2>&1
then
echo "ERROR: This script requires docker."
echo " Please install Docker and try again."
echo
usage
exit 2
fi
# Handle a single parsed option. Returns 1 (exit loop) on --.
function handle_opt {
case "$1" in
-l|--live) SERVE=true ;;
-b|--build) BUILD=true ;;
--lock) LOCK=true ;;
-h|--help) usage; exit 0 ;;
--pelican-help) ensure_image; $DOCKER_CMD pelican -h; exit 0 ;;
--) return 1 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
}
# Use GNU getopt when available (detected via exit code 4 from getopt -T).
# GNU getopt gives proper error messages and handles --opt=value and option
# reordering. Falls back to a plain bash loop which handles all our flag options.
getopt -T &>/dev/null; _getopt_test=$?
if [[ $_getopt_test -eq 4 ]]; then
specs=$(_getopt_specs)
PARSED=$(getopt -o "${specs%%|*}" --long "${specs#*|}" -n "build.sh" -- "$@") || { usage; exit 1; }
eval set -- "$PARSED"
while true; do
if handle_opt "$1"; then shift; else shift; break; fi
done
else
while [[ $# -gt 0 ]]; do
if handle_opt "$1"; then shift; else shift; break; fi
done
fi
if [[ $LOCK ]]; then
regen_lockfile
if [[ ! $BUILD ]]; then
echo "Run './build.sh -b' to rebuild the Docker image with the updated lockfile."
exit 0
fi
fi
if [[ $BUILD ]]; then
build_image
else
ensure_image
check_requirements_update
fi
if [[ $SERVE ]]; then
echo "Building Solr site locally. Goto http://localhost:8000 to view."
echo "Edits you do to the source tree will be compiled immediately!"
$DOCKER_CMD sh -c "$PELICAN_CMD --autoreload --listen -b 0.0.0.0 $*"
else
echo "Building Solr site locally."
echo "To build and serve live edits locally, run this script with -l argument. Use -h for help."
$DOCKER_CMD sh -c "$PELICAN_CMD $*"
fi