Skip to content

fix(clone): allow ee site clone onto a host with no existing sites - #501

Open
mrrobot47 wants to merge 5 commits into
EasyEngine:developfrom
mrrobot47:fix/clone-empty-host-site-list
Open

mrrobot47 wants to merge 5 commits into
EasyEngine:developfrom
mrrobot47:fix/clone-empty-host-site-list

Conversation

@mrrobot47

@mrrobot47 mrrobot47 commented Jul 16, 2026 •

Copy link
Copy Markdown
Member

Problem

ee site clone aborts with:

Warning: Encountered error while cloning site. Rolling back.
Error: Unable to get site list on @localhost

whenever the destination host has zero existing EasyEngine sites — making it impossible to clone the very first site onto a fresh/empty host. ee site sync hits the same misleading error.

Root cause

Cloner::site_exists() inspected stdout for the Error: No sites found! sentinel, but on an empty host ee site list calls \EE::error( 'No sites found!' ), which writes that message to stderr and exits 1. The special-case never matched, so execution fell through to the generic Unable to get site list throw.

It fails whenever the streams stay separate: a local source or destination, and ssh when ee isn't run from a terminal (ssh -t only allocates a pty when stdin is a tty, so automation such as EasyDash hits it on remote hosts too). From an interactive terminal, ssh -t's pty merges the remote stderr into stdout, which the old stdout match happened to catch.

Fix

  • Match the sentinel against stderr . stdout (covers both the local separate-streams case and the ssh -t merged case), using strpos to tolerate a trailing newline / surrounding whitespace.
  • Extract the check into is_no_sites_error(), gated on exit code 1 (the code \EE::error always exits with) so an unrelated non-zero failure can't be mistaken for "no sites" via a substring match.
  • validate_parent_site_present_on_host() (reached on same-name --ssl=inherit clones) no longer runs foreach over null on an empty host. Only the "No sites found!" error counts as an empty host; any other unparseable ee site list result, including exit 0 with non-JSON output, now fails with Unable to get site list instead of being reported as "parent site not found".
  • site_exists() now also fails with Unable to get site list when ee site list exits 0 with output that isn't a JSON list (e.g. shell-rc output or a PHP notice on stdout). Before, it reported the site as missing, so a clone onto an existing destination site tried to create it, failed, and the rollback then deleted the existing destination site.
  • The parent-site check for same-name --ssl=inherit clones accepts the wildcard flag as an int. On PHP 8.1+ ee site info --format=json returns site_ssl_wildcard as 1, not '1', so the strict === '1' comparison never matched and these clones to another host always failed with "Parent site … not found", even when the parent had wildcard SSL. Both fields are now checked with empty().

Verification

  • Empty host: ee site clone a.test b.test and ee site sync a.test b.test now fail with Unable to find 'a.test' on 'localhost' instead of Unable to get site list on @localhost. The same happens with a remote source when ee runs without a terminal. The pty-merged ssh -t output is still recognized.
  • Clone onto an empty destination host gets past the existence check and runs ee site create there (before: Unable to get site list on user@host).
  • ee site sync onto an empty destination still fails, because the destination site must exist; the error is now Unable to find '<site>' on '<host>'.
  • --ssl=inherit same-name clone onto an empty host: "Parent site … not found" with no PHP warning; exit 0 with unparseable output gives Unable to get site list.
  • With existing sites: a local clone, cloning onto an existing site (refused, destination untouched), sync --files, sync to a missing site, a clone from a remote source, and a disabled source all behave as before.
  • Clone onto an existing destination site whose ssh session prints extra output before ee site list (exit 0): Unable to get site list on <host>, no ee site delete is run, and the destination site keeps working.
  • --ssl=inherit same-name clone to another host whose parent has wildcard SSL: the parent is found and the clone goes on to ee site create … --ssl=inherit (before: "Parent site … not found").

Tested on Ubuntu 26.04 with EasyEngine 4.12.0 with local clones and syncs, and over real ssh to root@localhost: the empty-host case with and without a terminal, a clone from root@localhost onto the local host, and the destination with extra output in its ssh session. Because the test host is both source and destination, the clone onto an empty remote destination and the cross-host --ssl=inherit clone were exercised with ssh replaced by a local stand-in that runs the commands locally and replays the destination's recorded ee site list output.

`ee site clone`/`ee site sync` aborted with "Unable to get site list"
whenever the destination host had zero existing EE sites. On an empty
host `ee site list` calls \EE::error('No sites found!'), which writes to
stderr and exits 1, but site_exists() inspected stdout for the sentinel,
so the special-case never matched and the generic error path threw.

Match the sentinel against stderr . stdout (covering both the local
separate-streams case and the ssh -t merged-output case) and use strpos
to tolerate trailing whitespace/debug noise.

Also guard validate_parent_site_present_on_host() against a null
json_decode on an empty host, which previously triggered a PHP warning
from foreach(null).

Refs rtCamp/EasyDash#4135
- Extract is_no_sites_error() so site_exists() and
  validate_parent_site_present_on_host() share one sentinel check.
- Narrow the gate back to exit code 1 (the code \EE::error always exits
  with) so an unrelated non-zero failure can no longer be mistaken for
  'no sites' via a substring match.
- validate_parent_site_present_on_host() now distinguishes an empty host
  from a genuine 'ee site list' failure, surfacing the latter as
  'Unable to get site list' instead of masking it as 'parent not found'.
validate_parent_site_present_on_host() treated any non-JSON 'ee site list' output with exit code 0 as an empty host, so garbled output (e.g. stderr noise merged into stdout under ssh -t) was still reported as 'Parent site not found'. Only the 'No sites found!' error now counts as an empty host.
site_exists() read `ee site list` output that exited 0 but wasn't JSON (e.g. shell-rc noise under ssh -t) as "site doesn't exist". For an existing destination site, the clone then ran `ee site create`, which failed, and the create step's rollback ran `ee site delete --yes` on that existing site. Throw "Unable to get site list" instead, like the parent-site check does.
On PHP 8.1+ PDO SQLite returns native ints, so `ee site info --format=json` gives `site_ssl_wildcard: 1` and the strict `=== '1'` check never matched. Same-name `--ssl=inherit` clones to another host then always failed with "Parent site not found", even when the parent had wildcard SSL. Check both fields with `empty()` instead.
@mrrobot47 mrrobot47 changed the title fix(clone): allow ee site clone/sync onto a host with no existing sites fix(clone): allow ee site clone onto a host with no existing sites Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant