#!/usr/bin/env php * * 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/updates_xml_sync.php * VERSION: 05.00.01 * BRIEF: Sync updates.xml to target branches via Gitea API * NOTE: Called by pre-release and auto-release workflows after updates.xml * is modified on the current branch. Pushes the file to other branches * without requiring a git checkout (avoids merge conflicts). * * Usage: * php updates_xml_sync.php --path /repo --branches main,dev --current dev * php updates_xml_sync.php --path /repo --branches main --current dev --version 02.01.27 * * Options: * --path Repository root containing updates.xml (default: .) * --branches Comma-separated target branches to sync to (default: main,dev) * --current Current branch to skip (required) * --version Version string for commit message (optional) * --token Gitea API token (default: env GA_TOKEN) * --gitea-url Gitea instance URL (default: env GITEA_URL or https://git.mokoconsulting.tech) * --org Organization (default: env GITEA_ORG) * --repo Repository name (default: env GITEA_REPO) */ declare(strict_types=1); // ── Argument parsing ──────────────────────────────────────────────────── $path = '.'; $branches = 'main,dev'; $current = ''; $version = ''; $token = getenv('GA_TOKEN') ?: ''; $giteaUrl = getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech'; $org = getenv('GITEA_ORG') ?: ''; $repo = getenv('GITEA_REPO') ?: ''; foreach ($argv as $i => $arg) { if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1]; if ($arg === '--branches' && isset($argv[$i + 1])) $branches = $argv[$i + 1]; if ($arg === '--current' && isset($argv[$i + 1])) $current = $argv[$i + 1]; if ($arg === '--version' && isset($argv[$i + 1])) $version = $argv[$i + 1]; if ($arg === '--token' && isset($argv[$i + 1])) $token = $argv[$i + 1]; if ($arg === '--gitea-url' && isset($argv[$i + 1])) $giteaUrl = $argv[$i + 1]; if ($arg === '--org' && isset($argv[$i + 1])) $org = $argv[$i + 1]; if ($arg === '--repo' && isset($argv[$i + 1])) $repo = $argv[$i + 1]; } if ($current === '') { fwrite(STDERR, "Error: --current is required\n"); exit(1); } if ($token === '') { fwrite(STDERR, "Error: --token or GA_TOKEN env is required\n"); exit(1); } if ($org === '' || $repo === '') { fwrite(STDERR, "Error: --org and --repo (or GITEA_ORG/GITEA_REPO env) are required\n"); exit(1); } $updatesFile = rtrim($path, '/') . '/updates.xml'; if (!file_exists($updatesFile)) { fwrite(STDERR, "No updates.xml found at {$updatesFile}\n"); exit(0); } $content = file_get_contents($updatesFile); $encoded = base64_encode($content); $giteaUrl = rtrim($giteaUrl, '/'); $apiBase = "{$giteaUrl}/api/v1/repos/{$org}/{$repo}"; $vLabel = $version !== '' ? " {$version}" : ''; $targets = array_filter( array_map('trim', explode(',', $branches)), fn($b) => $b !== '' && $b !== $current ); if (empty($targets)) { fwrite(STDERR, "No target branches to sync to (current: {$current})\n"); exit(0); } $synced = 0; $failed = 0; foreach ($targets as $branch) { fwrite(STDERR, "Syncing updates.xml -> {$branch}...\n"); $sha = getFileSha($apiBase, $token, $branch); if ($sha === null) { fwrite(STDERR, " WARNING: could not get SHA from {$branch}\n"); $failed++; continue; } $ok = putFile($apiBase, $token, $branch, $encoded, $sha, "chore: sync updates.xml{$vLabel} from {$current} [skip ci]"); if ($ok) { fwrite(STDERR, " Synced to {$branch}\n"); $synced++; } else { fwrite(STDERR, " WARNING: push to {$branch} failed\n"); $failed++; } } fwrite(STDERR, "Done: {$synced} synced, {$failed} failed\n"); exit($failed > 0 ? 1 : 0); // ═══════════════════════════════════════════════════════════════════════ function getFileSha(string $apiBase, string $token, string $branch): ?string { $resp = apiCall('GET', "{$apiBase}/contents/updates.xml?ref={$branch}", $token); return $resp['sha'] ?? null; } function putFile(string $apiBase, string $token, string $branch, string $encoded, string $sha, string $msg): bool { $resp = apiCall('PUT', "{$apiBase}/contents/updates.xml", $token, [ 'content' => $encoded, 'sha' => $sha, 'message' => $msg, 'branch' => $branch, ]); return $resp !== null; } function apiCall(string $method, string $url, string $token, ?array $data = null): ?array { $headers = [ "Authorization: token {$token}", 'Content-Type: application/json', 'Accept: application/json', ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); if ($data !== null) { curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_SLASHES)); } $body = curl_exec($ch); $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return ($code >= 200 && $code < 300) ? (json_decode($body, true) ?: []) : null; }