Skip to content

Make Sail Server compliant with Laravel 12 #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions resources/scripts/php.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ docker run --rm \
-v "$(pwd)":/opt \
-w /opt \
laravelsail/php{{ php }}-composer:latest \
bash -c "laravel new {{ name }} --no-interaction && cd {{ name }} && php ./artisan sail:install --with={{ with }} {{ devcontainer }}"
bash -c "composer global require laravel/installer && rm /usr/bin/laravel && ln -s ~/.composer/vendor/bin/laravel /usr/bin/laravel && laravel new {{ name }} {{ frontend }} {{ authProvider }} {{ testFramework }} --no-interaction && cd {{ name }} && php ./artisan sail:install --with={{ with }} {{ devcontainer }}"

cd {{ name }}

Expand Down Expand Up @@ -42,11 +42,11 @@ fi

if $SUDO -n true 2>/dev/null; then
$SUDO chown -R $USER: .
echo -e "${BOLD}Get started with:${NC} cd {{ name }} && ./vendor/bin/sail up"
echo -e "${BOLD}Get started with:${NC} cd {{ name }} && ./vendor/bin/sail up && ./vendor/bin/sail npm install && ./vendor/bin/sail npm run dev"
else
echo -e "${BOLD}Please provide your password so we can make some final adjustments to your application's permissions.${NC}"
echo ""
$SUDO chown -R $USER: .
echo ""
echo -e "${BOLD}Thank you! We hope you build something incredible. Dive in with:${NC} cd {{ name }} && ./vendor/bin/sail up"
echo -e "${BOLD}Thank you! We hope you build something incredible. Dive in with:${NC} cd {{ name }} && ./vendor/bin/sail up && ./vendor/bin/sail npm install && ./vendor/bin/sail npm run dev"
fi
55 changes: 53 additions & 2 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'mysql',
'pgsql',
'mariadb',
'mongodb',
'redis',
'valkey',
'memcached',
Expand All @@ -25,16 +26,39 @@
'soketi',
];

$availableFrontends = [
'react',
'vue',
'livewire',
'livewire-class-components',
];

$availableAuthentication = [
'workos',
];

$availableTestFrameworks = [
'phpunit',
'pest',
];

$php = $request->query('php', '84');

$with = array_unique(explode(',', $request->query('with', 'mysql,redis,meilisearch,mailpit,selenium')));

$frontend = $request->query('frontend', 'livewire');
$auth = $request->query('auth', null);
$tests = $request->query('tests', 'pest');

try {
Validator::validate(
[
'name' => $name,
'php' => $php,
'with' => $with,
'frontend' => $frontend,
'auth' => $auth,
'tests' => $tests,
],
[
'name' => 'string|alpha_dash',
Expand All @@ -45,6 +69,10 @@
'string',
count($with) === 1 && in_array('none', $with) ? Rule::in(['none']) : Rule::in($availableServices)
],

'frontend' => ['string', Rule::in($availableFrontends)],
'auth' => ['nullable', 'string', Rule::in($availableAuthentication)],
'tests' => ['string', Rule::in($availableTestFrameworks)],
]
);
} catch (ValidationException $e) {
Expand All @@ -61,6 +89,18 @@
if (array_key_exists('with', $errors)) {
return response('Invalid service name. Please provide one or more of the supported services ('.implode(', ', $availableServices).') or "none".', 400);
}

if (array_key_exists('frontend', $errors)) {
return response('Invalid frontend. Please provide one supported frontend ('.implode(', ', $availableFrontends).') or leave it empty (it will use livewire).', 400);
}

if (array_key_exists('auth', $errors)) {
return response('Invalid Authentication Provider. Please provide one supported Authentication Provider ('.implode(', ', $availableAuthentication).') or leave it empty (it will use laravel).', 400);
}

if (array_key_exists('tests', $errors)) {
return response('Invalid testing framework. Please provide one supported testing framework ('.implode(', ', $availableTestFrameworks).') or leave it empty (it will use pest).', 400);
}
}

$services = implode(' ', $with);
Expand All @@ -69,9 +109,20 @@

$devcontainer = $request->has('devcontainer') ? '--devcontainer' : '';

//Prepend -- to frontend, auth and test
$frontend = ($frontend) ? "--{$frontend}" : null;
$testFramework = ($tests) ? "--{$tests}" : null;

/*
* Adding a trailing space because if not on all tests i have to fix to
* laravel new example-app --livewire --no-interaction
* It will have two spaces after --livewire
*/
$auth = ($auth) ? "--{$auth} " : null;

$script = str_replace(
['{{ php }}', '{{ name }}', '{{ with }}', '{{ devcontainer }}', '{{ services }}'],
[$php, $name, $with, $devcontainer, $services],
['{{ php }}', '{{ name }}', '{{ frontend }}', '{{ authProvider }} ', '{{ testFramework }}', '{{ with }}', '{{ devcontainer }}', '{{ services }}'],
[$php, $name, "$frontend", "$auth", "$testFramework", $with, $devcontainer, $services],
file_get_contents(resource_path('scripts/php.sh'))
);

Expand Down
34 changes: 34 additions & 0 deletions tests/Feature/Laravel12AuthTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class Laravel12AuthTest extends TestCase
{
public function test_it_accepts_no_authentication()
{
$response = $this->get('/example-app');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --livewire --pest --no-interaction', false);
}

public function test_it_accepts_auth_workos()
{
$response = $this->get('/example-app?auth=workos');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --livewire --workos --pest --no-interaction', false);
}

public function test_it_does_not_accept_invalid_auth()
{
$response = $this->get('/example-app?auth=invalid');

$response->assertStatus(400);
$response->assertSee('Invalid Authentication Provider. Please provide one supported Authentication Provider (workos) or leave it empty (it will use laravel).');
}
}
58 changes: 58 additions & 0 deletions tests/Feature/Laravel12FrontendTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class Laravel12FrontendTest extends TestCase
{
public function test_it_accepts_no_frontend_and_uses_livewire()
{
$response = $this->get('/example-app');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --livewire --pest --no-interaction', false);
}

public function test_it_accepts_frontend_livewire()
{
$response = $this->get('/example-app?frontend=livewire');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --livewire --pest --no-interaction', false);
}

public function test_it_accepts_frontend_react()
{
$response = $this->get('/example-app?frontend=react');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --react --pest --no-interaction', false);
}

public function test_it_accepts_frontend_vue()
{
$response = $this->get('/example-app?frontend=vue');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --vue --pest --no-interaction', false);
}

public function test_it_accepts_frontend_livewire_class_components()
{
$response = $this->get('/example-app?frontend=livewire-class-components');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --livewire-class-components --pest --no-interaction', false);
}

public function test_it_does_not_accept_invalid_frontend()
{
$response = $this->get('/example-app?frontend=invalid');

$response->assertStatus(400);
$response->assertSee('Invalid frontend. Please provide one supported frontend (react, vue, livewire, livewire-class-components) or leave it empty (it will use livewire).');
}
}
18 changes: 18 additions & 0 deletions tests/Feature/Laravel12Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class Laravel12Test extends TestCase
{
public function test_it_should_update_laravel_installer()
{
$response = $this->get('/example-app');

$response->assertStatus(200);
$response->assertSee('bash -c "composer global require laravel/installer && rm /usr/bin/laravel && ln -s ~/.composer/vendor/bin/laravel /usr/bin/laravel && laravel new example-app --livewire --pest --no-interaction && cd example-app && php ./artisan sail:install --with=mysql,redis,meilisearch,mailpit,selenium "', false);
}
}
42 changes: 42 additions & 0 deletions tests/Feature/Laravel12TestFrameworkTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class Laravel12TestFrameworkTest extends TestCase
{
public function test_it_accepts_no_test_and_uses_pest()
{
$response = $this->get('/example-app');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --livewire --pest --no-interaction', false);
}

public function test_it_accepts_test_pest()
{
$response = $this->get('/example-app?tests=pest');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --livewire --pest --no-interaction', false);
}

public function test_it_accepts_test_phpunit()
{
$response = $this->get('/example-app?tests=phpunit');

$response->assertStatus(200);
$response->assertSee('laravel new example-app --livewire --phpunit --no-interaction', false);
}

public function test_it_does_not_accept_invalid_test()
{
$response = $this->get('/example-app?tests=invalid');

$response->assertStatus(400);
$response->assertSee('Invalid testing framework. Please provide one supported testing framework (phpunit, pest) or leave it empty (it will use pest).');
}
}
12 changes: 6 additions & 6 deletions tests/Feature/SailServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function test_it_can_return_the_sail_install_script()

$response->assertStatus(200);
$response->assertSee("laravelsail/php84-composer:latest");
$response->assertSee('bash -c "laravel new example-app --no-interaction && cd example-app && php ./artisan sail:install --with=mysql,redis,meilisearch,mailpit,selenium "', false);
$response->assertSee('bash -c "composer global require laravel/installer && rm /usr/bin/laravel && ln -s ~/.composer/vendor/bin/laravel /usr/bin/laravel && laravel new example-app --livewire --pest --no-interaction && cd example-app && php ./artisan sail:install --with=mysql,redis,meilisearch,mailpit,selenium "', false);
}

public function test_different_php_versions_can_be_picked()
Expand Down Expand Up @@ -49,15 +49,15 @@ public function test_it_removes_duplicated_valid_services()
$response = $this->get('/example-app?with=redis,redis');

$response->assertStatus(200);
$response->assertSee('bash -c "laravel new example-app --no-interaction && cd example-app && php ./artisan sail:install --with=redis "', false);
$response->assertSee('bash -c "composer global require laravel/installer && rm /usr/bin/laravel && ln -s ~/.composer/vendor/bin/laravel /usr/bin/laravel && laravel new example-app --livewire --pest --no-interaction && cd example-app && php ./artisan sail:install --with=redis "', false);
}

public function test_it_adds_the_devcontainer_upon_request()
{
$response = $this->get('/example-app?with=pgsql&devcontainer');

$response->assertStatus(200);
$response->assertSee('bash -c "laravel new example-app --no-interaction && cd example-app && php ./artisan sail:install --with=pgsql --devcontainer"', false);
$response->assertSee('bash -c "composer global require laravel/installer && rm /usr/bin/laravel && ln -s ~/.composer/vendor/bin/laravel /usr/bin/laravel && laravel new example-app --livewire --pest --no-interaction && cd example-app && php ./artisan sail:install --with=pgsql --devcontainer"', false);
}

public function test_it_does_not_accepts_domains_with_a_dot()
Expand Down Expand Up @@ -89,22 +89,22 @@ public function test_it_does_not_accept_empty_with_query_when_present()
$response = $this->get('/example-app?with');

$response->assertStatus(400);
$response->assertSee('Invalid service name. Please provide one or more of the supported services (mysql, pgsql, mariadb, redis, valkey, memcached, meilisearch, typesense, minio, mailpit, selenium, soketi) or "none".', false);
$response->assertSee('Invalid service name. Please provide one or more of the supported services (mysql, pgsql, mariadb, mongodb, redis, valkey, memcached, meilisearch, typesense, minio, mailpit, selenium, soketi) or "none".', false);
}

public function test_it_does_not_accept_invalid_services()
{
$response = $this->get('/example-app?with=redis,invalid_service_name');

$response->assertStatus(400);
$response->assertSee('Invalid service name. Please provide one or more of the supported services (mysql, pgsql, mariadb, redis, valkey, memcached, meilisearch, typesense, minio, mailpit, selenium, soketi) or "none".', false);
$response->assertSee('Invalid service name. Please provide one or more of the supported services (mysql, pgsql, mariadb, mongodb, redis, valkey, memcached, meilisearch, typesense, minio, mailpit, selenium, soketi) or "none".', false);
}

public function test_it_does_not_accept_none_with_other_services()
{
$response = $this->get('/example-app?with=none,redis');

$response->assertStatus(400);
$response->assertSee('Invalid service name. Please provide one or more of the supported services (mysql, pgsql, mariadb, redis, valkey, memcached, meilisearch, typesense, minio, mailpit, selenium, soketi) or "none".', false);
$response->assertSee('Invalid service name. Please provide one or more of the supported services (mysql, pgsql, mariadb, mongodb, redis, valkey, memcached, meilisearch, typesense, minio, mailpit, selenium, soketi) or "none".', false);
}
}