07ea171af9
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Generic: Repo Health / Release configuration (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 43s
New CLI tools: - manifest_element.php — extract element/type/prefix from any platform manifest - release_create.php — create/overwrite Gitea releases with proper naming - release_package.php — build ZIP+tar.gz, SHA-256, upload assets - release_promote.php — promote releases between channels (dev→RC→stable) - version_reset_dev.php — reset platform version on dev branch after release Updated CLI tools: - version_bump.php — now writes to manifests, Dolibarr mod, composer.json (not just README) - release_cascade.php — added --version for version-aware deletion of stale releases - release_validate.php — auto-detect platform, --github-output, source dir check Workflow changes (auto-release.yml): - Draft PR to main → auto-promote highest pre-release to RC - Merged PR to main → promote RC to stable (skip rebuild when RC exists) - Removed paths filter for Go/Node/generic repo compatibility - Fixed cascade --api-base parameter bug Workflow changes (pre-release.yml): - Auto-trigger development pre-release on feature branch merge to dev - Removed paths filter Infrastructure: - RepositorySynchronizer: fixed template repo names, .mokogitea/workflows path, universal workflow cascade (Template-Generic → other templates) - bulk_sync.php: syncs universal workflows to templates before repo sync - PHPDoc added to 4 classes missing class-level docs - Version bump 09.00.00 → 09.01.00 Closes #152 #153 #154 #155 #156 #157 #158 #159 #161 #162 Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
209 lines
6.6 KiB
PHP
209 lines
6.6 KiB
PHP
#!/usr/bin/env php
|
|
<?php
|
|
|
|
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* FILE INFORMATION
|
|
* DEFGROUP: moko-platform.CLI
|
|
* INGROUP: moko-platform
|
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
|
* PATH: /cli/release_cascade.php
|
|
* BRIEF: Delete lesser pre-release channels from Gitea when promoting stability
|
|
*
|
|
* Usage:
|
|
* php release_cascade.php --stability stable --token TOKEN --api-base URL
|
|
* php release_cascade.php --stability rc --token TOKEN --api-base URL
|
|
* php release_cascade.php --stability stable --version 09.01.00 --token TOKEN --api-base URL
|
|
*
|
|
* Cascade rules:
|
|
* stable -> deletes development, alpha, beta, release-candidate
|
|
* rc -> deletes development, alpha, beta
|
|
* beta -> deletes development, alpha
|
|
* alpha -> deletes development
|
|
*
|
|
* When --version is given, also deletes releases on any channel whose version
|
|
* is lower than the specified version (prevents stale pre-releases lingering).
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
$stability = null;
|
|
$token = null;
|
|
$apiBase = null;
|
|
$version = null;
|
|
|
|
foreach ($argv as $i => $arg) {
|
|
if ($arg === '--stability' && isset($argv[$i + 1])) {
|
|
$stability = $argv[$i + 1];
|
|
}
|
|
if ($arg === '--token' && isset($argv[$i + 1])) {
|
|
$token = $argv[$i + 1];
|
|
}
|
|
if ($arg === '--api-base' && isset($argv[$i + 1])) {
|
|
$apiBase = $argv[$i + 1];
|
|
}
|
|
if ($arg === '--version' && isset($argv[$i + 1])) {
|
|
$version = $argv[$i + 1];
|
|
}
|
|
}
|
|
|
|
// Allow token from environment
|
|
if ($token === null) {
|
|
$token = getenv('GA_TOKEN') ?: getenv('GITEA_TOKEN') ?: null;
|
|
}
|
|
|
|
if ($stability === null || $token === null || $apiBase === null) {
|
|
fwrite(STDERR, "Usage: release_cascade.php --stability [stable|rc|beta|alpha] --token TOKEN --api-base URL\n");
|
|
fwrite(STDERR, " --api-base: e.g. https://git.mokoconsulting.tech/api/v1/repos/Org/Repo\n");
|
|
fwrite(STDERR, " Token can also be set via GA_TOKEN or GITEA_TOKEN env var\n");
|
|
exit(1);
|
|
}
|
|
|
|
// Define cascade hierarchy
|
|
$cascadeMap = [
|
|
'stable' => ['development', 'alpha', 'beta', 'release-candidate'],
|
|
'release-candidate' => ['development', 'alpha', 'beta'],
|
|
'rc' => ['development', 'alpha', 'beta'],
|
|
'beta' => ['development', 'alpha'],
|
|
'alpha' => ['development'],
|
|
];
|
|
|
|
if (!isset($cascadeMap[$stability])) {
|
|
fwrite(STDERR, "Unknown stability level: {$stability}\n");
|
|
fwrite(STDERR, "Valid options: stable, rc, beta, alpha\n");
|
|
exit(1);
|
|
}
|
|
|
|
$tagsToDelete = $cascadeMap[$stability];
|
|
$deleted = 0;
|
|
|
|
foreach ($tagsToDelete as $tag) {
|
|
// Get release by tag
|
|
$ch = curl_init("{$apiBase}/releases/tags/{$tag}");
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
|
|
CURLOPT_TIMEOUT => 30,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode !== 200 || empty($response)) {
|
|
continue;
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
$releaseId = $data['id'] ?? null;
|
|
|
|
if ($releaseId === null) {
|
|
continue;
|
|
}
|
|
|
|
// Delete release
|
|
$ch = curl_init("{$apiBase}/releases/{$releaseId}");
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_CUSTOMREQUEST => 'DELETE',
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
|
|
CURLOPT_TIMEOUT => 30,
|
|
]);
|
|
curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
// Delete tag
|
|
$ch = curl_init("{$apiBase}/tags/{$tag}");
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_CUSTOMREQUEST => 'DELETE',
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
|
|
CURLOPT_TIMEOUT => 30,
|
|
]);
|
|
curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
echo "Deleted: {$tag} (release id: {$releaseId})\n";
|
|
$deleted++;
|
|
}
|
|
|
|
// ── Version-aware cleanup: delete releases with lesser version numbers ───────
|
|
if ($version !== null) {
|
|
// Normalize version for comparison (strip any suffix)
|
|
$baseVersion = preg_replace('/-[a-z]+$/', '', $version);
|
|
|
|
// Check all channels (including ones not in the cascade map for this stability)
|
|
$allChannels = ['development', 'alpha', 'beta', 'release-candidate', 'stable'];
|
|
foreach ($allChannels as $tag) {
|
|
// Skip the current stability channel
|
|
if ($tag === $stability) {
|
|
continue;
|
|
}
|
|
// Skip channels already deleted by cascade above
|
|
if (in_array($tag, $tagsToDelete, true)) {
|
|
continue;
|
|
}
|
|
|
|
$ch = curl_init("{$apiBase}/releases/tags/{$tag}");
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
|
|
CURLOPT_TIMEOUT => 30,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode !== 200 || empty($response)) {
|
|
continue;
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
$releaseId = $data['id'] ?? null;
|
|
$releaseName = $data['name'] ?? '';
|
|
if ($releaseId === null) {
|
|
continue;
|
|
}
|
|
|
|
// Extract version from release name (e.g. "element 09.00.01 (development)")
|
|
$releaseVersion = null;
|
|
if (preg_match('/(\d{2}\.\d{2}\.\d{2})/', $releaseName, $vm)) {
|
|
$releaseVersion = $vm[1];
|
|
}
|
|
|
|
if ($releaseVersion === null) {
|
|
continue;
|
|
}
|
|
|
|
// Delete if release version is less than the promoted version
|
|
if (version_compare($releaseVersion, $baseVersion, '<')) {
|
|
$delCh = curl_init("{$apiBase}/releases/{$releaseId}");
|
|
curl_setopt_array($delCh, [
|
|
CURLOPT_CUSTOMREQUEST => 'DELETE',
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
|
|
CURLOPT_TIMEOUT => 30,
|
|
]);
|
|
curl_exec($delCh);
|
|
curl_close($delCh);
|
|
|
|
$tagCh = curl_init("{$apiBase}/tags/{$tag}");
|
|
curl_setopt_array($tagCh, [
|
|
CURLOPT_CUSTOMREQUEST => 'DELETE',
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
|
|
CURLOPT_TIMEOUT => 30,
|
|
]);
|
|
curl_exec($tagCh);
|
|
curl_close($tagCh);
|
|
|
|
echo "Deleted: {$tag} — version {$releaseVersion} < {$baseVersion}\n";
|
|
$deleted++;
|
|
}
|
|
}
|
|
}
|
|
|
|
echo "Cleaned up {$deleted} pre-release channel(s)\n";
|
|
exit(0);
|