Skip to content

Commit 57e492f

Browse files
committed
Sort package versions
1 parent 248d7cd commit 57e492f

5 files changed

Lines changed: 184 additions & 25 deletions

File tree

src/Controller/Dashboard/DashboardPackagesInfoController.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,9 @@ public function versionInfo(string $packageName, string $packageVersion): Respon
6363
public function versions(string $packageName): Response
6464
{
6565
$package = $this->packageRepository->findOneBy(['name' => $packageName]);
66-
$versions = $package->getVersions()->toArray();
67-
68-
usort($versions, Package::class . '::sortVersions');
6966

7067
return $this->render('dashboard/packages/package_versions.html.twig', [
7168
'package' => $package,
72-
'versions' => $versions,
7369
]);
7470
}
7571

src/Doctrine/Entity/Package.php

Lines changed: 149 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use CodedMonkey\Dirigent\Doctrine\Repository\PackageRepository;
66
use CodedMonkey\Dirigent\Validator\UniquePackage;
7+
use Composer\Package\Version\VersionParser;
78
use Composer\Pcre\Preg;
89
use Doctrine\Common\Collections\ArrayCollection;
910
use Doctrine\Common\Collections\Collection;
@@ -91,6 +92,8 @@ class Package
9192
*/
9293
private array $cachedVersions;
9394

95+
private array $sortedVersions;
96+
9497
public function __construct()
9598
{
9699
$this->installations = new PackageInstallations($this);
@@ -376,18 +379,30 @@ public function getPrettyBrowsableRepositoryUrl(): ?string
376379
}
377380

378381
/**
379-
* Returns the default branch or latest version of the package.
382+
* @return Version[]
383+
*/
384+
public function getSortedVersions(): array
385+
{
386+
if (!isset($this->sortedVersions)) {
387+
$this->sortedVersions = $this->versions->toArray();
388+
389+
usort($this->sortedVersions, [static::class, 'sortVersions']);
390+
}
391+
392+
return $this->sortedVersions;
393+
}
394+
395+
/**
396+
* Returns the default branch or the latest version of the package.
380397
*/
381398
public function getDefaultVersion(): ?Version
382399
{
383-
$versions = $this->versions->toArray();
400+
$versions = $this->getSortedVersions();
384401

385402
if (!count($versions)) {
386403
return null;
387404
}
388405

389-
usort($versions, [static::class, 'sortVersions']);
390-
391406
$latestVersion = reset($versions);
392407
foreach ($versions as $version) {
393408
if ($version->isDefaultBranch()) {
@@ -399,18 +414,17 @@ public function getDefaultVersion(): ?Version
399414
}
400415

401416
/**
402-
* Returns the latest (numbered) version of the package, or the default version if no versions were found.
417+
* The latest (numbered) version of the package, or the default version if no versions were found.
403418
*/
404419
public function getLatestVersion(): ?Version
405420
{
406-
$versions = $this->versions->toArray();
421+
$versions = $this->getSortedVersions();
407422

408423
if (!count($versions)) {
409424
return null;
410425
}
411426

412-
usort($versions, [static::class, 'sortVersions']);
413-
427+
// Return the first non-development version
414428
foreach ($versions as $version) {
415429
if (!$version->isDevelopment()) {
416430
return $version;
@@ -420,6 +434,133 @@ public function getLatestVersion(): ?Version
420434
return $this->getDefaultVersion();
421435
}
422436

437+
/**
438+
* The latest version of each major version.
439+
*
440+
* @return Version[]
441+
*/
442+
public function getActiveVersions(): array
443+
{
444+
$activeVersions = [];
445+
$activePrereleaseVersions = [];
446+
447+
foreach ($this->getSortedVersions() as $version) {
448+
if ('stable' !== VersionParser::parseStability($version->getNormalizedVersion())) {
449+
continue;
450+
}
451+
452+
[$majorVersion, $minorVersion] = explode('.', $version->getNormalizedVersion());
453+
454+
if ('0' === $majorVersion) {
455+
$prereleaseVersion = "$majorVersion.$minorVersion";
456+
457+
$activePrereleaseVersions[$prereleaseVersion] ??= $version;
458+
if (version_compare($version->getNormalizedVersion(), $activePrereleaseVersions[$prereleaseVersion]->getNormalizedVersion(), '>')) {
459+
$activePrereleaseVersions[$prereleaseVersion] = $version;
460+
}
461+
462+
continue;
463+
}
464+
465+
$activeVersions[$majorVersion] ??= $version;
466+
if (version_compare($version->getNormalizedVersion(), $activeVersions[$majorVersion]->getNormalizedVersion(), '>')) {
467+
$activeVersions[$majorVersion] = $version;
468+
}
469+
}
470+
471+
$activeDevelopmentVersions = [];
472+
$activePrereleaseDevelopmentVersions = [];
473+
474+
// Find newer unstable releases of active versions
475+
foreach ($this->getSortedVersions() as $version) {
476+
if (in_array(VersionParser::parseStability($version->getNormalizedVersion()), ['stable', 'dev'], true)) {
477+
continue;
478+
}
479+
480+
[$majorVersion, $minorVersion] = explode('.', $version->getNormalizedVersion());
481+
482+
$developmentVersion = "$majorVersion.$minorVersion";
483+
484+
if ('0' === $majorVersion) {
485+
if (isset($activePrereleaseVersions[$developmentVersion]) && !version_compare($version->getNormalizedVersion(), $activePrereleaseVersions[$developmentVersion]->getNormalizedVersion(), '>')) {
486+
continue;
487+
}
488+
489+
$activePrereleaseDevelopmentVersions[$developmentVersion] ??= $version;
490+
if (version_compare($version->getNormalizedVersion(), $activePrereleaseDevelopmentVersions[$developmentVersion]->getNormalizedVersion(), '>')) {
491+
$activePrereleaseDevelopmentVersions[$developmentVersion] = $version;
492+
}
493+
494+
continue;
495+
}
496+
497+
if (isset($activeVersions[$majorVersion]) && !version_compare($version->getNormalizedVersion(), $activeVersions[$majorVersion]->getNormalizedVersion(), '>')) {
498+
continue;
499+
}
500+
501+
$activeDevelopmentVersions[$developmentVersion] ??= $version;
502+
if (version_compare($version->getNormalizedVersion(), $activeDevelopmentVersions[$developmentVersion]->getNormalizedVersion(), '>')) {
503+
$activeDevelopmentVersions[$version->getNormalizedVersion()] = $version;
504+
}
505+
}
506+
507+
$activeVersions = [...$activeVersions, ...$activeDevelopmentVersions];
508+
509+
if (count($activeVersions)) {
510+
usort($activeVersions, [static::class, 'sortVersions']);
511+
512+
return $activeVersions;
513+
}
514+
515+
// Only show pre-release versions (0.x.x) if no versions after 1.0.0 was found
516+
$activePrereleaseVersions = [...$activePrereleaseVersions, ...$activePrereleaseDevelopmentVersions];
517+
518+
usort($activePrereleaseVersions, [static::class, 'sortVersions']);
519+
520+
return $activePrereleaseVersions;
521+
}
522+
523+
/**
524+
* All non-development versions that are not part of the active versions.
525+
*
526+
* @return Version[]
527+
*/
528+
public function getHistoricalVersions(): array
529+
{
530+
$historicalVersions = array_filter($this->getSortedVersions(), static fn (Version $version) => !$version->isDevelopment());
531+
532+
return array_diff($historicalVersions, $this->getActiveVersions());
533+
}
534+
535+
/**
536+
* All development versions associated with a version number (2.0.x-dev, 0.1.x-dev).
537+
*
538+
* @return Version[]
539+
*/
540+
public function getDevVersions(): array
541+
{
542+
$parser = new VersionParser();
543+
544+
return array_filter($this->getSortedVersions(), function (Version $version) use ($parser) {
545+
if (str_ends_with($version->getNormalizedVersion(), '.9999999-dev')) {
546+
return true;
547+
}
548+
549+
550+
return $version->hasVersionAlias() && str_ends_with($parser->normalize($version->getVersionAlias()), '.9999999-dev');
551+
});
552+
}
553+
554+
/**
555+
* All development versions associated with a branch (dev-main, dev-master, dev-develop).
556+
*
557+
* @return Version[]
558+
*/
559+
public function getDevBranchVersions(): array
560+
{
561+
return array_filter($this->getSortedVersions(), static fn (Version $version) => str_starts_with($version->getNormalizedVersion(), 'dev-'));
562+
}
563+
423564
public static function sortVersions(Version $a, Version $b): int
424565
{
425566
$aVersion = $a->getNormalizedVersion();

templates/dashboard/packages/package_statistics.html.twig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
{{ include('dashboard/packages/_package_header.html.twig', {currentPage: 'statistics'}) }}
77

88
<div class="mb-3">
9-
<h2>{{ 'Installations'|trans }}</h2>
9+
<h2 class="h4">{{ 'Installations'|trans }}</h2>
1010
<div class="row">
1111
<div class="col-md-4">
1212
<div id="total_all" class="card mb-2">
@@ -36,7 +36,7 @@
3636
</div>
3737

3838
<div class="mb-3">
39-
<h2>{{ 'Daily installations'|trans }}</h2>
39+
<h2 class="h4">{{ 'Daily installations'|trans }}</h2>
4040
<div
4141
data-controller="chart"
4242
data-chart-data-value="{{ package.installations.data ? package.installations.data|json_encode|escape : '{}' }}"

templates/dashboard/packages/package_versions.html.twig

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,34 @@
55
{% block page_content %}
66
{{ include('dashboard/packages/_package_header.html.twig', {currentPage: 'versions'}) }}
77

8-
<div class="list-group list-group-flush mb-3">
9-
{% for version in versions %}
10-
{% set packageVersionInfoUrl = path('dashboard_packages_version_info', {packageName: package.name, packageVersion: version.version}) %}
11-
<a href="{{ packageVersionInfoUrl }}" class="list-group-item">
12-
<div class="d-flex justify-content-between">
8+
{{ _self.versionList('Latest versions', package.activeVersions) }}
9+
{{ _self.versionList('Development versions', package.devVersions) }}
10+
{{ _self.versionList('Historical versions', package.historicalVersions) }}
11+
{{ _self.versionList('Development branch versions', package.devBranchVersions) }}
12+
{% endblock %}
13+
14+
{% macro versionList(title, versions) %}
15+
{% if versions|length %}
16+
<h2 class="h4">{{ title|trans }}</h2>
17+
<div class="list-group list-group-flush border-bottom mb-3">
18+
{% for version in versions %}
19+
{{ _self.versionListItem(version) }}
20+
{% endfor %}
21+
</div>
22+
{% endif %}
23+
{% endmacro %}
24+
25+
{% macro versionListItem(version) %}
26+
{% set packageVersionInfoUrl = path('dashboard_packages_version_info', {packageName: version.package.name, packageVersion: version.version}) %}
27+
<a href="{{ packageVersionInfoUrl }}" class="list-group-item">
28+
<div class="d-flex justify-content-between">
1329
<span>
1430
{{ version.version }}
1531
{% if version.hasVersionAlias() %}
1632
/ {{ version.versionAlias }}
1733
{% endif %}
1834
</span>
19-
<span class="text-muted">{{ version.releasedAt|date }}</span>
20-
</div>
21-
</a>
22-
{% endfor %}
23-
</div>
24-
{% endblock %}
35+
<span class="text-muted">{{ version.releasedAt.format('Y-m-d H:i') }} UTC</span>
36+
</div>
37+
</a>
38+
{% endmacro %}

translations/messages.en.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,16 @@ License: License
5050
Providers: Providers
5151
Statistics: Statistics
5252
Suggesters: Suggesters
53+
54+
# Package labels (Statistics)
5355
Total installations: Total installations
5456

57+
# Package labels (Versions)
58+
Development branch versions: Development branch versions
59+
Development versions: Development versions
60+
Historical versions: Historical versions
61+
Latest versions: Latest versions
62+
5563
# Page actions
5664
Impersonate: Impersonate
5765
Move Down: Move down

0 commit comments

Comments
 (0)