Skip to content

fix(create): roll back only the database and user this create made - #115

Merged
mrrobot47 merged 2 commits into
EasyEngine:developfrom
mrrobot47:fix/site-name-collisions
Sep 28, 2026
Merged

mrrobot47 merged 2 commits into
EasyEngine:developfrom
mrrobot47:fix/site-name-collisions

Conversation

@mrrobot47

@mrrobot47 mrrobot47 commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Depends on EasyEngine/site-command#503 and must be merged after it: this uses its new reserve_global_db_names() and check_site_name_conflicts().

This is a guard against rare name collisions and mistakes, not something a normal ee site create --type=php runs into. It only matters with --with-db when two site names map to the same database name or docker volume prefix, or when --dbname/--dbuser names a database or user that already exists. In those cases a failed create's rollback could drop the other site's database or user, or the new site could end up on the other site's volumes and containers.

When it happens

  • Colliding default DB names: a-b.test, a.b.test and a_b.test all map to a_b_test, so the second create fails and its rollback drops the first site's database.
  • A taken --dbname or --dbuser: the create fails and the rollback drops that database or user.
  • Colliding volume prefix or compose project (a-b.test / ab.test, blog.example.com / blogexample.com): the second site mounted the first site's volumes and recreated its containers.
  • A failed --ssl=custom check also removed a sites/<site> directory that already existed.

What changes

  • The rollback (catch_clean() and the signal handler) drops the database and user only when this run created them on the global DB.
  • The global DB names go through reserve_global_db_names() before anything is created.
  • check_site_name_conflicts() refuses a site whose webroot already exists or whose volumes or compose project another site uses, before the database step.
  • Level 0 lasts until the webroot exists, so an early failure keeps a directory this run did not create.
  • An interrupted create (for example Ctrl+C during the database or webroot step) now rolls back the database, user and webroot it had just made, instead of leaving them behind.

Behaviour changes

  • A taken --dbname or --dbuser is refused with a clear error. Such a create never succeeded before either.
  • A taken default DB name gets a _2 (then _3, …) suffix, stored in the site row as before. Default names are cut to 64 characters.
  • A site whose volume prefix or compose project collides with another site's is refused before anything is created.
  • Existing sites, including pairs that already collide, are untouched.

Testing

  • A harness that runs the real code against a simulated docker and DB, on PHP 7.4 and 8.5: every case reproduces on develop and passes with the fix.
  • A live host run of each case with the nightly and with a phar built from these branches: the nightly shows the problem, the fix refuses or rolls back cleanly. A normal --with-db create and delete works as before.
  • Tested live on a host with the final branch: the full collision matrix, an interrupt at each create step, and regular creates and deletes all behave as expected.

`ee site create --type=php --with-db` dropped another site's database or user when its create failed: `--dbname=<existing db>` (even `--dbname=mysql`), `--dbuser=<existing user>`, or the default name `str_replace(['.','-'],'_',site)` colliding (`a-b.test`, `a.b.test`, `a_b.test`). A failed `--ssl=custom` check also removed a pre-existing webroot.

- The rollback (`catch_clean()` and the signal handler) now drops the database and user only when this run created them on the global db.
- The global db names go through site-command's `reserve_global_db_names()` before anything is created: a taken `--dbname`/`--dbuser` is refused, a taken default name gets a numeric suffix, and the default is cut to 64 characters.
- `check_site_name_conflicts()` refuses a site whose webroot already exists or whose docker volumes or compose project another site uses, before the database step.
- Level 0 lasts until the webroot exists, so an early failure keeps a directory this run did not create.

Needs the site-command functions of the same change.
…ignal interrupts a step

Signals are handled only between statements of the site-type file, never inside the site-command helpers it calls. So a signal during `create_user_in_db()` was handled before `$this->created_global_db = true` ran, and the rollback left the new database and user behind. The flag is now set in the same statement as the call.

For the same reason the level is raised to 1 just before `create_site_root()`, not after it. At level 0 an interrupted create, or a failed `chown` inside it, would keep the webroot it had just made, and every retry would then be refused with "Webroot directory already exists".
@mrrobot47
mrrobot47 merged commit 2fa6ec3 into EasyEngine:develop Sep 28, 2026
0 of 4 checks passed
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