Zum Hauptinhalt springen
← Blog

Wenn der Build durchläuft, das Asset aber fehlt: Unity CI/CD Build-Debugging mit Rider und MCP

Optimieren Sie Ihr Unity-CI/CD-Build-Debugging mit JetBrains Rider und MCP, um stille Asset-Fehler in automatisierten Pipelines sicher zu beheben.

8 Min. LesezeitSimon Daniel März
Wenn der Build durchläuft, das Asset aber fehlt: Unity CI/CD Build-Debugging mit Rider und MCPMit Hilfe von KI generiert

Ihr Unity-Projekt kompiliert lokal ohne eine einzige Warnung, läuft im Playmode des Editors absolut flüssig und passiert Ihre automatisierte CI-Pipeline mit Exit-Code 0. Doch sobald das QA-Team den Staging-Build auf einem echten Zielgerät startet, ist die gesamte Hintergrundumgebung verschwunden, ersetzt durch magentafarbene Leerstellen oder ein tiefes Schwarz.

Die Ursache dieses lautlosen Fehlers zu finden, bedeutet üblicherweise: Browser öffnen, ein unformatiertes Editor.log-Artefakt mit 90.000 Zeilen durchforsten und Asset-GUIDs manuell mit der Git-Historie abgleichen.

Diese Reibungsverluste bremsen Entwicklungszyklen in Spielestudios und interaktiven Enterprise-Teams spürbar aus. Glücklicherweise eröffnet das Zusammenspiel von JetBrains Rider, automatisierten Build-Tools wie der TeamCity CLI und dem Model Context Protocol (MCP) einen weitaus schnelleren Weg, um Asset-Diskrepanzen direkt in der IDE zu diagnostizieren.

Die Kosten lautloser Fehler in Build-Pipelines

In der klassischen Enterprise-Webentwicklung gilt meist: Ein automatisierter Build kompiliert erfolgreich oder schlägt fehl. In Unity kann ein Build jedoch aus Sicht des Betriebssystems fehlerfrei durchlaufen, während gleichzeitig wichtige Shader fehlen, Addressable-Asset-Gruppen beschädigt werden oder Texturen aufgrund plattformspezifischer Import-Overrides schlicht ignoriert werden.

Dies führt zu einem trügerischen Fehlerzustand: Der Continuous-Integration-Server meldet Grün, die Binärdatei ist jedoch funktionsunfähig. Verlassen sich Teams auf manuelle QA-Checks, um solche Regressionen aufzudecken, dehnt sich die Feedbackschleife von Minuten auf Stunden, oder gar Tage, aus.

Betrachten wir ein beispielhaftes Team von acht Entwicklern, das an jedem Arbeitstag zwei Staging-Client-Builds erstellt. Verbringt ein Engineer zweimal wöchentlich rund 45 Minuten damit, Remote-CI-Logs herunterzuladen, Build-Step-Outputs zu parsen und Asset-Stripping-Bugs lokal zu reproduzieren, kostet diese Pipeline-Ineffizienz jeden Monat Dutzende produktive Entwicklungsstunden.

Das Problem verschärft sich, sobald komplexe Grafik-Pipelines oder Asset-Streaming-Ebenen verwaltet werden. Nutzt Ihr Projekt benutzerdefinierte Asset-Bundles oder anspruchsvolle Rendering-Techniken, ähnlich den Herausforderungen, die wir in unserem Leitfaden zu Echtzeit-Global-Illumination in Unity beschreiben, kann eine fehlende Shader-Variante oder ein nicht verknüpftes Material die visuelle Darstellung komplett lahmlegen, ohne dass ein fataler Kompilierungsfehler ausgelöst wird.

Warum Unity-Pipelines täuschen: Das Batchmode-Dilemma

Um Builds zu automatisieren, führen CI-Server den Unity-Editor im Headless-Batchmode über Befehlszeilenargumente wie -batchmode -nographics -quit aus. Um zu verstehen, warum dieses Setup kritische Fehler verbirgt, muss man analysieren, wie Unity Runtime-Exceptions und Log-Level während der Ausführung behandelt:

  • Exit-Codes spiegeln nicht die Build-Integrität wider: Standardmäßig gibt der Unity-Editor-Prozess einen Exit-Code von 0 zurück, selbst wenn Skriptfehler oder schwere Asset-Serialisierungsprobleme während des Build-Skripts auftraten, es sei denn, Ihr Build-Skript ruft explizit EditorApplication.Exit(1) auf.
  • Unterschiede beim Stripping zwischen Editor und Standalone: Im lokalen Unity-Editor haben Szenen und Prefabs direkten Zugriff auf die vollständige AssetDatabase. In Standalone-Builds entfernt (strippt) Unity nicht referenzierte Assets, ungenutzte Code-Assemblies und nicht zugewiesene Shader-Varianten nach strengen Plattformregeln.
  • Simulationsmodi verbergen fehlerhafte Pfade: Addressable Assets laufen bei lokaler Entwicklung oft im Modus „Use Asset Database (Fastest)“. Werden sie auf der CI via „Pack Together By Label“ oder „Pack Separately“ gebaut, fallen fehlende Bundle-Abhängigkeiten oder ungültige Adressen erst auf, sobald der Remote-Build sie paketiert.
  • Inkonsistenzen im Platform-Target-Cache: Lokale Entwickler-Workstations cachen Library-Artefakte typischerweise für eine spezifische Plattform. Wechselt ein Headless-Build-Runner das Ziel von Windows Standalone zu iOS oder Android ohne sauberen Cache Server, können intermediäre Metadaten beschädigt werden.

Wenn ein Hintergrund-Sprite im Staging-Build verschwindet, liegt das selten an einem Anwendungsabsturz. Fast immer handelt es sich um einen unbemerkten Ausschluss zur Build-Zeit, bei dem das Asset beim Bundle-Packing übersprungen oder bei der Szenenaggregation gestrippt wurde.

Die Diagnose-Brücke: Rider, TeamCity CLI und MCP verbinden

Während einer Demonstration auf dem GameDev Day zeigte TeamCity Solutions Engineer Artem Rokhin eine klassische Staging-Diskrepanz: Ein Unity-Spiel funktionierte im lokalen Playmode einwandfrei, doch in der Staging-Bereitstellung fehlte der Hintergrund-Sprite-Layer vollständig. Die CI-Pipeline meldete Erfolg, sodass der Entwickler isolierte Systeme manuell durchsuchen musste, um dem Problem auf den Grund zu gehen.

Bislang musste der Entwickler dafür seine IDE verlassen, sich im CI-Web-Dashboard anmelden, die Build-Konfiguration heraussuchen, rohe Log-Artefakte herunterladen, einen separaten Texteditor öffnen, nach bestimmten Log-Mustern suchen und Zeilennummern mühsam mit den eigenen C#-Skripten abgleichen.

Die Integration der TeamCity CLI in Rider über das Model Context Protocol (MCP) eliminiert diesen Kontextwechsel. MCP bietet einen offenen Standard, über den Anwendungen strukturierte Tools und Kontext für Entwicklungsumgebungen und intelligente Agenten bereitstellen können.

Funktionsweise des MCP-gestützten Workflows

Anstatt manuell Web-Portale und Textdateien zu durchforsten, fungiert die IDE als zentrale Diagnosekonsole:

  1. Direkte CLI-Abfragen: Die lokale IDE interagiert im Hintergrund über die TeamCity CLI mit dem CI-Runner und lädt rohe Artefakte, Build-Step-Status und Maschinen-Metadaten direkt in den Entwickler-Workspace.
  2. Kontextbezogenes Log-Parsing via MCP: Ein parallel zu den CI-Tools ausgeführter MCP-Server stellt strukturierte Endpunkte bereit. Er liefert nicht einfach ein unstrukturiertes 50-MB-Text-Log, sondern durchsuchbare Funktionen wie get_build_log_errors, get_asset_bundle_manifest_diff und inspect_build_step_parameters.
  3. Semantische Korrelation: Der in die IDE integrierte Assistent fragt den MCP-Server an: „Vergleiche Build #412 mit dem lokalen Commit HEAD und erkläre, warum der Background-Layer fehlt.“
  4. Präzise Problemlösung: Das MCP-Tool prüft das Build-Log, erkennt, dass ein Asset-Label im Addressables-Build-Skript ausgelassen wurde, markiert die genaue Zeile in Ihrer C#-Build-Konfiguration und blendet den Fix direkt in Rider ein.

Dieser Workflow verwandelt die Log-Analyse von einer mühsamen manuellen Suche in eine unmittelbare, abfragebasierte Diagnose-Session.

Konkrete Implementierung: Eine strikte Unity-Build-Pipeline aufbauen

Um das Debugging von Unity-CI/CD-Builds zuverlässig zu gestalten, sei es mit MCP-Tools oder klassischen Log-Parsern, müssen Ihre automatisierten Build-Skripte strikte Fehlergrenzen erzwingen und strukturierte Metadaten ausgeben.

Das folgende produktionsreife C#-Editor-Skript veranschaulicht, wie Sie den Build-Prozess kapseln, stillschweigende Auslassungen von Asset-Bundles abfangen und saubere Diagnose-Metadaten für die CLI ausgeben:

#if UNITY_EDITOR
using System;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;

namespace [ProjectMakers](https://projectmakers.de).BuildPipeline
{
    public static class CIBuildController
    {
        private static readonly string BuildLogPrefix = "[CI-BUILD]";

        public static void PerformStagingBuild()
        {
            Debug.Log($"{BuildLogPrefix} Starting automated staging build pipeline...");

            string[] scenes = EditorBuildSettings.scenes
                .Where(s => s.enabled)
                .Select(s => s.path)
                .ToArray();

            if (scenes.Length == 0)
            {
                Debug.LogError($"{BuildLogPrefix} FATAL: No enabled scenes found in EditorBuildSettings.");
                EditorApplication.Exit(101);
                return;
            }

            // Enforce explicit verification before launching the build player
            if (!ValidateAssetDependencies())
            {
                Debug.LogError($"{BuildLogPrefix} Pre-build asset validation failed. Aborting compilation.");
                EditorApplication.Exit(102);
                return;
            }

            string targetPath = "Builds/Staging/GameClient.exe";
            
            BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions
            {
                scenes = scenes,
                locationPathName = targetPath,
                target = BuildTarget.StandaloneWindows64,
                options = BuildOptions.StrictMode | BuildOptions.DetailedBuildReport
            };

            BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
            ProcessBuildReport(report);
        }

        private static bool ValidateAssetDependencies()
        {
            // Verify that critical assets exist in the project database
            string requiredBackgroundPath = "Assets/Game/Environment/Background_Staging_01.png";
            var backgroundAsset = AssetDatabase.LoadAssetAtPath<Sprite>(requiredBackgroundPath);

            if (backgroundAsset == null)
            {
                Debug.LogError($"{BuildLogPrefix} VALIDATION ERROR: Required asset missing at '{requiredBackgroundPath}'.");
                return false;
            }

            // Ensure asset labels for automated bundling are present
            string[] labels = AssetDatabase.GetLabels(backgroundAsset);
            if (!labels.Contains("PreloadEnvironment"))
            {
                Debug.LogError($"{BuildLogPrefix} METADATA ERROR: '{requiredBackgroundPath}' missing mandatory label 'PreloadEnvironment'.");
                return false;
            }

            Debug.Log($"{BuildLogPrefix} Asset validation passed successfully.");
            return true;
        }

        private static void ProcessBuildReport(BuildReport report)
        {
            BuildSummary summary = report.summary;

            Debug.Log($"{BuildLogPrefix} Build finished with result: {summary.result}");
            Debug.Log($"{BuildLogPrefix} Total Errors: {summary.totalErrors}, Total Warnings: {summary.totalWarnings}");
            Debug.Log($"{BuildLogPrefix} Output Size: {summary.totalSize / (1024 * 1024)} MB");

            if (summary.result == BuildResult.Succeeded)
            {
                Debug.Log($"{BuildLogPrefix} Build succeeded without fatal execution errors.");
                EditorApplication.Exit(0);
            }
            else
            {
                Debug.LogError($"{BuildLogPrefix} Build failed or was cancelled. Code: {summary.result}");
                
                // Write structured error output for MCP log scrapers
                foreach (BuildStep step in report.steps)
                {
                    foreach (BuildStepMessage message in step.messages)
                    {
                        if (message.type == LogType.Error || message.type == LogType.Exception)
                        {
                            Debug.LogError($"{BuildLogPrefix} Step [{step.name}] Failed: {message.content}");
                        }
                    }
                }
                
                EditorApplication.Exit(1);
            }
        }
    }
}
#endif

Build-Zustand über MCP-Server-Tools bereitstellen

Um diesen Pipeline-Output direkt an Rider anzubinden, kann ein MCP-Tool die generierten strukturierten Logs auslesen. Das folgende TypeScript-Beispiel zeigt, wie eine MCP-Tool-Definition diese Build-Fehlerdaten für den IDE-Assistenten zugänglich macht:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import * as fs from "fs";

const server = new McpServer({
  name: "unity-teamcity-diagnostics",
  version: "1.0.0"
});

// Tool to query specific build step discrepancies
server.tool(
  "inspect-unity-build-log",
  {
    buildId: z.string().describe("The unique TeamCity Build ID"),
    logFilePath: z.string().describe("Path to the downloaded Editor.log artifact")
  },
  async ({ buildId, logFilePath }) => {
    if (!fs.existsSync(logFilePath)) {
      return {
        content: [{ type: "text", text: `Log file not found for build #${buildId}` }]
      };
    }

    const logContent = fs.readFileSync(logFilePath, "utf-8");
    const lines = logContent.split("\n");

    const errors = lines
      .filter((line) => line.includes("[CI-BUILD] VALIDATION ERROR") || line.includes("[CI-BUILD] METADATA ERROR"))
      .map((line) => line.trim());

    if (errors.length === 0) {
      return {
        content: [{ type: "text", text: `Build #${buildId} contains no explicit [CI-BUILD] validation errors.` }]
      };
    }

    return {
      content: [
        {
          type: "text",
          text: `Found ${errors.length} validation failure(s) in Build #${buildId}:\n\n` + errors.join("\n")
        }
      ]
    };
  }
);

Fragt ein Entwickler in Rider: „Warum ist der Hintergrund in Build #412 fehlgeschlagen?“, scannt der MCP-Server das Log, extrahiert den eindeutigen [CI-BUILD] METADATA ERROR und informiert darüber, dass das Label PreloadEnvironment in diesem Branch entfernt wurde. Das Problem wird innerhalb von Sekunden lokalisiert, ohne die IDE zu verlassen.

Erkenntnisse aus der Game-CI auf Enterprise-Anwendungen übertragen

Die Erkenntnisse aus dem Debugging von Asset-Diskrepanzen in der Spieleentwicklung lassen sich nahtlos auf Business-Software, Serious Games und interaktive Enterprise-Tools übertragen.

Moderne plattformübergreifende Software setzt häufig auf dynamisches Asset-Loading, modulare Paketierung und Live-Synchronisation mit dem Backend. Beim Aufbau hochverfügbarer Architekturen, wie etwa den operativen Grundlagen, die wir in unserem Beitrag zum Aufbau eines Game-Backends mit horizOn behandeln, wirkt sich die clientseitige Build-Integrität direkt auf die Service-Verfügbarkeit aus.

Wenn Ihr Client-Deployment Netzwerkprotokoll-Definitionen, Asset-Kataloge oder gebündelte Interface-Schemas lautlos übergeht, versagt der Live-Service für Endanwender, selbst wenn die Backend-Microservices einen perfekten Systemstatus melden.

Typische Fehlerquellen in Gamified Apps und Live Services

  • Plattformspezifische Kompression: Eine auf Desktop-Systemen importierte Textur mag gestochen scharf wirken, aggressive ASTC- oder ETC2-Kompressionseinstellungen auf mobilen CI-Runnern können jedoch Textbeschriftungen unlesbar machen oder Alphakanäle vollständig entfernen.
  • Shader-Kompilierungsverkürzung: Enterprise-3D-Konfiguratoren auf WebGL-Basis leiden häufig unter fehlenden Varianten, wenn der Build-Server zur Beschleunigung der CI-Durchläufe nur eine minimale Fallback-Shader-Sammlung kompiliert.
  • Ausschluss von Lokalisierungs-Bundles: Serious Games und Trainingssimulationen, die Übersetzungskataloge über Remote-Bundles nachladen, erzeugen lautlose Fehler mit leeren Textfeldern, falls das Sprachschlüssel-Manifest bei der automatisierten Paketierung nicht korrekt serialisiert wird.

Entscheidend für einen verlässlichen Release-Prozess ist es, Ihren Game-Client oder Ihre interaktive 3D-Anwendung als integralen Bestandteil Ihres Continuous-Deployment-Systems zu begreifen, und nicht als isolierte Binärdatei, die manuell auf dem Rechner eines Artists kompiliert wird.

Best Practices für die Unity-CI/CD-Diagnostik

Der Aufbau eines effizienten Workflows zur Fehlerbehebung im Build-Prozess erfordert Disziplin bei der Statusausgabe in Skripten und beim Zugriff auf Build-Artefakte über Entwickler-Tools.

1. Strict Mode und benutzerdefinierte Exit-Codes erzwingen

Verlassen Sie sich keinesfalls auf den Standard-Rückgabewert von Unity. Aktivieren Sie BuildOptions.StrictMode in Ihren Build-Skripten, um sicherzustellen, dass jede als Fehler eingestufte Compilerwarnung den Build abbricht. Kombinieren Sie dies mit expliziten EditorApplication.Exit(customCode)-Aufrufen, um sauber zwischen Szenenkonfigurationsfehlern, fehlenden Lizenzaktivierungen und fehlgeschlagener Asset-Validierung zu differenzieren.

2. Deterministische Überprüfung von Asset-Bundle-Manifesten etablieren

Führen Sie vor der Freigabe von Staging- oder Production-Client-Builds eine automatisierte Verifikation durch, die das neu generierte Asset-Bundle-Manifest mit dem des vorherigen, fehlerfreien Deployments vergleicht. Wenn ein Asset-Typ, wie etwa Hintergrundtexturen oder UI-Fonts, ohne entsprechende Änderungsmeldung im Changelog unter einen erwarteten Schwellenwert fällt, muss die Pipeline sofort stoppen.

3. Umgebungsmetadaten in Build-Artefakten ausgeben

Führen Sie am Anfang jedes Build-Logs plattformspezifische Umgebungsdetails auf: die exakte Unity-Patch-Version, das aktive Scripting-Backend (Mono vs. IL2CPP), die Zielarchitektur und den Status der Verbindung zum Cache Server. Weicht das lokale Verhalten von der CI ab, sind Differenzen zwischen inkrementellen lokalen Caches und sauberen Remote-Caches häufig die eigentliche Ursache.

4. Tooling direkt in die Entwickler-IDE integrieren

Zwingen Sie Entwickler nicht zu ständigen Kontextwechseln in DevOps-Tools. Ob über die TeamCity CLI mit JetBrains Rider oder durch unternehmensweit bereitgestellte MCP-Endpunkte: Die Log-Analyse gehört direkt in den Editor. Direkter Abfragezugriff auf Build-Zustände bewahrt den Flow und beschleunigt das Beheben von Regressionen enorm.

Ausfallsichere interaktive Pipelines aufbauen

Der Aufbau verlässlicher Continuous-Integration-Workflows für Echtzeit-Engines erfordert fundiertes Know-how sowohl im Bereich moderner Grafik-Pipelines als auch automatisierter Infrastrukturen. Viele Unternehmen stellen fest, dass Webanwendungen zwar reibungslos deployt werden, ihre interaktiven 3D-Tools, Gamification-Systeme und Unity-Projekte jedoch unter fragilen Pipelines, langen Durchlaufzeiten und schwer diagnostizierbaren Asset-Verlusten leiden.

Eine solche Diagnose-Infrastruktur intern aufzubauen, verlangt tiefes Verständnis für die Interna der Unity-Engine, CI-Runner-Architekturen und moderne Entwickler-Tools. Teams, die robuste Pipelines etablieren wollen, ohne ihr Produktteam von der Feature-Entwicklung abzulenken, setzen daher oft auf erfahrene Spezialisten. Bei ProjectMakers entwickeln wir saubere Deployment-Workflows, Editor-Erweiterungen und performante Game-Clients, die Release-Zyklen planbar machen.

Wenn Ihr Team mit instabilen Build-Pipelines oder fehlenden Assets im Staging kämpft, werfen Sie einen Blick auf unsere Game-Development-Services und erfahren Sie, wie wir ausfallsichere, produktionsreife interaktive Systeme realisieren.


Quelle: Unity-Builds in Rider mit TeamCity CLI und MCP debuggen

In diesem Thema weiterlesen

Interaktive Systeme →