feature: Initial commit

This commit is contained in:
Keith Solomon
2025-10-25 16:55:22 -05:00
commit 3e779343b2
8 changed files with 642 additions and 0 deletions

284
wp-bootstrap.sh Normal file
View File

@@ -0,0 +1,284 @@
#!/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"