Skip to content
Merged
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
71 changes: 71 additions & 0 deletions app/Console/Commands/BackfillPluginMobileVersions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\Plugin;
use Illuminate\Console\Command;
use Illuminate\Support\Str;

final class BackfillPluginMobileVersions extends Command
{
protected $signature = 'plugins:backfill-mobile-versions
{--dry-run : List the versions each plugin would get without saving them}';

protected $description = 'Work out which NativePHP Mobile versions each plugin supports from the composer.json saved at its last sync';

/**
* Nothing is fetched from GitHub, so it's quick and safe to run again, e.g. after
* adding a major version to config('plugins.mobile_major_versions').
*/
public function handle(): int
{
$dryRun = (bool) $this->option('dry-run');

/** @var array<int, array{0: string, 1: string, 2: string}> $changed */
$changed = [];
/** @var array<int, array{0: string, 1: string}> $unmatched */
$unmatched = [];

foreach (Plugin::query()->orderBy('name')->get() as $plugin) {
$constraint = data_get($plugin->composer_data, 'require.nativephp/mobile');
$versions = Plugin::mobileVersionsFromComposer($plugin->composer_data);

if (is_string($constraint) && $versions === null) {
$unmatched[] = [$plugin->name, $constraint];
}

if (($versions ?? []) === $plugin->supportedMobileVersions()) {
continue;
}

$changed[] = [$plugin->name, is_string($constraint) ? $constraint : '—', $versions ? implode(', ', $versions) : '—'];

if (! $dryRun) {
$plugin->update(['mobile_versions' => $versions]);
}
}

if ($changed !== []) {
$this->table(['Plugin', 'Requires', 'Versions'], $changed);
}

if ($unmatched !== []) {
$majorVersions = collect(config('plugins.mobile_major_versions', []))->map(fn (int $major): string => "{$major}.x");

$this->newLine();
$this->warn("These require nativephp/mobile but don't allow any {$majorVersions->join(', ', ' or ')} release, so they have no versions:");
$this->table(['Plugin', 'Requires'], $unmatched);
}

$this->newLine();
$this->info(sprintf('%d %s %s.', count($changed), Str::plural('plugin', count($changed)), $dryRun ? 'would change' : 'updated'));

if ($dryRun) {
$this->warn('Dry run, so nothing was saved. Run it again without --dry-run to save these.');
}

return self::SUCCESS;
}
}
42 changes: 19 additions & 23 deletions app/Livewire/PluginDirectory.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ class PluginDirectory extends Component
*/
public const string CATEGORY_UNCATEGORIZED = 'uncategorized';

/**
* Filter value representing plugins with no `mobile_min_version` recorded.
*/
public const string MOBILE_VERSION_UNSPECIFIED = 'unspecified';

#[Url]
public string $search = '';

Expand All @@ -52,7 +47,8 @@ class PluginDirectory extends Component

/**
* Drop any `type`/`category` value that isn't a real enum case (a stale
* bookmark, a hand-edited URL) instead of crashing later on `::from()`.
* bookmark, a hand-edited URL) instead of crashing later on `::from()`,
* and any `mobileVersion` that isn't one of the filter's options.
*/
public function mount(): void
{
Expand All @@ -65,6 +61,10 @@ public function mount(): void
&& PluginCategory::tryFrom($this->category) === null) {
$this->category = '';
}

if ($this->mobileVersion !== '' && ! in_array($this->mobileVersion, $this->mobileVersionOptions(), true)) {
$this->mobileVersion = '';
}
}

public function updatedSearch(): void
Expand Down Expand Up @@ -177,16 +177,7 @@ public function render(): View
$query->whereJsonContains('categories', $this->category);
})
->when($this->mobileVersion !== '', function (Builder $query): void {
if ($this->mobileVersion === self::MOBILE_VERSION_UNSPECIFIED) {
$query->whereNull('mobile_min_version');

return;
}

$query->where(function (Builder $q): void {
$q->where('mobile_min_version', 'like', "{$this->mobileVersion}.%")
->orWhere('mobile_min_version', $this->mobileVersion);
});
$query->supportsMobileVersion((int) $this->mobileVersion);
})
->orderByDesc('featured')
->latest()
Expand All @@ -211,9 +202,8 @@ public function render(): View
'authorUser' => $authorUser,
'typeOptions' => PluginType::cases(),
'categoryOptions' => PluginCategory::cases(),
'mobileVersionOptions' => config('plugins.mobile_major_versions', []),
'mobileVersionOptions' => $this->mobileVersionOptions(),
'categoryUncategorizedValue' => self::CATEGORY_UNCATEGORIZED,
'mobileVersionUnspecifiedValue' => self::MOBILE_VERSION_UNSPECIFIED,
'typeLabel' => $this->type !== '' ? PluginType::from($this->type)->label() : null,
'categoryLabel' => $this->categoryLabel(),
'mobileVersionLabel' => $this->mobileVersionLabel(),
Expand All @@ -231,10 +221,16 @@ protected function categoryLabel(): ?string

protected function mobileVersionLabel(): ?string
{
return match (true) {
$this->mobileVersion === '' => null,
$this->mobileVersion === self::MOBILE_VERSION_UNSPECIFIED => 'Version unspecified',
default => "v{$this->mobileVersion}.x and up",
};
return $this->mobileVersion === '' ? null : "NativePHP {$this->mobileVersion}.x";
}

/**
* The NativePHP Mobile major versions plugins can be filtered by, newest first.
*
* @return array<int, string>
*/
protected function mobileVersionOptions(): array
{
return array_map(strval(...), config('plugins.mobile_major_versions', []));
}
}
48 changes: 48 additions & 0 deletions app/Models/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use App\Notifications\PluginRejected;
use App\Services\OgImageService;
use App\Services\PluginSyncService;
use App\Support\ComposerConstraint;
use App\Support\DemoVideo;
use App\Support\PluginReadme;
use BladeUI\Icons\Exceptions\SvgNotFound;
Expand Down Expand Up @@ -348,6 +349,40 @@ public function worksInJump(): bool
return $this->works_in_jump ?? false;
}

/**
* The lowest NativePHP Mobile release the plugin works with in each major version
* it supports, keyed by major version, oldest first: [3 => '3.2.1', 4 => '4.0'].
*
* @return array<int, string>
*/
public function supportedMobileVersions(): array
{
$versions = $this->mobile_versions ?? [];

ksort($versions);

return $versions;
}

/**
* Work out from a composer.json which NativePHP Mobile versions a plugin supports,
* shaped like supportedMobileVersions(). Null if it doesn't require nativephp/mobile
* or allows none of the major versions in config('plugins.mobile_major_versions').
*
* @param array<string, mixed>|null $composerData
* @return array<int, string>|null
*/
public static function mobileVersionsFromComposer(?array $composerData): ?array
{
$constraint = $composerData['require']['nativephp/mobile'] ?? null;

if (! is_string($constraint)) {
return null;
}

return ComposerConstraint::lowestVersionsByMajor($constraint, config('plugins.mobile_major_versions', [])) ?: null;
}

public function isSatisSynced(): bool
{
return $this->satis_synced_at !== null;
Expand Down Expand Up @@ -497,6 +532,18 @@ protected function uncategorized(Builder $query): Builder
->orWhereJsonLength('categories', 0));
}

/**
* Plugins whose composer.json allows a release of the given NativePHP Mobile major version.
*
* @param Builder<Plugin> $query
* @return Builder<Plugin>
*/
#[Scope]
protected function supportsMobileVersion(Builder $query, int $majorVersion): Builder
{
return $query->whereJsonContainsKey("mobile_versions->{$majorVersion}");
}

/**
* The categories Jev suggested for this plugin, likeliest first.
*
Expand Down Expand Up @@ -1028,6 +1075,7 @@ protected function casts(): array
'is_active' => 'boolean',
'is_official' => 'boolean',
'works_in_jump' => 'boolean',
'mobile_versions' => 'array',
'composer_data' => 'array',
'nativephp_data' => 'array',
'last_synced_at' => 'datetime',
Expand Down
1 change: 1 addition & 0 deletions app/Services/PluginSyncService.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public function sync(Plugin $plugin): bool

if ($composerData) {
$updateData['mobile_min_version'] = $composerData['require']['nativephp/mobile'] ?? null;
$updateData['mobile_versions'] = Plugin::mobileVersionsFromComposer($composerData);
}

if ($readme) {
Expand Down
Loading
Loading