230 lines
7.3 KiB
Bash
Executable file
230 lines
7.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -e
|
|
|
|
PROJECT="nomad"
|
|
PROJECT_URL="www.nomadproject.io"
|
|
FASTLY_SERVICE_ID="7GrxRJP3PVBuqQbyxYQ0MV"
|
|
FASTLY_DICTIONARY_ID="4OEpQ4S6HbEu7wkfTvrWUG"
|
|
REDIRECTS_FILE="./source/redirects.txt"
|
|
EXTERNAL_REDIRECTS_DICT_ID="7CPeY9SLVGuOfSkPgmBr93"
|
|
RELATIVE_REDIRECTS_DICT_ID="3tBkC2O6iOHqTuxxUtAdS1"
|
|
|
|
# This function posts a dictionary of key-value pairs for redirects to Fastly
|
|
function post_redirects {
|
|
# Arguments:
|
|
# $1 - jq_query
|
|
# $2 - dictionary_id
|
|
# $3 - jq_args
|
|
#
|
|
# Returns:
|
|
# 0 - success
|
|
# * - failure
|
|
|
|
declare -a arr=("${!3}")
|
|
|
|
# Do not post empty items (the API gets sad)
|
|
if [ "${#arr[@]}" -ne 0 ]; then
|
|
json="$(jq "${arr[@]}" "$1" <<<'{"items": []}')"
|
|
|
|
# Post the JSON body
|
|
curl \
|
|
--request "PATCH" \
|
|
--header "Fastly-Key: $FASTLY_API_KEY" \
|
|
--header "Content-type: application/json" \
|
|
--header "Accept: application/json" \
|
|
--data "$json"\
|
|
"https://api.fastly.com/service/$FASTLY_SERVICE_ID/dictionary/$2/items"
|
|
fi
|
|
}
|
|
|
|
# Ensure the proper AWS environment variables are set
|
|
if [ -z "$AWS_ACCESS_KEY_ID" ]; then
|
|
echo "Missing AWS_ACCESS_KEY_ID!"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
|
echo "Missing AWS_SECRET_ACCESS_KEY!"
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure the proper Fastly keys are set
|
|
if [ -z "$FASTLY_API_KEY" ]; then
|
|
echo "Missing FASTLY_API_KEY!"
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure we have s3cmd installed
|
|
if ! command -v "s3cmd" >/dev/null 2>&1; then
|
|
echo "Missing s3cmd!"
|
|
exit 1
|
|
fi
|
|
|
|
# Get the parent directory of where this script is and cd there
|
|
DIR="$(cd "$(dirname "$(readlink -f "$0")")/.." && pwd)"
|
|
|
|
# Delete any .DS_Store files for our OS X friends.
|
|
find "$DIR" -type f -name '.DS_Store' -delete
|
|
|
|
# Upload the files to S3 - we disable mime-type detection by the python library
|
|
# and just guess from the file extension because it's surprisingly more
|
|
# accurate, especially for CSS and javascript. We also tag the uploaded files
|
|
# with the proper Surrogate-Key, which we will later purge in our API call to
|
|
# Fastly.
|
|
if [ -z "$NO_UPLOAD" ]; then
|
|
echo "Uploading to S3..."
|
|
|
|
# Check that the site has been built
|
|
if [ ! -d "$DIR/build" ]; then
|
|
echo "Missing compiled website! Run 'make build' to compile!"
|
|
exit 1
|
|
fi
|
|
|
|
# Set browser-side cache-control to ~4h, but tell Fastly to cache for much
|
|
# longer. We manually purge the Fastly cache, so setting it to a year is more
|
|
# than fine.
|
|
s3cmd \
|
|
--quiet \
|
|
--delete-removed \
|
|
--guess-mime-type \
|
|
--no-mime-magic \
|
|
--acl-public \
|
|
--recursive \
|
|
--add-header="Cache-Control: max-age=14400" \
|
|
--add-header="x-amz-meta-surrogate-control: max-age=31536000, stale-white-revalidate=86400, stale-if-error=604800" \
|
|
--add-header="x-amz-meta-surrogate-key: site-$PROJECT" \
|
|
sync "$DIR/build/" "s3://hc-sites/$PROJECT/latest/"
|
|
|
|
# The s3cmd guessed mime type for text files is often wrong. This is
|
|
# problematic for some assets, so force their mime types to be correct.
|
|
echo "Overriding javascript mime-types..."
|
|
s3cmd \
|
|
--mime-type="application/javascript" \
|
|
--add-header="Cache-Control: max-age=31536000" \
|
|
--exclude "*" \
|
|
--include "*.js" \
|
|
--recursive \
|
|
modify "s3://hc-sites/$PROJECT/latest/"
|
|
|
|
echo "Overriding css mime-types..."
|
|
s3cmd \
|
|
--mime-type="text/css" \
|
|
--add-header="Cache-Control: max-age=31536000" \
|
|
--exclude "*" \
|
|
--include "*.css" \
|
|
--recursive \
|
|
modify "s3://hc-sites/$PROJECT/latest/"
|
|
|
|
echo "Overriding svg mime-types..."
|
|
s3cmd \
|
|
--mime-type="image/svg+xml" \
|
|
--add-header="Cache-Control: max-age=31536000" \
|
|
--exclude "*" \
|
|
--include "*.svg" \
|
|
--recursive \
|
|
modify "s3://hc-sites/$PROJECT/latest/"
|
|
fi
|
|
|
|
# Add redirects if they exist
|
|
# By default, the redirects file is in the source/ directory
|
|
|
|
if [ -z "$NO_REDIRECTS" ] || [ ! test -f "$REDIRECTS_FILE" ]; then
|
|
echo "Adding redirects..."
|
|
fields=()
|
|
while read -r line; do
|
|
[[ "$line" =~ ^#.* ]] && continue
|
|
[[ -z "$line" ]] && continue
|
|
|
|
# Read fields
|
|
IFS=" " read -ra parts <<<"$line"
|
|
fields+=("${parts[@]}")
|
|
done < "$REDIRECTS_FILE"
|
|
|
|
# Check we have pairs
|
|
if [ $((${#fields[@]} % 2)) -ne 0 ]; then
|
|
echo "Bad redirects (not an even number)!"
|
|
exit 1
|
|
fi
|
|
|
|
# Check we don't have more than 1000 entries (yes, it says 2000 below, but that
|
|
# is because we've split into multiple lines).
|
|
if [ "${#fields}" -gt 2000 ]; then
|
|
echo "More than 1000 entries!"
|
|
exit 1
|
|
fi
|
|
|
|
# Validations
|
|
for field in "${fields[@]}"; do
|
|
if [ "${#field}" -gt 256 ]; then
|
|
echo "'$field' is > 256 characters!"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Build the payload for single-request updates.
|
|
jq_args_external=()
|
|
jq_query_external="."
|
|
jq_args_relative=()
|
|
jq_query_relative="."
|
|
for (( i=0; i<${#fields[@]}; i+=2 )); do
|
|
# if the redirect is external, add the entries to the external list
|
|
if [[ "${fields[i+1]}" =~ http[s]*:\/\/ ]]; then
|
|
external_original="${fields[i]}"
|
|
external_redirect="${fields[i+1]}"
|
|
echo "Redirecting external ${external_original} -> ${external_redirect}"
|
|
# The key and value indexes are for the whole redirects file so it may not be contiguous
|
|
# This doesn't matter at the end as long as the query matches the same key/value items
|
|
jq_args_external+=(--arg "key$((i/2))" "${external_original}")
|
|
jq_args_external+=(--arg "value$((i/2))" "${external_redirect}")
|
|
jq_query_external+="| .items |= (. + [{op: \"upsert\", item_key: \$key$((i/2)), item_value: \$value$((i/2))}])"
|
|
else
|
|
relative_original="${fields[i]}"
|
|
relative_redirect="${fields[i+1]}"
|
|
echo "Redirecting relative ${relative_original} -> ${relative_redirect}"
|
|
# The key and value indexes are for the whole redirects file so it may not be contiguous
|
|
# This doesn't matter at the end as long as the query matches the same key/value items
|
|
jq_args_relative+=(--arg "key$((i/2))" "${relative_original}")
|
|
jq_args_relative+=(--arg "value$((i/2))" "${relative_redirect}")
|
|
jq_query_relative+="| .items |= (. + [{op: \"upsert\", item_key: \$key$((i/2)), item_value: \$value$((i/2))}])"
|
|
fi
|
|
done
|
|
post_redirects "$jq_query_external" $EXTERNAL_REDIRECTS_DICT_ID jq_args_external[@]
|
|
post_redirects "$jq_query_relative" $RELATIVE_REDIRECTS_DICT_ID jq_args_relative[@]
|
|
fi
|
|
|
|
# Perform a purge of the surrogate key.
|
|
if [ -z "$NO_PURGE" ]; then
|
|
echo "Purging Fastly cache..."
|
|
curl \
|
|
--fail \
|
|
--silent \
|
|
--output /dev/null \
|
|
--request "POST" \
|
|
--header "Accept: application/json" \
|
|
--header "Fastly-Key: $FASTLY_API_KEY" \
|
|
--header "Fastly-Soft-Purge: 1" \
|
|
"https://api.fastly.com/service/$FASTLY_SERVICE_ID/purge/site-$PROJECT"
|
|
fi
|
|
|
|
# Warm the cache with recursive wget.
|
|
if [ -z "$NO_WARM" ]; then
|
|
echo "Warming Fastly cache..."
|
|
echo ""
|
|
echo "If this step fails, there are likely missing or broken assets or links"
|
|
echo "on the website. Run the following command manually on your laptop, and"
|
|
echo "search for \"ERROR\" in the output:"
|
|
echo ""
|
|
echo "wget --recursive --delete-after https://$PROJECT_URL/"
|
|
echo ""
|
|
wget \
|
|
--delete-after \
|
|
--level inf \
|
|
--no-directories \
|
|
--no-host-directories \
|
|
--no-verbose \
|
|
--page-requisites \
|
|
--recursive \
|
|
--spider \
|
|
"https://$PROJECT_URL/"
|
|
fi
|