From 99118daad9ca71c877bcf021397e3690ba3712b2 Mon Sep 17 00:00:00 2001 From: Nabeel S Date: Mon, 10 Feb 2020 17:47:46 -0500 Subject: [PATCH] Auto update #449 (#545) * Add custom repository type for updates * Direct to self update module * Formatting --- .../Admin/MaintenanceController.php | 41 ++++-- app/Http/Routes/admin.php | 4 + app/Providers/EventServiceProvider.php | 11 +- app/Services/VersionService.php | 2 + app/Support/HttpClient.php | 17 +++ config/app.php | 1 + config/phpvms.php | 5 + config/self-update.php | 22 ++-- .../Http/Controllers/UpdateController.php | 44 ++++++- modules/Updater/Lib/VmsRepositoryType.php | 124 ++++++++++++++++++ .../Providers/UpdateServiceProvider.php | 4 + .../views/downloader/downloader.blade.php | 12 ++ .../views/admin/maintenance/update.blade.php | 26 ++-- resources/views/layouts/default/nav.blade.php | 13 +- 14 files changed, 279 insertions(+), 47 deletions(-) create mode 100644 modules/Updater/Lib/VmsRepositoryType.php create mode 100644 modules/Updater/Resources/views/downloader/downloader.blade.php diff --git a/app/Http/Controllers/Admin/MaintenanceController.php b/app/Http/Controllers/Admin/MaintenanceController.php index 45fb9274f..82fe3c78f 100644 --- a/app/Http/Controllers/Admin/MaintenanceController.php +++ b/app/Http/Controllers/Admin/MaintenanceController.php @@ -5,6 +5,7 @@ use App\Contracts\Controller; use App\Repositories\KvpRepository; use App\Services\CronService; +use App\Services\VersionService; use Codedge\Updater\UpdaterManager; use Illuminate\Http\Request; use Illuminate\Support\Facades\Artisan; @@ -16,15 +17,18 @@ class MaintenanceController extends Controller private $cronSvc; private $kvpRepo; private $updateManager; + private $versionSvc; public function __construct( CronService $cronSvc, KvpRepository $kvpRepo, - UpdaterManager $updateManager + UpdaterManager $updateManager, + VersionService $versionSvc ) { $this->cronSvc = $cronSvc; $this->kvpRepo = $kvpRepo; $this->updateManager = $updateManager; + $this->versionSvc = $versionSvc; } public function index() @@ -32,7 +36,7 @@ public function index() return view('admin.maintenance.index', [ 'cron_path' => $this->cronSvc->getCronExecString(), 'cron_problem_exists' => $this->cronSvc->cronProblemExists(), - 'new_version' => $this->kvpRepo->get('new_version_available', false), + 'new_version' => true, //$this->kvpRepo->get('new_version_available', false), 'new_version_tag' => $this->kvpRepo->get('latest_version_tag'), ]); } @@ -71,26 +75,41 @@ public function cache(Request $request) } /** - * Update the phpVMS install + * Force an update check * * @param \Illuminate\Http\Request $request * * @return mixed */ - public function update(Request $request) + public function forcecheck(Request $request) { + $this->versionSvc->isNewVersionAvailable(); + $new_version_avail = $this->kvpRepo->get('new_version_available', false); + $new_version_tag = $this->kvpRepo->get('latest_version_tag'); + + Log::info('Force check, available='.$new_version_avail.', tag='.$new_version_tag); + if (!$new_version_avail) { - Flash::error('A newer version is not available!'); - return redirect(route('admin.maintenance.index')); + Flash::success('No new version available'); + } else { + Flash::success('New version available: '.$new_version_tag); } + return redirect(route('admin.maintenance.index')); + } + + /** + * Update the phpVMS install + * + * @param \Illuminate\Http\Request $request + * + * @return mixed + */ + public function update(Request $request) + { $new_version_tag = $this->kvpRepo->get('latest_version_tag'); Log::info('Attempting to update to '.$new_version_tag); - - $this->updateManager->source()->update($new_version_tag); - - Flash::success('phpVMS was updated!'); - return redirect(route('/update')); + return redirect('/update/downloader'); } } diff --git a/app/Http/Routes/admin.php b/app/Http/Routes/admin.php index ae02c0d37..79a7ee1cf 100644 --- a/app/Http/Routes/admin.php +++ b/app/Http/Routes/admin.php @@ -165,6 +165,10 @@ static function () { ->name('maintenance.update') ->middleware('ability:admin,maintenance'); + Route::match(['post'], 'maintenance/forcecheck', 'MaintenanceController@forcecheck') + ->name('maintenance.forcecheck') + ->middleware('ability:admin,maintenance'); + // subfleet Route::get('subfleets/export', 'SubfleetController@export') ->name('subfleets.export') diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 7df4e162a..4f3b09940 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -13,8 +13,6 @@ use App\Notifications\EventHandler; use Codedge\Updater\Events\UpdateAvailable; use Codedge\Updater\Events\UpdateSucceeded; -use Codedge\Updater\Listeners\SendUpdateAvailableNotification; -use Codedge\Updater\Listeners\SendUpdateSucceededNotification; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -38,13 +36,8 @@ class EventServiceProvider extends ServiceProvider AwardListener::class, ], - UpdateAvailable::class => [ - SendUpdateAvailableNotification::class, - ], - - UpdateSucceeded::class => [ - SendUpdateSucceededNotification::class, - ], + UpdateAvailable::class => [], + UpdateSucceeded::class => [], ]; diff --git a/app/Services/VersionService.php b/app/Services/VersionService.php index 26b397214..fe34d916a 100644 --- a/app/Services/VersionService.php +++ b/app/Services/VersionService.php @@ -96,6 +96,7 @@ private function getLatestVersionGithub() foreach ($releases as $release) { if ($release['prerelease'] === true) { if ($include_prerelease) { + Log::info('Found latest pre-release of '.$release['tag_name']); return $this->setLatestRelease( $release['tag_name'], $this->getGithubAsset($release) @@ -104,6 +105,7 @@ private function getLatestVersionGithub() continue; } + Log::info('Found latest release of '.$release['tag_name']); return $this->setLatestRelease( $release['tag_name'], $this->getGithubAsset($release) diff --git a/app/Support/HttpClient.php b/app/Support/HttpClient.php index cef761361..ac3349203 100644 --- a/app/Support/HttpClient.php +++ b/app/Support/HttpClient.php @@ -44,4 +44,21 @@ public function get($uri, array $opts = []) return $body; } + + /** + * Download a file to a given path + * + * @param $uri + * @param $local_path + * + * @return string + */ + public function download($uri, $local_path) + { + $response = $this->httpClient->request('GET', $uri, [ + 'sink' => $local_path, + ]); + + return $response; + } } diff --git a/config/app.php b/config/app.php index 2fafc44c8..3697095c8 100755 --- a/config/app.php +++ b/config/app.php @@ -118,6 +118,7 @@ 'Session' => Illuminate\Support\Facades\Session::class, 'Storage' => Illuminate\Support\Facades\Storage::class, 'Theme' => Igaster\LaravelTheme\Facades\Theme::class, + 'Updater' => Codedge\Updater\UpdaterFacade::class, 'URL' => Illuminate\Support\Facades\URL::class, 'Utils' => App\Facades\Utils::class, 'Validator' => Illuminate\Support\Facades\Validator::class, diff --git a/config/phpvms.php b/config/phpvms.php index 6f3a5b41a..445901626 100644 --- a/config/phpvms.php +++ b/config/phpvms.php @@ -75,6 +75,11 @@ */ 'version_file' => 'https://api.github.com/repos/nabeelio/phpvms/releases', + /** + * The URL to download the latest phpVMS version from + */ + 'distrib_url' => 'http://downloads.phpvms.net/phpvms-{VERSION}.zip', + /* * Where the KVP file is stored */ diff --git a/config/self-update.php b/config/self-update.php index 48656de0e..7d039b951 100644 --- a/config/self-update.php +++ b/config/self-update.php @@ -1,8 +1,8 @@ 'http', - 'version_installed' => env('SELF_UPDATER_VERSION_INSTALLED', ''), + 'default' => 'vms', + 'version_installed' => '', 'repository_types' => [ 'github' => [ @@ -10,19 +10,19 @@ 'repository_vendor' => 'nabeelio', 'repository_name' => 'phpvms', 'repository_url' => 'https://github.com/nabeelio/phpvms', - 'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'), + 'download_path' => storage_path('app'), 'private_access_token' => '', - 'use_branch' => 'master', - ], - 'http' => [ - 'type' => 'http', - 'repository_url' => 'http://downloads.phpvms.net', - 'pkg_filename_format' => 'phpvms-_VERSION_.zip', - 'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'), - 'private_access_token' => env('SELF_UPDATER_HTTP_PRIVATE_ACCESS_TOKEN', ''), + 'use_branch' => '', ], ], + 'mail_to' => [ + 'address' => 'no-reply@phpvms.net', + 'name' => 'no name', + 'subject_update_available' => 'Update available', + 'subject_update_succeeded' => 'Update succeeded', + ], + 'exclude_folders' => [ 'node_modules', 'bootstrap/cache', diff --git a/modules/Updater/Http/Controllers/UpdateController.php b/modules/Updater/Http/Controllers/UpdateController.php index ea205d576..77ad6fc17 100644 --- a/modules/Updater/Http/Controllers/UpdateController.php +++ b/modules/Updater/Http/Controllers/UpdateController.php @@ -3,9 +3,11 @@ namespace Modules\Updater\Http\Controllers; use App\Contracts\Controller; +use App\Repositories\KvpRepository; use App\Services\Installer\InstallerService; use App\Services\Installer\MigrationService; use App\Services\Installer\SeederService; +use Codedge\Updater\UpdaterManager; use function count; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -13,22 +15,30 @@ class UpdateController extends Controller { private $installerSvc; + private $kvpRepo; private $migrationSvc; private $seederSvc; + private $updateManager; /** * @param InstallerService $installerSvc * @param MigrationService $migrationSvc * @param SeederService $seederSvc + * @param KvpRepository $kvpRepo + * @param UpdaterManager $updateManager */ public function __construct( InstallerService $installerSvc, + KvpRepository $kvpRepo, MigrationService $migrationSvc, - SeederService $seederSvc + SeederService $seederSvc, + UpdaterManager $updateManager ) { $this->migrationSvc = $migrationSvc; $this->seederSvc = $seederSvc; $this->installerSvc = $installerSvc; + $this->kvpRepo = $kvpRepo; + $this->updateManager = $updateManager; } /** @@ -94,4 +104,36 @@ public function complete(Request $request) { return redirect('/login'); } + + /** + * Show the update page with the latest version + * + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function updater(Request $request) + { + $version = $this->kvpRepo->get('latest_version_tag'); + + return view('updater::downloader/downloader', [ + 'version' => $version, + ]); + } + + /** + * Download the actual update and then forward the user to the updater page + * + * @param \Illuminate\Http\Request $request + * + * @return mixed + */ + public function update_download(Request $request) + { + $version = $this->kvpRepo->get('latest_version_tag'); + $this->updateManager->source('github')->update($version); + + Log::info('Update completed to '.$version.', redirecting'); + return redirect('/update'); + } } diff --git a/modules/Updater/Lib/VmsRepositoryType.php b/modules/Updater/Lib/VmsRepositoryType.php new file mode 100644 index 000000000..825a6ec04 --- /dev/null +++ b/modules/Updater/Lib/VmsRepositoryType.php @@ -0,0 +1,124 @@ +config = $config; + $this->client = $client; + $this->storagePath = Str::finish($this->config['download_path'], DIRECTORY_SEPARATOR); + $this->versionSvc = app(VersionService::class); + } + + /** + * Check repository if a newer version than the installed one is available. + * + * @param string $currentVersion + * + * @throws InvalidArgumentException + * @throws Exception + * + * @return bool + */ + public function isNewVersionAvailable(string $currentVersion = ''): bool + { + return $this->versionSvc->isNewVersionAvailable($currentVersion); + } + + /** + * Get the latest version that has been published in a certain repository. + * Example: 2.6.5 or v2.6.5. + * + * @param string $prepend Prepend a string to the latest version + * @param string $append Append a string to the latest version + * + * @throws Exception + * + * @return string + */ + public function getVersionAvailable(string $prepend = '', string $append = ''): string + { + return $this->versionSvc->getLatestVersion(); + } + + /** + * Fetches the latest version. If you do not want the latest version, specify one and pass it. + * + * @param string $version + * + * @throws Exception + * + * @return void + */ + public function fetch($version = ''): void + { + $response = $this->getRepositoryReleases(); + $releaseCollection = collect(\GuzzleHttp\json_decode($response->getBody()->getContents())); + + if ($releaseCollection->isEmpty()) { + throw new Exception('Cannot find a release to update. Please check the repository you\'re pulling from'); + } + + if (!File::exists($this->storagePath)) { + File::makeDirectory($this->storagePath, 493, true, true); + } + + if (!empty($version)) { + $release = $releaseCollection->where('name', $version)->first(); + } else { + $release = $releaseCollection->first(); + } + + Log::info('Found release='.$release->name.', path='.$release->zipball_url); + $storageFolder = $this->storagePath.$release->name.'-'.now()->timestamp; + $storageFilename = $storageFolder.'.zip'; + + if (!$this->isSourceAlreadyFetched($release->name)) { + $this->downloadRelease($this->client, $release->zipball_url, $storageFilename); + $this->unzipArchive($storageFilename, $storageFolder); + $this->createReleaseFolder($storageFolder, $release->name); + } + } + + protected function getRepositoryReleases(): ResponseInterface + { + $url = self::GITHUB_API_URL + .'/repos/'.$this->config['repository_vendor'] + .'/'.$this->config['repository_name'] + .'/tags'; + + $headers = []; + return $this->client->request('GET', $url, ['headers' => $headers]); + } +} diff --git a/modules/Updater/Providers/UpdateServiceProvider.php b/modules/Updater/Providers/UpdateServiceProvider.php index 2db32de49..7739ce39f 100644 --- a/modules/Updater/Providers/UpdateServiceProvider.php +++ b/modules/Updater/Providers/UpdateServiceProvider.php @@ -33,6 +33,10 @@ protected function registerRoutes() Route::post('/run-migrations', 'UpdateController@run_migrations')->name('run_migrations'); Route::get('/complete', 'UpdateController@complete')->name('complete'); + + // Routes for the update downloader + Route::get('/downloader', 'UpdateController@updater')->name('updater'); + Route::post('/downloader', 'UpdateController@update_download')->name('update_download'); }); } diff --git a/modules/Updater/Resources/views/downloader/downloader.blade.php b/modules/Updater/Resources/views/downloader/downloader.blade.php new file mode 100644 index 000000000..961d2d2b5 --- /dev/null +++ b/modules/Updater/Resources/views/downloader/downloader.blade.php @@ -0,0 +1,12 @@ +@extends('installer::app') +@section('title', 'Update phpVMS') + +@section('content') +

phpvms updater

+

Click run to complete the update to version {{ $version }}

+ {{ Form::open(['route' => 'update.update_download', 'method' => 'post']) }} +

+ {{ Form::submit('Run >>', ['class' => 'btn btn-success']) }} +

+ {{ Form::close() }} +@endsection diff --git a/resources/views/admin/maintenance/update.blade.php b/resources/views/admin/maintenance/update.blade.php index 5a32e8d42..21adf1a19 100644 --- a/resources/views/admin/maintenance/update.blade.php +++ b/resources/views/admin/maintenance/update.blade.php @@ -7,14 +7,24 @@
- @if ($new_version) -

An update to version {{ $new_version_tag }} is available.

- {{ Form::open(['route' => 'admin.maintenance.update']) }} - {{ Form::button('Update', ['type' => 'submit', 'class' => 'btn btn-success']) }} - {{ Form::close() }} - @else -

There is no new version available

- @endif +
+
+ @if ($new_version) +

An update to version {{ $new_version_tag }} is available.

+ {{ Form::open(['route' => 'admin.maintenance.update']) }} + {{ Form::button('Update', ['type' => 'submit', 'class' => 'btn btn-success']) }} + {{ Form::close() }} + @else +

There is no new version available

+ @endif +
+
+

Force new version check

+ {{ Form::open(['route' => 'admin.maintenance.forcecheck']) }} + {{ Form::button('Force update check', ['type' => 'submit', 'class' => 'btn btn-success']) }} + {{ Form::close() }} +
+
diff --git a/resources/views/layouts/default/nav.blade.php b/resources/views/layouts/default/nav.blade.php index fc7fb0291..f23e9863d 100644 --- a/resources/views/layouts/default/nav.blade.php +++ b/resources/views/layouts/default/nav.blade.php @@ -35,19 +35,18 @@ @endforeach @if(!Auth::check()) + - - @else