Skip to content

Instantly share code, notes, and snippets.

@doubleedesign
Last active July 11, 2025 23:57
Show Gist options
  • Save doubleedesign/f61f25ef8096c30eb8ae4117d76cb185 to your computer and use it in GitHub Desktop.
Save doubleedesign/f61f25ef8096c30eb8ae4117d76cb185 to your computer and use it in GitHub Desktop.
New WordPress site setup with Laravel Herd and custom blueprint

HerdPress

WordPress local dev site setup automation for Laravel Herd.

Kind of like wp-env but without Docker, enabling you to use Herd's debugging and mail catching + inspection capabilities. Also supports installing ClassicPress instead of WordPress.

For Windows + PowerShell users.

Not affiliated with or endorsed by Beyond Code (the makers of Laravel Herd).

Features

  • Creates site directory and database
  • Allows you to use an existing database if it matches the expected configuration
  • Downloads and installs WordPress or ClassicPress
  • Handles configuration for Herd to serve the site, including local SSL
  • Symlinks or copies your own "blueprint" of plugins and themes (ideal for a minimal testing setup that might include commercial and other non-WP.org plugins)
  • Allows you to specify an additional directory of your own plugins to symlink or copy (e.g., those that have their own Git repos so are separate from your blueprint repo if you have one and/or are not used for a minimal testing setup), or an array of locations of individual plugins
  • If called from a plugin or theme directory, option to symlink that to the dev site
  • Handles host file requirements when using a non-standard Herd TLD
  • Option to run Composer install/update
  • Automated cleanup.

Prerequisites

  • Laravel Herd Pro installed
  • WP-CLI installed and available on the command line
  • Herd's instances of PHP and MySQL available on the command line
  • Git available on the command line
  • herdpress command available in PowerShell (see instructions below)
  • Ability to create symlinks - enable Developer Mode in Windows settings if not running PowerShell as admin

If you are not me, you will need to create your own wp-content-blueprint repo and add its local location and GitHub URL if desired to a .herdpress.json config file in the root of your plugin/theme project (Mine is private because it contains commercial plugins.)

Installing WP-CLI on Windows

Download it:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

Check it:

php wp-cli.phar --info

Create a batch script called wp.bat to alias the wp command, and put it in your Herd bin folder or another location your PATH is aware of:

@echo off
php "C:\Users\{YOUR_USERNAME}\wp-cli.phar" %*

Check that it works:

wp --info

herdpress command

Setup

Save the below PowerShell script as herdpress.ps1 and the batch script as herdpress.bat in your Herd bin folder or another location your PATH is aware of.

Usage

By having the herdpress command available in your PATH, you can run it from anywhere, but if you're developing a plugin it is ideal to run it from that plugin's directory. The script will look for a .herdpress.json config file there, and provides an option to automatically create a symlink to your plugin in the relevant location.

Config file

If you wish to specify database credentials, admin user credentials, wp-content blueprint location, and other plugins to install, you can do so using a .herdpress.json config file in the folder you run the script from. An example is included in this gist.

Create new dev site

herdpress site-name

Note: If you are running from a plugin directory and your plugin is already located within a local WordPress install, that install will be ignored. This script creates a fresh install in its own directory.

When done, you will be prompted to have your terminal either stay in the site's root or move back to the directory you called the script from. However if you close and reopen the terminal while in the plugin project in PhpStorm, it will default back to the plugin directory, so you will just need to cd to the site root to run wp and herd commands. (Alternatively you can of course open the whole site as a project in PhpStorm.)

In the site folder, WP CLI commands are available. In the project directory (the plugin or theme you're developing), its own Composer and test commands are available (if applicable).

Site cleanup

To delete the dev site directory and database and delete its Herd server configuration, run the script with the -c flag:

herdpress site-name -c
herdpress -c site-name

This will not delete the plugin directory you called this script from (if applicable).

Optional: Use Herd's Dumps feature

Herd uses Symfony's VarDumper under the hood, so if it's not already installed in your site or available globally on your machine, you can do the latter like so:

composer global require symfony/var-dumper

Optional: Work with emails in Herd

To configure your site so that Herd's SMTP server will pick up the emails so you can see them, I put this code in a small local dev plugin (but you could put it in your own, or in a theme's functions.php, etc):

<?php
if (wp_get_environment_type() === 'local') {
	// Use Laravel Herd SMTP server for sending emails locally
	function herd_mailer($phpmailer): void {
		$phpmailer->isSMTP();
		$phpmailer->Host = '127.0.0.1';
		$phpmailer->SMTPAuth = true;
		$phpmailer->Port = 2525;
		$phpmailer->Username = 'WordPress';
		$phpmailer->Password = '';
	}
	add_action('phpmailer_init', 'herd_mailer');
}
{
"type": "plugin",
"database": {
"user": "root",
"password": "",
"host": "127.0.0.1",
"port": 3309
},
"admin": {
"email": "[email protected]",
"username": "doubleedesign",
"password": "doubleedesign"
},
"blueprint": {
"local": "$PROJECTS_DIR/wp-content-blueprint",
"github": "doubleedesign/wp-content-blueprint"
},
"otherLocalPlugins": "$PROJECTS_DIR/wp-plugins",
"wpOrgPlugins": [
"simply-disable-comments"
]
}
@echo off
powershell -ExecutionPolicy Bypass -File "%~dp0herdpress.ps1" %*
param(
[string]$SiteName,
[switch]$Cleanup
)
if ($Help -or [string]::IsNullOrEmpty($SiteName)) {
Write-Host "Usage:" -ForegroundColor Cyan
Write-Host " herdpress site-name # Create site"
Write-Host " herdpress -c site-name # Delete site"
exit 0
}
function Merge-Config($default, $override) {
$result = $default.Clone()
foreach ($key in $override.PSObject.Properties.Name) {
if ($override.$key -is [PSCustomObject]) {
$result.$key = Merge-Config $result.$key $override.$key
} elseif ($null -ne $override.$key -and $override.$key -ne "") {
$result.$key = $override.$key
}
}
return $result
}
# Store location the script was called from
$scriptLocation = Get-Location
$username = $env:USERNAME
$defaultProjectsDir = "C:\Users\$username\PhpStormProjects"
$PROJECTS_DIR = Read-Host "Enter the path to your projects directory (default is $defaultProjectsDir)"
if ([string]::IsNullOrEmpty($PROJECTS_DIR)) {
$PROJECTS_DIR = $defaultProjectsDir
}
Write-Host "Using projects directory: $PROJECTS_DIR" -ForegroundColor Cyan
$SiteDir = "$PROJECTS_DIR\$SiteName"
Write-Host "Site directory will be: $SiteDir" -ForegroundColor Cyan
# Default config object
$config = @{
type = ""
admin = @{
username = "admin"
password = "password"
email = "[email protected]"
}
database = @{
name = $SiteName + "_dev"
user = "root"
password = ""
host = "127.0.0.1"
port = "3306"
}
blueprint = @{
local = $null
github = $null
}
}
# Check for config JSON file
$configFile = Join-Path $scriptLocation "/.herdpress.json"
if(Test-Path $configFile) {
Write-Host "✔ Found custom configuration file: $configFile" -ForegroundColor Green
$json = Get-Content $configFile | ConvertFrom-Json
# Merge most config
$config = Merge-Config $config $json
# Handle some special cases
if ($json.blueprint -and $json.blueprint.local) {
$blueprintDir = $json.blueprint.local.toString().Trim()
$config.blueprint.local = $blueprintDir.Replace('$PROJECTS_DIR', $PROJECTS_DIR)
}
if ($json.otherLocalPlugins -is [string]) {
$config.otherLocalPlugins = $json.otherLocalPlugins.Replace('$PROJECTS_DIR', $scriptLocation)
}
elseif ($config.otherLocalPlugins -is [array]) {
$config.otherLocalPlugins = $json.otherLocalPlugins | ForEach-Object { $_.Replace('$PROJECTS_DIR', $scriptLocation) }
}
}
else {
Write-Host "No config file found at: $configFile" -ForegroundColor Yellow
$continue = Read-Host "Do you want to continue without custom configuration? (y/N)"
if ($continue -ne 'y' -and $continue -ne 'Y') {
Write-Host "Exiting script." -ForegroundColor Yellow
exit 0
}
else {
Write-Host "Continuing without custom configuration." -ForegroundColor Cyan
}
}
Write-Host "============================================================================" -ForegroundColor Cyan
# Output config
Write-Host "Using configuration:" -ForegroundColor Magenta
$config | ConvertTo-Json -Depth 3 | Write-Host
Write-Host "============================================================================" -ForegroundColor Cyan
$confirm = Read-Host "Proceed with this configuration? (Y/n)"
if ($confirm -ne 'y' -and $confirm -ne 'Y' -and $confirm -ne '') {
Write-Host "Exiting script." -ForegroundColor Yellow
Write-Host "Modify configuration in $scriptLocation/.herdpress.json and try again." -ForegroundColor Cyan
Write-Host ""
exit 0
}
Write-Host "============================================================================" -ForegroundColor Cyan
Write-Host ""
if ($Cleanup) {
$confirm = Read-Host "Are you sure you want to delete this dev site? (y/N)"
if ($confirm -ne 'y' -and $confirm -ne 'Y') {
Write-Host "Exiting." -ForegroundColor Yellow
exit 0
}
Set-Location $SiteDir
Write-Host "Moved to site directory: $SiteDir" -ForegroundColor Magenta
# Check if site is secured in Herd before trying to unsecure
$secured = herd secured | Where-Object { $_ -match $SiteName }
if($secured) {
herd unsecure $SiteName
}
else {
Write-Host "✔ Site $SiteName is not secured in Herd, nothing to do." -ForegroundColor Green
}
# Check if site is linked in Herd before trying to unlink
$linked = herd links | Where-Object { $_ -match $SiteName }
if($linked) {
herd unlink $SiteName
}
else {
Write-Host "✔ Site $SiteName is not linked in Herd, nothing to do." -ForegroundColor Green
}
try {
if (Test-Path $SiteDir) {
# If the script was called from the site directory, navigate to user directory so we can delete it
if ((Resolve-Path $scriptLocation).Path -eq (Resolve-Path $SiteDir).Path) {
Set-Location "C:\Users\$username"
Write-Host "Moved to user directory: C:\Users\$username" -ForegroundColor Magenta
}
try {
Write-Host "Deleting directory: $SiteDir" -ForegroundColor Cyan
Remove-Item -Path $SiteDir -Recurse -Force -ProgressAction SilentlyContinue
}
catch {
Write-Host "✘" + $_ -ForegroundColor Red
cd ..
Write-Host "Moved up one level to: $(Get-Location)" -ForegroundColor Magenta
try {
Remove-Item -Path $SiteName -Recurse -Force -ProgressAction SilentlyContinue
Write-Host "✔ Deleted directory: $SiteName" -ForegroundColor Green
}
catch {
Write-Host "✘ Failed to delete directory: $SiteName" -ForegroundColor Red
Write-Host "✘ Please delete it manually." -ForegroundColor Red
exit 1
}
}
}
mysql -h $DbHost -P $dbPort -u $DbUser -e "DROP DATABASE IF EXISTS $DbName;"
Write-Host "✔ Dropped database: $DbName" -ForegroundColor Green
Write-Host "✔ Dev site $SiteName has been deleted." -ForegroundColor Green
exit 0
}
catch {
Write-Host "✘ Failed to delete the site" -ForegroundColor Red
Write-Host "✘" + $_ -ForegroundColor Red
exit 1
}
}
# Check if we can create symlinks
$testDir = [System.IO.Path]::GetTempPath()
$testSource = Join-Path $testDir "symlink_test_source.tmp"
$testTarget = Join-Path $testDir "symlink_test_target.tmp"
try {
# Create a test file
"test" | Out-File -FilePath $testSource -Force
# Try to create a symbolic link
New-Item -ItemType SymbolicLink -Path $testTarget -Target $testSource -ErrorAction Stop | Out-Null
# Clean up
Remove-Item $testSource, $testTarget -Force -ErrorAction SilentlyContinue
Write-Host "✔ Symlink creation is available" -ForegroundColor Green
}
catch {
Write-Host "✘ Current user/process cannot create symlinks." -ForegroundColor Red
Write-Host "Try enabling developer mode in your Windows settings, or running this script as an administrator." -ForegroundColor Red
exit 1
}
# Check if wp-cli is available
if (-not (Get-Command wp -ErrorAction SilentlyContinue)) {
Write-Host "✘ wp-cli is not available." -ForegroundColor Red
exit 1
}
Write-Host "✔ wp-cli is available" -ForegroundColor Green
# Check if other vital commands are available
$commands = @("php", "mysql", "git", "herd", "robocopy")
foreach ($command in $commands) {
if (-not (Get-Command $command -ErrorAction SilentlyContinue)) {
Write-Host "✘ $command is not available." -ForegroundColor Red
exit 1
}
Write-Host "✔ $command is available" -ForegroundColor Green
}
# Check if optional commands are available
$optionalCommands = @("composer")
foreach ($command in $optionalCommands) {
if (-not (Get-Command $command -ErrorAction SilentlyContinue)) {
Write-Host "✘ $command is not available" -ForegroundColor Yellow
}
else {
Write-Host "✔ $command is available" -ForegroundColor Green
}
}
# Check if curl and openssl are available via PHP
$found = @()
php -m | Select-String -Pattern "curl|openssl" | ForEach-Object {
$found += $_.ToString().Trim()
}
if ($found.Count -ne 2) {
Write-Host "✘ PHP is missing required extensions: curl and/or openssl." -ForegroundColor Red
Write-Host "Current PHP instance: "
Get-Command php
Write-Host "If this isn't Herd's PHP and you are in PhpStorm's terminal, please check your interpreter settings." -ForegroundColor Cyan
Write-Host "Otherwise, check your system environment variables to ensure Herd takes precedence over other PHP installations." -ForegroundColor Cyan
exit 1;
}
Write-Host "✔ PHP has required extensions enabled: $( $found -join ', ' )" -ForegroundColor Green
# If directory exists, prompt to delete or exit
if (Test-Path $SiteDir) {
Write-Host "Directory $SiteDir already exists." -ForegroundColor Yellow
$choice = Read-Host "Do you want to delete it and start fresh? (y/N)"
if ($choice -eq 'y' -or $choice -eq 'Y') {
try {
Remove-Item -Path $SiteDir -Recurse -Force -ProgressAction SilentlyContinue
Write-Host "✔ Deleted existing directory: $SiteDir" -ForegroundColor Green
}
catch {
Write-Host "✘" + $_ -ForegroundColor Red
Write-Host "Try navigating up a level and running the cleanup script." -ForegroundColor Red
}
}
else {
Write-Host "Exiting script." -ForegroundColor Yellow
exit 0
}
}
# If database already exists, prompt to delete or continue with existing
$dbExists = $false
$errorOutput = mysql -h $config.database.host -P $config.database.port -u $config.database.user -e "USE $dbName;" 2>&1
if ($LASTEXITCODE -eq 0) {
# Database exists
$dbName = $config.database.name
Write-Host "Database $dbName already exists." -ForegroundColor Yellow
$choice = Read-Host "Do you want to delete it and start fresh? (y/N)"
if ($choice -eq 'y' -or $choice -eq 'Y') {
mysql -h $config.database.host -P $config.database.port -u $config.database.user -e "DROP DATABASE IF EXISTS $DbName;" 2> $null
Write-Host "✔ Dropped existing database: $dbName" -ForegroundColor Green
}
else {
$dbExists = $true
}
}
else {
# Check if it's just "database doesn't exist" error
if ($errorOutput -match "Unknown database" -or $errorOutput -match "doesn't exist") {
Write-Host "✔ Database $config.database.name does not yet exist" -ForegroundColor Green
}
else {
# Some other error - show it
Write-Host "✘ Database connection error: $errorOutput" -ForegroundColor Red
exit 1
}
}
# Create directory
New-Item -ItemType Directory -Path $SiteDir -ProgressAction SilentlyContinue | Out-Null
Write-Host "✔ Created directory: $SiteDir" -ForegroundColor Green
# Check Herd TLD and set site URL
$herdTLD = herd tld
if ($herdTLD -ne "test") {
Write-Host "Using non-standard Herd TLD: $herdTLD" -ForegroundColor Yellow
Write-Host "To use this TLD, you will need to manually add the site URL to your hosts file." -ForegroundColor Yellow
$proceed = Read-Host "Continue with this TLD? (y/n)"
if ($proceed -eq 'y' -or $proceed -eq 'Y' -or $proceed -eq '') {
Write-Host "No change made. Continuing with existing TLD: $herdTLD" -ForegroundColor Cyan
}
else {
Write-Host "IMPORTANT: Changing the Herd TLD affects all sites served by Herd." -ForegroundColor Yellow
$changeTLD = Read-Host "Do you want to change the Herd TLD to its default ('test')? (y/n)"
if ($changeTLD -eq 'y') {
herd tld test
$herdTLD = "test"
Write-Host "Changed Herd TLD to 'test'" -ForegroundColor Green
}
else {
Write-Host "Exiting script." -ForegroundColor Yellow
exit 0
}
}
}
$SiteUrl = "https://$SiteName.$herdTLD"
Write-Host "✔ Set site URL to: $SiteUrl" -ForegroundColor Green
# Change to site directory
Set-Location $SiteDir
Write-Host "Moved to site directory: $SiteDir" -ForegroundColor Magenta
# Download WordPress or ClassicPress
$classic = Read-Host "Would you like to use ClassicPress? (Default is WordPress) (y/N)"
if ($classic -eq 'y' -or $classic -eq 'Y') {
Write-Host "Downloading ClassicPress..." -ForegroundColor Cyan
wp core download "https://github.com/ClassicPress/ClassicPress-release/archive/refs/heads/main.zip"
}
else {
Write-Host "Downloading WordPress..." -ForegroundColor Cyan
wp core download --locale=en_AU --skip-content
}
# Check if the download worked, and stop and delete the directory if it didn't
if (!(Test-Path "$SiteDir\wp-load.php")) {
Write-Host "✘ Failed to download WordPress. Deleting directory: $SiteDir" -ForegroundColor Red
Remove-Item -Path $SiteDir -Recurse -Force
exit 1
}
# Create wp-config.php
Write-Host "Creating wp-config.php..." -ForegroundColor Cyan
$dbHostWithPort = $config.database.host + ":" + $config.database.port
$DbName = $config.database.name
$DbUser = $config.database.user
$DbPass = ""
try {
wp config create --dbname=$DbName --dbuser=$DbUser --dbpass=$DbPass --dbhost=$dbHostWithPort
wp config set WP_ENVIRONMENT_TYPE local --type=constant
}
catch {
Write-Host "✘" + $_ -ForegroundColor Red
exit 1
}
if (!$dbExists) {
try {
# Create database if it didn't already exist
Write-Host "Creating database..." -ForegroundColor Cyan
wp db create
# Install WordPress
$adminUser = $config.admin.username
$adminPassword = $config.admin.password
$adminEmail = $config.admin.email
Write-Host "Installing WordPress..." -ForegroundColor Cyan
wp core install --url=$SiteUrl --title=$SiteName --admin_user=$adminUser --admin_password=$adminPassword --admin_email=$adminEmail
}
catch {
Write-Host "✘ Failed to create database or install WordPress: $_" -ForegroundColor Red
Write-Host "Please check your database credentials and try again." -ForegroundColor Red
exit 1
}
}
# Delete default plugins and themes
Remove-Item -Path "$SiteDir\wp-content\themes" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -Path "$SiteDir\wp-content\plugins" -Recurse -Force -ErrorAction SilentlyContinue
# Symlink or copy the contents of custom blueprint folders
if($config.blueprint) {
$blueprintDir = $config.blueprint.local
if (-not (Test-Path $blueprintDir)) {
Write-Host "Blueprint directory does not exist: $blueprintDir" -ForegroundColor Yellow
$download = Read-Host "Would you like to download it from GitHub? (Y/n)"
if ($download -eq 'Y' -or $download -eq 'y' -or $download -eq '') {
try {
Set-Location $PROJECTS_DIR
Write-Host "Moved to directory: $PROJECTS_DIR" -ForegroundColor Magenta
git clone "https://github.com/$blueprint.github"
}
catch {
Write-Host "✘" + $_ -ForegroundColor Red
}
finally {
Set-Location $SiteDir
Write-Host "Moved back to site directory: $SiteDir" -ForegroundColor Magenta
}
}
}
if (Test-Path $blueprintDir) {
Write-Host "Base plugins and themes directory: $blueprintDir" -ForegroundColor Cyan
$symlinkBlueprint = Read-Host "Do you want to symlink the plugins and themes in your blueprint? (Y/n)"
if ($symlinkBlueprint -eq 'Y' -or $symlinkBlueprint -eq 'y' -or $symlinkBlueprint -eq '') {
Write-Host "Symlinking blueprint plugins and themes..." -ForegroundColor Cyan
# Symlink the individual plugin and theme directories
if (Test-Path "$blueprintDir\plugins") {
Get-ChildItem "$blueprintDir\plugins" | ForEach-Object {
$symlinkPath = Join-Path $SiteDir "wp-content\plugins\$( $_.Name )"
try {
New-Item -ItemType SymbolicLink -Path $symlinkPath -Target $_.FullName -Force
}
catch {
Write-Host "✘ " + $_ -ForegroundColor Red
exit 1
}
}
}
if (Test-Path "$blueprintDir\themes") {
Get-ChildItem "$blueprintDir\themes" | ForEach-Object {
$symlinkPath = Join-Path $SiteDir "wp-content\themes\$( $_.Name )"
try {
New-Item -ItemType SymbolicLink -Path $symlinkPath -Target $_.FullName -Force
Write-Host "✔ Symlinked theme: $_" -ForegroundColor Green
}
catch {
Write-Host "✘ " + $_ -ForegroundColor Red
exit 1
}
}
}
}
else {
$copyBluePrint = Read-Host "Do you want to copy the plugins and themes from your blueprint? (Y/n)"
if ($copyBlueprint -eq 'Y' -or $copyBlueprint -eq 'y' -or $copyBlueprint -eq '') {
Write-Host "Copying custom blueprint contents..." -ForegroundColor Cyan
if (Test-Path "$blueprintDir\plugins") {
robocopy "$blueprintDir\plugins" "$SiteDir\wp-content\plugins" /E /IS /IT /NFL /NDL
}
if (Test-Path "$blueprintDir\themes") {
robocopy "$blueprintDir\themes" "$SiteDir\wp-content\themes" /E /IS /IT /NFL /NDL
}
Write-Host "✔ Installed plugins:" -ForegroundColor Green
Get-ChildItem "$SiteDir\wp-content\plugins" | ForEach-Object {
Write-Host $_.Name
}
Write-Host "✔ Installed themes:" -ForegroundColor Green
Get-ChildItem "$SiteDir\wp-content\themes" | ForEach-Object {
Write-Host $_.Name
}
}
else {
Write-Host "Skipping blueprint plugins and themes" -ForegroundColor Yellow
}
}
}
else {
Write-Host "Blueprint path $blueprintDir does not exist, skipping" -ForegroundColor Yellow
}
}
# Symlink or copy additional local plugins
$pluginPath = $config.otherLocalPlugins -replace '$PROJECTS_DIR', $PROJECTS_DIR
if([string]::IsNullOrEmpty($pluginPath)) {
Write-Host "No additional local plugins path specified in config." -ForegroundColor Cyan
Write-Host "If you have a directory with additional local plugins, please specify it as `otherLocalPlugins` in the config file." -ForegroundColor Cyan
Write-Host "Alternatively you can list an array of plugin paths if they are in different locations." -ForegroundColor Cyan
}
# Is it a single path?
if ($pluginPath -is [string]) {
if (Test-Path $pluginPath) {
$symlinkPlugins = Read-Host "Do you want to symlink the plugins in this directory? (Y/n)"
if ($symlinkPlugins -eq 'Y' -or $symlinkPlugins -eq 'y' -or $symlinkPlugins -eq '') {
Write-Host "Symlinking additional plugins..." -ForegroundColor Cyan
Get-ChildItem $pluginPath | ForEach-Object {
$symlinkPath = Join-Path $SiteDir "wp-content\plugins\$( $_.Name )"
try {
New-Item -ItemType SymbolicLink -Path $symlinkPath -Target $_.FullName -Force
Write-Host "Symlinked plugin: $_" -ForegroundColor Green
}
catch {
Write-Host "✘ " + $_ -ForegroundColor Red
exit 1
}
}
}
else {
Write-Host "Copying additional plugins..." -ForegroundColor Cyan
robocopy $pluginPath "$SiteDir\wp-content\plugins" /E /IS /IT /NFL /NDL
Write-Host "Copied additional plugins." -ForegroundColor Green
}
}
else {
Write-Host "✘ Directory does not exist: $pluginPath" -ForegroundColor Red
Write-Host "✘ Skipping additional plugins. Please add them manually." -ForegroundColor Red
}
}
# Is it an array of paths?
elseif ($pluginPath -is [array]) {
$symlinkPlugins = Read-Host "Do you want to symlink your additional local plugins? (Y/n)"
if ($symlinkPlugins -eq 'Y' -or $symlinkPlugins -eq 'y' -or $symlinkPlugins -eq '') {
foreach ($path in $pluginPath) {
if (Test-Path $path) {
$symlinkPath = Join-Path $SiteDir "wp-content\plugins\$( Split-Path $path -Leaf )"
try {
New-Item -ItemType SymbolicLink -Path $symlinkPath -Target $path -Force
}
catch {
Write-Host "✘ " + $_ -ForegroundColor Red
}
}
else {
Write-Host "✘ Directory does not exist: $path" -ForegroundColor Red
}
}
}
}
# Ask whether to symlink the called-from directory as a plugin if it isn't specified in the config
$isPlugin = $config.type -eq "plugin"
$symlinkChoice = $null
if (-not $isPlugin) {
$symlinkChoice = Read-Host "Do you want to symlink $scriptLocation as a plugin? (Y/n)"
}
if ($isPlugin -or $symlinkChoice -eq 'y' -or $symlinkChoice -eq 'Y' -or $symlinkChoice -eq '') {
$pluginDir = Join-Path $SiteDir "wp-content\plugins"
$symlinkPath = Join-Path $pluginDir (Split-Path $scriptLocation -Leaf)
if (!(Test-Path $symlinkPath)) {
New-Item -ItemType SymbolicLink -Path $symlinkPath -Target $scriptLocation -Force
}
else {
Write-Host "Symlink already exists: $symlinkPath" -ForegroundColor Yellow
}
}
else {
#...or a theme
$isTheme = $config.type -eq "theme"
$symlinkThemeChoice = $null
if (-not $isTheme) {
$symlinkThemeChoice = Read-Host "Do you want to symlink $scriptLocation as a theme? (Y/n)"
}
if ($isTheme -or $symlinkThemeChoice -eq 'y' -or $symlinkThemeChoice -eq 'Y' -or $symlinkThemeChoice -eq '') {
$themeDir = Join-Path $SiteDir "wp-content\themes"
if (-not (Test-Path $themeDir)) {
New-Item -ItemType Directory -Path $themeDir
Write-Host "✔ Created themes directory: $themeDir" -ForegroundColor Green
}
$symlinkPath = Join-Path $themeDir (Split-Path $scriptLocation -Leaf)
if (!(Test-Path $symlinkPath)) {
try {
New-Item -ItemType SymbolicLink -Path $symlinkPath -Target $scriptLocation
Write-Host "✔ Symlink created: $symlinkPath" -ForegroundColor Green
Write-Host ""
}
catch {
Write-Host "✘ Failed to create symlink: $_" -ForegroundColor Red
Write-Host "✘ Please create it manually." -ForegroundColor Red
Write-Host ""
}
}
else {
Write-Host "Symlink already exists: $symlinkPath" -ForegroundColor Yellow
Write-Host ""
}
}
}
# Install wp.org plugins specified in the config (expecting array of plugin slugs)
$plugins = $config.wpOrgPlugins
if ($plugins -is [array]) {
Set-Location $SiteDir
Write-Host "Installing plugins from WordPress.org..." -ForegroundColor Cyan
foreach ($plugin in $plugins) {
try {
wp plugin install $plugin
Write-Host "✔ Installed $plugin" -ForegroundColor Green
}
catch {
Write-Host "✘ $_." -ForegroundColor Red
}
}
}
# Activate plugins
Write-Host "Activating plugins..." -ForegroundColor Cyan
$inactivePlugins = wp plugin list --status=inactive --field=name
foreach ($plugin in $inactivePlugins) {
try {
wp plugin activate $plugin
}
catch {
Write-Host "✘ Failed to activate $plugin`: $( $_.Exception.Message )" -ForegroundColor Red
# Continue to next plugin
}
}
# If there is no theme, install TwentySixteen
if (-not (Test-Path "$SiteDir\wp-content\themes")) {
Write-Host ""
Write-Host "No themes directory found, creating one..." -ForegroundColor Cyan
New-Item -ItemType Directory -Path "$SiteDir\wp-content\themes" -ProgressAction SilentlyContinue | Out-Null
if (Test-Path "$SiteDir\wp-content\themes") {
Write-Host "✔ Created themes directory: $SiteDir\wp-content\themes" -ForegroundColor Green
}
else {
Write-Host "✘ Failed to create themes directory: $SiteDir\wp-content\themes" -ForegroundColor Red
Write-Host "Please create it manually." -ForegroundColor Red
Write-Host ""
}
}
if (-not (Get-ChildItem "$SiteDir\wp-content\themes" | Where-Object { $_.PSIsContainer })) {
Write-Host "No themes found, installing Twenty Seventeen..." -ForegroundColor Cyan
wp theme install twentysixteen --activate
Write-Host ""
}
# Configure to serve via Herd
Write-Host "Configuring Herd to serve the site..." -ForegroundColor Cyan
herd link $SiteName
herd secure
# If Herd is set to use a custom TLD, we need to add the site URL to the hosts file ourselves
if ($herdTLD -ne "test") {
$hostsFilePath = "C:\Windows\System32\drivers\etc\hosts"
if (-not (Get-Content $hostsFilePath | Select-String -Pattern "$SiteUrl")) {
try {
Add-Content -Path $hostsFilePath -Value "127.0.0.1" + $SiteUrl
if (Get-Content $hostsFilePath | Select-String -Pattern "$SiteUrl") {
Write-Host "✔ Successfully added $SiteUrl to hosts file." -ForegroundColor Green
}
else {
throw "Failed to add $SiteUrl to hosts file."
}
}
catch {
Write-Host "$_" -ForegroundColor Red
Write-Host "✘ Could not update hosts file. Please do so manually." -ForegroundColor Red
# Launch PowerToys hosts editor
Write-Host "Opening PowerToys..." -ForegroundColor Cyan
try {
Start-Process -FilePath "C:\Program Files\PowerToys\PowerToys.exe" -ErrorAction Stop
}
catch {
Write-Host "✘ Could not launch PowerToys hosts editor." -ForegroundColor Red
Write-Host "Add this line to your hosts file manually: 127.0.0.1 $SiteUrl" -ForegroundColor Cyan
}
}
}
else {
Write-Host "✔ $SiteUrl already exists in hosts file." -ForegroundColor Green
}
}
# Check if called-from folder has a composer.json file
if (Test-Path "$scriptLocation\composer.json") {
Write-Host "Found composer.json in $scriptLocation" -ForegroundColor Cyan
if (Get-Command composer -ErrorAction SilentlyContinue) {
$installDeps = Read-Host "Do you want to install/update Composer dependencies? (Y/n)"
if ($installDeps -eq 'Y' -or $installDeps -eq 'y' -or $installDeps -eq '') {
Set-Location $scriptLocation
Write-Host "Moved to project directory: $scriptLocation" -ForegroundColor Magenta
if (Test-Path "composer.lock") {
Write-Host "Updating Composer dependencies..." -ForegroundColor Cyan
composer update
}
else {
Write-Host "Installing Composer dependencies..." -ForegroundColor Cyan
composer install
}
}
}
else {
Write-Host "✘ Composer is not available. You will need to fix that and install dependencies separately." -ForegroundColor Yellow
}
}
# Change back to the site directory
Set-Location $SiteDir
Write-Host "Moved back to site directory: $SiteDir" -ForegroundColor Magenta
# Summary
Write-Host ""
Write-Host "============================ Setup Complete ================================" -ForegroundColor Green
Write-Host "Admin URL: $SiteUrl/wp-admin" -ForegroundColor Cyan
Write-Host "Username: $AdminUser" -ForegroundColor Cyan
Write-Host "Password: $AdminPassword" -ForegroundColor Cyan
Write-Host "Plugins and themes:" -ForegroundColor Magenta
wp plugin list
wp theme list
Write-Host "============================================================================" -ForegroundColor Green
Write-Host ""
Write-Host "Currently in site directory: $SiteDir" -ForegroundColor Magenta
$switchToProject = Read-Host "Do you want to switch back to the project directory instead? (Y/n)"
if ($switchToProject -eq 'Y' -or $switchToProject -eq 'y' -or $switchToProject -eq '') {
Set-Location $scriptLocation
Write-Host "✔ Switched back to project directory: $scriptLocation" -ForegroundColor Green
Write-Host "You can run Composer and test commands from here."
Write-Host "Navigate to the site directory $SiteDir to run wp-cli commands."
}
else {
Write-Host "✔ Staying in site directory: $SiteDir" -ForegroundColor Green
Write-Host "wp-cli commands can be run from here."
Write-Host "Navigate to the project directory $scriptLocation to run Composer and test commands."
}
Write-Host ""
Write-Host "============================================================================" -ForegroundColor Green
Write-Host ""
# Launch browser
Start-Process "$SiteUrl/wp-admin"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment