feat: UpdateAgent Selbstaktualisierung & kopflose Installation, .NET 10 Migration, Backend- & SDK-Fixes
- .NET 10 Migration: Alle .NET-Projekte auf net10.0 umgestellt (#459) - Client-SDK: ResolveAgentPath bereinigt und DLL-Pruefung ergaenzt (#458) - Packager: Commit-Ermittlung aus publishDir / Assembly InformationalVersion (#456) - BuildInfo.targets: ExitCode-Abfang und Regex-Validierung gegen Git-Fehlertexte (#363) - UpdateService: Re-Publish erhaelt bestehende Manifeste und Werte (#457) - Bugtracker API: on_roadmap-Filter in manage?action=list durchgereicht (#349) - Watchdog API: project_slug-Filter in GET /status ergaenzt (#420) - UpdateService API: no_release_for_platform mit verfuegbaren Plattformen (#421) - Bugtracker Core: Fehler-Normalisierung gegen zersplitterte error_hashes (#419) - UpdateAgent: Kopfloser Installationsmodus via --answers (#89) - Installer & Agent: Automatische Selbstaktualisierung beim Start - Tooling: AGENTS.md, CLAUDE.md und Standalone-CLI scripts/dc.py hinzugefuegt
This commit is contained in:
@@ -2382,7 +2382,8 @@ final class BugRepo
|
||||
|
||||
/**
|
||||
* Entfernt Rauschen, das denselben Fehler sonst als neu erscheinen liesse:
|
||||
* Zeilennummern, Speicheradressen, Zeitstempel, GUIDs, Mehrfach-Leerzeichen.
|
||||
* Zeilennummern, Speicheradressen, Zeitstempel, GUIDs, Dauern/Laufzeiten,
|
||||
* Groessenangaben, IDs, IP-Adressen und Mehrfach-Leerzeichen (#419).
|
||||
*/
|
||||
private static function normalizeForHash(string $value): string
|
||||
{
|
||||
@@ -2392,6 +2393,14 @@ final class BugRepo
|
||||
'/0x[0-9a-f]+/' => '0xADDR',
|
||||
'/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/' => 'GUID',
|
||||
'/\d{4}-\d{2}-\d{2}[t ]\d{2}:\d{2}:\d{2}/' => 'TIMESTAMP',
|
||||
'/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?\b/' => 'IP:PORT',
|
||||
|
||||
// Dauern und Messwerte (z. B. ("2,196"ms), 45ms, 123.45ms, 30.5s, 5000 ms)
|
||||
// Verhindert das Zersplittern von EF-Core- und DB-Meldungen (#419)
|
||||
'/\(?["\']?\d+([.,]\d+)?["\']?\s*(ms|s|sec|seconds|sekunden|min|minutes|minuten|µs|us|ns)\b\)?/' => 'DURATION',
|
||||
|
||||
// Datenmengen (z. B. 4,096 bytes, 500 kb, 10 mb)
|
||||
'/\d+([.,]\d+)?\s*(b|bytes|kb|kib|mb|mib|gb|gib)\b/' => 'SIZE',
|
||||
|
||||
// Werte in Anfuehrungszeichen, die mindestens eine Ziffer
|
||||
// enthalten, sind praktisch immer konkrete Datensatzwerte und
|
||||
@@ -2404,7 +2413,16 @@ final class BugRepo
|
||||
'/"[^"]*\d[^"]*"/' => '"VALUE"',
|
||||
|
||||
'/:line \d+/' => ':line N',
|
||||
'/\b\d{3,}\b/' => 'N',
|
||||
|
||||
// Formatierte Zahlen mit Tausender- oder Dezimaltrennern (z. B. 2,196 oder 12.34)
|
||||
'/\b\d+([.,]\d+)+\b/' => 'N',
|
||||
|
||||
// Zahlen nach Zuweisungen (=, :, #)
|
||||
'/(?<=[=:#])\s*\d+\b/' => ' N',
|
||||
|
||||
// Eigenstaendige mehrstellige Zahlen
|
||||
'/\b\d{2,}\b/' => 'N',
|
||||
|
||||
'/\s+/' => ' ',
|
||||
];
|
||||
|
||||
|
||||
@@ -145,6 +145,30 @@ final class UpdateManager
|
||||
return Version::highest($this->releasesFor($productSlug, $channel, $platform));
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert alle Plattformen, fuer die Releases fuer dieses Produkt existieren.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getAvailablePlatforms(string $productSlug, string $channel = 'prod'): array
|
||||
{
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT DISTINCT platform FROM updateservice_releases
|
||||
WHERE product_slug = :slug AND channel = :channel
|
||||
ORDER BY platform ASC
|
||||
');
|
||||
$stmt->execute([':slug' => $productSlug, ':channel' => $channel]);
|
||||
$rows = $stmt->fetchAll() ?: [];
|
||||
$platforms = [];
|
||||
foreach ($rows as $r) {
|
||||
$p = (string)($r['platform'] ?? '');
|
||||
if ($p !== '') {
|
||||
$platforms[] = $p;
|
||||
}
|
||||
}
|
||||
return $platforms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legt ein Release an oder aktualisiert es.
|
||||
*
|
||||
@@ -169,6 +193,31 @@ final class UpdateManager
|
||||
|
||||
$existing = $this->findRelease($productSlug, $version, $channel, $platform);
|
||||
|
||||
// Bestehende Werte nicht mit null oder leer ueberschreiben, wenn sie im Aufruf weggelassen wurden (#457)
|
||||
if ($existing !== null) {
|
||||
if ($manifestJson === null && !empty($existing['manifest_json'])) {
|
||||
$manifestJson = (string)$existing['manifest_json'];
|
||||
}
|
||||
if (($manifestSignature === null || $manifestSignature === '') && !empty($existing['manifest_signature'])) {
|
||||
$manifestSignature = (string)$existing['manifest_signature'];
|
||||
}
|
||||
if ($releaseNotes === null && !empty($existing['release_notes'])) {
|
||||
$releaseNotes = (string)$existing['release_notes'];
|
||||
}
|
||||
if (($sha256Hash === null || $sha256Hash === '') && !empty($existing['sha256_hash'])) {
|
||||
$sha256Hash = (string)$existing['sha256_hash'];
|
||||
}
|
||||
if (($gitCommit === null || $gitCommit === '') && !empty($existing['git_commit'])) {
|
||||
$gitCommit = (string)$existing['git_commit'];
|
||||
}
|
||||
if ($sizeBytes <= 0 && !empty($existing['size_bytes'])) {
|
||||
$sizeBytes = (int)$existing['size_bytes'];
|
||||
}
|
||||
if (($downloadUrl === '') && !empty($existing['download_url'])) {
|
||||
$downloadUrl = (string)$existing['download_url'];
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->db->prepare('
|
||||
INSERT INTO updateservice_releases (
|
||||
product_slug, version, channel, platform, release_notes, download_url,
|
||||
@@ -179,13 +228,13 @@ final class UpdateManager
|
||||
:hash, :git, :size, :manifest, :signature,
|
||||
:critical
|
||||
) ON DUPLICATE KEY UPDATE
|
||||
release_notes = VALUES(release_notes),
|
||||
download_url = VALUES(download_url),
|
||||
sha256_hash = VALUES(sha256_hash),
|
||||
git_commit = VALUES(git_commit),
|
||||
size_bytes = VALUES(size_bytes),
|
||||
manifest_json = VALUES(manifest_json),
|
||||
manifest_signature = VALUES(manifest_signature),
|
||||
release_notes = COALESCE(VALUES(release_notes), release_notes),
|
||||
download_url = CASE WHEN VALUES(download_url) != "" THEN VALUES(download_url) ELSE download_url END,
|
||||
sha256_hash = COALESCE(VALUES(sha256_hash), sha256_hash),
|
||||
git_commit = COALESCE(VALUES(git_commit), git_commit),
|
||||
size_bytes = CASE WHEN VALUES(size_bytes) > 0 THEN VALUES(size_bytes) ELSE size_bytes END,
|
||||
manifest_json = COALESCE(VALUES(manifest_json), manifest_json),
|
||||
manifest_signature = COALESCE(VALUES(manifest_signature), manifest_signature),
|
||||
is_critical = VALUES(is_critical)
|
||||
');
|
||||
|
||||
|
||||
@@ -52,8 +52,18 @@ final class MonitorRepo
|
||||
}
|
||||
|
||||
/** @return list<array<string,mixed>> */
|
||||
public function getAllMonitors(): array
|
||||
public function getAllMonitors(?string $projectSlug = null): array
|
||||
{
|
||||
if ($projectSlug !== null && $projectSlug !== '' && $projectSlug !== 'all') {
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT * FROM watchdog_monitors
|
||||
WHERE project_slug = :project
|
||||
ORDER BY COALESCE(group_key, "zzz") ASC, source ASC
|
||||
');
|
||||
$stmt->execute([':project' => $projectSlug]);
|
||||
return $stmt->fetchAll() ?: [];
|
||||
}
|
||||
|
||||
$stmt = $this->db->query('
|
||||
SELECT * FROM watchdog_monitors
|
||||
ORDER BY COALESCE(group_key, "zzz") ASC, source ASC
|
||||
|
||||
Reference in New Issue
Block a user