refactor update

This commit is contained in:
Pig Fang 2020-06-24 10:49:53 +08:00
parent ce43f9a586
commit ffef98ad2e
13 changed files with 52 additions and 169 deletions

View File

@ -2,7 +2,11 @@
namespace App\Console\Commands;
use Composer\Semver\Comparator;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel as Artisan;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Cache;
class UpdateCommand extends Command
{
@ -10,9 +14,38 @@ class UpdateCommand extends Command
protected $description = 'Execute update.';
public function handle()
public function handle(Artisan $artisan, Filesystem $filesystem)
{
app()->call('App\Http\Controllers\UpdateController@update');
$this->procedures()->each(function ($procedure, $version) {
if (Comparator::lessThan(option('version'), $version)) {
$procedure();
}
});
option(['version' => config('app.version')]);
$artisan->call('migrate', ['--force' => true]);
$artisan->call('view:clear');
$filesystem->put(storage_path('install.lock'), '');
Cache::flush();
$this->info(trans('setup.updates.success.title'));
}
/**
* @codeCoverageIgnore
*/
protected function procedures()
{
return collect([
'0.0.1' => function () {
// this is just for testing
event('__0.0.1');
},
'5.0.0' => function () {
if (option('home_pic_url') === './app/bg.jpg') {
option(['home_pic_url' => './app/bg.webp']);
}
},
]);
}
}

View File

@ -6,11 +6,9 @@ use App\Services\Unzip;
use Cache;
use Composer\CaBundle\CaBundle;
use Composer\Semver\Comparator;
use Illuminate\Contracts\Console\Kernel as Artisan;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
use Symfony\Component\Finder\SplFileInfo;
class UpdateController extends Controller
{
@ -58,28 +56,6 @@ class UpdateController extends Controller
}
}
public function update(Filesystem $filesystem, Artisan $artisan)
{
collect($filesystem->files(database_path('update_scripts')))
->filter(function (SplFileInfo $file) {
$name = $file->getFilenameWithoutExtension();
return preg_match('/^\d+\.\d+\.\d+$/', $name) > 0
&& Comparator::greaterThanOrEqualTo($name, option('version'));
})
->each(function (SplFileInfo $file) use ($filesystem) {
$filesystem->getRequire($file->getPathname());
});
option(['version' => config('app.version')]);
$artisan->call('migrate', ['--force' => true]);
$artisan->call('view:clear');
$filesystem->put(storage_path('install.lock'), '');
Cache::flush();
return view('setup.updates.success');
}
protected function getUpdateInfo()
{
$response = Http::withOptions([

View File

@ -9,7 +9,7 @@ class CheckInstallation
public function handle($request, \Closure $next)
{
$hasLock = resolve(Filesystem::class)->exists(storage_path('install.lock'));
if ($hasLock && !$request->is('setup/*update')) {
if ($hasLock) {
return response()->view('setup.locked');
}

View File

@ -5,6 +5,7 @@ namespace App\Http\Middleware;
use Closure;
use Composer\Semver\Comparator;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Artisan;
class RedirectToSetup
{
@ -20,14 +21,7 @@ class RedirectToSetup
}
if ($hasLock && !$request->is('setup*') && Comparator::greaterThan($version, option('version', $version))) {
$user = $request->user();
if ($user && $user->isAdmin()) {
return redirect('/setup/update');
} elseif ($request->is('auth/login')) {
return $next($request);
} else {
abort(503);
}
Artisan::call('update');
}
if ($hasLock || $request->is('setup*')) {

View File

@ -1,7 +0,0 @@
<?php
Artisan::call('migrate', ['--force' => true]);
if (option('home_pic_url') === './app/bg.jpg') {
option(['home_pic_url' => './app/bg.webp']);
}

View File

@ -3,16 +3,7 @@
{% block title %}503 Service Unavailable{% endblock %}
{% block message %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13.0/css/all.min.css">
<p>
{{ trans('errors.exception.detail', {msg: exception.message ?: trans('errors.http.msg-503')}) }}
</p>
{% if not auth_check() %}
<div class="mt-5">
<a href="{{ url('/auth/login') }}" class="btn btn-primary">
<i class="icon fas fa-sign-in-alt mr-1"></i>
{{ trans('general.login') }}
</a>
</div>
{% endif %}
{% endblock %}

View File

@ -1,16 +0,0 @@
{% extends 'setup.base' %}
{% block title %}{{ trans('setup.updates.master.title') }}{% endblock %}
{% block subtitle %}{{ trans('setup.updates.success.title') }}{% endblock %}
{% block content %}
<p>
{{ trans('setup.updates.success.text', {version: config('app.version')}) }}
</p>
<p>
<a href="{{ url('/') }}" class="btn btn-primary">
{{ trans('setup.updates.welcome.button') }}
</a>
</p>
{% endblock %}

View File

@ -1,16 +0,0 @@
{% extends 'setup.base' %}
{% block title %}{{ trans('setup.updates.master.title') }}{% endblock %}
{% block subtitle %}{{ trans('setup.updates.welcome.title') }}{% endblock %}
{% block content %}
<p>
{{ trans('setup.updates.welcome.text', {version: config('app.version')})|nl2br }}
</p>
<p>
<a href="{{ url('/setup/exec-update') }}" class="btn btn-primary">
{{ trans('setup.updates.welcome.button') }}
</a>
</p>
{% endblock %}

View File

@ -204,9 +204,4 @@ Route::prefix('setup')->group(function () {
Route::view('info', 'setup.wizard.info');
Route::post('finish', 'SetupController@finish');
});
Route::middleware('authorize')->group(function () {
Route::view('update', 'setup.updates.welcome')->middleware('setup');
Route::any('exec-update', 'UpdateController@update')->middleware('setup');
});
});

View File

@ -4,6 +4,8 @@ namespace Tests;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Event;
class UpdateCommandTest extends TestCase
{
@ -11,25 +13,23 @@ class UpdateCommandTest extends TestCase
public function testUpdate()
{
Event::fake();
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->andReturn(true);
$mock->shouldReceive('put')
->with(storage_path('install.lock'), '')
->once()
->andReturn(true);
$mock->shouldReceive('files')
->with(database_path('update_scripts'))
->once()
->andReturn([]);
});
config(['app.version' => '100.0.0']);
Cache::partialMock()->shouldReceive('flush')->once();
option(['version' => '0.0.0']);
config([
'app.version' => '0.0.1',
'translation-loader.translation_loaders' => [],
]);
$this->artisan('update')
->expectsOutput(trans('setup.updates.success.title'));
$this->assertEquals('100.0.0', option('version'));
$this->assertEquals('0.0.1', option('version'));
Event::assertDispatched('__0.0.1');
}
}

View File

@ -3,11 +3,8 @@
namespace Tests;
use App\Services\Unzip;
use Illuminate\Contracts\Console\Kernel as Artisan;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Http;
use Symfony\Component\Finder\SplFileInfo;
class UpdateControllerTest extends TestCase
{
@ -71,51 +68,6 @@ class UpdateControllerTest extends TestCase
->assertJson(['code' => 0, 'message' => trans('admin.update.complete')]);
}
public function testUpdate()
{
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->andReturn(true);
$mock->shouldReceive('put')
->with(storage_path('install.lock'), '')
->once()
->andReturn(true);
$mock->shouldReceive('files')
->with(database_path('update_scripts'))
->once()
->andReturn([
new SplFileInfo('/1.0.0.php', '', ''),
new SplFileInfo('/99.0.0.php', '', ''),
new SplFileInfo('/100.0.0.php', '', ''),
]);
$mock->shouldNotReceive('getRequire')->with('/1.0.0.php');
$mock->shouldReceive('getRequire')
->with('/99.0.0.php')
->once();
$mock->shouldReceive('getRequire')
->with('/100.0.0.php')
->once();
});
$this->spy(Artisan::class, function ($spy) {
$spy->shouldReceive('call')
->with('migrate', ['--force' => true])
->once();
$spy->shouldReceive('call')->with('view:clear')->once();
});
config(['app.version' => '100.0.0']);
$this->actingAs(factory(\App\Models\User::class)->states('superAdmin')->create())
->get('/setup/exec-update')
->assertViewIs('setup.updates.success');
$this->assertEquals('100.0.0', option('version'));
}
protected function fakeUpdateInfo(string $version, $extra = [])
{
return array_merge([

View File

@ -19,22 +19,10 @@ class CheckInstallationTest extends TestCase
->with(storage_path('install.lock'))
->twice()
->andReturn(false);
$mock->shouldReceive('exists')
->with(base_path('.env'))
->andReturn(true);
});
$this->get('/setup')->assertSee(trans(
'setup.wizard.welcome.text',
['version' => config('app.version')]
));
$this->actingAs(factory(User::class)->states('superAdmin')->make());
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->andReturn(true);
});
config(['app.version' => '100.0.0']);
$this->get('/setup/update')->assertSee(trans('setup.updates.welcome.title'));
}
}

View File

@ -2,9 +2,9 @@
namespace Tests;
use App\Models\User;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Artisan;
class RedirectToSetupTest extends TestCase
{
@ -12,23 +12,16 @@ class RedirectToSetupTest extends TestCase
public function testHandle()
{
$superAdmin = factory(User::class)->states('superAdmin')->create();
$current = config('app.version');
config(['app.version' => '100.0.0']);
$this->get('/')->assertStatus(503);
$this->get('/auth/login')->assertViewIs('auth.login');
$this->actingAs($superAdmin)->get('/')->assertRedirect('/setup/update');
Artisan::shouldReceive('call')->with('update')->once();
$this->get('/');
config(['app.version' => $current]);
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->andReturn(true, false, false);
$mock->shouldReceive('exists')
->with(base_path('.env'))
->andReturn(true);
});
$this->get('/')->assertViewIs('home');
$this->get('/setup')->assertViewIs('setup.wizard.welcome');