* Fix CI/CD php version

* Add Pint
* Format code
This commit is contained in:
Fabián Gonzalo Artur de la Villarmois
2026-03-29 17:49:51 +13:00
parent 0993bfa666
commit bae79d68ab
17 changed files with 114 additions and 59 deletions
+25 -2
View File
@@ -16,7 +16,7 @@ jobs:
- name: Set up PHP - name: Set up PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: '8.2' php-version: '8.4'
extensions: mbstring extensions: mbstring
coverage: none coverage: none
@@ -34,10 +34,33 @@ jobs:
working-directory: www working-directory: www
run: php artisan test run: php artisan test
lint:
name: Check code style (Pint)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
extensions: mbstring
coverage: none
- name: Install Composer dependencies
working-directory: www
run: composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Run Pint
working-directory: www
run: ./vendor/bin/pint --test
docker: docker:
name: Build and push Docker image name: Build and push Docker image
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test needs: [test, lint]
steps: steps:
- name: Checkout - name: Checkout
+24 -1
View File
@@ -16,7 +16,7 @@ jobs:
- name: Set up PHP - name: Set up PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: '8.2' php-version: '8.4'
extensions: mbstring extensions: mbstring
coverage: none coverage: none
@@ -33,3 +33,26 @@ jobs:
- name: Run tests - name: Run tests
working-directory: www working-directory: www
run: php artisan test run: php artisan test
lint:
name: Check code style (Pint)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
extensions: mbstring
coverage: none
- name: Install Composer dependencies
working-directory: www
run: composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Run Pint
working-directory: www
run: ./vendor/bin/pint --test
+1 -1
View File
@@ -18,7 +18,7 @@ enum NintendoFileExtension: string
*/ */
public static function values(): array public static function values(): array
{ {
return array_map(fn($case) => $case->value, self::cases()); return array_map(fn ($case) => $case->value, self::cases());
} }
/** /**
+3 -2
View File
@@ -5,6 +5,7 @@ namespace App\Helpers;
class FileSize class FileSize
{ {
private const UNITS = ['', 'K', 'M', 'G', 'T']; private const UNITS = ['', 'K', 'M', 'G', 'T'];
private const THRESHOLD = 1024; private const THRESHOLD = 1024;
/** /**
@@ -18,10 +19,10 @@ class FileSize
$unitIndex = (int) floor(log($bytes, self::THRESHOLD)); $unitIndex = (int) floor(log($bytes, self::THRESHOLD));
$unitIndex = min($unitIndex, count(self::UNITS) - 1); $unitIndex = min($unitIndex, count(self::UNITS) - 1);
$size = $bytes / pow(self::THRESHOLD, $unitIndex); $size = $bytes / pow(self::THRESHOLD, $unitIndex);
$decimals = $unitIndex === 0 ? 0 : 2; $decimals = $unitIndex === 0 ? 0 : 2;
return number_format($size, $decimals, '.', '') . self::UNITS[$unitIndex]; return number_format($size, $decimals, '.', '').self::UNITS[$unitIndex];
} }
} }
@@ -35,6 +35,7 @@ class IndexController extends Controller
private function getRequestPath(Request $request): ?string private function getRequestPath(Request $request): ?string
{ {
$path = $request->route('path'); $path = $request->route('path');
return $path ? rawurldecode($path) : null; return $path ? rawurldecode($path) : null;
} }
@@ -51,9 +52,9 @@ class IndexController extends Controller
*/ */
private function serveFile(string $path): Response|BinaryFileResponse private function serveFile(string $path): Response|BinaryFileResponse
{ {
$storagePath = self::GAMES_DIRECTORY . '/' . $path; $storagePath = self::GAMES_DIRECTORY.'/'.$path;
if (!Storage::exists($storagePath)) { if (! Storage::exists($storagePath)) {
return response('File not found', 404); return response('File not found', 404);
} }
@@ -68,19 +69,19 @@ class IndexController extends Controller
*/ */
private function serveDirectoryListing(?string $path, Request $request): Response private function serveDirectoryListing(?string $path, Request $request): Response
{ {
$storagePath = $path $storagePath = $path
? self::GAMES_DIRECTORY . '/' . $path ? self::GAMES_DIRECTORY.'/'.$path
: self::GAMES_DIRECTORY; : self::GAMES_DIRECTORY;
$directories = $this->getDirectories($storagePath); $directories = $this->getDirectories($storagePath);
$files = $this->getFiles($storagePath); $files = $this->getFiles($storagePath);
// Determine if this is a DBI client or regular browser // Determine if this is a DBI client or regular browser
$isDBI = str_starts_with($request->userAgent() ?? '', 'DBI/'); $isDBI = str_starts_with($request->userAgent() ?? '', 'DBI/');
// Determine base URL for links based on request path // Determine base URL for links based on request path
$baseUrl = str_starts_with($request->path(), 'api/dbi') ? '/api/dbi/' : '/'; $baseUrl = str_starts_with($request->path(), 'api/dbi') ? '/api/dbi/' : '/';
$html = $this->buildDirectoryListingHtml($directories, $files, $path ?? '', $baseUrl, $isDBI); $html = $this->buildDirectoryListingHtml($directories, $files, $path ?? '', $baseUrl, $isDBI);
return response($html)->header('Content-Type', 'text/html'); return response($html)->header('Content-Type', 'text/html');
@@ -131,8 +132,8 @@ class IndexController extends Controller
*/ */
private function buildDirectoryListingHtml(Collection $directories, Collection $files, string $currentPath, string $baseUrl, bool $isDBI): string private function buildDirectoryListingHtml(Collection $directories, Collection $files, string $currentPath, string $baseUrl, bool $isDBI): string
{ {
$displayPath = $currentPath $displayPath = $currentPath
? '/games/' . rawurldecode($currentPath) . '/' ? '/games/'.rawurldecode($currentPath).'/'
: '/games/'; : '/games/';
return view('dbi.directory-listing', [ return view('dbi.directory-listing', [
@@ -151,7 +152,7 @@ class IndexController extends Controller
*/ */
private function getParentDirectoryUrl(string $currentPath, string $baseUrl, bool $isDBI): ?string private function getParentDirectoryUrl(string $currentPath, string $baseUrl, bool $isDBI): ?string
{ {
if (!$currentPath) { if (! $currentPath) {
return null; return null;
} }
@@ -167,8 +168,8 @@ class IndexController extends Controller
return $baseUrl; return $baseUrl;
} }
return rtrim($baseUrl, '/') . '/' . collect(explode('/', $parentPath)) return rtrim($baseUrl, '/').'/'.collect(explode('/', $parentPath))
->map(fn ($segment) => rawurlencode($segment)) ->map(fn ($segment) => rawurlencode($segment))
->implode('/'); ->implode('/');
} }
} }
@@ -18,7 +18,7 @@ class IndexController extends Controller
{ {
return response()->json([ return response()->json([
'files' => $this->getInstallableFiles()->values(), 'files' => $this->getInstallableFiles()->values(),
'success' => config('app.name') 'success' => config('app.name'),
]); ]);
} }
@@ -31,7 +31,7 @@ class IndexController extends Controller
->filter(fn ($path) => NintendoFileExtension::isSupported($path)) ->filter(fn ($path) => NintendoFileExtension::isSupported($path))
->map(fn ($path) => [ ->map(fn ($path) => [
'url' => url($path), 'url' => url($path),
'size' => Storage::size($path) 'size' => Storage::size($path),
]); ]);
} }
} }
@@ -6,20 +6,17 @@ use Closure;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
class EnsureIsTinfoilClient class EnsureIsTinfoilClient
{ {
/** /**
* Handle an incoming request. * Handle an incoming request.
* *
* @param Request $request * @param Closure(Request): (Response|RedirectResponse) $next
* @param Closure(Request): (Response|RedirectResponse) $next
* @return Response|RedirectResponse
*/ */
public function handle(Request $request, Closure $next): Response|RedirectResponse public function handle(Request $request, Closure $next): Response|RedirectResponse
{ {
if (!!$request->header('theme')) { if ((bool) $request->header('theme')) {
return redirect()->route('tinfoil'); return redirect()->route('tinfoil');
} }
+3 -1
View File
@@ -1,5 +1,7 @@
<?php <?php
use App\Providers\AppServiceProvider;
return [ return [
App\Providers\AppServiceProvider::class, AppServiceProvider::class,
]; ];
+2 -2
View File
@@ -9,14 +9,14 @@
], ],
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^8.2", "php": "^8.4",
"laravel/framework": "^12.0", "laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1" "laravel/tinker": "^2.10.1"
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.23", "fakerphp/faker": "^1.23",
"laravel/pail": "^1.2.2", "laravel/pail": "^1.2.2",
"laravel/pint": "^1.24", "laravel/pint": "^1.29",
"laravel/sail": "^1.41", "laravel/sail": "^1.41",
"mockery/mockery": "^1.6", "mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6", "nunomaduro/collision": "^8.6",
+14 -13
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "cf274898566d29ebdc912d11f0d0a6e3", "content-hash": "f10e5685efd94a89d824ca6ed03c8b85",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@@ -6525,16 +6525,16 @@
}, },
{ {
"name": "laravel/pint", "name": "laravel/pint",
"version": "v1.26.0", "version": "v1.29.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/pint.git", "url": "https://github.com/laravel/pint.git",
"reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f" "reference": "bdec963f53172c5e36330f3a400604c69bf02d39"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/69dcca060ecb15e4b564af63d1f642c81a241d6f", "url": "https://api.github.com/repos/laravel/pint/zipball/bdec963f53172c5e36330f3a400604c69bf02d39",
"reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f", "reference": "bdec963f53172c5e36330f3a400604c69bf02d39",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -6545,13 +6545,14 @@
"php": "^8.2.0" "php": "^8.2.0"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^3.90.0", "friendsofphp/php-cs-fixer": "^3.94.2",
"illuminate/view": "^12.40.1", "illuminate/view": "^12.54.1",
"larastan/larastan": "^3.8.0", "larastan/larastan": "^3.9.3",
"laravel-zero/framework": "^12.0.4", "laravel-zero/framework": "^12.0.5",
"mockery/mockery": "^1.6.12", "mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^2.3.3", "nunomaduro/termwind": "^2.4.0",
"pestphp/pest": "^3.8.4" "pestphp/pest": "^3.8.6",
"shipfastlabs/agent-detector": "^1.1.0"
}, },
"bin": [ "bin": [
"builds/pint" "builds/pint"
@@ -6588,7 +6589,7 @@
"issues": "https://github.com/laravel/pint/issues", "issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint" "source": "https://github.com/laravel/pint"
}, },
"time": "2025-11-25T21:15:52+00:00" "time": "2026-03-12T15:51:39+00:00"
}, },
{ {
"name": "laravel/sail", "name": "laravel/sail",
@@ -9339,7 +9340,7 @@
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "^8.2" "php": "^8.4"
}, },
"platform-dev": {}, "platform-dev": {},
"plugin-api-version": "2.9.0" "plugin-api-version": "2.9.0"
+3 -1
View File
@@ -1,5 +1,7 @@
<?php <?php
use App\Models\User;
return [ return [
/* /*
@@ -62,7 +64,7 @@ return [
'providers' => [ 'providers' => [
'users' => [ 'users' => [
'driver' => 'eloquent', 'driver' => 'eloquent',
'model' => env('AUTH_MODEL', App\Models\User::class), 'model' => env('AUTH_MODEL', User::class),
], ],
// 'users' => [ // 'users' => [
+3 -2
View File
@@ -1,6 +1,7 @@
<?php <?php
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Pdo\Mysql;
return [ return [
@@ -59,7 +60,7 @@ return [
'strict' => true, 'strict' => true,
'engine' => null, 'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([ 'options' => extension_loaded('pdo_mysql') ? array_filter([
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
]) : [], ]) : [],
], ],
@@ -79,7 +80,7 @@ return [
'strict' => true, 'strict' => true,
'engine' => null, 'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([ 'options' => extension_loaded('pdo_mysql') ? array_filter([
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
]) : [], ]) : [],
], ],
+4 -2
View File
@@ -1,5 +1,7 @@
<?php <?php
use App\Http\Controllers\DBI\IndexController as DBIIndexController;
use App\Http\Controllers\Tinfoil\IndexController as TinfoilIndexController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
/* /*
@@ -7,9 +9,9 @@ use Illuminate\Support\Facades\Route;
| API Routes | API Routes
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
*/ */
Route::get('tinfoil', \App\Http\Controllers\Tinfoil\IndexController::class) Route::get('tinfoil', TinfoilIndexController::class)
->name('tinfoil'); ->name('tinfoil');
Route::get('dbi/{path?}', \App\Http\Controllers\DBI\IndexController::class) Route::get('dbi/{path?}', DBIIndexController::class)
->where('path', '.*') ->where('path', '.*')
->name('dbi'); ->name('dbi');
+9 -9
View File
@@ -6,37 +6,37 @@ use Illuminate\Support\Facades\Route;
Route::view('/', 'welcome')->middleware([ Route::view('/', 'welcome')->middleware([
EnsureIsDbiClient::class, EnsureIsDbiClient::class,
EnsureIsTinfoilClient::class EnsureIsTinfoilClient::class,
]); ]);
Route::get('/docs/{page}', function ($page) { Route::get('/docs/{page}', function ($page) {
$allowedPages = ['TINFOIL', 'DBI']; $allowedPages = ['TINFOIL', 'DBI'];
if (!in_array($page, $allowedPages)) { if (! in_array($page, $allowedPages)) {
abort(404); abort(404);
} }
$content = file_get_contents(base_path("docs/{$page}.md")); $content = file_get_contents(base_path("docs/{$page}.md"));
// Convert relative image paths to absolute paths for web viewing // Convert relative image paths to absolute paths for web viewing
$content = str_replace( $content = str_replace(
'](images/', '](images/',
'](/docs-images/', '](/docs-images/',
$content $content
); );
return view('docs', ['content' => $content, 'title' => $page]); return view('docs', ['content' => $content, 'title' => $page]);
})->name('docs'); })->name('docs');
Route::get('/docs-images/{path}', function ($path) { Route::get('/docs-images/{path}', function ($path) {
$imagePath = base_path("docs/images/{$path}"); $imagePath = base_path("docs/images/{$path}");
if (!file_exists($imagePath)) { if (! file_exists($imagePath)) {
abort(404); abort(404);
} }
return response()->file($imagePath); return response()->file($imagePath);
})->where('path', '.*')->name('docs.images'); })->where('path', '.*')->name('docs.images');
Route::get('/{path}', fn() => response('Not Found', 404)) Route::get('/{path}', fn () => response('Not Found', 404))
->where('path', '(?!api|docs-images).*') ->where('path', '(?!api|docs-images).*')
->middleware(EnsureIsDbiClient::class); ->middleware(EnsureIsDbiClient::class);
@@ -106,7 +106,7 @@ test('DBI user-agent receives relative links', function () {
$response = $this->get('/api/dbi/', ['HTTP_USER_AGENT' => 'DBI/1.0']); $response = $this->get('/api/dbi/', ['HTTP_USER_AGENT' => 'DBI/1.0']);
// DBI clients need a relative href like "GameA/" not an absolute path // DBI clients need a relative href like "GameA/" not an absolute path
$response->assertSee('href="' . rawurlencode('GameA') . '/"', false); $response->assertSee('href="'.rawurlencode('GameA').'/"', false);
}); });
test('browser user-agent receives absolute links', function () { test('browser user-agent receives absolute links', function () {
@@ -114,7 +114,7 @@ test('browser user-agent receives absolute links', function () {
$response = $this->get('/api/dbi/'); $response = $this->get('/api/dbi/');
$response->assertSee('href="/api/dbi/' . rawurlencode('GameA') . '"', false); $response->assertSee('href="/api/dbi/'.rawurlencode('GameA').'"', false);
}); });
test('displays the current subdirectory path in the page title', function () { test('displays the current subdirectory path in the page title', function () {
@@ -47,7 +47,7 @@ test('only includes files with supported Nintendo Switch extensions', function (
$files = collect($response->json('files')); $files = collect($response->json('files'));
expect($files)->toHaveCount(4); expect($files)->toHaveCount(4);
expect($files->pluck('url')->every(fn ($url) => !str_ends_with($url, '.txt') && !str_ends_with($url, '.jpg')))->toBeTrue(); expect($files->pluck('url')->every(fn ($url) => ! str_ends_with($url, '.txt') && ! str_ends_with($url, '.jpg')))->toBeTrue();
}); });
test('file url is an absolute URL', function () { test('file url is an absolute URL', function () {
+3 -1
View File
@@ -1,5 +1,7 @@
<?php <?php
use Tests\TestCase;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Test Case | Test Case
@@ -11,7 +13,7 @@
| |
*/ */
pest()->extend(Tests\TestCase::class) pest()->extend(TestCase::class)
// ->use(Illuminate\Foundation\Testing\RefreshDatabase::class) // ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
->in('Feature'); ->in('Feature');