diff --git a/Scripts/bootstrap b/Scripts/bootstrap index e5cfd71..eeb33fd 100755 --- a/Scripts/bootstrap +++ b/Scripts/bootstrap @@ -35,11 +35,46 @@ printf 'Site display name (PascalCase, e.g. Berlin) [Site]: ' read -r NAME NAME="${NAME:-Site}" +# The name is interpolated into sed replacements, a Swift string literal, the +# Xcode project's directory name and the pbxproj. In a replacement `&` stands for +# the whole match and `\` escapes, so either would corrupt the rewrites silently +# rather than fail them — reject anything but letters and digits. +case "$NAME" in + *[!A-Za-z0-9]*) + echo "error: the display name must be letters and digits only (got \"$NAME\")." >&2 + exit 1 + ;; +esac +case "$NAME" in + [A-Za-z]*) ;; + *) + echo "error: the display name must start with a letter (got \"$NAME\")." >&2 + exit 1 + ;; +esac + 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}" +# The slug carries the same sed hazard as the name, and additionally becomes the +# Compose project name and the PostgreSQL database name and role, which admit +# lowercase alphanumerics, hyphens and underscores alone. +case "$SLUG" in + *[!a-z0-9_-]*) + echo "error: the project slug must be lowercase letters, digits, hyphens or underscores (got \"$SLUG\")." >&2 + exit 1 + ;; +esac +case "$SLUG" in + [a-z0-9]*) ;; + *) + echo "error: the project slug must start with a lowercase letter or digit (got \"$SLUG\")." >&2 + exit 1 + ;; +esac + printf 'Canonical site URL (scheme + host, e.g. https://berlin.example.com) [%s]: ' "$PLACEHOLDER_URL" read -r URL URL="${URL:-$PLACEHOLDER_URL}" @@ -84,7 +119,12 @@ rewrite() { shift [ -f "$file" ] || return 0 tmp="${file}.bootstrap.tmp" - sed "$@" "$file" >"$tmp" && mv "$tmp" "$file" + if sed "$@" "$file" >"$tmp"; then + mv "$tmp" "$file" + else + rm -f "$tmp" + return 1 + fi } W="Services/Website"