From 7e483d06ce401a448f37dda4d955ede193bada3b Mon Sep 17 00:00:00 2001 From: Darko Gjorgjijoski <5760249+gdarko@users.noreply.github.com> Date: Wed, 29 Jul 2026 12:37:41 +0200 Subject: [PATCH] fix(installation): support mariadb in the setup wizard (#707) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport of InvoiceShelf/InvoiceShelf#704 to 2.x. Fixes InvoiceShelf/docker#79. A fresh install using the shipped docker-compose.mysql.yml cannot get past the database step, because that compose file sets DB_CONNECTION=mariadb. getDatabaseEnvironment() switched on sqlite, pgsql and mysql with no arm for mariadb and no default, so it answered {"config":[]}. The wizard renders , which resolves to nothing, so the step came out blank — and nothing reached the log, because the app never errored, it just replied with nothing. Adds the mariadb arm plus a default, so an unrecognised driver is echoed back with server defaults rather than producing an unrenderable response. MariaDB is registered as an alias of the MySQL form, whose fields are identical, and offered in each driver picker. Tested against the original controller, where three of the new cases fail with "Failed asserting that null is identical to 'mariadb'". --- .../DatabaseConfigurationController.php | 22 +++++++ .../installation/Step3DatabaseConfig.vue | 4 ++ .../installation/database/MysqlDatabase.vue | 2 +- .../installation/database/PgsqlDatabase.vue | 2 +- .../installation/database/SqliteDatabase.vue | 2 +- .../DatabaseConfigurationTest.php | 58 +++++++++++++++++++ 6 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 tests/Feature/Installation/DatabaseConfigurationTest.php diff --git a/app/Http/Controllers/V1/Installation/DatabaseConfigurationController.php b/app/Http/Controllers/V1/Installation/DatabaseConfigurationController.php index 446a1e31..1c6971bf 100644 --- a/app/Http/Controllers/V1/Installation/DatabaseConfigurationController.php +++ b/app/Http/Controllers/V1/Installation/DatabaseConfigurationController.php @@ -77,6 +77,28 @@ class DatabaseConfigurationController extends Controller break; + case 'mariadb': + $databaseData = [ + 'database_connection' => 'mariadb', + 'database_host' => '127.0.0.1', + 'database_port' => 3306, + ]; + + break; + + default: + // Never return an empty config: the wizard picks its form from + // database_connection, so an unrecognised driver used to render + // a blank step with no way forward. Echo it back with the + // server defaults instead. + $databaseData = [ + 'database_connection' => $connection, + 'database_host' => '127.0.0.1', + 'database_port' => 3306, + ]; + + break; + } return response()->json([ diff --git a/resources/scripts/admin/views/installation/Step3DatabaseConfig.vue b/resources/scripts/admin/views/installation/Step3DatabaseConfig.vue index 1c442078..619365a4 100644 --- a/resources/scripts/admin/views/installation/Step3DatabaseConfig.vue +++ b/resources/scripts/admin/views/installation/Step3DatabaseConfig.vue @@ -28,6 +28,10 @@ export default { Mysql, Pgsql, Sqlite, + // Resolved by . MariaDB takes the same + // fields as MySQL, so it reuses that form rather than duplicating it — + // without an entry here the step renders blank. See InvoiceShelf/docker#79. + Mariadb: Mysql, }, emits: ['next'], diff --git a/resources/scripts/admin/views/installation/database/MysqlDatabase.vue b/resources/scripts/admin/views/installation/database/MysqlDatabase.vue index a0b422f8..5c945df6 100644 --- a/resources/scripts/admin/views/installation/database/MysqlDatabase.vue +++ b/resources/scripts/admin/views/installation/database/MysqlDatabase.vue @@ -133,7 +133,7 @@ const props = defineProps({ const emit = defineEmits(['submit-data', 'on-change-driver']) -const connections = reactive(['sqlite', 'mysql', 'pgsql']) +const connections = reactive(['sqlite', 'mysql', 'mariadb', 'pgsql']) const { t } = useI18n() const utils = inject('utils') diff --git a/resources/scripts/admin/views/installation/database/PgsqlDatabase.vue b/resources/scripts/admin/views/installation/database/PgsqlDatabase.vue index 2c0667a5..52745140 100644 --- a/resources/scripts/admin/views/installation/database/PgsqlDatabase.vue +++ b/resources/scripts/admin/views/installation/database/PgsqlDatabase.vue @@ -153,7 +153,7 @@ const props = defineProps({ const emit = defineEmits(['submit-data', 'on-change-driver']) -const connections = reactive(['sqlite', 'mysql', 'pgsql']) +const connections = reactive(['sqlite', 'mysql', 'mariadb', 'pgsql']) const { t } = useI18n() const utils = inject('utils') diff --git a/resources/scripts/admin/views/installation/database/SqliteDatabase.vue b/resources/scripts/admin/views/installation/database/SqliteDatabase.vue index c4b2e3f0..f7b8dfc7 100644 --- a/resources/scripts/admin/views/installation/database/SqliteDatabase.vue +++ b/resources/scripts/admin/views/installation/database/SqliteDatabase.vue @@ -97,7 +97,7 @@ const props = defineProps({ const emit = defineEmits(['submit-data', 'on-change-driver']) -const connections = reactive(['sqlite', 'mysql', 'pgsql']) +const connections = reactive(['sqlite', 'mysql', 'mariadb', 'pgsql']) const { t } = useI18n() const utils = inject('utils') diff --git a/tests/Feature/Installation/DatabaseConfigurationTest.php b/tests/Feature/Installation/DatabaseConfigurationTest.php new file mode 100644 index 00000000..07d436a6 --- /dev/null +++ b/tests/Feature/Installation/DatabaseConfigurationTest.php @@ -0,0 +1,58 @@ +assertOk(); + + expect($response->json('config.database_connection'))->toBe($connection); +})->with([ + 'mysql', + 'mariadb', + 'pgsql', + 'sqlite', +]); + +test('sqlite is told where its database file lives', function () { + $response = getJson('/api/v1/installation/database/config?connection=sqlite'); + + $response->assertOk(); + + expect($response->json('config.database_name'))->not->toBeEmpty(); +}); + +test('server-based drivers are given a host and port to prefill', function (string $connection) { + $response = getJson("/api/v1/installation/database/config?connection={$connection}"); + + expect($response->json('config.database_host'))->not->toBeEmpty() + ->and($response->json('config.database_port'))->not->toBeEmpty(); +})->with([ + 'mysql', + 'mariadb', + 'pgsql', +]); + +/** + * A driver we do not recognise must still produce something the wizard can + * render, rather than the empty config that caused #79. + */ +test('an unrecognised driver still returns a renderable config', function () { + $response = getJson('/api/v1/installation/database/config?connection=cockroach'); + + $response->assertOk(); + + expect($response->json('config.database_connection'))->toBe('cockroach') + ->and($response->json('config'))->not->toBeEmpty(); +});