Skip to content

Instantly share code, notes, and snippets.

@nandordudas
Created October 13, 2025 14:53
Show Gist options
  • Save nandordudas/3d0f05b9455a17c4069b3247fcb74627 to your computer and use it in GitHub Desktop.
Save nandordudas/3d0f05b9455a17c4069b3247fcb74627 to your computer and use it in GitHub Desktop.
RewriteEngine On
RewriteRule ^updates\.json$ updates.php [L]
<?php
declare(strict_types=1);
// https://extensionworkshop.com/documentation/manage/updating-your-extension/
// cat /proc/sys/kernel/random/uuid
final readonly class Uuid
{
private function __construct(public string $value) {}
public static function fromString(string $value): ?self
{
$pattern = '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i';
return preg_match($pattern, $value) ? new self($value) : null;
}
public function withBraces(): string
{
return '{' . $this->value . '}';
}
}
final readonly class AddonUpdate
{
public function __construct(
public string $version,
public string $update_link,
) {}
}
final readonly class UpdatesGenerator
{
public function __construct(
private string $addonsPath,
private string $baseUrl,
) {}
public function generate(): string
{
$addons = [];
foreach (glob($this->addonsPath . '/*', GLOB_ONLYDIR) as $addonDir) {
$uuid = Uuid::fromString(basename($addonDir));
if ($uuid === null) {
continue;
}
$updates = $this->scanAddonUpdates($addonDir, $uuid);
if ($updates !== []) {
$addons[$uuid->withBraces()] = ['updates' => $updates];
}
}
return json_encode(
['addons' => $addons],
JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR,
);
}
private function scanAddonUpdates(string $addonDir, Uuid $uuid): array
{
$updates = [];
foreach (glob($addonDir . '/*.{zip,xpi}', GLOB_BRACE) as $file) {
if (!preg_match('/^(.+)-(\d+\.\d+\.\d+)\.(zip|xpi)$/', $filename = basename($file), $matches)) {
continue;
}
$updates[] = new AddonUpdate(
version: $matches[2],
update_link: $this->baseUrl . '/' . $uuid->value . '/' . $filename,
);
}
usort($updates, fn($a, $b) => version_compare($a->version, $b->version));
return array_map(
fn(AddonUpdate $u) => [
'version' => $u->version,
'update_link' => $u->update_link,
],
$updates,
);
}
}
function main(): void
{
$generator = new UpdatesGenerator(
addonsPath: __DIR__,
baseUrl: 'https://.../add-ons',
);
header('Content-Type: application/json');
header('Cache-Control: max-age=60');
echo $generator->generate();
}
// [TODO] add missing error handling
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment