Files
ccn/Scripts/bootstrap
T

214 lines
7.2 KiB
Bash
Raw Normal View History

#!/usr/bin/env sh
#
# bootstrap — turn this template into a concrete website.
#
# The template ships as a fully buildable reference site whose identity is the
# neutral placeholder "Site" / "site". This script rewrites those placeholders,
# in place, to the values you provide — then removes itself.
#
# Run once, from the repository root:
#
# ./Tools/bootstrap
#
# It is idempotent only in the sense that it self-destructs: once run, the
# placeholders are gone and the script (and its scaffolding) are deleted.
set -eu
# --- Preconditions ------------------------------------------------------------
if [ ! -d "Services/Website" ]; then
echo "error: run this from the repository root (Services/Website not found)." >&2
exit 1
fi
XCODEPROJ="./Site.xcodeproj"
# The placeholder origin the crawler files ship with. It is an RFC 2606 reserved
# domain, so an un-bootstrapped copy can never point a crawler at a real site.
PLACEHOLDER_URL="https://site.example.com"
PLACEHOLDER_URL_PATTERN='https://site\.example\.com'
# --- Prompts ------------------------------------------------------------------
printf 'Site display name (PascalCase, e.g. Berlin) [Site]: '
read -r NAME
NAME="${NAME:-Site}"
DEFAULT_SLUG="$(printf '%s' "$NAME" | tr '[:upper:]' '[:lower:]')"
printf 'Project slug (lowercase, used for owner/db/compose, e.g. loud-berlin) [%s]: ' "$DEFAULT_SLUG"
read -r SLUG
SLUG="${SLUG:-$DEFAULT_SLUG}"
printf 'Canonical site URL (scheme + host, e.g. https://berlin.example.com) [%s]: ' "$PLACEHOLDER_URL"
read -r URL
URL="${URL:-$PLACEHOLDER_URL}"
URL="${URL%/}" # the rewrites append their own path, so drop a trailing slash
# The URL is interpolated into a sed replacement (with `|` as the delimiter) and
# into the crawler files, so reject anything that is not a plain scheme + host.
case "$URL" in
http://* | https://*) ;;
*)
echo "error: the canonical site URL must start with http:// or https:// (got \"$URL\")." >&2
exit 1
;;
esac
case "$URL" in
*[!-A-Za-z0-9:/._~]*)
echo "error: the canonical site URL contains unexpected characters (got \"$URL\")." >&2
exit 1
;;
esac
echo
echo " display name : $NAME (server name \"${NAME}Website\", ${NAME}.xcodeproj)"
echo " slug : $SLUG (container owner, database name/user, compose project)"
echo " canonical URL: $URL (robots.txt sitemap reference, sitemap.xml entry)"
echo
printf 'Apply these values? [y/N]: '
read -r CONFIRM
case "$CONFIRM" in
y | Y | yes | YES) ;;
*)
echo "Aborted. Nothing was changed."
exit 1
;;
esac
# --- Helpers ------------------------------------------------------------------
# Portable in-place sed (works on both BSD/macOS and GNU/Linux).
rewrite() {
file="$1"
shift
[ -f "$file" ] || return 0
tmp="${file}.bootstrap.tmp"
sed "$@" "$file" >"$tmp" && mv "$tmp" "$file"
}
W="Services/Website"
# --- Rewrites -----------------------------------------------------------------
# Swift constants: server name + database name/username.
rewrite "$W/Sources/Library/Public/Extensions/String+Constants.swift" \
-e "s/SiteWebsite/${NAME}Website/g" \
-e "s/= \"site\"/= \"${SLUG}\"/g"
# Local env example.
rewrite "$W/.env.local" \
-e "s/SiteWebsite/${NAME}Website/g" \
-e "s/^HOST_OWNER=site\$/HOST_OWNER=${SLUG}/" \
-e "s/^DATABASE_NAME=site\$/DATABASE_NAME=${SLUG}/" \
-e "s/^DATABASE_USERNAME=site\$/DATABASE_USERNAME=${SLUG}/" \
-e "s/^DATABASE_PASSWORD=site\$/DATABASE_PASSWORD=${SLUG}/"
# Production compose.
rewrite "$W/docker-compose.yml" \
-e "s/SiteWebsite/${NAME}Website/g" \
-e "s/^name: site-platform\$/name: ${SLUG}-platform/" \
-e "s/DATABASE_NAME:-site}/DATABASE_NAME:-${SLUG}}/" \
-e "s/DATABASE_USERNAME:-site}/DATABASE_USERNAME:-${SLUG}}/"
# Local override compose.
rewrite "$W/docker-compose.override.yml" \
-e "s/HOST_OWNER:-site}/HOST_OWNER:-${SLUG}}/" \
-e "s/DATABASE_NAME:-site}/DATABASE_NAME:-${SLUG}}/" \
-e "s/DATABASE_USERNAME:-site}/DATABASE_USERNAME:-${SLUG}}/" \
-e "s/DATABASE_PASSWORD:-site}/DATABASE_PASSWORD:-${SLUG}}/"
# Makefile db-shell fallbacks.
rewrite "$W/Makefile" \
-e "s/),site)/),${SLUG})/g"
# Crawler files: the absolute origin they must carry. `|` is the sed delimiter,
# since both the placeholder and the replacement contain slashes.
rewrite "$W/Resources/Static/robots.txt" \
-e "s|${PLACEHOLDER_URL_PATTERN}|${URL}|g"
rewrite "$W/Resources/Static/sitemap.xml" \
-e "s|${PLACEHOLDER_URL_PATTERN}|${URL}|g"
# Service README.
rewrite "$W/README.md" \
-e "s/^# Site Website\$/# ${NAME} Website/" \
-e "s/\*\*Site\*\*/**${NAME}**/" \
-e "s/SiteWebsite/${NAME}Website/g" \
-e "s/\`site\`/\`${SLUG}\`/g"
# Xcode project: PBXProject name + directory.
if [ -d "$XCODEPROJ" ]; then
rewrite "$XCODEPROJ/project.pbxproj" -e "s/\"Site\"/\"${NAME}\"/g"
# Skip the rename when the display name was left as the placeholder: the
# source and destination are then the same path, and `mv dir dir` fails
# (it moves the directory into itself), which would abort the run here —
# after the rewrites, but before the self-cleanup.
XCODEPROJ_RENAMED="./${NAME}.xcodeproj"
if [ "$XCODEPROJ" != "$XCODEPROJ_RENAMED" ]; then
if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
git mv "$XCODEPROJ" "$XCODEPROJ_RENAMED" 2>/dev/null || mv "$XCODEPROJ" "$XCODEPROJ_RENAMED"
else
mv "$XCODEPROJ" "$XCODEPROJ_RENAMED"
fi
fi
fi
# --- Optional: fresh git history ---------------------------------------------
echo
printf 'Start a fresh git history (drops the template history)? [y/N]: '
read -r FRESH
case "$FRESH" in
y | Y | yes | YES)
if command -v git >/dev/null 2>&1; then
rm -rf .git
git init -q
echo "Reinitialised git. Remember to add your remote and commit."
fi
;;
*) ;;
esac
# --- Self-cleanup -------------------------------------------------------------
rm -f Scripts/bootstrap
rmdir Scripts 2>/dev/null || true
rm -f Makefile # root convenience Makefile (bootstrap only)
rm -f README.md # template usage docs
rm -rf .gitea # Gitea template metadata
# --- Done ---------------------------------------------------------------------
cat <<EOF
Done. "${NAME}" (slug "${SLUG}") is ready.
Next steps:
1. Replace the placeholder content:
- $W/Sources/Library/Catalogs/Localizable.xcstrings (copy)
- $W/Sources/Library (landing / 404 pages)
- $W/Resources/Static (css, js, favicon, icons)
- $W/Resources/Static/site.webmanifest (name / short_name)
2. Set a real database password in a git-ignored $W/.env
(the committed .env.local defaults the password to the slug — do NOT ship that).
3. Point the git remote at your new repository:
git remote set-url origin <new-repo-url> # or 'git remote add origin ...'
4. Build and run:
cd $W && make site-run
The Persistence package still ships an ExampleRecord / ExampleRepository sample
model — replace it with your real domain models when you add persistence.
EOF
if [ "$URL" = "$PLACEHOLDER_URL" ]; then
cat <<EOF
WARNING: the canonical site URL was left as the placeholder "${PLACEHOLDER_URL}".
Set the real origin in $W/Resources/Static/robots.txt and sitemap.xml before
going live, or crawlers will be pointed at a reserved example domain.
EOF
fi