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
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@ package-lock.json
public/uploads/

# Generated by bin/generate-packages (daily cron); not tracked so it never conflicts on pull
/public/packages.json
/public/packages.json.tmp
/public/dotkernel-packages.json
/public/dotkernel-packages.json.tmp
2 changes: 1 addition & 1 deletion config/autoload/packages.global.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
'zend-expressive-hal',
'zf1',
],
'dataFile' => __DIR__ . '/../../public/packages.json',
'dataFile' => __DIR__ . '/../../public/dotkernel-packages.json',
'includeArchived' => true,
'timeout' => 10,
'connectTimeout' => 5,
Expand Down
2 changes: 1 addition & 1 deletion public/css/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/js/app.js

Large diffs are not rendered by default.

162 changes: 161 additions & 1 deletion src/App/assets/scss/components/_custom.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
html[data-theme="light"] {
--bg: #FBF9F4;
--surface: #FFFFFF;
--surface-2: #F3EFE6;
--surface-2: #f8f6f2;
--border: #E3DDCF;
--border-soft: #EBE6DA;
--text: #0B0B0B;
Expand All @@ -17,6 +17,17 @@ html[data-theme="light"] {
--teal-dim: rgba(163, 60, 75, .12);
--nav-bg: rgba(251, 249, 244, .86);
--on-accent: #FBF6F0;

/* OSS lifecycle states. The brand palette is deliberately one warm hue, so these are the
only tokens that carry a second signal; they stay muted to sit beside the red. */
--state-active: #2E7D64;
--state-active-dim: rgba(46, 125, 100, .12);
--state-maintenance: #96620A;
--state-maintenance-dim: rgba(150, 98, 10, .12);
--state-security: #C71F38;
--state-security-dim: rgba(199, 31, 56, .12);
--state-archived: #6B665C;
--state-archived-dim: rgba(107, 102, 92, .1);
}

html[data-theme="dark"] {
Expand All @@ -37,6 +48,16 @@ html[data-theme="dark"] {
--amber-dim: rgba(218, 53, 80, .16);
--teal-dim: rgba(232, 99, 122, .16);
--nav-bg: rgba(11, 11, 11, .86);

/* Lifted for the dark surfaces; same four states as the light theme. */
--state-active: #58B79A;
--state-active-dim: rgba(88, 183, 154, .16);
--state-maintenance: #DCA23C;
--state-maintenance-dim: rgba(220, 162, 60, .16);
--state-security: #E8637A;
--state-security-dim: rgba(232, 99, 122, .16);
--state-archived: #8A847C;
--state-archived-dim: rgba(138, 132, 124, .14);
}

html {
Expand Down Expand Up @@ -722,6 +743,145 @@ html[data-theme="light"] .theme-toggle {
}
}

/* ============================================================
Package lifecycle cards
============================================================ */

/* auto-fill keeps the packages grid responsive without breakpoints, since the
number of packages changes with every cron run. */
.packages-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 18px;
}

.package-card {
border-top-width: 4px;
transition: box-shadow .2s ease, transform .2s ease;

&:hover {
box-shadow: 0 6px 20px var(--accent-dim);
transform: translateY(-2px);
}

.card-top {
margin-bottom: 12px;
}

h3 {
margin: 0;
}
}

/* The pill carries the state, so it is the loudest thing on the card after the name. */
.package-state {
display: inline-flex;
align-items: center;
gap: 7px;
margin-bottom: 8px;
padding: 4px 10px;
border: 1px solid var(--accent);
border-radius: 999px;
background: var(--accent-dim);
color: var(--accent);
font-family: var(--mono);
font-size: 10.5px;
font-weight: 600;
letter-spacing: .1em;
text-transform: uppercase;

/* Each state also changes the marker's shape, so the four are still telling apart
when the hues are not (colour blindness, greyscale printing, forced-colours mode). */
&::before {
content: '';
flex: none;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--accent);
}
}

.package-card--active {
--accent: var(--state-active);
--accent-dim: var(--state-active-dim);
}

/* Hollow marker: still supported, no longer moving. */
.package-card--maintenance {
--accent: var(--state-maintenance);
--accent-dim: var(--state-maintenance-dim);

.package-state::before {
background: transparent;
box-shadow: inset 0 0 0 2px var(--accent);
}
}

/* Haloed marker reads as a warning without leaning on hue alone. */
.package-card--security-only {
--accent: var(--state-security);
--accent-dim: var(--state-security-dim);

.package-state::before {
box-shadow: 0 0 0 3px var(--accent-dim);
}
}

/* Archived and unrecognised states recede: dashed edge, quieter surface, square marker. */
.package-card--archived,
.package-card--unknown {
--accent: var(--state-archived);
--accent-dim: var(--state-archived-dim);
border-style: dashed;
border-top-style: solid;

h3 {
color: var(--text-muted);
}

.package-state::before {
border-radius: 2px;
background: transparent;
box-shadow: inset 0 0 0 2px var(--accent);
}

.badges img {
opacity: .65;
}
}

/* Repository description straight from GitHub, so its length is out of our hands. `desc`
already grows to fill the card, which keeps the badges and buttons on one line across the
row however long the sentence turns out to be. */
.package-description {
margin: 0 0 14px;
color: var(--text-muted);
font-size: 14px;
}

.package-archived {
margin: 0;
color: var(--text-dim);
font-size: 12px;
}

.packages-generated-at {
margin-top: 22px;
color: var(--text-dim);
}

/* Respect a reduced-motion preference: the lift is decoration, the colour is the message. */
@media (prefers-reduced-motion: reduce) {
.package-card {
transition: none;

&:hover {
transform: none;
}
}
}

.standards-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
Expand Down
2 changes: 1 addition & 1 deletion src/App/src/Factory/PackageGeneratorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function __invoke(ContainerInterface $container): PackageGenerator

return new PackageGenerator(
$client,
(string) ($packages['dataFile'] ?? 'data/packages.json'),
(string) ($packages['dataFile'] ?? 'data/dotkernel-packages.json'),
(string) ($github['org'] ?? 'dotkernel'),
$ignoreRepos,
(bool) ($packages['includeArchived'] ?? true),
Expand Down
2 changes: 1 addition & 1 deletion src/App/src/Handler/GetPackagesViewHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function __construct(
public function handle(ServerRequestInterface $request): ResponseInterface
{
// no inline regeneration
// data is cached in `public/packages.json` by `bin/generate-packages`
// data is cached in `public/dotkernel-packages.json` by `bin/generate-packages`
$data = $this->packageGenerator->read();
if (! is_array($data)) {
$data = [];
Expand Down
27 changes: 22 additions & 5 deletions src/App/src/Service/PackageGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
* @phpstan-type PackageData array{
* name: string,
* url: string,
* description: string|null,
* lifecycle: string,
* php: string|null,
* archived: bool,
Expand Down Expand Up @@ -175,11 +176,12 @@ public function write(): array
}

$packages[] = [
'name' => $name,
'url' => self::GITHUB_ROOT . $this->org . '/' . $name,
'lifecycle' => $lifecycle,
'php' => $php,
'archived' => $archived,
'name' => $name,
'url' => self::GITHUB_ROOT . $this->org . '/' . $name,
'description' => $this->parseDescription($repository['description'] ?? null),
'lifecycle' => $lifecycle,
'php' => $php,
'archived' => $archived,
];
}

Expand Down Expand Up @@ -292,6 +294,21 @@ private function buildIgnoreLookup(): array
return array_flip($names);
}

/**
* GitHub sends `null` for a repository with no description, and the field is free text, so
* anything that is not a non-blank string is recorded as absent rather than as an empty label.
*/
private function parseDescription(mixed $description): ?string
{
if (! is_string($description)) {
return null;
}

$description = trim($description);

return $description === '' ? null : $description;
}

private function parseLifecycle(string $contents): ?string
{
if (preg_match('/osslifecycle\s*=\s*([^\s#]+)/i', $contents, $matches) !== 1) {
Expand Down
35 changes: 25 additions & 10 deletions src/Blog/templates/page/dotkernel-packages-oss-lifecycle.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@

{% block content %}
{#
Rendered from data/packages.json, rebuilt daily by bin/generate-packages.
Rendered from public/dotkernel-packages.json, rebuilt daily by bin/generate-packages.
The lifecycle value comes from each repository's OSSMETADATA file.
#}
{% set lifecycleMeta = {
active: {label: 'Active', accent: 'var(--teal)'},
maintenance: {label: 'Maintenance', accent: 'var(--brand-red)'},
'security-only': {label: 'Security-only', accent: 'var(--amber)'},
archived: {label: 'Archived', accent: 'var(--periwinkle)'},
active: {label: 'Active', title: 'Actively developed, with new features and fixes.'},
maintenance: {label: 'Maintenance', title: 'Still supported, receiving fixes but no new features.'},
'security-only': {label: 'Security-only', title: 'Only security fixes are released. Plan an upgrade.'},
archived: {label: 'Archived', title: 'No longer maintained. Do not use in new projects.'},
} %}

<div class="wrap post-body-wrap">
Expand All @@ -28,16 +28,28 @@
{{ include('@partial/left-menu.html.twig') }}

<main class="article">
<div class="products-grid" style="grid-template-columns:repeat(auto-fill,minmax(240px,1fr));">
<div class="packages-grid">

{% for package in packages %}
{% set meta = lifecycleMeta[package.lifecycle] ?? {label: package.lifecycle|capitalize, accent: 'var(--periwinkle)'} %}
<div class="card" style="--accent:{{ meta.accent }}">
{% set state = lifecycleMeta[package.lifecycle] is defined ? package.lifecycle : 'unknown' %}
{% set meta = lifecycleMeta[state]|default({
label: package.lifecycle|capitalize,
title: 'This package does not declare a recognised lifecycle.',
}) %}
<div class="card package-card package-card--{{ state }}">
<div class="card-top">
<div><span class="kicker">{{ meta.label }}</span><h3>{{ package.name }}</h3></div>
<div>
<span class="package-state" title="{{ meta.title }}">{{ meta.label }}</span>
<h3>{{ package.name }}</h3>
</div>
</div>
{# GitHub repository description. hidden, if empty #}
{% if package.description %}
<p class="desc package-description">{{ package.description }}</p>
{% endif %}
<div class="badges">
<img alt="PHP version for dotkernel/{{ package.name }}" loading="lazy" src="https://img.shields.io/packagist/dependency-v/dotkernel/{{ package.name }}/php?style=social">
<img alt="PHP version for dotkernel/{{ package.name }}" loading="lazy"
src="https://img.shields.io/packagist/dependency-v/dotkernel/{{ package.name }}/php?color=blue">
{% if package.archived %}
<p class="package-archived">Archived on GitHub</p>
{% endif %}
Expand All @@ -53,6 +65,9 @@

{% if generatedAt %}
<p class="packages-generated-at"><small>Last updated {{ generatedAt|date('M d, Y H:i T') }}</small></p>
<div class="card-btns">
<a class="btn btn-ghost btn-sm" href="/dotkernel-packages.json">View as JSON</a>
</div>
{% endif %}
</main>
</div>
Expand Down
2 changes: 1 addition & 1 deletion test/Unit/App/Factory/PackageGeneratorFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function testInvokeFallsBackToDefaults($config): void
{
$generator = (new PackageGeneratorFactory())($this->createContainer($config));

$this->assertSame('data/packages.json', $generator->getDataFile());
$this->assertSame('data/dotkernel-packages.json', $generator->getDataFile());
$this->assertSame('dotkernel', $this->readProperty($generator, 'org'));
$this->assertSame([], $this->readProperty($generator, 'ignoreRepos'));
$this->assertTrue($this->readProperty($generator, 'includeArchived'));
Expand Down
8 changes: 7 additions & 1 deletion test/Unit/App/Handler/GetPackagesViewHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ class GetPackagesViewHandlerTest extends UnitTest
public function testHandlePassesTheGeneratedListingToTheTemplate(): void
{
$packages = [
['name' => 'dot-cache', 'lifecycle' => 'active', 'php' => '~8.4.0', 'archived' => false],
[
'name' => 'dot-cache',
'description' => 'Dotkernel cache component',
'lifecycle' => 'active',
'php' => '~8.4.0',
'archived' => false,
],
];

$parameters = $this->render([
Expand Down
Loading