adsad
All checks were successful
Deploy / deploy-staging (push) Successful in 24s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-06-25 01:33:21 +02:00
parent c164dfff10
commit 99ca2c401c
4 changed files with 107 additions and 13 deletions

View File

@@ -8,6 +8,6 @@ return [
'owner' => 'private', 'owner' => 'private',
'repositories' => [], 'repositories' => [],
'poll_idle_seconds' => 5, 'poll_idle_seconds' => 5,
'poll_running_seconds' => 1, 'poll_running_seconds' => 3,
'visibility' => 'admins', 'visibility' => 'admins',
]; ];

View File

@@ -8,6 +8,6 @@ return [
'owner' => 'private', 'owner' => 'private',
'repositories' => [], 'repositories' => [],
'poll_idle_seconds' => 5, 'poll_idle_seconds' => 5,
'poll_running_seconds' => 1, 'poll_running_seconds' => 3,
'visibility' => 'admins', 'visibility' => 'admins',
]; ];

View File

@@ -8,6 +8,6 @@ return [
'owner' => 'private', 'owner' => 'private',
'repositories' => [], 'repositories' => [],
'poll_idle_seconds' => 5, 'poll_idle_seconds' => 5,
'poll_running_seconds' => 1, 'poll_running_seconds' => 3,
'visibility' => 'admins', 'visibility' => 'admins',
]; ];

View File

@@ -6,6 +6,8 @@ namespace SystemAddons\GiteaDeployStatus;
final class GiteaDeployStatusService final class GiteaDeployStatusService
{ {
private const REPOSITORY_CACHE_TTL = 60;
/** /**
* @param array<string, mixed> $config * @param array<string, mixed> $config
*/ */
@@ -85,9 +87,19 @@ final class GiteaDeployStatusService
try { try {
$repositories = $this->resolveRepositories(); $repositories = $this->resolveRepositories();
$runningJobs = []; $runningJobs = [];
$warnings = [];
foreach ($repositories as $repository) { foreach ($repositories as $repository) {
$runs = $this->listActionRuns((string) $repository['owner'], (string) $repository['repo']); try {
$runs = $this->listActionRuns((string) $repository['owner'], (string) $repository['repo']);
} catch (\RuntimeException $exception) {
if ($this->isForbiddenException($exception)) {
$warnings[] = sprintf('403 fuer %s uebersprungen.', (string) $repository['full_name']);
continue;
}
throw $exception;
}
foreach ($runs as $run) { foreach ($runs as $run) {
if (!$this->isRunActive($run)) { if (!$this->isRunActive($run)) {
@@ -122,6 +134,7 @@ final class GiteaDeployStatusService
'message' => $message, 'message' => $message,
'running_jobs' => $runningJobs, 'running_jobs' => $runningJobs,
'repositories_checked' => count($repositories), 'repositories_checked' => count($repositories),
'warnings' => $warnings,
'diagnostics' => $this->diagnostics(), 'diagnostics' => $this->diagnostics(),
]; ];
} }
@@ -129,9 +142,14 @@ final class GiteaDeployStatusService
return [ return [
'state' => 'idle', 'state' => 'idle',
'poll_seconds' => $idleSeconds, 'poll_seconds' => $idleSeconds,
'message' => sprintf('Kein laufendes Gitea-Deployment in %d Repositories.', count($repositories)), 'message' => sprintf(
'Kein laufendes Gitea-Deployment in %d Repositories.%s',
count($repositories),
$warnings !== [] ? ' ' . implode(' ', array_slice($warnings, 0, 3)) : ''
),
'running_jobs' => [], 'running_jobs' => [],
'repositories_checked' => count($repositories), 'repositories_checked' => count($repositories),
'warnings' => $warnings,
'diagnostics' => $this->diagnostics(), 'diagnostics' => $this->diagnostics(),
]; ];
} catch (\Throwable $exception) { } catch (\Throwable $exception) {
@@ -161,6 +179,11 @@ final class GiteaDeployStatusService
*/ */
private function resolveRepositories(): array private function resolveRepositories(): array
{ {
$cached = $this->loadRepositoryCache();
if ($cached !== []) {
return $cached;
}
$configured = []; $configured = [];
foreach ((array) ($this->config['repositories'] ?? []) as $entry) { foreach ((array) ($this->config['repositories'] ?? []) as $entry) {
$normalized = $this->normalizeRepositoryEntry($entry); $normalized = $this->normalizeRepositoryEntry($entry);
@@ -170,7 +193,9 @@ final class GiteaDeployStatusService
} }
if ($configured !== []) { if ($configured !== []) {
return array_values($configured); $resolved = array_values($configured);
$this->storeRepositoryCache($resolved);
return $resolved;
} }
$repositories = []; $repositories = [];
@@ -186,22 +211,34 @@ final class GiteaDeployStatusService
foreach ($this->normalizeRepositoriesPayload($payload) as $repository) { foreach ($this->normalizeRepositoriesPayload($payload) as $repository) {
$repositories[$repository['full_name']] = $repository; $repositories[$repository['full_name']] = $repository;
} }
} catch (\Throwable) { } catch (\RuntimeException $exception) {
if ($this->isForbiddenException($exception)) {
continue;
}
continue; continue;
} }
} }
} }
$payload = $this->requestJson('/user/repos?limit=100&page=1'); try {
foreach ($this->normalizeRepositoriesPayload($payload) as $repository) { $payload = $this->requestJson('/user/repos?limit=100&page=1');
$repositories[$repository['full_name']] = $repository; foreach ($this->normalizeRepositoriesPayload($payload) as $repository) {
$repositories[$repository['full_name']] = $repository;
}
} catch (\RuntimeException $exception) {
if (!$this->isForbiddenException($exception) || $repositories === []) {
throw $exception;
}
} }
if ($repositories === []) { if ($repositories === []) {
throw new \RuntimeException('Keine Repositories fuer den Gitea-Status gefunden.'); throw new \RuntimeException('Keine Repositories fuer den Gitea-Status gefunden.');
} }
return array_values($repositories); $resolved = array_values($repositories);
$this->storeRepositoryCache($resolved);
return $resolved;
} }
/** /**
@@ -296,7 +333,7 @@ final class GiteaDeployStatusService
*/ */
private function listActionRuns(string $owner, string $repo): array private function listActionRuns(string $owner, string $repo): array
{ {
$payload = $this->requestJson('/repos/' . rawurlencode($owner) . '/' . rawurlencode($repo) . '/actions/runs?limit=10&page=1'); $payload = $this->requestJson('/repos/' . rawurlencode($owner) . '/' . rawurlencode($repo) . '/actions/runs?limit=1&page=1');
if (is_array($payload['workflow_runs'] ?? null)) { if (is_array($payload['workflow_runs'] ?? null)) {
return array_values(array_filter($payload['workflow_runs'], 'is_array')); return array_values(array_filter($payload['workflow_runs'], 'is_array'));
@@ -371,7 +408,7 @@ final class GiteaDeployStatusService
} }
if ($status >= 400) { if ($status >= 400) {
throw new \RuntimeException('Gitea-API antwortet mit HTTP ' . $status . '.'); throw new \RuntimeException('Gitea-API antwortet mit HTTP ' . $status . ' fuer ' . $path . '.');
} }
$decoded = json_decode($raw, true); $decoded = json_decode($raw, true);
@@ -402,4 +439,61 @@ final class GiteaDeployStatusService
return $decoded; return $decoded;
} }
private function isForbiddenException(\RuntimeException $exception): bool
{
return str_contains($exception->getMessage(), 'HTTP 403');
}
/**
* @return array<int, array{owner: string, repo: string, full_name: string}>
*/
private function loadRepositoryCache(): array
{
$cacheFile = $this->repositoryCacheFile();
if (!is_file($cacheFile)) {
return [];
}
$mtime = filemtime($cacheFile);
if ($mtime === false || ($mtime + self::REPOSITORY_CACHE_TTL) < time()) {
return [];
}
$raw = file_get_contents($cacheFile);
if (!is_string($raw) || trim($raw) === '') {
return [];
}
$decoded = json_decode($raw, true);
if (!is_array($decoded)) {
return [];
}
return $this->normalizeRepositoriesPayload($decoded);
}
/**
* @param array<int, array{owner: string, repo: string, full_name: string}> $repositories
*/
private function storeRepositoryCache(array $repositories): void
{
$cacheFile = $this->repositoryCacheFile();
$directory = dirname($cacheFile);
if (!is_dir($directory)) {
@mkdir($directory, 0775, true);
}
@file_put_contents($cacheFile, json_encode(array_values($repositories), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
}
private function repositoryCacheFile(): string
{
$owner = trim((string) ($this->config['owner'] ?? ''));
$scope = $owner !== '' ? $owner : 'user';
$safeScope = preg_replace('/[^a-z0-9._-]+/i', '-', $scope) ?: 'user';
return sys_get_temp_dir() . '/desktop-gitea-repositories-' . $safeScope . '.json';
}
} }