Last active
June 25, 2025 12:41
-
-
Save doubleedesign/d33856c7e3ce1b8f1b883f704a7a2ba8 to your computer and use it in GitHub Desktop.
Local by Flywheel + Laravel Herd, best of both worlds! Use Local for pushing and pulling and Herd for local development (so you can use the Dumps window and test emails). If you put these in your herd bin folder, you can run "local-to-herd" from anywhere to transfer the site's database and link the install to Herd's server, and "local-export-db"…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is a custom script that automates the transition of a WordPress site | |
# from Local by Flywheel to Laravel Herd for local development. | |
# When placed in your user directory -> .config/herd/bin alongside a .bat file to run it, | |
# you can run it from anywhere using the command "local-to-herd" in PowerShell. | |
param( | |
[string]$SiteName | |
) | |
# Function to check if a command exists | |
function Test-Command { | |
param([string]$Command) | |
$null = Get-Command $Command -ErrorAction SilentlyContinue | |
return $? | |
} | |
# Common variables | |
$username = $env:USERNAME | |
# Note: If this is in the system environment PATH, you can use just "mysql" instead of this full path. | |
# Just check first that the alias is resolving to the correct MySQL instance using "Get-Command mysql". | |
$herdMySQLPath = "C:\Users\$username\.config\herd\bin\services\mysql\8.0.36\bin\mysql.exe" | |
Write-Host "`n=========== Local to Herd Site Setup Script ===========" -ForegroundColor Green | |
Write-Host "Warnings:" -ForegroundColor Yellow | |
Write-Host "`t - This script will close Local by Flywheel if it is running." -ForegroundColor Yellow | |
Write-Host "`t - If you have used this before for this site, this script will overwrite the previous version of the database in Herd's MySQL server" -ForegroundColor Yellow | |
Write-Host "`t - Ensure Herd and Local are running compatible versions of MySQL before proceeding, `n`t or you may have problems transferring the database back to Local `n`t and more importantly, to your live site if it is on Flywheel/WP Engine." -ForegroundColor Yellow | |
Write-Host "This script assumes that:" | |
Write-Host "`t - You have pulled down your site from Local and the database in the app/sql directory is the one you want to use" | |
Write-Host "`t Note: To ensure this is the latest version, stop your site in Local before proceeding" | |
Write-Host "`t - Your Local sites are located in C:\Users\$username\LocalSites\" | |
Write-Host "`t Note: This script will not move any site files. `n`t This script configures Herd to serve your site from the same directory as Local." | |
Write-Host "`t - Herd's MySQL service is running on port 3309 [NOT THE STANDARD 3306]" | |
Write-Host "`t - You have set Laravel Herd to use .local as the TLD (if not, you will need to do a find and replace in the database)" | |
Write-Host "`t - Your Herd MySQL username is 'root' and the password is empty" | |
Write-Host "========================================================" -ForegroundColor Green | |
try { | |
# First, check prerequisites | |
$prerequisites = @( | |
"herd", | |
$herdMySQLPath | |
) | |
foreach ($prerequisite in $prerequisites) { | |
if (-not (Test-Command $prerequisite)) { | |
throw "Error: Required command or executable path '$prerequisite' not found." | |
} | |
} | |
# Make sure Local is not running | |
$localProcess = Get-Process -Name "Local" -ErrorAction SilentlyContinue | |
if ($localProcess) { | |
Stop-Process -Name "Local" -Force | |
} | |
# Get site name from the user if not provided in the initial command | |
if (-not $SiteName) { | |
$SiteName = Read-Host "Enter the site name as it appears in Local (probably snake-case)" | |
} | |
if ( [string]::IsNullOrEmpty($SiteName)) { | |
throw "Site name cannot be empty" | |
} | |
# Get Local database from app/sql | |
$sqlFilePath = "C:\Users\$username\LocalSites\$SiteName\app\sql\local.sql" | |
if (-not (Test-Path $sqlFilePath)) { | |
throw "SQL file does not exist: $sqlFilePath" | |
} | |
# Import it into Herd's MySQL server | |
$newDatabaseName = "$SiteName" + "_local" | |
try { | |
$createDbCommand = "CREATE DATABASE IF NOT EXISTS ``$newDatabaseName``;" | |
$result = & $herdMySQLPath -u root -P 3309 -e $createDbCommand | |
Write-Host "Database '$newDatabaseName' created successfully, or was already there." -ForegroundColor Green | |
} | |
catch { | |
throw "Error creating database: $_" | |
} | |
try { | |
Write-Host "Importing SQL file from Local into Herd database '$newDatabaseName'..." -ForegroundColor Cyan | |
$mysqlImportArgs = @("-u", "root", "-P", "3309", $newDatabaseName) | |
$importResult = Get-Content $sqlFilePath | & $herdMySQLPath $mysqlImportArgs | |
if ($LASTEXITCODE -ne 0) { | |
throw "Database import error: $importResult" | |
} | |
else { | |
Write-Host $importResult -ForegroundColor Green | |
} | |
} | |
catch { | |
throw "Error importing SQL file: $_" | |
} | |
# Construct site path | |
$sitePath = "C:\Users\$username\LocalSites\$SiteName\app\public" | |
# Check if site directory exists | |
if (-not (Test-Path $sitePath)) { | |
throw "Site directory does not exist: $sitePath" | |
} | |
# Navigate to site directory | |
Set-Location $sitePath | |
# Run herd link | |
$linkResult = & herd link $SiteName | |
if ($LASTEXITCODE -ne 0) { | |
throw $linkResult | |
} | |
else { | |
Write-Host $linkResult -ForegroundColor Green | |
} | |
# Run herd secure | |
$secureResult = & herd secure | |
if ($LASTEXITCODE -ne 0) { | |
Write-Warning "Herd secure command returned non-zero exit code: $secureResult" | |
} | |
else { | |
Write-Host $secureResult | |
} | |
# Setup for wp-config-local.php | |
$wpConfigPath = "C:\Users\$username\LocalSites\$SiteName\app\public" | |
$wpConfigFile = Join-Path $wpConfigPath "wp-config-local.php" | |
$getPrefixCommand = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '$newDatabaseName' AND TABLE_NAME LIKE '%_postmeta'" | |
# Get the table prefix from the database by getting the postmeta table name | |
$tablePrefixResult = & $herdMySQLPath -u root -P 3309 -D $newDatabaseName -e $getPrefixCommand | |
if ($LASTEXITCODE -ne 0) { | |
throw "Error retrieving table prefix: $tablePrefixResult" | |
} | |
# Extract the table prefix from the result and prepare it for the wp-config-local.php file | |
$tablePrefix = $tablePrefixResult[1] | |
$tablePrefix = $tablePrefix -replace 'postmeta$', '' # Remove postmeta from the result | |
$tablePrefix = $tablePrefix.trim() # Trim any remaining whitespace | |
$tablePrefix = "`$table_prefix = '$tablePrefix';" | |
# Make a request to https://api.wordpress.org/secret-key/1.1/salt/ and put the returned keys in a variable | |
$saltKeysUrl = "https://api.wordpress.org/secret-key/1.1/salt/" | |
$saltKeys = Invoke-RestMethod -Uri $saltKeysUrl -UseBasicParsing | |
if (-not $saltKeys) { | |
throw "Failed to retrieve WordPress salt keys from $saltKeysUrl" | |
} | |
# Prepare the wp-config-local.php content | |
$wpConfigContent = @" | |
<?php | |
/** | |
* Local WordPress configuration | |
* Generated by Herd Setup Script | |
*/ | |
// Database settings | |
define('DB_NAME', '$newDatabaseName'); | |
define('DB_USER', 'root'); | |
define('DB_PASSWORD', ''); | |
define('DB_HOST', '127.0.0.1:3309'); | |
define('DB_CHARSET', 'utf8'); | |
define('DB_COLLATE', ''); | |
$saltKeys | |
// Debug settings for local development | |
define('WP_DEBUG', true); | |
define('WP_DEBUG_LOG', true); | |
define('WP_DEBUG_DISPLAY', false); | |
define('WP_ENVIRONMENT_TYPE', 'local'); | |
// Globally installed Composer packages - enables Symfony Vardumper if it's not installed in the project and you have installed it globally | |
// If not, composer global require symfony/var-dumper | |
require_once('C:\Users\$username\AppData\Roaming\Composer\vendor\autoload.php'); | |
$tablePrefix | |
/** Sets up WordPress vars and included files. */ | |
require_once ABSPATH . 'wp-settings.php'; | |
// Additional local configuration can be added here | |
"@ | |
# Write the content to wp-config-local.php | |
try { | |
Set-Content $wpConfigFile -Value $wpConfigContent -Encoding UTF8 | |
Write-Host "wp-config-local.php created successfully at: $wpConfigFile" -ForegroundColor Green | |
} | |
catch { | |
throw "Error creating wp-config-local.php: $_" | |
} | |
# Add redirect at the top of the existing wp-config.php file if it isn't already there | |
$wpConfigFilePath = Join-Path $wpConfigPath "wp-config.php" | |
if (Test-Path $wpConfigFilePath) { | |
$redirectContent = @" | |
<?php | |
if (file_exists(__DIR__ . '/wp-config-local.php')) { | |
require_once __DIR__ . '/wp-config-local.php'; | |
} | |
"@ | |
try { | |
# Read existing content | |
$existingContent = Get-Content $wpConfigFilePath -Raw | |
# Check if the redirect already exists | |
if ($existingContent -match "require_once __DIR__ . '/wp-config-local.php';") { | |
Write-Host "Redirect already exists in wp-config.php, skipping addition." -ForegroundColor Yellow | |
} | |
else { | |
# If it doesn't exist, prepend the redirect content | |
# Remove the first <?php tag if it exists, because we're adding it back in the redirect content | |
if ($existingContent -match '^\s*<\?php') { | |
$existingContent = $existingContent -replace '^\s*<\?php', '' | |
} | |
Set-Content $wpConfigFilePath -Value ($redirectContent + $existingContent) -Encoding UTF8 | |
Write-Host "Local config redirect added to wp-config.php successfully." -ForegroundColor Green | |
} | |
} | |
catch { | |
throw "Error adding to wp-config.php: $_" | |
} | |
} | |
# Summary | |
Write-Host "" | |
Write-Host "==================== Setup Complete ====================" -ForegroundColor Green | |
Write-Host "URL: https://$SiteName.local" | |
Write-Host "To export your database back to Local, run 'local-export-db'." | |
Write-Host "========================================================" -ForegroundColor Green | |
} | |
catch { | |
Write-Host "Error: $_" -ForegroundColor Red | |
exit 1 | |
} | |
finally { | |
Set-Location $sitePath | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is a custom script that exports the database from Herd to the local.sql file where Local by Flywheel can use it. | |
# When placed in your user directory -> .config/herd/bin alongside a .bat file to run it, | |
# you can run it from anywhere using the command "local-export-db" in PowerShell. | |
param( | |
[string]$SiteName | |
) | |
# Function to check if a command exists | |
function Test-Command { | |
param([string]$Command) | |
$null = Get-Command $Command -ErrorAction SilentlyContinue | |
return $? | |
} | |
# Common variables | |
$username = $env:USERNAME | |
# Note: If these are in the system environment PATH, you can use just "mysql" and "mysqldump" instead of these full paths. | |
# Just check first that the aliases are resolving to the correct MySQL instances using "Get-Command mysql" and "Get-Command mysqldump". | |
$herdMySQLPath = "C:\Users\$username\.config\herd\bin\services\mysql\8.0.36\bin\mysql.exe" | |
$herdMySQLDumpPath = "C:\Users\$username\.config\herd\bin\services\mysql\8.0.36\bin\mysqldump.exe" | |
Write-Host "`n========== Herd to Local Database Export Script ==========" -ForegroundColor Green | |
Write-Host "`n Warnings:" | |
Write-Host "`t Ensure Herd and Local are running compatible versions of MySQL." | |
Write-Host "This script assumes that:" | |
Write-Host "`t - Herd's MySQL service is running on port 3309 [NOT THE STANDARD 3306]" | |
Write-Host "`t - You have set Laravel Herd to use .local as the TLD (if not, you will need to do a find and replace in the database)" | |
Write-Host "`t - Your Herd MySQL username is 'root' and the password is empty" | |
Write-Host "========================================================" -ForegroundColor Green | |
try { | |
# First, check prerequisites | |
$prerequisites = @( | |
"herd", | |
$herdMySQLPath, | |
$herdMySQLDumpPath | |
) | |
foreach ($prerequisite in $prerequisites) { | |
if (-not (Test-Command $prerequisite)) { | |
throw "Error: Required command or executable path '$prerequisite' not found." | |
} | |
} | |
# Get site name from the user if not provided in the initial command | |
if (-not $SiteName) { | |
$SiteName = Read-Host "Enter the site name as it appears in Local (probably snake-case)" | |
} | |
if ([string]::IsNullOrEmpty($SiteName)) { | |
throw "Site name cannot be empty" | |
} | |
# Check if the database exists in Herd's MySQL service | |
$herdDatabaseName = "$SiteName" + "_local" | |
$checkCommand = "SHOW DATABASES LIKE '$herdDatabaseName';" | |
$checkResult = & $herdMySQLPath -u root -P 3309 -e $checkCommand | |
if ($LASTEXITCODE -ne 0) { | |
throw $checkResult | |
} | |
$sqlFilePath = "C:\Users\$username\LocalSites\$SiteName\app\sql\local.sql" | |
# Rename the existing app/sql/local.sql file if it exists, so we don't overwrite it | |
if (Test-Path $sqlFilePath) { | |
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss" | |
$backupFilePath = "$sqlFilePath.bk-$timestamp.sql" | |
Rename-Item -Path $sqlFilePath -NewName $backupFilePath | |
Write-Host "Existing Local SQL file renamed to: $backupFilePath" -ForegroundColor Yellow | |
} | |
# Export the database from Herd to the local.sql file in the same location as $sqlFilePath was before renaming | |
$exportResult = & $herdMySQLDumpPath -u root -P 3309 $herdDatabaseName > $sqlFilePath | |
if ($LASTEXITCODE -ne 0) { | |
throw $exportResult | |
} | |
else { | |
Write-Host $exportResult | |
Write-Host "Database exported successfully to: $sqlFilePath" -ForegroundColor Green | |
} | |
} | |
catch { | |
Write-Host "Error: $_" -ForegroundColor Red | |
exit 1 | |
} | |
finally { | |
Set-Location $sitePath | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@echo off | |
powershell -ExecutionPolicy Bypass -File "%~dp02-local-export-db.ps1" %* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@echo off | |
powershell -ExecutionPolicy Bypass -File "%~dp01-local-to-herd.ps1" %* |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment