✨feature: Enhance bootstrap scripts with improved configuration handling and plugin management
This commit is contained in:
281
wp-bootstrap.sh
281
wp-bootstrap.sh
@@ -1,10 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
export WP_CLI_STRICT_ARGS_MODE=1
|
||||
|
||||
# Load machine/user defaults
|
||||
if [[ -f "$HOME/.vdi-wp-bootstraprc" ]]; then
|
||||
if [[ -f "$HOME/.wp-bootstraprc" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$HOME/.vdi-wp-bootstraprc"
|
||||
source "$HOME/.wp-bootstraprc"
|
||||
fi
|
||||
|
||||
# Sensible fallbacks if config missing
|
||||
@@ -23,9 +26,52 @@ require() {
|
||||
command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1"; exit 1; }
|
||||
}
|
||||
|
||||
for bin in wp git curl openssl mysql; do
|
||||
PHP_BIN="${WP_PHP_BIN:-php}"
|
||||
|
||||
for bin in git curl openssl mysql composer npm; do
|
||||
require "$bin"
|
||||
done
|
||||
require "$PHP_BIN"
|
||||
|
||||
if command -v wp >/dev/null 2>&1; then
|
||||
WP_CLI_CMD=(wp)
|
||||
else
|
||||
WP_CLI_PHAR_DEFAULT="$SCRIPT_DIR/wp-cli.phar"
|
||||
if [[ ! -f "$WP_CLI_PHAR_DEFAULT" && -f "$SCRIPT_DIR/windows/wp-cli.phar" ]]; then
|
||||
WP_CLI_PHAR_DEFAULT="$SCRIPT_DIR/windows/wp-cli.phar"
|
||||
fi
|
||||
WP_CLI_PHAR="${WP_CLI_PHAR:-$WP_CLI_PHAR_DEFAULT}"
|
||||
if [[ -f "$WP_CLI_PHAR" ]]; then
|
||||
WP_CLI_CMD=("$PHP_BIN" -f "$WP_CLI_PHAR" --)
|
||||
else
|
||||
echo "Missing dependency: wp (command) or wp-cli.phar (set WP_CLI_PHAR)." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
invoke_wp() {
|
||||
local args=("$@")
|
||||
WP_CLI_STRICT_ARGS_MODE=1 "${WP_CLI_CMD[@]}" --path="$PROJECT_DIR" "${args[@]}"
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: wp-bootstrap.sh --project-name "Example Site" [options]
|
||||
|
||||
Options:
|
||||
--project-name NAME Human-friendly project name (required)
|
||||
--admin-user USER WordPress admin username
|
||||
--admin-email EMAIL WordPress admin email
|
||||
--theme-starter-repo URL Git URL for the starter theme
|
||||
--herd-workspace PATH Herd workspace directory
|
||||
--local-tld TLD Local development TLD (e.g., test)
|
||||
--mysql-host HOST MySQL host
|
||||
--mysql-port PORT MySQL port
|
||||
--mysql-root-user USER MySQL root username
|
||||
--mysql-root-pass PASS MySQL root password
|
||||
--help Show this help and exit
|
||||
USAGE
|
||||
}
|
||||
|
||||
slugify() {
|
||||
# lower, spaces->-, strip non [a-z0-9-]
|
||||
@@ -50,33 +96,33 @@ prompt() {
|
||||
}
|
||||
|
||||
setup_pages_and_reading() {
|
||||
echo "Setting up default pages and reading options…"
|
||||
echo "Setting up default pages and reading options..."
|
||||
|
||||
HOME_ID=$(wp post list --post_type=page --name='home' --field=ID --format=ids)
|
||||
HOME_ID=$(invoke_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)
|
||||
HOME_ID=$(invoke_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)
|
||||
NEWS_ID=$(invoke_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)
|
||||
NEWS_ID=$(invoke_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
|
||||
ID=$(invoke_wp post list --post_type=page --name="$SLUG" --field=ID --format=ids)
|
||||
[[ -z "$ID" ]] && invoke_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"
|
||||
invoke_wp option update show_on_front 'page'
|
||||
invoke_wp option update page_on_front "$HOME_ID"
|
||||
invoke_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
|
||||
invoke_wp post delete 1 --force >/dev/null 2>&1 || true
|
||||
invoke_wp post delete 2 --force >/dev/null 2>&1 || true
|
||||
|
||||
wp rewrite structure '/%postname%/'
|
||||
wp rewrite flush --hard
|
||||
invoke_wp rewrite structure '/%postname%/'
|
||||
invoke_wp rewrite flush --hard
|
||||
echo "Default pages and reading options configured."
|
||||
}
|
||||
|
||||
@@ -84,9 +130,19 @@ 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
|
||||
local plugins_path=""
|
||||
if [[ -f "$PROJECT_DIR/plugins.json" ]]; then
|
||||
plugins_path="$PROJECT_DIR/plugins.json"
|
||||
elif [[ -f "$SCRIPT_DIR/plugins.json" ]]; then
|
||||
plugins_path="$SCRIPT_DIR/plugins.json"
|
||||
fi
|
||||
|
||||
if [[ -n "$plugins_path" ]]; then
|
||||
local pretty_path="$plugins_path"
|
||||
[[ "$pretty_path" == "$PROJECT_DIR/"* ]] && pretty_path="plugins.json"
|
||||
echo "Installing plugins from $pretty_path..." | tee -a "$LOG"
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
jq -c '.[]' plugins.json | while read -r item; do
|
||||
jq -c '.[]' "$plugins_path" | 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')
|
||||
@@ -94,17 +150,19 @@ install_and_activate_plugins() {
|
||||
|
||||
if [[ -n "$ZIP" ]]; then
|
||||
echo "Installing from zip: $ZIP" | tee -a "$LOG"
|
||||
if wp plugin install "$ZIP" --force $( [[ "$ACT" == "true" ]] && echo --activate ); then
|
||||
local args=(plugin install "$ZIP" --force)
|
||||
[[ "$ACT" == "true" ]] && args+=(--activate)
|
||||
if invoke_wp "${args[@]}"; 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")
|
||||
local 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
|
||||
if invoke_wp "${args[@]}"; then
|
||||
echo "OK slug: $SLUG (activate=$ACT)" | tee -a "$LOG"
|
||||
else
|
||||
echo "FAIL slug: $SLUG" | tee -a "$LOG"
|
||||
@@ -114,7 +172,7 @@ install_and_activate_plugins() {
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "plugins.json found but jq is not installed; skipping plugin install." | tee -a "$LOG"
|
||||
echo "$pretty_path 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"
|
||||
@@ -123,8 +181,81 @@ install_and_activate_plugins() {
|
||||
echo "=== $(date -u '+%F %T') :: Plugin bootstrap end ===" | tee -a "$LOG"
|
||||
}
|
||||
|
||||
echo "— VDI WP Bootstrap (Herd + DBngin) —"
|
||||
PROJECT_NAME="$(prompt 'Project name (Human-readable)')"
|
||||
PROJECT_NAME_CLI=""
|
||||
ADMIN_USER_CLI=""
|
||||
ADMIN_EMAIL_CLI=""
|
||||
THEME_STARTER_REPO_FLAG=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--project-name)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --project-name" >&2; usage >&2; exit 1; }
|
||||
PROJECT_NAME_CLI="$2"
|
||||
shift 2
|
||||
;;
|
||||
--admin-user)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --admin-user" >&2; usage >&2; exit 1; }
|
||||
ADMIN_USER_CLI="$2"
|
||||
shift 2
|
||||
;;
|
||||
--admin-email)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --admin-email" >&2; usage >&2; exit 1; }
|
||||
ADMIN_EMAIL_CLI="$2"
|
||||
shift 2
|
||||
;;
|
||||
--theme-starter-repo)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --theme-starter-repo" >&2; usage >&2; exit 1; }
|
||||
THEME_STARTER_REPO="$2"
|
||||
THEME_STARTER_REPO_FLAG=1
|
||||
shift 2
|
||||
;;
|
||||
--herd-workspace)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --herd-workspace" >&2; usage >&2; exit 1; }
|
||||
HERD_WORKSPACE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--local-tld)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --local-tld" >&2; usage >&2; exit 1; }
|
||||
LOCAL_TLD="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mysql-host)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --mysql-host" >&2; usage >&2; exit 1; }
|
||||
MYSQL_HOST="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mysql-port)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --mysql-port" >&2; usage >&2; exit 1; }
|
||||
MYSQL_PORT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mysql-root-user)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --mysql-root-user" >&2; usage >&2; exit 1; }
|
||||
MYSQL_ROOT_USER="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mysql-root-pass)
|
||||
[[ $# -lt 2 ]] && { echo "Missing value for --mysql-root-pass" >&2; usage >&2; exit 1; }
|
||||
MYSQL_ROOT_PASS="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
echo "VDI WP Bootstrap (Herd + DBngin)"
|
||||
|
||||
PROJECT_NAME="${PROJECT_NAME_CLI:-}"
|
||||
if [[ -z "$PROJECT_NAME" ]]; then
|
||||
PROJECT_NAME="$(prompt 'Project name (Human-readable)')"
|
||||
fi
|
||||
[[ -z "$PROJECT_NAME" ]] && { echo "Project name is required."; exit 1; }
|
||||
|
||||
PROJECT_SLUG="$(slugify "$PROJECT_NAME")"
|
||||
@@ -138,70 +269,115 @@ 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")"
|
||||
if [[ -n "$ADMIN_USER_CLI" ]]; then
|
||||
ADMIN_USER="$ADMIN_USER_CLI"
|
||||
else
|
||||
ADMIN_USER="$(prompt 'Admin username' "$DEFAULT_ADMIN_USER")"
|
||||
fi
|
||||
|
||||
if [[ -n "$ADMIN_EMAIL_CLI" ]]; then
|
||||
ADMIN_EMAIL="$ADMIN_EMAIL_CLI"
|
||||
else
|
||||
ADMIN_EMAIL="$(prompt 'Admin email' "$DEFAULT_ADMIN_EMAIL")"
|
||||
fi
|
||||
|
||||
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")"
|
||||
if (( THEME_STARTER_REPO_FLAG )); then
|
||||
THEME_REPO_URL="$THEME_STARTER_REPO"
|
||||
else
|
||||
THEME_REPO_URL="$(prompt 'Theme starter repo URL' "$THEME_STARTER_REPO")"
|
||||
fi
|
||||
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…"
|
||||
echo "Creating project directory..."
|
||||
mkdir -p "$PROJECT_DIR"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
echo "Downloading WordPress core…"
|
||||
wp core download --force
|
||||
echo "Downloading WordPress core..."
|
||||
invoke_wp core download --force
|
||||
|
||||
echo "Creating database and user in MySQL ($MYSQL_HOST:$MYSQL_PORT)…"
|
||||
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
|
||||
|
||||
if ! mysql "${MYSQL_AUTH[@]}" -e "SELECT VERSION();" >/dev/null 2>&1; then
|
||||
echo "Cannot connect to MySQL as root (${MYSQL_ROOT_USER}@${MYSQL_HOST}:${MYSQL_PORT})." >&2
|
||||
exit 1
|
||||
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'@'localhost' IDENTIFIED BY '$DB_PASS';
|
||||
CREATE USER IF NOT EXISTS '$DB_USER'@'127.0.0.1' IDENTIFIED BY '$DB_PASS';
|
||||
CREATE USER IF NOT EXISTS '$DB_USER'@'%' IDENTIFIED BY '$DB_PASS';
|
||||
GRANT ALL PRIVILEGES ON \`$DB_NAME\`.* TO '$DB_USER'@'localhost';
|
||||
GRANT ALL PRIVILEGES ON \`$DB_NAME\`.* TO '$DB_USER'@'127.0.0.1';
|
||||
GRANT ALL PRIVILEGES ON \`$DB_NAME\`.* TO '$DB_USER'@'%';
|
||||
FLUSH PRIVILEGES;
|
||||
SQL
|
||||
|
||||
echo "Generating wp-config.php…"
|
||||
wp config create \
|
||||
if ! mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$DB_USER" -p"$DB_PASS" -D "$DB_NAME" -e "SELECT 1;" >/dev/null 2>&1; then
|
||||
echo "New MySQL user '$DB_USER' cannot access database '$DB_NAME' at ${MYSQL_HOST}:${MYSQL_PORT}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Generating wp-config.php..."
|
||||
invoke_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
|
||||
if invoke_wp config shuffle-salts >/dev/null 2>&1; then
|
||||
echo "Salt keys shuffled."
|
||||
else
|
||||
echo "Fetching salts from api.wordpress.org…"
|
||||
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");'
|
||||
invoke_wp config set AUTH_KEY "dummy" --type=constant --raw >/dev/null 2>&1 || true
|
||||
"$PHP_BIN" -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 \
|
||||
echo "Installing WordPress..."
|
||||
invoke_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"
|
||||
invoke_wp option update siteurl "$LOCAL_URL"
|
||||
invoke_wp option update home "$LOCAL_URL"
|
||||
|
||||
setup_pages_and_reading
|
||||
|
||||
echo "Cloning theme starter (history will be stripped)…"
|
||||
HTACCESS_FILE="$PROJECT_DIR/.htaccess"
|
||||
if [[ ! -f "$HTACCESS_FILE" ]]; then
|
||||
cat >"$HTACCESS_FILE" <<'HTACCESS'
|
||||
# BEGIN WordPress
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.php [L]
|
||||
</IfModule>
|
||||
# END WordPress
|
||||
HTACCESS
|
||||
echo "Created default .htaccess"
|
||||
fi
|
||||
|
||||
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"
|
||||
@@ -217,15 +393,22 @@ if [[ -f "$THEME_DIR/style.css" ]]; then
|
||||
rm -f "$THEME_DIR/style.css.bak"
|
||||
fi
|
||||
|
||||
echo "Activating theme…"
|
||||
wp theme activate "${PROJECT_SLUG}-theme" || {
|
||||
echo "Installing theme dependencies and building assets..."
|
||||
pushd "$THEME_DIR" >/dev/null
|
||||
composer install
|
||||
npm install
|
||||
npm run build
|
||||
popd >/dev/null
|
||||
|
||||
echo "Activating theme..."
|
||||
invoke_wp theme activate "${PROJECT_SLUG}-theme" || {
|
||||
echo "Activation failed (maybe theme slug mismatch). Listing themes:"
|
||||
wp theme list
|
||||
invoke_wp theme list
|
||||
}
|
||||
|
||||
install_and_activate_plugins
|
||||
|
||||
echo "Initializing theme repo…"
|
||||
echo "Initializing theme repo..."
|
||||
pushd "$THEME_DIR" >/dev/null
|
||||
git init -b main
|
||||
git add -A
|
||||
|
||||
Reference in New Issue
Block a user