Improve the logic behind the different features

This commit is contained in:
SychO9 2021-11-20 16:31:26 +01:00
parent 1fcd9b86d7
commit 3497028cc0
20 changed files with 177 additions and 184 deletions

View File

@ -13,11 +13,10 @@ use Flarum\Extend;
use Flarum\Foundation\Paths;
use Flarum\Frontend\Document;
use Flarum\PackageManager\Exception\ComposerCommandFailedException;
use Flarum\PackageManager\Exception\ComposerCommandFailedExceptionHandler;
use Flarum\PackageManager\Exception\ExceptionHandler;
use Flarum\PackageManager\Exception\ComposerRequireFailedException;
use Flarum\PackageManager\Exception\ComposerUpdateFailedException;
use Flarum\PackageManager\Exception\ExtensionAlreadyInstalledException;
use Flarum\PackageManager\Exception\ExtensionNotInstalledException;
use Flarum\PackageManager\Exception\MajorUpdateFailedException;
return [
(new Extend\Routes('api'))
@ -25,7 +24,8 @@ return [
->patch('/package-manager/extensions/{id}', 'package-manager.extensions.update', Api\Controller\UpdateExtensionController::class)
->delete('/package-manager/extensions/{id}', 'package-manager.extensions.remove', Api\Controller\RemoveExtensionController::class)
->post('/package-manager/check-for-updates', 'package-manager.check-for-updates', Api\Controller\CheckForUpdatesController::class)
->post('/package-manager/minor-update', 'package-manager.minor-update', Api\Controller\MinorFlarumUpdateController::class)
->post('/package-manager/minor-update', 'package-manager.minor-update', Api\Controller\MinorUpdateController::class)
->post('/package-manager/major-update', 'package-manager.major-update', Api\Controller\MajorUpdateController::class)
->post('/package-manager/global-update', 'package-manager.global-update', Api\Controller\GlobalUpdateController::class),
(new Extend\Frontend('admin'))
@ -44,15 +44,23 @@ return [
new Extend\Locales(__DIR__ . '/locale'),
(new Extend\Settings())
->default('flarum-package-manager.last_update_check', json_encode([
'checkedAt' => null,
'updates' => [
'installed' => [],
],
])),
(new Extend\ServiceProvider)
->register(PackageManagerServiceProvider::class),
(new Extend\ErrorHandling)
->handler(ComposerCommandFailedException::class, ComposerCommandFailedExceptionHandler::class)
->handler(ComposerRequireFailedException::class, ComposerCommandFailedExceptionHandler::class)
->handler(ComposerUpdateFailedException::class, ComposerCommandFailedExceptionHandler::class)
->type(ExtensionAlreadyInstalledException::class, 'extension_already_installed')
->handler(ComposerCommandFailedException::class, ExceptionHandler::class)
->handler(ComposerRequireFailedException::class, ExceptionHandler::class)
->handler(ComposerUpdateFailedException::class, ExceptionHandler::class)
->handler(MajorUpdateFailedException::class, ExceptionHandler::class)
->status('extension_already_installed', 409)
->type(ExtensionNotInstalledException::class, 'extension_not_installed')
->status('extension_not_installed', 409),
->status('extension_not_installed', 409)
->status('no_new_major_version', 409),
];

View File

@ -140,68 +140,6 @@ function _setPrototypeOf(o, p) {
return _setPrototypeOf(o, p);
}
/***/ }),
/***/ "./src/admin/components/ComposerFailureModal.tsx":
/*!*******************************************************!*\
!*** ./src/admin/components/ComposerFailureModal.tsx ***!
\*******************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ComposerFailureModal; });
/* harmony import */ var _babel_runtime_helpers_esm_inheritsLoose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/esm/inheritsLoose */ "./node_modules/@babel/runtime/helpers/esm/inheritsLoose.js");
/* harmony import */ var flarum_admin_app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! flarum/admin/app */ "flarum/admin/app");
/* harmony import */ var flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(flarum_admin_app__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var flarum_common_components_Modal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! flarum/common/components/Modal */ "flarum/common/components/Modal");
/* harmony import */ var flarum_common_components_Modal__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(flarum_common_components_Modal__WEBPACK_IMPORTED_MODULE_2__);
var ComposerFailureModal = /*#__PURE__*/function (_Modal) {
Object(_babel_runtime_helpers_esm_inheritsLoose__WEBPACK_IMPORTED_MODULE_0__["default"])(ComposerFailureModal, _Modal);
function ComposerFailureModal() {
return _Modal.apply(this, arguments) || this;
}
var _proto = ComposerFailureModal.prototype;
_proto.oninit = function oninit(vnode) {
_Modal.prototype.oninit.call(this, vnode);
if (this.attrs.error.guessed_cause) {
this.alertAttrs = {
type: 'error',
content: flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.translator.trans("flarum-package-manager.admin.failure_modal.guessed_cause." + this.attrs.error.guessed_cause)
};
}
};
_proto.className = function className() {
return 'Modal--large ComposerFailureModal';
};
_proto.title = function title() {
return flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.translator.trans('flarum-package-manager.admin.failure_modal.title');
};
_proto.content = function content() {
return m("div", {
className: "Modal-body"
}, m("details", null, m("summary", null, flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.translator.trans('flarum-package-manager.admin.failure_modal.show_composer_output')), m("pre", {
className: "ComposerFailureModal-output"
}, this.attrs.error.output)));
};
return ComposerFailureModal;
}(flarum_common_components_Modal__WEBPACK_IMPORTED_MODULE_2___default.a);
/***/ }),
/***/ "./src/admin/components/Installer.tsx":
@ -813,8 +751,6 @@ flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.initializers.add('flarum
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var flarum_admin_app__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! flarum/admin/app */ "flarum/admin/app");
/* harmony import */ var flarum_admin_app__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(flarum_admin_app__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _components_ComposerFailureModal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/ComposerFailureModal */ "./src/admin/components/ComposerFailureModal.tsx");
/* harmony default export */ __webpack_exports__["default"] = (function (e) {
var error = e.response.errors[0];
@ -825,9 +761,13 @@ __webpack_require__.r(__webpack_exports__);
switch (error.code) {
case 'composer_command_failure':
flarum_admin_app__WEBPACK_IMPORTED_MODULE_0___default.a.modal.show(_components_ComposerFailureModal__WEBPACK_IMPORTED_MODULE_1__["default"], {
error: error
});
if (error.guessed_cause) {
flarum_admin_app__WEBPACK_IMPORTED_MODULE_0___default.a.alerts.show({
type: 'error'
}, flarum_admin_app__WEBPACK_IMPORTED_MODULE_0___default.a.translator.trans("flarum-package-manager.admin.exceptions.guessed_cause." + error.guessed_cause));
flarum_admin_app__WEBPACK_IMPORTED_MODULE_0___default.a.modal.close();
}
break;
case 'extension_already_installed':
@ -935,17 +875,6 @@ module.exports = flarum.core.compat['common/components/LoadingIndicator'];
/***/ }),
/***/ "flarum/common/components/Modal":
/*!****************************************************************!*\
!*** external "flarum.core.compat['common/components/Modal']" ***!
\****************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = flarum.core.compat['common/components/Modal'];
/***/ }),
/***/ "flarum/common/components/Tooltip":
/*!******************************************************************!*\
!*** external "flarum.core.compat['common/components/Tooltip']" ***!

File diff suppressed because one or more lines are too long

View File

@ -1,41 +0,0 @@
import app from 'flarum/admin/app';
import Modal from 'flarum/common/components/Modal';
import { ComponentAttrs } from 'flarum/common/Component';
import Alert from 'flarum/common/components/Alert';
import Mithril from 'mithril';
interface Attrs extends ComponentAttrs {
output: string;
}
export default class ComposerFailureModal<T extends Attrs = Attrs> extends Modal<T> {
oninit(vnode: Mithril.Vnode<T, this>) {
super.oninit(vnode);
if (this.attrs.error.guessed_cause) {
this.alertAttrs = {
type: 'error',
content: app.translator.trans(`flarum-package-manager.admin.failure_modal.guessed_cause.${this.attrs.error.guessed_cause}`),
};
}
}
className() {
return 'Modal--large ComposerFailureModal';
}
title() {
return app.translator.trans('flarum-package-manager.admin.failure_modal.title');
}
content() {
return (
<div className="Modal-body">
<details>
<summary>{app.translator.trans('flarum-package-manager.admin.failure_modal.show_composer_output')}</summary>
<pre className="ComposerFailureModal-output">{this.attrs.error.output}</pre>
</details>
</div>
);
}
}

View File

@ -4,14 +4,13 @@ import Component from 'flarum/common/Component';
import Button from 'flarum/common/components/Button';
import Stream from 'flarum/common/utils/Stream';
import LoadingModal from 'flarum/admin/components/LoadingModal';
import ComposerFailureModal from './ComposerFailureModal';
import errorHandler from '../utils/errorHandler';
export default class Installer extends Component {
export default class Installer<Attrs> extends Component<Attrs> {
packageName!: Stream<string>;
isLoading: boolean = false;
oninit(vnode: Mithril.Vnode): void {
oninit(vnode: Mithril.Vnode<Attrs, this>): void {
super.oninit(vnode);
this.packageName = Stream('');

View File

@ -1,5 +1,4 @@
import app from 'flarum/admin/app';
import ComposerFailureModal from '../components/ComposerFailureModal';
export default function (e: any) {
const error = e.response.errors[0];
@ -10,7 +9,10 @@ export default function (e: any) {
switch (error.code) {
case 'composer_command_failure':
app.modal.show(ComposerFailureModal, { error });
if (error.guessed_cause) {
app.alerts.show({type: 'error'}, app.translator.trans(`flarum-package-manager.admin.exceptions.guessed_cause.${error.guessed_cause}`))
app.modal.close();
}
break;
case 'extension_already_installed':

View File

@ -4,6 +4,12 @@ flarum-package-manager:
extension_already_installed: Extension is already installed.
extension_not_installed: Extension not found.
guessed_cause:
extension_incompatible_with_instance: The extension is most likely incompatible with your current Flarum instance.
extensions_incompatible_with_new_major: >
Some installed extensions are not compatible with the newest major release.
Please wait until the extensions are updated to be compatible by the authors, or remove them before proceeding.
extensions:
install: Install a new extension
install_help: Fill in the extension package name to proceed. Visit {extiverse} to browse extensions.
@ -13,12 +19,6 @@ flarum-package-manager:
successful_update: "{extension} was updated successfully, redirecting.."
update: Update
failure_modal:
title: Operation Failed
guessed_cause:
extension_incompatible_with_instance: The extension is most likely incompatible with your current Flarum instance.
show_composer_output: Show Advanced Details
file_permissions: >
The package manager requires read and write permissions on the following files and directories: composer.json, composer.lock, vendor, storage/.composer

View File

@ -33,7 +33,7 @@ class MajorUpdateController implements RequestHandlerInterface
public function handle(ServerRequestInterface $request): ResponseInterface
{
$actor = RequestUtil::getActor($request);
$dryRun = (bool) (int) Arr::get($request->getParsedBody(), 'data.dryRun');
$dryRun = (bool) (int) Arr::get($request->getParsedBody(), 'data.dryRun', 0);
$this->bus->dispatch(
new MajorUpdate($actor, $dryRun)

View File

@ -15,9 +15,9 @@ use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Flarum\PackageManager\Command\MinorFlarumUpdate;
use Flarum\PackageManager\Command\MinorUpdate;
class MinorFlarumUpdateController implements RequestHandlerInterface
class MinorUpdateController implements RequestHandlerInterface
{
/**
* @var Dispatcher
@ -37,7 +37,7 @@ class MinorFlarumUpdateController implements RequestHandlerInterface
$actor = RequestUtil::getActor($request);
$this->bus->dispatch(
new MinorFlarumUpdate($actor)
new MinorUpdate($actor)
);
return new EmptyResponse();

View File

@ -71,7 +71,9 @@ class CheckForUpdatesHandler
if ($majorUpdates) {
$secondOutput = $this->runComposerCommand(true);
$secondOutput = json_decode($secondOutput, true);
} else {
}
if (! isset($secondOutput)) {
$secondOutput = ['installed' => []];
}
@ -101,14 +103,17 @@ class CheckForUpdatesHandler
*/
protected function runComposerCommand(bool $minorOnly): string
{
$output = $this->composer->run(
new ArrayInput([
'command' => 'outdated',
'-D' => true,
'--minor-only' => $minorOnly,
'--format' => 'json',
])
);
$input = [
'command' => 'outdated',
'-D' => true,
'--format' => 'json',
];
if ($minorOnly) {
$input['--minor-only'] = true;
}
$output = $this->composer->run(new ArrayInput($input));
if ($output->getExitCode() !== 0) {
throw new ComposerCommandFailedException('', $output->getContents());

View File

@ -11,9 +11,10 @@ namespace Flarum\PackageManager\Command;
use Flarum\PackageManager\Composer\ComposerAdapter;
use Flarum\PackageManager\Composer\ComposerJson;
use Flarum\PackageManager\Exception\MajorUpdateFailedException;
use Flarum\PackageManager\Exception\NoNewMajorVersionException;
use Illuminate\Contracts\Events\Dispatcher;
use Flarum\PackageManager\Event\FlarumUpdated;
use Flarum\PackageManager\Exception\ComposerUpdateFailedException;
use Flarum\PackageManager\LastUpdateCheck;
use Symfony\Component\Console\Input\ArrayInput;
@ -61,7 +62,7 @@ class MajorUpdateHandler
* Run migrations.
*
* @throws \Flarum\User\Exception\PermissionDeniedException
* @throws ComposerUpdateFailedException
* @throws NoNewMajorVersionException|MajorUpdateFailedException
*/
public function handle(MajorUpdate $command)
{
@ -70,12 +71,12 @@ class MajorUpdateHandler
$majorVersion = $this->lastUpdateCheck->getNewMajorVersion();
if (! $majorVersion) {
return false;
throw new NoNewMajorVersionException();
}
$this->updateComposerJson($majorVersion);
$this->runCommand($command->dryRun);
$this->runCommand($command->dryRun, $majorVersion);
if ($command->dryRun) {
$this->composerJson->revert();
@ -94,29 +95,34 @@ class MajorUpdateHandler
protected function updateComposerJson(string $majorVersion): void
{
$versionNumber = str_replace('v', '', $majorVersion);
$this->composerJson->require('*', '*');
$this->composerJson->require('flarum/core', '^'.str_replace('v', '', $majorVersion));
$this->composerJson->require('flarum/core', '^'.$versionNumber);
}
/**
* @throws ComposerUpdateFailedException
* @throws MajorUpdateFailedException
*/
protected function runCommand(bool $dryRun): void
protected function runCommand(bool $dryRun, string $majorVersion): void
{
$output = $this->composer->run(
new ArrayInput([
'command' => 'update',
'--prefer-dist' => true,
'--no-plugins' => true,
'--no-dev' => true,
'-a' => true,
'--with-all-dependencies' => true,
'--dry-run' => $dryRun,
])
);
$input = [
'command' => 'update',
'--prefer-dist' => true,
'--no-plugins' => true,
'--no-dev' => true,
'-a' => true,
'--with-all-dependencies' => true,
];
if ($dryRun) {
$input['--dry-run'] = true;
}
$output = $this->composer->run(new ArrayInput($input));
if ($output->getExitCode() !== 0) {
throw new ComposerUpdateFailedException('*', $output->getContents());
throw new MajorUpdateFailedException('*', $output->getContents(), $majorVersion);
}
}
}

View File

@ -11,7 +11,7 @@ namespace Flarum\PackageManager\Command;
use Flarum\User\User;
class MinorFlarumUpdate
class MinorUpdate
{
/**
* @var \Flarum\User\User

View File

@ -17,7 +17,7 @@ use Flarum\PackageManager\Exception\ComposerUpdateFailedException;
use Flarum\PackageManager\LastUpdateCheck;
use Symfony\Component\Console\Input\StringInput;
class MinorFlarumUpdateHandler
class MinorUpdateHandler
{
/**
* @var ComposerAdapter
@ -51,7 +51,7 @@ class MinorFlarumUpdateHandler
* @throws \Flarum\User\Exception\PermissionDeniedException
* @throws ComposerUpdateFailedException
*/
public function handle(MinorFlarumUpdate $command)
public function handle(MinorUpdate $command)
{
$command->actor->assertAdmin();

View File

@ -14,6 +14,9 @@ use Flarum\PackageManager\OutputLogger;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
/**
* @internal
*/
class ComposerAdapter
{
/**
@ -26,19 +29,23 @@ class ComposerAdapter
*/
private $logger;
/**
* @var BufferedOutput
*/
private $output;
public function __construct(Application $application, OutputLogger $logger)
{
$this->application = $application;
$this->logger = $logger;
$this->output = new BufferedOutput();
}
public function run(InputInterface $input): ComposerOutput
{
$output = new BufferedOutput();
$exitCode = $this->application->run($input, $this->output);
$exitCode = $this->application->run($input, $output);
$outputContents = $output->fetch();
$outputContents = $this->output->fetch();
$this->logger->log($input->__toString(), $outputContents, $exitCode);

View File

@ -44,6 +44,10 @@ class ComposerJson
$composerJson['require'][$packageName] = $version;
} else {
foreach ($composerJson['require'] as $p => $v) {
if ($version === '*@dev') {
continue;
}
$wildcardPackageName = str_replace('\*', '.*', preg_quote($packageName, '/'));
if (Str::of($p)->test("/($wildcardPackageName)/")) {
@ -81,6 +85,6 @@ class ComposerJson
protected function set(array $json): void
{
$this->filesystem->put($this->getComposerJsonPath(), json_encode($json, JSON_PRETTY_PRINT));
$this->filesystem->put($this->getComposerJsonPath(), json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
}

View File

@ -18,6 +18,11 @@ class ComposerCommandFailedException extends Exception
*/
public $packageName;
/**
* @var array
*/
public $details = [];
public function __construct(string $packageName, string $output)
{
$this->packageName = $packageName;

View File

@ -11,7 +11,7 @@ namespace Flarum\PackageManager\Exception;
use Flarum\Foundation\ErrorHandling\HandledError;
class ComposerCommandFailedExceptionHandler
class ExceptionHandler
{
public function handle(ComposerCommandFailedException $e): HandledError
{
@ -32,6 +32,10 @@ class ComposerCommandFailedExceptionHandler
$details['guessed_cause'] = $guessedCause;
}
if (! empty($e->details)) {
$details = array_merge($details, $e->details);
}
return [$details];
}

View File

@ -0,0 +1,39 @@
<?php
namespace Flarum\PackageManager\Exception;
use Composer\Semver\Semver;
class MajorUpdateFailedException extends ComposerCommandFailedException
{
private const INCOMPATIBLE_REGEX = '/^ +- (?<ext>[A-z0-9\/-]+) [A-z0-9.-_\/]+ requires flarum\/core (?<coreReq>(?:[A-z0-9.><=_ -](?!->))+)/m';
/**
* @var string
*/
private $majorVersion;
public function __construct(string $packageName, string $output, string $majorVersion)
{
$this->majorVersion = $majorVersion;
parent::__construct($packageName, $output);
}
public function guessCause(): ?string
{
if (preg_match_all(self::INCOMPATIBLE_REGEX, $this->getMessage(), $matches) !== false) {
$this->details['incompatible_extensions'] = [];
foreach ($matches['ext'] as $k => $name) {
if (! Semver::satisfies($this->majorVersion, $matches['coreReq'][$k])) {
$this->details['incompatible_extensions'][] = $name;
}
}
return 'extensions_incompatible_with_new_major';
}
return null;
}
}

View File

@ -0,0 +1,26 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\PackageManager\Exception;
use Exception;
use Flarum\Foundation\KnownError;
class NoNewMajorVersionException extends Exception implements KnownError
{
public function __construct()
{
parent::__construct("No new major version known of. Try checking for updates first.");
}
public function getType(): string
{
return 'no_new_major_version';
}
}

View File

@ -42,12 +42,12 @@ class LastUpdateCheck
public function get(): array
{
return json_decode($this->settings->get(self::KEY, '{}'), true);
return json_decode($this->settings->get(self::KEY), true);
}
public function getNewMajorVersion(): ?string
{
$core = Arr::first($this->get()['updates']['installed'], function ($package) {
$core = Arr::first(Arr::get($this->get(), 'updates.installed', []), function ($package) {
return $package['name'] === 'flarum/core';
});