66e728b078
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) 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
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Generic: Repo Health / Release configuration (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 18s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: Auto Version Bump / Version Bump (push) Failing after 27s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 28s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 3s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 1m7s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 1m7s
Auto-fixed 5006 tab-indent and line-ending errors via phpcbf, then manually broke 100 lines exceeding 150-char limit. All 74 files in cli/, automation/, maintenance/, deploy/ now pass PHPCS PSR-12 clean. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
372 lines
15 KiB
PHP
372 lines
15 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_publish.php
|
|
* VERSION: 09.22.00
|
|
* BRIEF: Publish a release and create copies for all lesser stability streams.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
|
|
|
use MokoEnterprise\CliFramework;
|
|
|
|
class ReleasePublishCli extends CliFramework
|
|
{
|
|
protected function configure(): void
|
|
{
|
|
$this->setDescription('Publish a release and update stability streams');
|
|
$this->addArgument('--path', 'Repository root (default: .)', '.');
|
|
$this->addArgument('--stability', 'Target stability: dev|alpha|beta|rc|stable (required)', '');
|
|
$this->addArgument('--token', 'Gitea API token (required)', '');
|
|
$this->addArgument('--bump', 'Version bump type: patch|minor|none (default: none)', 'none');
|
|
$this->addArgument('--branch', 'Current branch (default: auto-detect)', '');
|
|
$this->addArgument('--gitea-url', 'Gitea URL', '');
|
|
$this->addArgument('--org', 'Organization', '');
|
|
$this->addArgument('--repo', 'Repository name', '');
|
|
$this->addArgument('--repo-url', 'Repository URL for git auth', '');
|
|
}
|
|
|
|
protected function run(): int
|
|
{
|
|
$path = $this->getArgument('--path');
|
|
$stability = $this->getArgument('--stability');
|
|
$token = $this->getArgument('--token');
|
|
$bumpType = $this->getArgument('--bump');
|
|
$branch = $this->getArgument('--branch');
|
|
$giteaUrl = $this->getArgument('--gitea-url') ?: (getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech');
|
|
$org = $this->getArgument('--org') ?: (getenv('GITEA_ORG') ?: '');
|
|
$repo = $this->getArgument('--repo') ?: (getenv('GITEA_REPO') ?: '');
|
|
$repoUrl = $this->getArgument('--repo-url');
|
|
|
|
if (empty($stability) || empty($token)) {
|
|
$this->log('ERROR', "Usage: release_publish.php --stability <dev|alpha|beta|rc|stable> --token TOKEN [options]");
|
|
return 1;
|
|
}
|
|
|
|
$cli = __DIR__;
|
|
$php = '"' . PHP_BINARY . '"';
|
|
$giteaUrl = rtrim($giteaUrl, '/');
|
|
|
|
// Resolve path early for shell commands (Windows needs native paths)
|
|
$resolvedPath = realpath($path) ?: $path;
|
|
|
|
// Auto-detect org/repo from git remote if not set
|
|
if (empty($org) || empty($repo)) {
|
|
$cd = PHP_OS_FAMILY === 'Windows' ? "cd /d " : "cd ";
|
|
$remote = trim((string) @shell_exec(
|
|
$cd . escapeshellarg($resolvedPath)
|
|
. " && git remote get-url origin 2>/dev/null"
|
|
));
|
|
if (preg_match('#/([^/]+)/([^/.]+?)(?:\.git)?$#', $remote, $m)) {
|
|
if (empty($org)) {
|
|
$org = $m[1];
|
|
}
|
|
if (empty($repo)) {
|
|
$repo = $m[2];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Auto-construct repo URL for git auth if not provided
|
|
if (empty($repoUrl) && !empty($token) && !empty($org) && !empty($repo)) {
|
|
$host = preg_replace('#^https?://#', '', $giteaUrl);
|
|
$repoUrl = "https://x-access-token:{$token}@{$host}/{$org}/{$repo}.git";
|
|
}
|
|
|
|
// Auto-detect branch
|
|
if (empty($branch)) {
|
|
$cdCmd = PHP_OS_FAMILY === 'Windows' ? "cd /d " : "cd ";
|
|
$branch = getenv('GITHUB_REF_NAME')
|
|
?: trim((string) @shell_exec(
|
|
$cdCmd . escapeshellarg($resolvedPath)
|
|
. " && git rev-parse --abbrev-ref HEAD 2>/dev/null"
|
|
));
|
|
}
|
|
|
|
$apiBase = "{$giteaUrl}/api/v1/repos/{$org}/{$repo}";
|
|
|
|
// Stability ordering and suffix mapping
|
|
$allStabilities = ['dev', 'alpha', 'beta', 'rc', 'stable'];
|
|
$suffixMap = [
|
|
'dev' => '-dev',
|
|
'alpha' => '-alpha',
|
|
'beta' => '-beta',
|
|
'rc' => '-rc',
|
|
'stable' => '',
|
|
];
|
|
$releaseTagMap = [
|
|
'dev' => 'development',
|
|
'alpha' => 'alpha',
|
|
'beta' => 'beta',
|
|
'rc' => 'release-candidate',
|
|
'stable' => 'stable',
|
|
];
|
|
|
|
$stabilityIndex = array_search($stability, $allStabilities);
|
|
if ($stabilityIndex === false) {
|
|
$this->log('ERROR', "Invalid stability: {$stability}");
|
|
return 1;
|
|
}
|
|
|
|
echo "=== Release Publish ===\n";
|
|
echo "Stability: {$stability} | Bump: {$bumpType} | Branch: {$branch}\n";
|
|
echo "Repo: {$org}/{$repo}\n";
|
|
|
|
// -- Step 1: Version bump (if requested) --
|
|
if ($bumpType !== 'none') {
|
|
$bumpFlag = $bumpType === 'minor' ? '--minor' : '';
|
|
echo "\n--- Step 1: Version bump ({$bumpType}) ---\n";
|
|
if (!$this->dryRun) {
|
|
passthru("{$php} {$cli}/version_bump.php --path " . escapeshellarg($path) . " {$bumpFlag} 2>&1");
|
|
} else {
|
|
echo "[DRY-RUN] Would run version_bump.php {$bumpFlag}\n";
|
|
}
|
|
}
|
|
|
|
// -- Step 2: Read version and set stability suffix --
|
|
echo "\n--- Step 2: Set version suffix ---\n";
|
|
$versionOutput = [];
|
|
$devNull = PHP_OS_FAMILY === 'Windows' ? '2>NUL' : '2>/dev/null';
|
|
exec("{$php} {$cli}/version_read.php --path " . escapeshellarg($resolvedPath) . " {$devNull}", $versionOutput);
|
|
$version = trim($versionOutput[0] ?? '');
|
|
if (empty($version)) {
|
|
$this->log('ERROR', 'No version found');
|
|
return 1;
|
|
}
|
|
// Strip existing suffix to get base version
|
|
$baseVersion = preg_replace('/(-(dev|alpha|beta|rc))+$/', '', $version);
|
|
|
|
if (!$this->dryRun) {
|
|
passthru("{$php} {$cli}/version_set_platform.php --path " . escapeshellarg($path)
|
|
. " --version " . escapeshellarg($baseVersion)
|
|
. " --branch " . escapeshellarg($branch)
|
|
. " --stability " . escapeshellarg($stability) . " 2>&1");
|
|
passthru("{$php} {$cli}/version_check.php --path " . escapeshellarg($path) . " --fix 2>/dev/null");
|
|
}
|
|
|
|
$releaseVersion = $baseVersion . $suffixMap[$stability];
|
|
echo "Release version: {$releaseVersion}\n";
|
|
|
|
// -- Step 2b: Update badges and changelog --
|
|
if (!$this->dryRun) {
|
|
passthru(
|
|
"{$php} {$cli}/badge_update.php --path "
|
|
. escapeshellarg($path) . " --version "
|
|
. escapeshellarg($baseVersion) . " 2>/dev/null"
|
|
);
|
|
|
|
$changelogFile = realpath($path) . '/CHANGELOG.md';
|
|
if (file_exists($changelogFile)) {
|
|
passthru(
|
|
"{$php} {$cli}/changelog_promote.php --path "
|
|
. escapeshellarg($path) . " --version "
|
|
. escapeshellarg($baseVersion) . " 2>/dev/null"
|
|
);
|
|
passthru(
|
|
"{$php} {$cli}/changelog_prune.php --path "
|
|
. escapeshellarg($path) . " --keep 5 2>/dev/null"
|
|
);
|
|
}
|
|
}
|
|
|
|
// -- Step 2c: Commit version changes before building --
|
|
$root = realpath($path) ?: $path;
|
|
if (!$this->dryRun) {
|
|
// Configure git
|
|
$cdPfx = PHP_OS_FAMILY === 'Windows' ? "cd /d " : "cd ";
|
|
$cdR = $cdPfx . escapeshellarg($root);
|
|
@shell_exec(
|
|
$cdR . " && git config --local user.email"
|
|
. " \"gitea-actions[bot]@mokoconsulting.tech\""
|
|
);
|
|
@shell_exec(
|
|
$cdR . " && git config --local user.name"
|
|
. " \"gitea-actions[bot]\""
|
|
);
|
|
if (!empty($repoUrl)) {
|
|
@shell_exec(
|
|
$cdR . " && git remote set-url origin "
|
|
. escapeshellarg($repoUrl)
|
|
);
|
|
}
|
|
|
|
// Ensure we're on the actual branch (not detached HEAD from PR merge)
|
|
@shell_exec(
|
|
$cdR . " && git fetch origin "
|
|
. escapeshellarg($branch) . " 2>/dev/null"
|
|
);
|
|
@shell_exec(
|
|
$cdR . " && git checkout -B "
|
|
. escapeshellarg($branch) . " FETCH_HEAD 2>/dev/null"
|
|
);
|
|
|
|
// Re-apply version changes on the checked-out branch
|
|
passthru("{$php} {$cli}/version_set_platform.php --path " . escapeshellarg($path)
|
|
. " --version " . escapeshellarg($baseVersion)
|
|
. " --branch " . escapeshellarg($branch)
|
|
. " --stability " . escapeshellarg($stability) . " 2>/dev/null");
|
|
passthru(
|
|
"{$php} {$cli}/version_check.php --path "
|
|
. escapeshellarg($path) . " --fix 2>/dev/null"
|
|
);
|
|
passthru(
|
|
"{$php} {$cli}/badge_update.php --path "
|
|
. escapeshellarg($path) . " --version "
|
|
. escapeshellarg($baseVersion) . " 2>/dev/null"
|
|
);
|
|
|
|
$diffCheck = trim((string) @shell_exec(
|
|
$cdR . " && git diff --quiet"
|
|
. " && git diff --cached --quiet"
|
|
. " 2>&1 && echo clean || echo dirty"
|
|
));
|
|
if ($diffCheck === 'dirty') {
|
|
@shell_exec($cdR . " && git add -A");
|
|
$commitMsg = "chore(release): build"
|
|
. " {$releaseVersion} [skip ci]";
|
|
@shell_exec(
|
|
$cdR . " && git commit -m "
|
|
. escapeshellarg($commitMsg)
|
|
. " --author=\"gitea-actions[bot]"
|
|
. " <gitea-actions[bot]@mokoconsulting.tech>\""
|
|
);
|
|
$pushResult = @shell_exec(
|
|
$cdR . " && git push origin "
|
|
. escapeshellarg($branch) . " 2>&1"
|
|
);
|
|
echo " Committed release changes\n";
|
|
echo " Push: " . trim($pushResult ?? '') . "\n";
|
|
}
|
|
}
|
|
|
|
// -- Step 3: Build release package --
|
|
echo "\n--- Step 3: Build and upload release ---\n";
|
|
$releaseTag = $releaseTagMap[$stability];
|
|
$sha256 = '';
|
|
|
|
if (!$this->dryRun) {
|
|
// Create release
|
|
passthru("{$php} {$cli}/release_create.php --path " . escapeshellarg($path)
|
|
. " --version " . escapeshellarg($releaseVersion)
|
|
. " --tag " . escapeshellarg($releaseTag)
|
|
. " --token " . escapeshellarg($token)
|
|
. " --api-base " . escapeshellarg($apiBase)
|
|
. " --repo " . escapeshellarg($repo)
|
|
. " --branch " . escapeshellarg($branch) . " 2>&1");
|
|
|
|
// Build and upload package
|
|
$packageOutput = [];
|
|
exec("{$php} {$cli}/release_package.php --path " . escapeshellarg($path)
|
|
. " --version " . escapeshellarg($releaseVersion)
|
|
. " --tag " . escapeshellarg($releaseTag)
|
|
. " --token " . escapeshellarg($token)
|
|
. " --api-base " . escapeshellarg($apiBase)
|
|
. " --repo " . escapeshellarg($repo)
|
|
. " --output /tmp 2>&1", $packageOutput);
|
|
foreach ($packageOutput as $line) {
|
|
echo $line . "\n";
|
|
// Extract SHA from output
|
|
if (preg_match('/sha256_zip=([a-f0-9]{64})/i', $line, $m)) {
|
|
$sha256 = $m[1];
|
|
}
|
|
}
|
|
// Also check GITHUB_OUTPUT
|
|
$ghOutput = getenv('GITHUB_OUTPUT');
|
|
if ($ghOutput && file_exists($ghOutput)) {
|
|
$ghContent = file_get_contents($ghOutput);
|
|
if (preg_match('/sha256_zip=([a-f0-9]{64})/i', $ghContent, $m)) {
|
|
$sha256 = $m[1];
|
|
}
|
|
}
|
|
} else {
|
|
echo "[DRY-RUN] Would build and upload {$releaseVersion} to {$releaseTag}\n";
|
|
}
|
|
|
|
// -- Step 4: No lesser stream copies --
|
|
echo "\n--- Step 4: Skipped (no lesser stream copies) ---\n";
|
|
|
|
// -- Step 5: Update ONLY this stream in updates.xml --
|
|
echo "\n--- Step 5: Update {$stability} stream in updates.xml ---\n";
|
|
$streamsToWrite = [$stability];
|
|
|
|
foreach ($streamsToWrite as $stream) {
|
|
$streamVersion = $releaseVersion;
|
|
$shaFlag = !empty($sha256) ? "--sha {$sha256}" : '';
|
|
|
|
echo " Writing {$stream} stream: {$streamVersion}\n";
|
|
if (!$this->dryRun) {
|
|
passthru("{$php} {$cli}/updates_xml_build.php --path " . escapeshellarg($path)
|
|
. " --version " . escapeshellarg($streamVersion)
|
|
. " --stability " . escapeshellarg($stream)
|
|
. " --gitea-url " . escapeshellarg($giteaUrl)
|
|
. " --org " . escapeshellarg($org)
|
|
. " --repo " . escapeshellarg($repo)
|
|
. " {$shaFlag} 2>&1");
|
|
}
|
|
}
|
|
|
|
// -- Step 6: Commit updates.xml and sync to all branches --
|
|
echo "\n--- Step 6: Commit and sync updates.xml ---\n";
|
|
$root = realpath($path) ?: $path;
|
|
|
|
if (!$this->dryRun) {
|
|
$cdX = PHP_OS_FAMILY === 'Windows' ? "cd /d " : "cd ";
|
|
$cdRt = $cdX . escapeshellarg($root);
|
|
$diffCheck = trim((string) @shell_exec(
|
|
$cdRt . " && git diff --quiet updates.xml"
|
|
. " 2>&1 && echo clean || echo dirty"
|
|
));
|
|
if ($diffCheck === 'dirty') {
|
|
@shell_exec($cdRt . " && git add updates.xml");
|
|
$chMsg = "chore: update channels for"
|
|
. " {$releaseVersion} [skip ci]";
|
|
@shell_exec(
|
|
$cdRt . " && git commit -m "
|
|
. escapeshellarg($chMsg)
|
|
. " --author=\"gitea-actions[bot]"
|
|
. " <gitea-actions[bot]@mokoconsulting.tech>\""
|
|
);
|
|
@shell_exec(
|
|
$cdRt . " && git push origin "
|
|
. escapeshellarg($branch) . " 2>&1"
|
|
);
|
|
echo " Committed updates.xml\n";
|
|
}
|
|
|
|
// Sync to all branches
|
|
passthru("{$php} {$cli}/updates_xml_sync.php --path " . escapeshellarg($path)
|
|
. " --current " . escapeshellarg($branch) . " --all"
|
|
. " --version " . escapeshellarg($releaseVersion)
|
|
. " --token " . escapeshellarg($token)
|
|
. " --gitea-url " . escapeshellarg($giteaUrl)
|
|
. " --org " . escapeshellarg($org)
|
|
. " --repo " . escapeshellarg($repo) . " 2>&1");
|
|
} else {
|
|
echo "[DRY-RUN] Would commit updates.xml and sync to all branches\n";
|
|
}
|
|
|
|
echo "\n=== Release published: {$releaseVersion} ===\n";
|
|
|
|
// Output for CI
|
|
$ghOutput = getenv('GITHUB_OUTPUT');
|
|
if ($ghOutput) {
|
|
file_put_contents($ghOutput, "version={$releaseVersion}\nbase_version={$baseVersion}\n", FILE_APPEND);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
$app = new ReleasePublishCli();
|
|
exit($app->execute());
|