Site Cloning
CloneSiteJob runs CloneEngine::clone() to copy an existing site to a target (e.g. staging → production or new clone). Files are synced with rsync, database is dumped and restored, then URLs are rewritten.
Flow
flowchart LR
subgraph clone [CloneEngine]
A[ensureTargetSystemUser]
B[ensureTargetDatabase]
C[syncFiles]
D[syncDatabase]
E[rewriteUrls]
F[ensureConfig]
G[ensureWphosterAgent]
H[configureCloudflare]
end
A --> B --> C --> D --> E --> F --> G --> H
Steps
- ensureTargetSystemUser — Creates OS user for target site if not set (
UserManager::createSiteUser). - ensureTargetDatabase — Creates MySQL database and user for target (or reuses if
direction !== 'clone'). - syncFiles —
rsync -a --delete --exclude wp-config.phpfrom source root to target root. wp-config is not copied so target gets its own DB credentials later. - syncDatabase —
mysqldumpsource DB to temp file, thenmysql ... source <file>into target DB. - rewriteUrls —
wp search-replacesource FQDN → target FQDN (all tables, skip guid), thenwp option update homeandsiteurl. - ensureConfig —
wp config createwith target DB credentials andwp config shuffle-salts. - ensureWphosterAgent — Re-install or update agent on target (same as provisioning).
- configureCloudflare — Upsert A record for target FQDN.
Key files
| Path | Purpose |
|---|---|
app/Services/Provisioning/CloneEngine.php | Clone logic |
app/Jobs/CloneSiteJob.php | Queue job |
Code snippet (syncFiles)
$this->runner->run([
'rsync',
'-a',
'--delete',
'--exclude',
'wp-config.php',
rtrim($source->root_path, '/') . '/',
rtrim($target->root_path, '/') . '/',
]);
Code snippet (rewriteUrls)
$this->runAsSiteUser($target, [
$wpCli,
'search-replace',
$source->fqdn,
$target->fqdn,
'--all-tables',
'--skip-columns=guid',
]);
$this->runAsSiteUser($target, [$wpCli, 'option', 'update', 'home', $target->fqdn]);
$this->runAsSiteUser($target, [$wpCli, 'option', 'update', 'siteurl', $target->fqdn]);