diff --git a/CHANGELOG.md b/CHANGELOG.md index 3af38f47..d8747605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -258,6 +258,13 @@ die Versionierung folgt (in der aktuellen Doku-Phase) dem Dokumentationsstand de sie belegt keine Verbesserung. ### Geändert +- **Beta-Pakete nur aus sauberem Baum: harte Sperre in `build-mac.sh` und + `build-linux.sh`.** Unsauberer Arbeitsbaum bricht vor dem Unity-Aufruf ab + (`--allow-dirty` als Ausweg, `-dirty` im Stempel bleibt, `build-mac.sh --fast` + ausgenommen). `--skip-build` prüft dasselbe und vergleicht gegen die neue + Sidecar-Datei `Builds//.build-commit`. HEAD ≠ `origin/main` ist nur + eine Warnung, offline bricht nichts ab. Neu: `--check-only`. README 2.0.0 → + 2.1.0 mit Bauort-Hinweis. Entwurf D-124 (#132) - **Die lebende Dokumentation heißt jetzt Hashkrieg (#14, Stufe 3).** 54 Dateien, 84 Stellen — die Marke in der Prosa unter `docs/**` und in den Wurzeldateien. Zwei Regeln haben den Umfang bestimmt. Erstens **E-3: nur die Marke.** Die diff --git a/docs/production/DecisionLog.md b/docs/production/DecisionLog.md index c00a15ca..ba438772 100644 --- a/docs/production/DecisionLog.md +++ b/docs/production/DecisionLog.md @@ -3662,6 +3662,84 @@ berührt. Ansturm-Gegner ab. Wer ohne Menü startet (AutoStart, Harness), spielt diesen, nicht r8. +### D-124 | Entwurf | Beta-Pakete nur aus sauberem Baum (harte Sperre in den Packaging-Skripten) + +**Status:** Entwurf aus dem Issue-Lauf vom 2026-10-17 (#132); verbindlich mit dem +Merge des PR. Die Endnummer vergibt der Integrator. + +**Kontext:** Issue #132 beschreibt eine Falle: eine untrackte Datei unter +`Assets/` (`InputGateDiagnostic.cs`) kompiliert in jeden Build aus der +Arbeitskopie, nicht aber aus einem frischen Klon. Teil 1 (die Datei) ist +überholt — sie liegt in keiner Arbeitskopie mehr und war nie getrackt. Teil 2 +(Betas nur aus sauberem Stand) stand bisher nur in +`tools/packaging/README.md`; `build-mac.sh` und `build-linux.sh` hängten bei +unsauberem Baum lediglich `-dirty` an und warnten. `--skip-build` schrieb +außerdem das Etikett des aktuellen Commits auf einen beliebig alten Build. Der +Editor-Hook `BuildCommitStamp` stempelt `-dirty` für den ganzen Baum +(`git status --porcelain`), also auch für harmlose untrackte Dateien. + +**Alternativen:** + +1. **Status quo** (Doku plus `-dirty`-Stempel). Kostenlos, aber die Regel wird + nicht erzwungen: ein `-dirty`-Paket kann rausgehen, und ein Tester meldet + dann ein Verhalten, das kein anderer Rechner reproduziert. +2. **Harte Sperre in den Skripten** vor dem Unity-Aufruf, `--allow-dirty` als + bewusster Ausweg, Sidecar-Datei für `--skip-build`. Klein, wirkt dort, wo + Pakete entstehen, und lässt den Editor-Code unberührt. Preis: Pakete aus + der Arbeitskopie mit Art-Änderungen gehen nur noch mit `--allow-dirty`. +3. **Eigenes Klon-Skript**, das `origin/main` in ein Scratch-Verzeichnis klont + und dort baut. Strengste Garantie, aber Unity-Kaltimport bei jedem Lauf und + ein zweiter Pfad neben den bestehenden Skripten; ohne Art-Paket im Klon + zeigt der Beta Graybox. +4. **Abbruch im Editor-Build-Hook** (`BuildCommitStamp`). Würde auch + Editor-Builds sperren und braucht Unity-Code, der in keiner CI läuft. + +**Entscheidung:** Alternative 2. + +- Beide Skripte prüfen **vor** jedem Unity-Aufruf `git status --porcelain` über + den ganzen Baum (deckungsgleich mit `BuildCommitStamp`) und brechen bei + nicht leerer Ausgabe ab. `--allow-dirty` erlaubt den Lauf, der Stempel behält + `-dirty`. `build-mac.sh --fast` (lokale Iteration, kein DMG) ist ausgenommen + und stempelt ebenfalls `-dirty`. +- Nach einem erfolgreichen Unity-Build steht `Builds//.build-commit` + (voller HEAD, Zustand `clean`/`dirty`). `--skip-build` läuft durch dieselbe + Prüfung und bricht ab, wenn die Datei fehlt oder nicht zu HEAD und Zustand + passt. Die Datei kommt nicht ins Linux-`tar.gz`. +- HEAD ≠ `origin/main` (nach `git fetch origin main`) ist **nur eine Warnung**. + Ein fehlgeschlagener Fetch (offline) bricht nichts ab. Ob ein Release-DMG + zwingend `main` sein muss, ist damit bewusst nicht entschieden. +- Neue Option `--check-only` beider Skripte: nur Prüfungen, Ausstieg vor Unity. +- Bauort: frischer Worktree von `origin/main` samt `skip-worktree` auf + `AssetMappingRegistry.asset`, weil `ArtAssetAutoSync` sie beim Import + verändert. README 2.0.0 → 2.1.0. +- Teil 1 von #132: die Datei wird nicht eingecheckt; der Inhaber bestätigt + außerhalb des Repos, dass auf keinem Rechner eine Kopie liegt. + +**Begründung:** Die Sperre sitzt dort, wo Pakete entstehen, und kostet keinen +Editor-Code. Dass sie vor Unity greift, spart den teuren Lauf, und der Sidecar +schließt die Lücke, durch die ein alter Build ein neues Etikett bekam. Dass die +Prüfung den ganzen Baum erfasst und nicht nur build-relevante Pfade, ist +absichtlich: es ist dieselbe Definition wie im Stempel, und eine Pfadliste +würde genau die Falle wieder öffnen, die das Issue beschreibt (untrackte +Dateien unter `Assets/`). + +**Konsequenzen:** + +- Ein Beta-Paket aus der Arbeitskopie mit Art-Änderungen bricht ab. Der Weg ist + ein frischer Worktree von `origin/main`; für lokale Pakete gibt es + `--allow-dirty`. +- Auch harmlose untrackte Dateien (Screenshots, `.playwright-mcp/`) sperren + ein Vollpaket. Das ist der Preis der deckungsgleichen Definition. +- Die Prüfung sieht den Stand vor dem Unity-Lauf. Verändert Unity den Baum + beim Import (Registry), stempelt der Editor-Hook `-dirty`, obwohl das Skript + „clean“ gesehen hat — daher `skip-worktree` im Bauort-Worktree. +- Nicht Teil der Entscheidung: die gitignorierte Kopie `tools/build/build-mac.sh` + (der Inhaber löscht sie lokal) und eine Pfadbegrenzung auf build-relevante + Dateien. +- Die Skripte laufen in keiner CI. Geprüft ist die Vorstufe (Sauberkeit, + Sidecar, Fetch) in einem Wegwerf-Repo; die Unity-Erzeugung und das + Schreiben des Sidecars nach einem echten Build sind nicht gelaufen. + ## Offene Punkte - Alle Sprint-4-Review-Befunde (105, davon 9 kritisch): 7 entscheidungsbedürftige kritische Befunde sind durch D-043–D-052 entschieden. diff --git a/reports/v8.6.0/issue-lauf-2026-10-17/05-issue-132-clean-beta-builds.md b/reports/v8.6.0/issue-lauf-2026-10-17/05-issue-132-clean-beta-builds.md new file mode 100644 index 00000000..dfd45bd3 --- /dev/null +++ b/reports/v8.6.0/issue-lauf-2026-10-17/05-issue-132-clean-beta-builds.md @@ -0,0 +1,60 @@ +--- +issue: 132 +paket: P-132 +branch: chore/132-enforce-clean-beta-builds +agent: builder (sonnet) +datum: 2026-10-17 +status: DONE +--- +# Beta-Pakete nur aus sauberem Baum (#132) + +## Aufgabe +Die Regel „verschickt wird nur, was aus einem sauberen Commit fällt“ stand nur in der README. `build-mac.sh` und `build-linux.sh` hängten bei unsauberem Baum nur `-dirty` an, und `--skip-build` stempelte einen alten Build mit dem aktuellen Commit. Umgesetzt: harte Sperre vor dem Unity-Aufruf, `--allow-dirty`, Sidecar-Datei für `--skip-build`, Warnung bei HEAD ≠ origin/main. Teil 1 von #132 (InputGateDiagnostic.cs) ist überholt, nichts zu tun. + +## Was geändert wurde — Files Changed +- tools/packaging/build-mac.sh — neuer Schritt „Quellstand“ vor Unity: Abbruch bei `git status --porcelain` ≠ leer, `--allow-dirty`, `--check-only`, Sidecar-Prüfung bei `--skip-build`, Sidecar schreiben nach Unity-Build, origin/main-Warnung; `--fast` ausgenommen (behält `-dirty`) +- tools/packaging/build-linux.sh — dasselbe (ohne `--fast`, das Linux nicht kennt); Sidecar wird aus dem `tar.gz` ausgeschlossen +- tools/packaging/README.md — 2.0.0 → 2.1.0: Optionstabelle, Abschnitt „Nur aus sauberem Baum“ statt der alten Absatzregel, Bauort (frischer Worktree von origin/main, skip-worktree auf `Assets/_Project/Data/Registries/AssetMappingRegistry.asset` wegen ArtAssetAutoSync), Verlaufszeile +- docs/production/DecisionLog.md — Entwurf D-124 vor „## Offene Punkte“ (Kopfversion/Verlauf nicht angefasst) +- CHANGELOG.md — eine Zeile unter [Unreleased] / Geändert (#132) + +## Tests und Prüfungen +- `bash -n` auf beide Skripte: ok. +- `dotnet test tools/Nova.SimRunner.Tests/Nova.SimRunner.Tests.csproj -c Release`: 778/778 grün (Baseline unverändert, keine C#-Änderung). +- `python3 .github/scripts/check_docs.py`: OK, 203 Markdown-Dateien, 5 Quality-JSONs. +- `python3 .github/scripts/check_baseline_guard.py --self-test`: bestanden. +- Shell-Gegenprobe (läuft in keiner CI) in einem Wegwerf-Repo mit Bare-Origin unter dem Scratchpad (Skripte hineinkopiert, `--check-only` bzw. Fake-Linux-Player): + - sauberer Baum: beide Skripte Exit 0, „HEAD = origin/main“ + - untrackte Datei: beide Exit 1 mit Pfadliste, vor Unity + - `--allow-dirty`: Exit 0, Stempel `b4a7bca-dirty`; mac `--fast` unsauber ebenso + - Linux `--skip-build`: ohne Sidecar Exit 1; Sidecar mit falschem HEAD Exit 1; Zustand dirty vs. clean Exit 1; passender Sidecar Exit 0 + - Linux `--skip-build` vollständig durchgelaufen (Fake-Player): Archiv enthält keine `.build-commit`, Stempel im Archiv stimmt; Wegwerf-Repo bleibt sauber (Builds/ ignoriert) + - mac `--skip-build` ohne Sidecar: Exit 1 + - Fetch auf nicht existierende URL bzw. ohne `origin`: kein Abbruch, Hinweis/Warnung; HEAD ≠ origin/main: nur Warnung, Exit 0 +- NICHT gelaufen: echter Unity-Build, Schreiben des Sidecars nach einem echten Build (Zweig nach dem Unity-Aufruf), Signieren/Notarisieren/DMG, mac `--skip-build` mit passendem Sidecar bis zum Ende (braucht Unity unter /Applications), echtes Verhalten von ArtAssetAutoSync nach Import. Unity-Tests: keine angelegt. + +## Entscheidung +D-124 (Entwurf): Alternative 2, harte Sperre in den Skripten; verworfen: Status quo, Klon-Skript, Abbruch im Editor-Hook. HEAD ≠ origin/main bleibt Warnung. Siehe docs/production/DecisionLog.md. + +## Nebenbefunde +- Die gitignorierte Kopie tools/build/build-mac.sh im Hauptarbeitsbaum bleibt ohne Sperre (nicht angefasst; Inhaber löscht sie lokal). +- Der Editor-Hook BuildCommitStamp stempelt `-dirty`, wenn Unity die AssetMappingRegistry beim Import verändert, obwohl das Skript „clean“ sah. Abhilfe steht in der README (skip-worktree), nicht im Code. +- Auch harmlose untrackte Dateien (z. B. `.playwright-mcp/` im Hauptbaum) sperren künftig ein Vollpaket. +- Die Triage nannte Art-Branch-Dateien (ArtViewScale.cs usw.) als untrackt-kompilierend: gleiche Falle wie #132, nicht Teil dieses Pakets. +- P-14 (Welle 4) benennt Build-Namen in denselben Skripten/README um und läuft danach; mögliche Merge-Konflikte in den Header-Zeilen. + +## Offene Fragen an den Inhaber +- Soll `build-mac.sh --fast` wirklich von der Sperre ausgenommen sein (so umgesetzt) oder auch `--allow-dirty` brauchen? +- Muss ein Release-DMG zwingend HEAD = origin/main sein (dann Warnung zu Abbruch machen)? +- Teil 1 von #132: Bestätigung, dass auf keinem Rechner eine Kopie von InputGateDiagnostic.cs liegt; Kommentar zu #132 nur mit Freigabe. + +## Vorschlag für Commit-Titel und PR-Beschreibung +Commit: `build(packaging): refuse beta packages from a dirty tree (#132)` + +PR-Beschreibung: +- Was: build-mac.sh/build-linux.sh brechen vor Unity bei unsauberem Baum ab; `--allow-dirty` (Stempel `-dirty`), `--check-only`; `--skip-build` vergleicht gegen `Builds//.build-commit`; HEAD ≠ origin/main nur Warnung, offline robust; README 2.1.0 mit Bauort. +- Warum: Die Regel stand nur in der Doku; ein Build aus der Arbeitskopie ist nicht reproduzierbar (#132). Entwurf D-124. +- Bereiche: tools/packaging, README, DecisionLog, CHANGELOG. Keine C#-, keine Simulationsänderung. +- Changelog: Geändert, „Beta-Pakete nur aus sauberem Baum …“ (#132). +- Unity-Spuren: nicht gelaufen (kein Unity-Build, kein Unity-Test). Shell-Gegenprobe im Wegwerf-Repo gelaufen, siehe oben. +- Spielabnahme: nicht gespielt; Restrisiko: Sidecar-Schreiben nach echtem Unity-Build und der macOS-Pfad ab Unity sind unverifiziert. diff --git a/reports/v8.6.0/issue-lauf-2026-10-17/05b-review-1-issue-132.md b/reports/v8.6.0/issue-lauf-2026-10-17/05b-review-1-issue-132.md new file mode 100644 index 00000000..adc05e79 --- /dev/null +++ b/reports/v8.6.0/issue-lauf-2026-10-17/05b-review-1-issue-132.md @@ -0,0 +1,122 @@ +--- +issue: 132 +paket: P-132 +reviewer: opus +runde: 1 +verdict: APPROVED +datum: 2026-10-17 +--- +# Review Runde 1 — P-132: Beta-Pakete nur aus sauberem Baum + +Unabhängiges Read-only-Review nach AGENTS.md §3. Ich habe keine Projektdatei +geändert. Geschrieben habe ich nur diesen Bericht und Wegwerf-Dateien unter +`scratchpad/build-132/review1/`. + +## Was geprüft wurde + +- `git status --short` und `git diff` im Worktree `chore-132` (Basis a6858d1), + vollständig gelesen: `tools/packaging/build-mac.sh`, `build-linux.sh`, + `README.md`, `docs/production/DecisionLog.md`, `CHANGELOG.md`, dazu der + untrackte Builder-Bericht `05-issue-132-clean-beta-builds.md`. +- Abgleich gegen Issue #132, die Paketspezifikation `paket-P-132.json` und die + Analyse samt Gegenprüfung `issue-132.json`. +- Schreibumfang: Geändert sind genau die fünf Dateien aus der Spezifikation plus + der Bericht. Die vier Guard-Dateien sind unberührt + (`git diff --stat -- tools/Nova.SimRunner.Tests/` ist leer). Es gibt keine + C#-Änderung, also auch keine Determinismusfrage. +- Gegengelesen: `BuildCommitStamp.cs:72` nutzt `status --porcelain` ohne + Zusatzflags. Die Aussage „deckungsgleich“ stimmt. `ArtAssetAutoSync.cs:95-96` + und `:158-175` machen die Registry beim Import schmutzig, wie in der README + beschrieben. `Builds/` ist gitignored (`.gitignore:118`). + +## Nachgerechnete Ergebnisse (selbst gemessen) + +| Prüfung | Befehl | Ergebnis | +|---|---|---| +| Simulationstests | `dotnet test tools/Nova.SimRunner.Tests/Nova.SimRunner.Tests.csproj -c Release` | 778/778 bestanden, 0 Fehler | +| Doku-Links | `python3 .github/scripts/check_docs.py` | OK, 204 Markdown-Dateien und 5 Quality-JSONs | +| Baseline-Guard | `python3 .github/scripts/check_baseline_guard.py --self-test` | bestanden, inkl. Negativkontrolle | +| Syntax | `bash -n` auf beide Skripte | ok | + +Der Builder meldet 203 statt 204 Markdown-Dateien. Den Unterschied macht sein +eigener Bericht. `check_docs.py` liest `git ls-files --others --exclude-standard`, +und `reports/` ist im Worktree tatsächlich nicht ignoriert (siehe Nebenbefund). +Vermutlich lief sein Check, bevor er den Bericht schrieb. Das ist erklärbar und +nicht unehrlich. + +**Eigene Shell-Gegenprobe:** Wegwerf-Repo mit Bare-Origin unter +`scratchpad/build-132/review1/`, beide Skripte unverändert hineinkopiert, eine +fiktive Unity-Version. Ergebnisse: + +- sauberer Baum, `--check-only`: beide Skripte Exit 0, „HEAD = origin/main“ +- untrackte Datei, Vollaufruf: beide Exit 1 mit „Arbeitsbaum nicht sauber“, vor dem Schritt „Preflight“ und vor Unity +- `--allow-dirty --check-only` (Linux) und `--fast --check-only` (mac) bei unsauberem Baum: Exit 0, Stempel `-dirty` +- mac `--skip-build --allow-dirty` ohne Sidecar: Exit 1 +- sauberer Baum, Linux-Vollaufruf: läuft durch die Prüfung und scheitert erst an „Unity … nicht gefunden“. Die Reihenfolge ist also belegt. +- Linux `--skip-build`: ohne Sidecar Exit 1. Mit passendem Sidecar und Fake-Player Exit 0. Das `tar.gz` enthält keine `.build-commit`, und der Baum bleibt sauber. +- Sidecar mit `state=dirty` bei sauberem Baum: Exit 1. Sidecar vom Vorgänger-Commit: Exit 1. +- `origin` auf einen nicht existierenden Pfad gesetzt: HINWEIS plus WARNUNG „HEAD ist nicht origin/main“, Exit 0 +- `origin` entfernt: HINWEIS plus „origin/main unbekannt“, Exit 0 + +Nicht gelaufen (bei mir wie beim Builder): echter Unity-Build, Schreiben des +Sidecars nach einem echten Build, Signieren, Notarisieren und DMG, mac +`--skip-build` bis zum Ende, das Verhalten von ArtAssetAutoSync und +BuildCommitStamp nach echtem Import, Unity-EditMode- und PlayMode-Tests (keine +angelegt). + +## Inhaltliche Bewertung + +Die Umsetzung trifft das Zielbild der Spezifikation vollständig: + +- Die Sperre steht vor Unity und gilt in beiden Skripten einheitlich. +- `--allow-dirty` behält den Stempel `-dirty`. `--fast` ist nur auf macOS ausgenommen. +- `--skip-build` prüft gegen einen Sidecar mit HEAD und Baumzustand. +- HEAD ≠ origin/main ist nur eine Warnung. Das Skript ist offline robust: `if !` und `|| true` greifen unter `set -e`, wie getestet. +- README 2.1.0 hat eine Verlaufszeile. +- Der D-124-Entwurf steht an der richtigen Stelle (nach D-114, vor „## Offene Punkte“). Er ist vollständig: Status Entwurf, Kontext, vier bewertete Alternativen, Entscheidung, Begründung, Konsequenzen mit Nicht-Zielen. Kopfversion und Verlauf des DecisionLog sind unberührt. +- Die CHANGELOG-Zeile steht unter `[Unreleased]` / `### Geändert` (Zeile 260 ff., vor `## [0.7.0]` in Zeile 2958) und nennt (#132). +- Die Docstrings zitieren (#132, D-124). +- Der Bericht weist alles Nicht-Gelaufene aus. + +## Befunde + +### hoch + +Keine. + +### mittel + +1. **Kein Nach-Check nach dem Unity-Lauf** — `tools/packaging/build-mac.sh:146-160`, `tools/packaging/build-linux.sh:117-131`. + - Was passiert: Sidecar, Dateiname, `Info.plist` und `NovaBuildCommit.txt` tragen den Zustand von *vor* Unity. Macht der Import den Baum schmutzig (Registry durch ArtAssetAutoSync, Re-Serialisierung), stempelt BuildCommitStamp im Player `-dirty`. Das äußere Etikett und der Sidecar sagen dagegen „clean“. Die Lobby vergleicht den inneren Stempel (D-094). + - Bewertung: D-124 benennt das selbst als Konsequenz, deckt es aber nur über eine README-Anleitung ab. Es ist keine Regression, denn der Bestand rechnete ebenfalls vor Unity. + - Vorschlag: nach erfolgreichem Unity-Build `git status --porcelain` erneut lesen. Ist der Baum von clean auf dirty gekippt, mit klarer Meldung abbrechen (oder mindestens `state=dirty` und `-dirty` setzen). Das kostet drei Zeilen und schließt die Lücke im Skript statt in der Doku. +2. **Bauort-Rezept ist unverifiziert und vermutlich unvollständig** — `tools/packaging/README.md:80-98`. + - Was passiert: Das Rezept nennt nur `skip-worktree` auf `AssetMappingRegistry.asset`. Unter `Assets/_Project/Art/` sind aber 153 Dateien getrackt, darunter `PROVENANCE.json`, `.gitkeep` und Ordner-Metas. Der Art-Pass hat `PROVENANCE.json` im Hauptbaum bereits verändert (git status dort). + - Folge: Ein Paket-Drop-in mit neueren Provenienzdateien oder neuen Ordnern macht den Beta-Worktree schmutzig, bevor Unity startet. Die Sperre greift dann, und der dokumentierte Weg führt nicht zum Paket. + - Vorschlag: im README als „beim ersten echten Bau zu prüfen“ markieren oder den Schritt „Art-Paket einspielen, dann `--check-only`“ ergänzen. + +### niedrig + +3. **Veralteter Sidecar bei gescheitertem Neubau** — `build-mac.sh:150-160`, `build-linux.sh:121-131`. Schlägt Unity fehl, bleibt der Sidecar des vorigen Builds neben einer womöglich teilweise überschriebenen Ausgabe liegen. Ist HEAD gleich geblieben, akzeptiert `--skip-build` sie danach. Vorschlag: `rm -f "$SIDECAR"` direkt vor dem Unity-Aufruf. +4. **README nennt einen Wegwerf-Worktree** — `tools/packaging/README.md:83`. Dort heißt `main-baseline` „Dauer-Worktree“. Laut Gegenprüfung ist er ein Artefakt dieses Analyselaufs (detached a6858d1, kein `Library/`) und gehört nicht in öffentliche Doku. Vorschlag: den Halbsatz streichen. +5. **Doppelter Abschnittsbanner** — `build-mac.sh:55-56`. `# --- Preflight ---` steht direkt über `# --- Quellstand pruefen ---`. Der alte Banner ist verwaist, weil der Preflight jetzt erst ab Zeile 119 beginnt. +6. **Hilfebereiche** — `build-linux.sh:22`: `sed -n '2,12p'` lässt die neue Regelzeile 13 („Bei unsauberem Arbeitsbaum brechen … ab“) aus. `build-mac.sh:39`: `2,24p` endet mitten im Satz („dessen“), eine Zeile früher als vorher. +7. **Fetch ohne Zeitlimit** — `build-mac.sh:102`, `build-linux.sh:84`. `GIT_TERMINAL_PROMPT=0` deckt keine SSH-Passphrase und keinen hängenden Verbindungsaufbau ab. Vorschlag: `GIT_SSH_COMMAND='ssh -o BatchMode=yes -o ConnectTimeout=5'` setzen. + +## Nebenbefunde (außerhalb des Pakets, nicht umsetzen) + +- `.gitignore:26` lautet `reports/ # lokale …`. In `.gitignore` gibt es keine Inline-Kommentare, das Muster greift deshalb nicht. `git check-ignore` auf den Bericht liefert rc=1, und `reports/` erscheint als untrackt. Daher kommt auch die Zählung 203/204 in `check_docs.py`. Für dieses Paket ist das harmlos. Es betrifft aber die Annahme „reports/ ist gitignored“ des Laufs und jeden `git status --porcelain`-Check in einem Baum mit lokalen Reports. +- Die Hinweise des Builders zu `tools/build/build-mac.sh` (gitignorierte Kopie ohne Sperre), zu den untrackten kompilierenden Dateien im Art-Branch und zum Konflikt mit P-14 teile ich. + +## Empfehlung für den Inhaber + +**APPROVED.** Es gibt keinen Befund der Schwere hoch. Die Prüfungen sind grün +und selbst nachgerechnet. Der Schreibumfang ist eingehalten, und der Bericht +ist ehrlich. + +- **Vor dem Merge:** Befund 1 (Nach-Check nach Unity) lohnt sich als kleine Nachbesserung, ebenso Befund 4 (eine README-Zeile). Beides blockiert nicht. +- **Nach dem Merge:** Befund 2 erledigt sich erst mit dem ersten echten Beta-Bau aus einem frischen Worktree mit Art-Paket. Diesen Bau und eine Badge-Prüfung (`BuildInfo.Commit` ohne `-dirty`) sollte der Inhaber einmal fahren, bevor er die Regel als abgenommen betrachtet. +- **Offene Inhaberfragen** aus dem Builder-Bericht, die bestehen bleiben: + - Bleibt `--fast` ausgenommen? + - Ist für das Release-DMG main Pflicht? + - Für Teil 1 von #132: Liegt auf keinem Rechner mehr eine Kopie? Der Kommentar wird nur mit Freigabe gepostet. diff --git a/tools/packaging/README.md b/tools/packaging/README.md index 4b2df030..da18c65d 100644 --- a/tools/packaging/README.md +++ b/tools/packaging/README.md @@ -1,6 +1,6 @@ # Verteilbare Builds -**Dokumentversion:** 2.0.0 | **Stand:** 2026-08-08 | **Governance-Tier:** 2 +**Dokumentversion:** 2.1.0 | **Stand:** 2026-10-17 | **Governance-Tier:** 2 Reproduzierbare Testpakete für macOS und Linux. macOS wird signiert und notarisiert als DMG verteilt; Linux wird als `tar.gz` mit einem von außen @@ -26,10 +26,14 @@ Ergebnisse: | `--fast` | nur bauen, unsigniert, kein DMG | – | | `--skip-build` | vorhandenen Build neu verpacken | vorhandenen Build neu stempeln und verpacken | | `--open` | Ergebnis danach öffnen | – | +| `--allow-dirty` | Bauen trotz unsauberem Baum, Stempel behält `-dirty` | wie macOS | +| `--check-only` | nur den Quellstand prüfen, vor Unity aussteigen | wie macOS | Für die eigene Iteration ist `--fast` der richtige Weg: Signieren und Notarisieren kosten Minuten und bringen auf dem eigenen Rechner nichts. Ein -lokal gebauter Build hat kein Quarantäne-Attribut und startet ohnehin. +lokal gebauter Build hat kein Quarantäne-Attribut und startet ohnehin. `--fast` +ist als einziger Weg von der Sauberkeitsprüfung ausgenommen (siehe unten) und +trägt bei unsauberem Baum den Stempel `-dirty`. ## Warum der Commit-Hash im Dateinamen steht @@ -49,9 +53,49 @@ defaults read /Applications/ProjectNova.app/Contents/Info.plist NovaBuildCommit cat ProjectNova_Data/NovaBuildCommit.txt ``` -Ein Build aus einem unsauberen Arbeitsbaum bekommt `-dirty` angehängt und ist -damit als nicht rekonstruierbar markiert. Für ein gemeinsames Match taugt er -nicht — verschickt wird nur, was aus einem sauberen Commit fällt. +## Nur aus sauberem Baum (#132, D-124) + +Verschickt wird nur, was aus einem sauberen Commit fällt. Das erzwingen die +Skripte, die Regel steht nicht mehr nur hier: + +- **Vor dem Unity-Aufruf** prüfen beide Skripte `git status --porcelain` über den + ganzen Baum — dieselbe Definition wie der Editor-Hook `BuildCommitStamp`. Ist + die Ausgabe nicht leer (auch untrackte Dateien: Unity kompiliert alles unter + `Assets/` mit), brechen sie ab und nennen die ersten 20 Pfade. +- **`--allow-dirty`** ist der Ausweg für lokale Pakete. Der Stempel behält dann + `-dirty` und markiert das Paket als nicht rekonstruierbar. Für ein gemeinsames + Match taugt es nicht. +- **`--fast`** (nur macOS) ist ausgenommen: kein DMG, nur für den eigenen Rechner. +- **`--skip-build`** läuft durch dieselbe Prüfung. Nach jedem erfolgreichen + Unity-Build schreiben die Skripte `Builds//.build-commit` (voller + HEAD und Baumzustand `clean`/`dirty`). `--skip-build` bricht ab, wenn die Datei + fehlt oder nicht zum aktuellen HEAD und Baumzustand passt — sonst bekäme ein + alter Build das Etikett des jetzigen Commits. Die Datei kommt nicht ins + Linux-`tar.gz`. +- **HEAD ≠ `origin/main`** ist nur eine Warnung (nach `git fetch origin main`). + Offline bricht der Fetch nicht ab; der Vergleich nutzt dann den Stand des + letzten Fetch und sagt das. +- **`--check-only`** führt nur diese Prüfungen aus und steigt vor Unity aus. + +### Wo gebaut wird + +Beta-Pakete entstehen in einem frischen Worktree von `origin/main`, nicht in der +Arbeitskopie und nicht im Dauer-Worktree `main-baseline`: + +```bash +git fetch origin main +git worktree add ../.hashkrieg-worktrees/beta origin/main +cd ../.hashkrieg-worktrees/beta +git update-index --skip-worktree Assets/_Project/Data/Registries/AssetMappingRegistry.asset +tools/packaging/build-mac.sh +``` + +`ArtAssetAutoSync` schreibt die `AssetMappingRegistry.asset` beim Import um +(`Assets/_Project/Editor/ArtAssetAutoSync.cs`). Ohne +`skip-worktree` auf dieser Datei steht der Baum nach dem ersten Unity-Lauf +unsauber da, der Editor-Hook stempelt `-dirty`, und ein anschließendes +`--skip-build` scheitert an der Prüfung. Das Art-Paket liegt außerhalb des Repos +(DemoRunbook): ohne es zeigt der Beta Graybox-Primitive. ## Voraussetzungen @@ -103,5 +147,6 @@ protokolliert werden. Die Skripte liefern nur das zuordenbare Artefakt. | Version | Datum | Änderung | Autor | |---|---|---|---| +| 2.1.0 | 2026-10-17 | Sauberkeitsprüfung vor Unity, `--allow-dirty`, `--check-only`, Build-Sidecar für `--skip-build`, Bauort-Hinweis (#132, D-124) | Project Nova Team | | 2.0.0 | 2026-08-08 | Linux-x64-Build, Commit-Stempel und `tar.gz`-Verteilung ergänzt; offene Netzabnahmen ehrlich benannt | Project Nova Team | | 1.0.0 | 2026-08-08 | macOS-Build-, Signatur-, Notarisierungs- und DMG-Weg dokumentiert | Project Nova Team | diff --git a/tools/packaging/build-linux.sh b/tools/packaging/build-linux.sh index 04928b3d..9cc7f20d 100755 --- a/tools/packaging/build-linux.sh +++ b/tools/packaging/build-linux.sh @@ -4,17 +4,22 @@ # # tools/packaging/build-linux.sh # tools/packaging/build-linux.sh --skip-build +# tools/packaging/build-linux.sh --allow-dirty # trotz unsauberem Baum (Stempel '-dirty') +# tools/packaging/build-linux.sh --check-only # nur Quellstand pruefen, kein Unity # tools/packaging/build-linux.sh --help # # Ergebnis: Builds/dist/ProjectNova-linux-x64-.tar.gz # Der Commit steht zusaetzlich in ProjectNova_Data/NovaBuildCommit.txt. +# Bei unsauberem Arbeitsbaum brechen Bauen und --skip-build ab (#132). set -euo pipefail -SKIP_BUILD=0 +SKIP_BUILD=0 ALLOW_DIRTY=0 CHECK_ONLY=0 while [ $# -gt 0 ]; do case "$1" in --skip-build) SKIP_BUILD=1; shift ;; - -h|--help) sed -n '2,10p' "${BASH_SOURCE[0]}"; exit 0 ;; + --allow-dirty) ALLOW_DIRTY=1; shift ;; + --check-only) CHECK_ONLY=1; shift ;; + -h|--help) sed -n '2,12p' "${BASH_SOURCE[0]}"; exit 0 ;; *) echo "FEHLER: unbekannte Option: $1" >&2; exit 1 ;; esac done @@ -24,25 +29,81 @@ BUILD_DIR="$REPO/Builds/Linux64" PLAYER="$BUILD_DIR/ProjectNova.x86_64" DATA_DIR="$BUILD_DIR/ProjectNova_Data" STAMP="$DATA_DIR/NovaBuildCommit.txt" +SIDECAR="$BUILD_DIR/.build-commit" DIST="$REPO/Builds/dist" step() { printf '\n\033[1m==> %s\033[0m\n' "$1"; } die() { printf '\033[31mFEHLER: %s\033[0m\n' "$1" >&2; exit 1; } +# --- Quellstand pruefen (#132, D-124) ----------------------------------------- +# Ein Paket, das die Testpartie spielt, muss aus einem Commit fallen, den ein +# frischer Klon reproduzieren kann. Deshalb steht diese Pruefung VOR jedem +# Unity-Aufruf: ein unsauberer Baum (auch untrackte Dateien — Unity kompiliert +# alles unter Assets/ mit) bricht ab. Gleiche Definition wie BuildCommitStamp.cs: +# `git status --porcelain` ueber den ganzen Baum. +step "Quellstand" + +HEAD_FULL="$(git -C "$REPO" rev-parse HEAD)" +COMMIT="$(git -C "$REPO" rev-parse --short HEAD)" +TREE_STATE=clean +DIRTY_LIST="$(git -C "$REPO" status --porcelain)" +if [ -n "$DIRTY_LIST" ]; then + TREE_STATE=dirty + if [ "$ALLOW_DIRTY" -eq 1 ]; then + COMMIT="$COMMIT-dirty" + echo " WARNUNG: Arbeitsbaum nicht sauber — Paket heisst '$COMMIT'." + echo " Ein '-dirty'-Build laesst sich nicht rekonstruieren." + else + { + echo "Geaenderte oder untrackte Dateien (git status --porcelain, erste 20):" + printf '%s\n' "$DIRTY_LIST" | head -20 | sed 's/^/ /' + } >&2 + die "Arbeitsbaum nicht sauber. Beta-Pakete entstehen aus einem sauberen Commit, am besten aus einem frischen Worktree von origin/main (siehe tools/packaging/README.md). Ausweg fuer lokale Pakete: --allow-dirty (Stempel behaelt '-dirty')." + fi +fi +echo " Commit: $COMMIT" + +# --skip-build verpackt einen vorhandenen Build neu. Ohne Gegenprobe bekaeme ein +# alter Build das Etikett des aktuellen Commits. Der Sidecar entsteht nach einem +# erfolgreichen Unity-Build und haelt fest, aus welchem HEAD er stammt. +if [ "$SKIP_BUILD" -eq 1 ]; then + [ -f "$SIDECAR" ] \ + || die "Kein $SIDECAR — woher der vorhandene Build stammt, ist unbekannt. Erst ohne --skip-build bauen." + SC_HEAD="$(sed -n 's/^head=//p' "$SIDECAR")" + SC_STATE="$(sed -n 's/^state=//p' "$SIDECAR")" + [ "$SC_HEAD" = "$HEAD_FULL" ] \ + || die "Der vorhandene Build stammt aus ${SC_HEAD:-unbekannt}, HEAD ist $HEAD_FULL. Neu bauen statt --skip-build." + [ "$SC_STATE" = "$TREE_STATE" ] \ + || die "Der vorhandene Build entstand aus einem Baum mit Zustand '${SC_STATE:-unbekannt}', jetzt ist er '$TREE_STATE'. Neu bauen statt --skip-build." + echo " Build-Sidecar: passt zu HEAD ($SC_STATE)" +fi + +# Nur eine Warnung: ein Beta-Paket soll normalerweise main sein, ob ein Branch- +# Paket (etwa fuer einen Test) trotzdem rausgehen darf, entscheidet der Inhaber. +# Offline darf das nie abbrechen — daher `if !` unter `set -e`. +if ! GIT_TERMINAL_PROMPT=0 git -C "$REPO" fetch --quiet origin main >/dev/null 2>&1; then + echo " HINWEIS: git fetch origin main fehlgeschlagen (offline oder kein 'origin') — Vergleich nur mit dem Stand des letzten Fetch." +fi +ORIGIN_MAIN="$(git -C "$REPO" rev-parse --verify --quiet origin/main || true)" +if [ -z "$ORIGIN_MAIN" ]; then + echo " WARNUNG: origin/main unbekannt — HEAD kann nicht gegen main verglichen werden." +elif [ "$ORIGIN_MAIN" != "$HEAD_FULL" ]; then + echo " WARNUNG: HEAD ist nicht origin/main (${ORIGIN_MAIN:0:7}). Ein Beta-Paket soll aus main fallen." +else + echo " HEAD = origin/main" +fi + +if [ "$CHECK_ONLY" -eq 1 ]; then + echo " --check-only: Quellstand geprueft, Abbruch vor Unity." + exit 0 +fi + step "Preflight" UNITY_VERSION="$(sed -n 's/^m_EditorVersion: //p' "$REPO/ProjectSettings/ProjectVersion.txt")" UNITY_ROOT="/Applications/Unity/Hub/Editor/$UNITY_VERSION" UNITY="$UNITY_ROOT/Unity.app/Contents/MacOS/Unity" LINUX_SUPPORT="$UNITY_ROOT/PlaybackEngines/LinuxStandaloneSupport" -COMMIT="$(git -C "$REPO" rev-parse --short HEAD)" -if [ -n "$(git -C "$REPO" status --porcelain)" ]; then - COMMIT="$COMMIT-dirty" - echo " WARNUNG: Arbeitsbaum nicht sauber — Paket heisst '$COMMIT'." - echo " Ein '-dirty'-Build laesst sich nicht rekonstruieren." -fi -echo " Commit: $COMMIT" - if [ "$SKIP_BUILD" -eq 0 ]; then [ -x "$UNITY" ] || die "Unity $UNITY_VERSION nicht gefunden unter $UNITY" [ -d "$LINUX_SUPPORT" ] \ @@ -65,6 +126,9 @@ if [ "$SKIP_BUILD" -eq 0 ]; then die "Unity-Linux-Build fehlgeschlagen." fi echo " Log: $LOG" + + # Sidecar fuer --skip-build: aus welchem HEAD und Baumzustand dieser Build kam. + printf 'head=%s\nstate=%s\n' "$HEAD_FULL" "$TREE_STATE" > "$SIDECAR" fi [ -x "$PLAYER" ] || die "kein ausfuehrbarer Linux-Player unter $PLAYER" @@ -83,6 +147,7 @@ rm -f "$ARCHIVE" tar -C "$BUILD_DIR" \ --exclude='*_BurstDebugInformation_DoNotShip' \ --exclude='.DS_Store' \ + --exclude='.build-commit' \ -czf "$ARCHIVE" . ARCHIVED_STAMP="$(tar -xOf "$ARCHIVE" ./ProjectNova_Data/NovaBuildCommit.txt | tr -d '\r\n')" diff --git a/tools/packaging/build-mac.sh b/tools/packaging/build-mac.sh index cc521c40..513fce17 100755 --- a/tools/packaging/build-mac.sh +++ b/tools/packaging/build-mac.sh @@ -7,6 +7,11 @@ # tools/packaging/build-mac.sh --fast # nur bauen, ad-hoc signiert, kein DMG # tools/packaging/build-mac.sh --skip-build # vorhandenen Build nur neu verpacken # tools/packaging/build-mac.sh --open # nach dem Bauen starten +# tools/packaging/build-mac.sh --allow-dirty # trotz unsauberem Baum (Stempel '-dirty') +# tools/packaging/build-mac.sh --check-only # nur Quellstand pruefen, kein Unity +# +# Voll- und --skip-build-Laeufe brechen bei unsauberem Arbeitsbaum ab (#132). +# --fast ist fuer die eigene Iteration und darf unsauber sein. # # WARUM DAS EIN SKRIPT IST UND KEINE ANLEITUNG: # Der Relay sperrt Matches zwischen ungleichen Builds (Sprint 12, A4 — @@ -23,13 +28,15 @@ # Dylibs, dann die Plugin-Bundles als Einheit, das Hauptbundle zuletzt. set -euo pipefail -FAST=0 SKIP_BUILD=0 OPEN_AFTER=0 +FAST=0 SKIP_BUILD=0 OPEN_AFTER=0 ALLOW_DIRTY=0 CHECK_ONLY=0 while [ $# -gt 0 ]; do case "$1" in --fast) FAST=1; shift ;; --skip-build) SKIP_BUILD=1; shift ;; --open) OPEN_AFTER=1; shift ;; - -h|--help) sed -n '2,20p' "${BASH_SOURCE[0]}"; exit 0 ;; + --allow-dirty) ALLOW_DIRTY=1; shift ;; + --check-only) CHECK_ONLY=1; shift ;; + -h|--help) sed -n '2,24p' "${BASH_SOURCE[0]}"; exit 0 ;; *) echo "FEHLER: unbekannte Option: $1" >&2; exit 1 ;; esac done @@ -38,6 +45,7 @@ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" ENTITLEMENTS="$REPO/tools/packaging/Nova.entitlements" BUILD_DIR="$REPO/Builds/MacOSArm64" APP="$BUILD_DIR/ProjectNova.app" +SIDECAR="$BUILD_DIR/.build-commit" DIST="$REPO/Builds/dist" NOTARIZE="$HOME/.claude/skills/apple-upload/scripts/notarize.sh" @@ -45,6 +53,69 @@ step() { printf '\n\033[1m==> %s\033[0m\n' "$1"; } die() { printf '\033[31mFEHLER: %s\033[0m\n' "$1" >&2; exit 1; } # --- Preflight -------------------------------------------------------------- +# --- Quellstand pruefen (#132, D-124) ----------------------------------------- +# Ein Paket, das die Testpartie spielt, muss aus einem Commit fallen, den ein +# frischer Klon reproduzieren kann. Deshalb steht diese Pruefung VOR jedem +# Unity-Aufruf: ein unsauberer Baum (auch untrackte Dateien — Unity kompiliert +# alles unter Assets/ mit) bricht ab. Gleiche Definition wie BuildCommitStamp.cs: +# `git status --porcelain` ueber den ganzen Baum. +step "Quellstand" + +HEAD_FULL="$(git -C "$REPO" rev-parse HEAD)" +COMMIT="$(git -C "$REPO" rev-parse --short HEAD)" +TREE_STATE=clean +DIRTY_LIST="$(git -C "$REPO" status --porcelain)" +if [ -n "$DIRTY_LIST" ]; then + TREE_STATE=dirty + if [ "$ALLOW_DIRTY" -eq 1 ] || [ "$FAST" -eq 1 ]; then + COMMIT="$COMMIT-dirty" + echo " WARNUNG: Arbeitsbaum nicht sauber — Paket heisst '$COMMIT'." + echo " Ein '-dirty'-Build laesst sich nicht rekonstruieren." + else + { + echo "Geaenderte oder untrackte Dateien (git status --porcelain, erste 20):" + printf '%s\n' "$DIRTY_LIST" | head -20 | sed 's/^/ /' + } >&2 + die "Arbeitsbaum nicht sauber. Beta-Pakete entstehen aus einem sauberen Commit, am besten aus einem frischen Worktree von origin/main (siehe tools/packaging/README.md). Ausweg fuer lokale Pakete: --allow-dirty (Stempel behaelt '-dirty')." + fi +fi +echo " Commit: $COMMIT" + +# --skip-build verpackt einen vorhandenen Build neu. Ohne Gegenprobe bekaeme ein +# alter Build das Etikett des aktuellen Commits. Der Sidecar entsteht nach einem +# erfolgreichen Unity-Build und haelt fest, aus welchem HEAD er stammt. +if [ "$SKIP_BUILD" -eq 1 ]; then + [ -f "$SIDECAR" ] \ + || die "Kein $SIDECAR — woher der vorhandene Build stammt, ist unbekannt. Erst ohne --skip-build bauen." + SC_HEAD="$(sed -n 's/^head=//p' "$SIDECAR")" + SC_STATE="$(sed -n 's/^state=//p' "$SIDECAR")" + [ "$SC_HEAD" = "$HEAD_FULL" ] \ + || die "Der vorhandene Build stammt aus ${SC_HEAD:-unbekannt}, HEAD ist $HEAD_FULL. Neu bauen statt --skip-build." + [ "$SC_STATE" = "$TREE_STATE" ] \ + || die "Der vorhandene Build entstand aus einem Baum mit Zustand '${SC_STATE:-unbekannt}', jetzt ist er '$TREE_STATE'. Neu bauen statt --skip-build." + echo " Build-Sidecar: passt zu HEAD ($SC_STATE)" +fi + +# Nur eine Warnung: ein Beta-Paket soll normalerweise main sein, ob ein Branch- +# Paket (etwa fuer einen Test) trotzdem rausgehen darf, entscheidet der Inhaber. +# Offline darf das nie abbrechen — daher `if !` unter `set -e`. +if ! GIT_TERMINAL_PROMPT=0 git -C "$REPO" fetch --quiet origin main >/dev/null 2>&1; then + echo " HINWEIS: git fetch origin main fehlgeschlagen (offline oder kein 'origin') — Vergleich nur mit dem Stand des letzten Fetch." +fi +ORIGIN_MAIN="$(git -C "$REPO" rev-parse --verify --quiet origin/main || true)" +if [ -z "$ORIGIN_MAIN" ]; then + echo " WARNUNG: origin/main unbekannt — HEAD kann nicht gegen main verglichen werden." +elif [ "$ORIGIN_MAIN" != "$HEAD_FULL" ]; then + echo " WARNUNG: HEAD ist nicht origin/main (${ORIGIN_MAIN:0:7}). Ein Beta-Paket soll aus main fallen." +else + echo " HEAD = origin/main" +fi + +if [ "$CHECK_ONLY" -eq 1 ]; then + echo " --check-only: Quellstand geprueft, Abbruch vor Unity." + exit 0 +fi + step "Preflight" UNITY_VERSION="$(sed -n 's/^m_EditorVersion: //p' "$REPO/ProjectSettings/ProjectVersion.txt")" @@ -58,14 +129,6 @@ if [ "$SKIP_BUILD" -eq 0 ] && [ -f "$REPO/Library/EditorInstance.json" ]; then die "Der Unity-Editor hat das Projekt offen. Erst schliessen, dann bauen." fi -COMMIT="$(git -C "$REPO" rev-parse --short HEAD)" -if [ -n "$(git -C "$REPO" status --porcelain)" ]; then - COMMIT="$COMMIT-dirty" - echo " WARNUNG: Arbeitsbaum nicht sauber — Paket heisst '$COMMIT'." - echo " Ein '-dirty'-Build laesst sich nicht rekonstruieren." -fi -echo " Commit: $COMMIT" - if [ "$FAST" -eq 0 ]; then IDENTITY="$(security find-identity -v -p codesigning 2>/dev/null \ | grep 'Developer ID Application' | head -1 | sed -E 's/.*"(.+)".*/\1/' || true)" @@ -92,6 +155,9 @@ if [ "$SKIP_BUILD" -eq 0 ]; then die "Unity-Build fehlgeschlagen." fi echo " Log: $LOG" + + # Sidecar fuer --skip-build: aus welchem HEAD und Baumzustand dieser Build kam. + printf 'head=%s\nstate=%s\n' "$HEAD_FULL" "$TREE_STATE" > "$SIDECAR" fi [ -d "$APP" ] || die "kein Build unter $APP"