diff --git a/README.md b/README.md index 5aa5d17..66a43b0 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,90 @@ # Easy WordPress Bootstrap -Easily set up a new WordPress dev environment utilizing Laravel Herd and DBngin (or other MySQL/MariaDB solutions). +Spin up a complete WordPress development site on macOS with Laravel Herd and DBngin (or any MySQL/MariaDB server) in one pass. For the Windows/PowerShell workflow, see [`windows/README-Windows.md`](windows/README-Windows.md). -One command to spin up a new WordPress project locally: +## What You Get -- Creates a project folder in your Herd workspace -- Provisions MySQL DB + user via DBngin or other MySQL/MariaDB solution. -- Generates wp-config.php with salts -- Installs WP and sets default options (Home/News, other useful pages, permalinks) -- Installs/activates plugins from `plugins.json` -- Clones theme starter (with fresh history), activates it, and initializes a clean repo +- Project directory under your Herd workspace with sanitized slug and host mapping. +- MySQL database + user created after verifying the supplied root credentials. +- Latest WordPress core, `wp-config.php` with fresh salts, permalinks set to `/%postname%/`, and Home/News pages. +- Plugin install/activation from `plugins.json`, resolved from the project folder first then the repository default. +- Starter theme cloned with shallow history, `.git` stripped, repo reinitialized, remote optionally set, and Composer/NPM build steps run when available. +- `.htaccess` seeded when Apache rewrites are missing, `bootstrap-summary.txt` written, and plugin install logs stored at `wp-content/plugin-bootstrap.log`. +- Automatic WP-CLI detection with a bundled `wp-cli.phar` fallback (`WP_CLI_PHAR` env/config override). ## Prerequisites -- macOS or [Windows](windows/README-Windows.md) (PowerShell) -- Herd installed and a workspace directory (default: `~/Herd`) -- DBngin or other MySQL/MariaDB server -- CLI tools in PATH: `wp`, `git`, `curl`, `openssl`, `mysql`, and optionally `jq` +- macOS with [Laravel Herd](https://herd.laravel.com/) configured (default workspace `~/Herd`). +- MySQL or MariaDB available locally (DBngin recommended) and the `mysql` client in `PATH`. +- CLI tools: `php`, `git`, `curl`, `openssl`, `composer`, `npm`, `mysql`, and optionally `jq` for pretty plugin output. +- Either `wp` in `PATH` or a downloadable `wp-cli.phar` placed beside the script (or in `windows/wp-cli.phar`). -## Quick Start +## Quick Start (Interactive) -1. Copy `wp-bootstraprc.example` to `~/.wp-bootstraprc` and edit values (one-time setup): +1. Copy the sample configuration (one time): ```bash cp wp-bootstraprc.example ~/.wp-bootstraprc ``` -2. (Optional) Edit `plugins.json`. - -3. Run the bootstrap: +2. Review `plugins.json` (optional override per project; place your own copy in the project root if needed). +3. Make the script executable, then run it and answer prompts: ```bash chmod +x wp-bootstrap.sh ./wp-bootstrap.sh ``` -4. Answer prompts. When finished, the script prints a summary and writes `bootstrap-summary.txt`. +When the bootstrap completes, open the URL printed in the summary and check `bootstrap-summary.txt` under the project root. -## Configuration +## Non-Interactive Usage -- **Herd workspace**: set `HERD_WORKSPACE` in `~/.wp-bootstraprc`. -- **Local TLD**: set `LOCAL_TLD` (`test` by default). -- **DBngin**: set `MYSQL_HOST`, `MYSQL_PORT`, `MYSQL_ROOT_USER`, `MYSQL_ROOT_PASS`. -- **Theme starter**: set `THEME_STARTER_REPO`. The script clones shallow, removes `.git`, then re-initializes the theme repo. -- **Theme remote**: set `DEFAULT_THEME_REMOTE_ORIGIN` if you want the script to push the new theme repo automatically. +Pass values via flags (help output lists every option): -## Plugins +```bash +./wp-bootstrap.sh \ + --project-name "Client Site" \ + --admin-user admin \ + --admin-email dev@example.com \ + --theme-starter-repo git@github.com:vendor/theme.git \ + --herd-workspace "$HOME/Herd" \ + --local-tld test \ + --mysql-host 127.0.0.1 \ + --mysql-port 3306 \ + --mysql-root-user root \ + --mysql-root-pass secret +``` -Baseline plugins are defined in `plugins.json`. Each entry accepts either a WordPress.org **slug** or a direct **zip** URL. +Any value omitted from the CLI falls back to `~/.wp-bootstraprc` or the script defaults. Supply `--help` to print the full usage guide. -> Premium/private zips: use signed/internal URLs or manually download and point to a local `/tmp/file.zip` (adjust the script to `wp plugin install /tmp/file.zip`). +## Configuration with `.wp-bootstraprc` -## Notes +The script sources `~/.wp-bootstraprc` on start. Key entries include: -- The script sets friendly permalinks (`/%postname%/`) and creates Home/News pages by default. -- Sample content is removed if present. -- A plugin install log is written to `wp-content/plugin-bootstrap.log`. +- `HERD_WORKSPACE`, `LOCAL_TLD` +- `MYSQL_HOST`, `MYSQL_PORT`, `MYSQL_ROOT_USER`, `MYSQL_ROOT_PASS` +- `DEFAULT_ADMIN_USER`, `DEFAULT_ADMIN_EMAIL` +- `THEME_STARTER_REPO`, `DEFAULT_THEME_REMOTE_ORIGIN` +- `WP_CLI_PHAR` to point at a custom `wp-cli.phar` + +Environment variables exported before running the script take precedence over both CLI flags and rc defaults when they share the same name. + +## Plugin Manifest + +`plugins.json` accepts WordPress.org slugs or zip URLs. The script first looks for `./plugins.json` inside the target project (useful for site-specific manifests) and falls back to the repository version when absent. Premium/private zips can point to signed URLs or local files; make sure the executing user has access. + +## Logs & Files Created + +- `bootstrap-summary.txt` – high-level run output, stored in the project root. +- `wp-content/plugin-bootstrap.log` – detailed plugin installation results. +- `.htaccess` – created only when missing, populated with standard WordPress rewrite rules. ## Troubleshooting -- If `wp` is not found, ensure WP-CLI is installed and available in `PATH`. -- If MySQL connection fails, confirm DBngin is running and credentials are correct in `~/.wp-bootstraprc`. -- On Windows, prefer WSL or Git Bash for best results. +- **WP-CLI missing** – install `wp` globally, or download `wp-cli.phar` into the script directory and set `WP_CLI_PHAR=/path/to/wp-cli.phar`. +- **MySQL connection failures** – confirm DBngin (or your server) is running, credentials match your `.wp-bootstraprc`, and the root user can create databases/users. +- **Composer/NPM not installed** – the script skips dependency installation but leaves a warning in the output; install the tooling and rerun if theme assets are required. +- **Permissions** – ensure the Herd workspace and MySQL socket/port are accessible to your user account. --- -DIY-first policy: keep `plugins.json` minimal. Add big off-the-shelf stacks (e.g., ecomm) only when warranted. +--- +DIY-first policy: keep `plugins.json` minimal; add heavier stacks only when a project truly needs them. diff --git a/windows/README-Windows.md b/windows/README-Windows.md index 57a0791..67d4f57 100644 --- a/windows/README-Windows.md +++ b/windows/README-Windows.md @@ -1,67 +1,80 @@ -# Easy WordPress Bootstrap (Herd + DBngin) — Windows Edition +# Easy WordPress Bootstrap (Windows + Herd) -This kit lets Windows devs spin up a new local WordPress project using **PowerShell only**. +PowerShell companion to the macOS bootstrap script. It provisions a full WordPress development site backed by Laravel Herd and DBngin (or any local MySQL/MariaDB server) with a single command. -## Includes +## Included Files -- `wp-bootstrap.ps1` — main PowerShell script -- `bootstrap.bat` — one-click launcher -- `plugins.json` — minimal default plugin list (use file from repo root) +- `wp-bootstrap.ps1` – main PowerShell script. +- `wp-cli.phar` – bundled WP-CLI used by the script when `wp` is not installed globally. +- `plugins.json` – default plugin manifest; copy/override per project if you need a different set. ## Prerequisites -- **PHP** installed and in PATH -- **MySQL client** (`mysql.exe`) in PATH (DBngin/MySQL installed and running) -- **Git** installed (for cloning the starter theme) -- **WP-CLI**: either installed globally as `wp`, **or** put `wp-cli.phar` next to this script and call with: - - `-WpCliPath "php .\wp-cli.phar"` +- Windows 10/11 with PowerShell 7 (`pwsh`) recommended. +- [Laravel Herd for Windows](https://herd.laravel.com/) installed so PHP is available (the script auto-detects Herd’s `php.exe`). +- DBngin or another MySQL/MariaDB server running locally, with `mysql.exe` available in `PATH`. +- Git for cloning the starter theme. +- Composer and npm in `PATH` so theme dependencies can be installed and built (the script warns and skips if they are missing). -## Quick Start +## First-Time Setup -1. Right-click **PowerShell** → *Run as Administrator* (first run only): +1. Allow local PowerShell scripts (only needs to be done once for your profile): ```powershell - Set-ExecutionPolicy RemoteSigned -Scope CurrentUser + Set-ExecutionPolicy -Scope CurrentUser RemoteSigned ``` -2. Open a normal PowerShell in this folder and run: +2. Copy the sample rc file and adjust values that should become your defaults: ```powershell - .\wp-bootstrap.ps1 -ProjectName "Client Site" + Copy-Item ..\wp-bootstraprc.example $HOME\.wp-bootstraprc ``` - or double-click `bootstrap.bat` and enter the project name when prompted. +3. Leave `wp-cli.phar` in the same folder as `wp-bootstrap.ps1` (or update the `WP_CLI_PHAR` entry in your rc file to point elsewhere). -### Optional parameters +## Running a Bootstrap -- `-AdminUser` (default: `vdidev`) -- `-AdminEmail` (default: `dev@vincentdesign.ca`) -- `-ThemeStarterRepo` (default: VDI starter theme) -- `-HerdWorkspace` (default: `"$HOME\Herd"`) -- `-LocalTld` (default: `test`) -- `-MysqlHost` / `-MysqlPort` / `-MysqlRootUser` / `-MysqlRootPass` -- `-WpCliPath` (set to `"php .\wp-cli.phar"` if not installed globally) +From this directory, run: -## What it does +```powershell +pwsh -File .\wp-bootstrap.ps1 -ProjectName "Client Site" +``` -- Creates project folder under your Herd workspace -- Downloads WordPress core -- Creates database + user with random password -- Generates `wp-config.php`, shuffles salts -- Installs WordPress and sets the site/home URLs -- Creates **Home** and **News** pages, sets **front page**/**posts page** -- Sets permalinks to `/%postname%/` and flushes -- Clones the starter theme, strips history, re-initializes a clean repo, and activates it -- Installs and (optionally) activates plugins from `plugins.json` -- Prints and saves a summary (`bootstrap-summary.txt`) +Optional parameters (all may also be supplied via `~\.wp-bootstraprc`): + +- `-AdminUser` +- `-AdminEmail` +- `-ThemeStarterRepo` +- `-HerdWorkspace` +- `-LocalTld` +- `-MysqlHost` +- `-MysqlPort` +- `-MysqlRootUser` +- `-MysqlRootPass` + +Omit parameters to fall back to your rc defaults. The script prints the detected configuration before it runs and exits early if required inputs are still missing. + +## What the Script Does + +- Creates a sanitized project folder in your Herd workspace and reports the local URL. +- Validates MySQL root credentials, creates the database/user, and confirms the new user can connect. +- Downloads WordPress core, generates `wp-config.php`, shuffles salts, sets permalinks, creates Home/News pages, and assigns them. +- Ensures a standard `.htaccess` exists for Apache-friendly rewrites. +- Clones the starter theme with shallow history, strips `.git`, reinitializes the repo, sets an optional remote, and activates the theme. +- Installs/activates plugins pulled from `plugins.json`, preferring a project-local manifest when present. +- Runs `composer install`, `npm install`, and `npm run build` inside the theme when those tools are available. +- Writes a `bootstrap-summary.txt` to the project root and logs plugin installs to `wp-content/plugin-bootstrap.log`. + +## Plugin Manifest Details + +`plugins.json` entries can be WordPress.org slugs or zip URLs (including private artifacts). Place a tailored `plugins.json` in your project directory to override the repository default for a single site. ## Troubleshooting -- **WP-CLI not found**: pass `-WpCliPath "php .\wp-cli.phar"` (place `wp-cli.phar` beside this script). -- **MySQL not found**: ensure `mysql.exe` is in PATH. With DBngin, add the MySQL bin folder to PATH. -- **Access denied creating DB**: verify `-MysqlRootUser`/`-MysqlRootPass`, or create a dev-only MySQL user with `CREATE` privileges. -- **Herd not serving**: Add/link the folder in Herd and browse to `http://.test` (or your chosen TLD). +- **WP-CLI** – ensure `wp-cli.phar` lives beside the script or set `WP_CLI_PHAR` in `~\.wp-bootstraprc` to an absolute path. +- **MySQL connectivity** – verify the server is running, your root user matches the rc values, and Windows Firewall allows the connection. +- **Composer/NPM missing** – install them via Scoop/winget/Homebrew on Windows, or remove their commands from the script if you do not build theme assets. +- **Permissions** – confirm Herd has access to the project folder and that the execution policy change succeeded. -## Philosophy - -DIY-first: keep `plugins.json` minimal. Only add big off‑the‑shelf stacks (e.g., ecomm) when warranted. +--- +DIY-first policy: keep `plugins.json` lean; add heavier stacks only when the project truly needs them. diff --git a/wp-bootstrap.sh b/wp-bootstrap.sh index 7c8b651..a39de2a 100644 --- a/wp-bootstrap.sh +++ b/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[@]}" </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 + +RewriteEngine On +RewriteBase / +RewriteRule ^index\.php$ - [L] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule . /index.php [L] + +# 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