Files
Easy-WP/wp-bootstrap.sh
2025-10-25 16:55:22 -05:00

285 lines
8.8 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
# Load machine/user defaults
if [[ -f "$HOME/.vdi-wp-bootstraprc" ]]; then
# shellcheck disable=SC1090
source "$HOME/.vdi-wp-bootstraprc"
fi
# Sensible fallbacks if config missing
HERD_WORKSPACE="${HERD_WORKSPACE:-$HOME/Herd}"
MYSQL_HOST="${MYSQL_HOST:-127.0.0.1}"
MYSQL_PORT="${MYSQL_PORT:-3306}"
MYSQL_ROOT_USER="${MYSQL_ROOT_USER:-root}"
MYSQL_ROOT_PASS="${MYSQL_ROOT_PASS:-}"
DEFAULT_ADMIN_EMAIL="${DEFAULT_ADMIN_EMAIL:-dev@vincentdesign.ca}"
DEFAULT_ADMIN_USER="${DEFAULT_ADMIN_USER:-vdidev}"
THEME_STARTER_REPO="${THEME_STARTER_REPO:-https://github.com/WordPress/twentytwentyfour.git}"
DEFAULT_THEME_REMOTE_ORIGIN="${DEFAULT_THEME_REMOTE_ORIGIN:-}"
LOCAL_TLD="${LOCAL_TLD:-test}"
require() {
command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1"; exit 1; }
}
for bin in wp git curl openssl mysql; do
require "$bin"
done
slugify() {
# lower, spaces->-, strip non [a-z0-9-]
echo "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g;s/^-+|-+$//g'
}
randpass() {
# 24 char base64, strip non-url-safe chars
openssl rand -base64 24 | tr -d '\n' | tr -d '=/+' | cut -c1-24
}
prompt() {
local q def ans
q="$1"; def="${2:-}"
if [[ -n "$def" ]]; then
read -r -p "$q [$def]: " ans || true
echo "${ans:-$def}"
else
read -r -p "$q: " ans || true
echo "$ans"
fi
}
setup_pages_and_reading() {
echo "Setting up default pages and reading options…"
HOME_ID=$(wp post list --post_type=page --name='home' --field=ID --format=ids)
if [[ -z "$HOME_ID" ]]; then
HOME_ID=$(wp post create --post_type=page --post_status=publish --post_title="Home" --porcelain)
fi
NEWS_ID=$(wp post list --post_type=page --name='news' --field=ID --format=ids)
if [[ -z "$NEWS_ID" ]]; then
NEWS_ID=$(wp post create --post_type=page --post_status=publish --post_title="News" --porcelain)
fi
for TITLE in "Page Not Found (Error 404)" "Contact Us"; do
SLUG="$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g;s/^-+|-+$//g')"
ID=$(wp post list --post_type=page --name="$SLUG" --field=ID --format=ids)
[[ -z "$ID" ]] && wp post create --post_type=page --post_status=publish --post_title="$TITLE" --porcelain >/dev/null
done
wp option update show_on_front 'page'
wp option update page_on_front "$HOME_ID"
wp option update page_for_posts "$NEWS_ID"
wp post delete 1 --force >/dev/null 2>&1 || true
wp post delete 2 --force >/dev/null 2>&1 || true
wp rewrite structure '/%postname%/'
wp rewrite flush --hard
echo "Default pages and reading options configured."
}
install_and_activate_plugins() {
local LOG="wp-content/plugin-bootstrap.log"
echo "=== $(date -u '+%F %T') :: Plugin bootstrap start ===" | tee -a "$LOG"
if [[ -f "plugins.json" ]]; then
if command -v jq >/dev/null 2>&1; then
jq -c '.[]' plugins.json | while read -r item; do
ZIP=$(echo "$item" | jq -r '.zip // empty')
SLUG=$(echo "$item" | jq -r '.slug // empty')
VER=$(echo "$item" | jq -r '.version // empty')
ACT=$(echo "$item" | jq -r '.activate // false')
if [[ -n "$ZIP" ]]; then
echo "Installing from zip: $ZIP" | tee -a "$LOG"
if wp plugin install "$ZIP" --force $( [[ "$ACT" == "true" ]] && echo --activate ); then
echo "OK zip: $ZIP (activate=$ACT)" | tee -a "$LOG"
else
echo "FAIL zip: $ZIP" | tee -a "$LOG"
fi
elif [[ -n "$SLUG" ]]; then
ARGS=(plugin install "$SLUG" --force)
[[ -n "$VER" ]] && ARGS+=("--version=$VER")
[[ "$ACT" == "true" ]] && ARGS+=("--activate")
echo "Installing slug: $SLUG ${VER:+(v$VER)}" | tee -a "$LOG"
if wp "${ARGS[@]}"; then
echo "OK slug: $SLUG (activate=$ACT)" | tee -a "$LOG"
else
echo "FAIL slug: $SLUG" | tee -a "$LOG"
fi
else
echo "SKIP item with no slug/zip: $item" | tee -a "$LOG"
fi
done
else
echo "plugins.json found but jq is not installed; skipping plugin install." | tee -a "$LOG"
fi
else
echo "No plugins.json present; skipping plugin install." | tee -a "$LOG"
fi
echo "=== $(date -u '+%F %T') :: Plugin bootstrap end ===" | tee -a "$LOG"
}
echo "— VDI WP Bootstrap (Herd + DBngin) —"
PROJECT_NAME="$(prompt 'Project name (Human-readable)')"
[[ -z "$PROJECT_NAME" ]] && { echo "Project name is required."; exit 1; }
PROJECT_SLUG="$(slugify "$PROJECT_NAME")"
FOLDER_NAME="$PROJECT_SLUG"
PROJECT_DIR="$HERD_WORKSPACE/$FOLDER_NAME"
LOCAL_URL="http://$PROJECT_SLUG.$LOCAL_TLD"
echo "Derived:"
echo " slug: $PROJECT_SLUG"
echo " path: $PROJECT_DIR"
echo " local URL: $LOCAL_URL"
echo
ADMIN_USER="$(prompt 'Admin username' "$DEFAULT_ADMIN_USER")"
ADMIN_EMAIL="$(prompt 'Admin email' "$DEFAULT_ADMIN_EMAIL")"
ADMIN_PASS="$(randpass)"
DB_NAME="vdi_${PROJECT_SLUG}"
DB_USER="$DB_NAME"
DB_PASS="$(randpass)"
THEME_REPO_URL="$(prompt 'Theme starter repo URL' "$THEME_STARTER_REPO")"
THEME_DIR="wp-content/themes/${PROJECT_SLUG}-theme"
THEME_REMOTE_ORIGIN="$(prompt 'New theme repo remote (origin) URL (leave blank to skip push)' "$DEFAULT_THEME_REMOTE_ORIGIN")"
echo
echo "Creating project directory…"
mkdir -p "$PROJECT_DIR"
cd "$PROJECT_DIR"
echo "Downloading WordPress core…"
wp core download --force
echo "Creating database and user in MySQL ($MYSQL_HOST:$MYSQL_PORT)…"
MYSQL_AUTH=(-h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_ROOT_USER")
if [[ -n "$MYSQL_ROOT_PASS" ]]; then
MYSQL_AUTH+=(-p"$MYSQL_ROOT_PASS")
fi
mysql "${MYSQL_AUTH[@]}" <<SQL
CREATE DATABASE IF NOT EXISTS \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '$DB_USER'@'%' IDENTIFIED BY '$DB_PASS';
GRANT ALL PRIVILEGES ON \`$DB_NAME\`.* TO '$DB_USER'@'%';
FLUSH PRIVILEGES;
SQL
echo "Generating wp-config.php…"
wp config create \
--dbname="$DB_NAME" \
--dbuser="$DB_USER" \
--dbpass="$DB_PASS" \
--dbhost="${MYSQL_HOST}:${MYSQL_PORT}" \
--force
if wp config shuffle-salts >/dev/null 2>&1; then
echo "Salt keys shuffled."
else
echo "Fetching salts from api.wordpress.org…"
SALTS="$(curl -fsSL https://api.wordpress.org/secret-key/1.1/salt/)"
wp config set AUTH_KEY "dummy" --type=constant --raw >/dev/null 2>&1 || true
php -r 'file_put_contents("wp-config.php", preg_replace("/\\?>\\s*$/","",file_get_contents("wp-config.php"))."\n".'"'"$SALTS"'"'."\n");'
fi
echo "Installing WordPress…"
wp core install \
--url="$LOCAL_URL" \
--title="$PROJECT_NAME" \
--admin_user="$ADMIN_USER" \
--admin_password="$ADMIN_PASS" \
--admin_email="$ADMIN_EMAIL"
wp option update siteurl "$LOCAL_URL"
wp option update home "$LOCAL_URL"
setup_pages_and_reading
echo "Cloning theme starter (history will be stripped)…"
TMP_DIR=".starter-tmp"
rm -rf "$TMP_DIR"
git clone --depth=1 "$THEME_REPO_URL" "$TMP_DIR"
mkdir -p "$(dirname "$THEME_DIR")"
rm -rf "$THEME_DIR"
mv "$TMP_DIR" "$THEME_DIR"
rm -rf "$THEME_DIR/.git"
if [[ -f "$THEME_DIR/style.css" ]]; then
sed -i.bak "s/Theme Name:.*/Theme Name: ${PROJECT_NAME}/" "$THEME_DIR/style.css" || true
sed -i.bak "s/Text Domain:.*/Text Domain: ${PROJECT_SLUG}-theme/" "$THEME_DIR/style.css" || true
rm -f "$THEME_DIR/style.css.bak"
fi
echo "Activating theme…"
wp theme activate "${PROJECT_SLUG}-theme" || {
echo "Activation failed (maybe theme slug mismatch). Listing themes:"
wp theme list
}
install_and_activate_plugins
echo "Initializing theme repo…"
pushd "$THEME_DIR" >/dev/null
git init -b main
git add -A
git commit -m "feat: bootstrap ${PROJECT_NAME} theme from starter"
if [[ -n "$THEME_REMOTE_ORIGIN" ]]; then
git remote add origin "$THEME_REMOTE_ORIGIN"
git push -u origin main
fi
popd >/dev/null
create_wpengine_staging() {
if [[ -z "${WPE_API_TOKEN:-}" ]]; then
echo "WPE_API_TOKEN not set; skipping staging creation."
return
fi
echo "Stub: Add WPE API call here to create staging for ${PROJECT_SLUG}."
}
create_wpengine_staging
SUMMARY_FILE="$PROJECT_DIR/bootstrap-summary.txt"
cat > "$SUMMARY_FILE" <<TXT
VDI WP Bootstrap — Summary
===========================
Project: $PROJECT_NAME
Slug: $PROJECT_SLUG
Folder: $PROJECT_DIR
Local URL: $LOCAL_URL
DB Host: $MYSQL_HOST
DB Port: $MYSQL_PORT
DB Name: $DB_NAME
DB User: $DB_USER
DB Pass: $DB_PASS
WP Admin User: $ADMIN_USER
WP Admin Pass: $ADMIN_PASS
WP Admin Email: $ADMIN_EMAIL
Theme Dir: $THEME_DIR
Theme Remote: ${THEME_REMOTE_ORIGIN:-<none>}
Log files:
- Plugin install log: wp-content/plugin-bootstrap.log
Next steps:
- If Herd doesnt auto-serve the folder, link it via Herd UI/CLI and open $LOCAL_URL
- Remove legacy activation.php from the starter theme if present (now handled by bootstrap)
- Wire CI/CD as needed (never push DB to production)
TXT
echo
echo "✅ Bootstrap complete."
echo "Summary saved to: $SUMMARY_FILE"
echo
cat "$SUMMARY_FILE"