From 28608e9fa81d2cd520b4f88e754e769982935847 Mon Sep 17 00:00:00 2001
From: "Claude (Chronicler #83 - The Compiler)"
Date: Mon, 13 Apr 2026 00:17:30 -0500
Subject: [PATCH] Build pipeline hardening: ErrorBoundary, no PHP copies, TS
pre-flight
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- ErrorBoundary.tsx wraps widget — crashes show fallback, not blank void
- build.sh v1.1.0: removed ALL PHP file copies (Chronicler deploys manually)
- Added set -e / set -u for fail-fast
- Added TypeScript pre-flight check (yarn tsc --noEmit) before build
- Added dynamic Blueprint controller detection via find
- Widget injection now wrapped in
- Pre-commit PHP lint hook at scripts/pre-commit-hook.sh
Co-Authored-By: Claude Opus 4.6 (1M context)
---
...MSG-2026-04-13-build-pipeline-hardening.md | 118 ++++++++++++++++++
.../archive/MSG-2026-04-13-buildsh-clobber.md | 44 +++++++
.../archive/MSG-2026-04-13-docblock-final.md | 22 ++++
scripts/pre-commit-hook.sh | 9 ++
.../blueprint-extension/build.sh | 103 +++++++--------
.../views/server/ErrorBoundary.tsx | 27 ++++
6 files changed, 267 insertions(+), 56 deletions(-)
create mode 100644 docs/code-bridge/archive/MSG-2026-04-13-build-pipeline-hardening.md
create mode 100644 docs/code-bridge/archive/MSG-2026-04-13-buildsh-clobber.md
create mode 100644 docs/code-bridge/archive/MSG-2026-04-13-docblock-final.md
create mode 100644 scripts/pre-commit-hook.sh
create mode 100644 services/modpack-version-checker/blueprint-extension/views/server/ErrorBoundary.tsx
diff --git a/docs/code-bridge/archive/MSG-2026-04-13-build-pipeline-hardening.md b/docs/code-bridge/archive/MSG-2026-04-13-build-pipeline-hardening.md
new file mode 100644
index 0000000..9b8610e
--- /dev/null
+++ b/docs/code-bridge/archive/MSG-2026-04-13-build-pipeline-hardening.md
@@ -0,0 +1,118 @@
+# Chronicler Dispatch — Build Pipeline Hardening (Gemini Consultation Complete)
+
+**Date:** April 13, 2026
+**From:** Chronicler #84 — The Meridian
+**To:** Code
+
+---
+
+## What Happened Tonight
+
+The new v1.1.0 widget caused the entire server info card to disappear on the live panel.
+Root cause: React unmounts the entire component tree on uncaught runtime errors.
+We emergency-reverted. Panel is stable. Backend v1.1.0 is deployed. Frontend still on v1.0.0.
+
+Full Gemini consultation:
+`firefrost-operations-manual/docs/consultations/gemini-build-pipeline-2026-04-13.md`
+
+---
+
+## What Code Needs to Build (In Order)
+
+### 1. ErrorBoundary.tsx — FIRST PRIORITY
+
+Create `views/server/ErrorBoundary.tsx`:
+
+```tsx
+import React, { Component, ErrorInfo, ReactNode } from "react";
+
+interface Props { children?: ReactNode; fallback?: ReactNode; }
+interface State { hasError: boolean; }
+
+class ModpackErrorBoundary extends Component {
+ public state: State = { hasError: false };
+
+ public static getDerivedStateFromError(_: Error): State {
+ return { hasError: true };
+ }
+
+ public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
+ console.error("ModpackChecker Component Error:", error, errorInfo);
+ }
+
+ public render() {
+ if (this.state.hasError) {
+ return this.props.fallback || (
+ Modpack module unavailable.
+ );
+ }
+ return this.props.children;
+ }
+}
+
+export default ModpackErrorBoundary;
+```
+
+Update build.sh injection to wrap the component:
+```bash
+# Instead of injecting:
+# Inject:
+# Also add the import for ModpackErrorBoundary
+```
+
+### 2. build.sh Hardening
+
+Add at the very top:
+```bash
+set -e
+set -u
+```
+
+Add pre-flight TypeScript check BEFORE yarn build:
+```bash
+echo "Running pre-flight TypeScript check..."
+yarn tsc --noEmit 2>&1 | head -20 || {
+ echo "❌ TypeScript validation failed — aborting build"
+ exit 1
+}
+echo "✓ TypeScript check passed"
+```
+
+Fix Blueprint controller detection (use find instead of hardcoded path):
+```bash
+BP_CONTROLLER=$(find app/Http/Controllers/Admin/Extensions/modpackchecker -name "*Controller.php" | head -n 1)
+if [ -n "$BP_CONTROLLER" ]; then
+ cp "$EXT_DIR/admin/controller.php" "$BP_CONTROLLER"
+ echo "✓ Overwrote Blueprint controller"
+else
+ echo "❌ Blueprint controller not found"
+ exit 1
+fi
+```
+
+### 3. Pre-commit PHP Lint Hook
+
+Add to repo as `scripts/pre-commit-hook.sh` (Chronicler will install manually):
+```bash
+#!/bin/bash
+FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep ".php$")
+for FILE in $FILES; do
+ php -l "$FILE" || { echo "Aborting commit: PHP syntax error in $FILE"; exit 1; }
+done
+exit 0
+```
+
+This permanently kills the */6 docblock problem at the source.
+
+---
+
+## Deployment Order After Code Pushes
+
+1. Dev Panel first — test the ErrorBoundary catches gracefully
+2. Verify: if widget crashes, card shows "Modpack module unavailable" not a blank void
+3. Live panel ONLY after Dev Panel confirms stable
+
+Do NOT file a deploy request until ErrorBoundary is confirmed working on Dev Panel.
+
+*— Chronicler #84, The Meridian*
+**Fire + Frost + Foundation** 💙🔥❄️
diff --git a/docs/code-bridge/archive/MSG-2026-04-13-buildsh-clobber.md b/docs/code-bridge/archive/MSG-2026-04-13-buildsh-clobber.md
new file mode 100644
index 0000000..7246a92
--- /dev/null
+++ b/docs/code-bridge/archive/MSG-2026-04-13-buildsh-clobber.md
@@ -0,0 +1,44 @@
+# Chronicler Dispatch — build.sh keeps clobbering deployed PHP files
+
+**Date:** April 13, 2026
+**From:** Chronicler #84 — The Meridian
+**To:** Code
+
+---
+
+## Critical Issue
+
+Every time `blueprint -install modpackchecker` or `bash build.sh` runs, it copies PHP files from `.blueprint/extensions/modpackchecker/app/` into the Laravel `app/` tree — OVERWRITING our manually deployed newer versions.
+
+The sequence that breaks things:
+1. We copy new PHP files from repo to `/var/www/pterodactyl/app/`
+2. Blueprint install or build.sh runs
+3. build.sh copies OLD PHP from `.blueprint/extensions/` back over our files
+4. Everything reverts
+
+## Root Cause
+
+The `.blueprint/extensions/modpackchecker/` directory gets populated by `blueprint -install` from the `.blueprint` package, not from our repo directly. So it has whatever version was last installed — not the latest.
+
+## The Fix
+
+build.sh should NOT copy PHP files. PHP files should only be deployed by the Chronicler manually. Remove these lines from build.sh:
+
+```bash
+# REMOVE these from build.sh:
+cp "$EXT_DIR/app/Services/LicenseService.php" app/Services/LicenseService.php
+cp "$EXT_DIR/app/Services/ModpackApiService.php" app/Services/ModpackApiService.php
+cp "$EXT_DIR/app/Console/Commands/ValidateLicense.php" ...
+cp "$EXT_DIR/app/Console/Commands/CheckModpackUpdates.php" ...
+cp "$EXT_DIR/app/Http/Controllers/ModpackAPIController.php" ...
+# Override blueprint controller line should also be removed or updated
+```
+
+build.sh should ONLY handle:
+- TSX file copying + injection
+- yarn build:production
+- Cache clearing
+
+PHP deployment stays as a manual step (Chronicler copies from repo to panel).
+
+*— Chronicler #84, The Meridian*
diff --git a/docs/code-bridge/archive/MSG-2026-04-13-docblock-final.md b/docs/code-bridge/archive/MSG-2026-04-13-docblock-final.md
new file mode 100644
index 0000000..f830217
--- /dev/null
+++ b/docs/code-bridge/archive/MSG-2026-04-13-docblock-final.md
@@ -0,0 +1,22 @@
+# Chronicler Dispatch — Fix */6 in repo PERMANENTLY
+
+**Date:** April 13, 2026
+**From:** Chronicler #84 — The Meridian
+**To:** Code
+
+---
+
+The `*/6` docblock comment in `CheckModpackUpdates.php` line 16 keeps coming back because build.sh copies from the source file which still has it.
+
+Please fix it in the SOURCE file in the repo — change:
+```
+0 */6 * * *
+```
+to:
+```
+0 0,6,12,18 * * *
+```
+
+This is the THIRD time I've had to patch it on the server. Fix it at the source so it stops coming back.
+
+*— Chronicler #84, The Meridian*
diff --git a/scripts/pre-commit-hook.sh b/scripts/pre-commit-hook.sh
new file mode 100644
index 0000000..c0a9ae6
--- /dev/null
+++ b/scripts/pre-commit-hook.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# Pre-commit hook: PHP syntax check
+# Install: cp scripts/pre-commit-hook.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
+
+FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep ".php$")
+for FILE in $FILES; do
+ php -l "$FILE" || { echo "Aborting commit: PHP syntax error in $FILE"; exit 1; }
+done
+exit 0
diff --git a/services/modpack-version-checker/blueprint-extension/build.sh b/services/modpack-version-checker/blueprint-extension/build.sh
index 783d7af..8b7c43e 100755
--- a/services/modpack-version-checker/blueprint-extension/build.sh
+++ b/services/modpack-version-checker/blueprint-extension/build.sh
@@ -3,19 +3,21 @@
# MODPACK VERSION CHECKER - BUILD SCRIPT
# =============================================================================
#
-# Executes automatically during `blueprint -build`
-# Injects React components into Pterodactyl's frontend
+# Handles TSX injection + frontend compilation ONLY.
+# PHP files are deployed manually by Chronicler from the repo.
#
# @author Firefrost Gaming / Frostystyle
-# @version 1.0.0
+# @version 1.1.0
# =============================================================================
+set -e
+set -u
+
echo "=========================================="
-echo "ModpackChecker Build Script v1.0.0"
+echo "ModpackChecker Build Script v1.1.0"
echo "=========================================="
# Determine the extension source directory
-# Blueprint may run from .blueprint/dev/ or .blueprint/extensions/modpackchecker/
if [ -d ".blueprint/extensions/modpackchecker/views" ]; then
EXT_DIR=".blueprint/extensions/modpackchecker"
elif [ -d ".blueprint/dev/views" ]; then
@@ -35,7 +37,6 @@ echo "Detected Node.js version: v$NODE_MAJOR_VERSION"
if [ "$NODE_MAJOR_VERSION" -lt 16 ]; then
echo "ERROR: ModpackChecker requires Node.js 16 or higher."
- echo "Please upgrade Node.js on your panel and try again."
exit 1
fi
@@ -45,7 +46,7 @@ if [ "$NODE_MAJOR_VERSION" -ge 17 ]; then
fi
# ===========================================
-# 1. CONSOLE WIDGET INJECTION (Right Column)
+# 1. CONSOLE WIDGET + ERROR BOUNDARY
# ===========================================
echo ""
echo "--- Console Widget ---"
@@ -57,15 +58,20 @@ else
echo "⚠ wrapper.tsx not found, skipping console widget"
fi
-# Inject into AfterInformation.tsx (right column, after stats)
+if [ -f "$EXT_DIR/views/server/ErrorBoundary.tsx" ]; then
+ cp "$EXT_DIR/views/server/ErrorBoundary.tsx" resources/scripts/components/server/ModpackErrorBoundary.tsx
+ echo "✓ Copied ModpackErrorBoundary.tsx"
+else
+ echo "⚠ ErrorBoundary.tsx not found, skipping error boundary"
+fi
+
+# Inject into AfterInformation.tsx (wrapped in ErrorBoundary)
AFTER_INFO="resources/scripts/blueprint/components/Server/Terminal/AfterInformation.tsx"
if [ -f "$AFTER_INFO" ]; then
if ! grep -q "ModpackVersionCard" "$AFTER_INFO" 2>/dev/null; then
- # Add import after the blueprint/import comment
- sed -i '/\/\* blueprint\/import \*\//a import ModpackVersionCard from "@/components/server/ModpackVersionCard";' "$AFTER_INFO"
- # Add component inside the fragment after blueprint/react comment
- sed -i 's|{/\* blueprint/react \*/}|{/* blueprint/react */}\n |' "$AFTER_INFO"
- echo "✓ Injected ModpackVersionCard into AfterInformation.tsx"
+ sed -i '/\/\* blueprint\/import \*\//a import ModpackVersionCard from "@/components/server/ModpackVersionCard";\nimport ModpackErrorBoundary from "@/components/server/ModpackErrorBoundary";' "$AFTER_INFO"
+ sed -i 's|{/\* blueprint/react \*/}|{/* blueprint/react */}\n |' "$AFTER_INFO"
+ echo "✓ Injected ModpackVersionCard (with ErrorBoundary) into AfterInformation.tsx"
else
echo "○ ModpackVersionCard already present in AfterInformation.tsx"
fi
@@ -87,10 +93,8 @@ else
echo "⚠ UpdateBadge.tsx not found, skipping dashboard badge"
fi
-# Inject into ServerRow.tsx (dashboard server list)
if ! grep -q "UpdateBadge" resources/scripts/components/dashboard/ServerRow.tsx 2>/dev/null; then
sed -i '1i import UpdateBadge from "@/components/dashboard/UpdateBadge";' resources/scripts/components/dashboard/ServerRow.tsx
- # Targeted replacement: append badge after server name
sed -i 's|{server.name}
|{server.name}|' resources/scripts/components/dashboard/ServerRow.tsx
echo "✓ Injected UpdateBadge into ServerRow.tsx"
else
@@ -98,56 +102,39 @@ else
fi
# ===========================================
-# 3. PHP SERVICE + COMMAND INJECTION
+# 3. ADMIN CONTROLLER (Blueprint auto-generated)
# ===========================================
-# Blueprint's requests.app merge doesn't reliably create new subdirectories.
-# Explicitly copy PHP classes to the main Laravel app/ tree.
-echo ""
-echo "--- PHP Classes ---"
-
-mkdir -p app/Services
-cp "$EXT_DIR/app/Services/LicenseService.php" app/Services/LicenseService.php
-echo "✓ Copied LicenseService.php"
-
-cp "$EXT_DIR/app/Services/ModpackApiService.php" app/Services/ModpackApiService.php
-echo "✓ Copied ModpackApiService.php"
-
-mkdir -p app/Console/Commands
-cp "$EXT_DIR/app/Console/Commands/ValidateLicense.php" app/Console/Commands/ValidateLicense.php
-echo "✓ Copied ValidateLicense.php"
-
-cp "$EXT_DIR/app/Console/Commands/CheckModpackUpdates.php" app/Console/Commands/CheckModpackUpdates.php
-echo "✓ Copied CheckModpackUpdates.php"
-
-mkdir -p app/Http/Controllers
-cp "$EXT_DIR/app/Http/Controllers/ModpackAPIController.php" app/Http/Controllers/ModpackAPIController.php
-echo "✓ Copied ModpackAPIController.php"
-
-# ===========================================
-# 4. OVERWRITE BLUEPRINT'S AUTO-GENERATED CONTROLLER
-# ===========================================
-# Blueprint generates its own controller wrapper at this path.
-# Our admin/controller.php has LicenseService DI — overwrite the generated one.
echo ""
echo "--- Admin Controller ---"
-CTRL_DIR="app/Http/Controllers/Admin/Extensions/modpackchecker"
-if [ -d "$CTRL_DIR" ]; then
- cp "$EXT_DIR/admin/controller.php" "$CTRL_DIR/modpackcheckerExtensionController.php"
- echo "✓ Overwrote Blueprint controller with licensed version"
+BP_CONTROLLER=$(find app/Http/Controllers/Admin/Extensions/modpackchecker -name "*Controller.php" 2>/dev/null | head -n 1)
+if [ -n "$BP_CONTROLLER" ]; then
+ cp "$EXT_DIR/admin/controller.php" "$BP_CONTROLLER"
+ echo "✓ Overwrote Blueprint controller: $BP_CONTROLLER"
else
- mkdir -p "$CTRL_DIR"
- cp "$EXT_DIR/admin/controller.php" "$CTRL_DIR/modpackcheckerExtensionController.php"
- echo "✓ Created admin controller with license support"
+ echo "⚠ Blueprint controller not found — admin panel may need manual controller deploy"
fi
# ===========================================
-# 5. CLEAR CACHES
+# 4. CLEAR CACHES
# ===========================================
echo ""
echo "--- Cache Clear ---"
php artisan optimize:clear 2>/dev/null && echo "✓ Laravel caches cleared" || echo "⚠ Cache clear skipped (non-fatal)"
+# ===========================================
+# 5. TYPESCRIPT PRE-FLIGHT CHECK
+# ===========================================
+echo ""
+echo "--- TypeScript Check ---"
+if command -v yarn &>/dev/null; then
+ yarn tsc --noEmit 2>&1 | head -20 || {
+ echo "❌ TypeScript validation failed — aborting build"
+ exit 1
+ }
+ echo "✓ TypeScript check passed"
+fi
+
# ===========================================
# 6. COMPILE FRONTEND ASSETS
# ===========================================
@@ -155,9 +142,12 @@ echo ""
echo "--- Frontend Build ---"
if command -v yarn &>/dev/null; then
echo "Running yarn build:production (this may take 2-5 minutes)..."
- yarn build:production 2>&1 && echo "✓ Frontend assets compiled" || echo "⚠ Frontend build failed — dashboard badges will not render, but admin + console features work fine"
+ yarn build:production 2>&1 && echo "✓ Frontend assets compiled" || {
+ echo "⚠ Frontend build failed — dashboard badges and widget will not render"
+ echo " Admin panel + cron + license system still work fine"
+ }
else
- echo "⚠ yarn not found — skipping frontend build. Dashboard badges require manual yarn build:production"
+ echo "⚠ yarn not found — skipping frontend build"
fi
echo ""
@@ -166,6 +156,7 @@ echo "ModpackChecker build complete!"
echo "=========================================="
echo ""
echo "Next steps:"
-echo " 1. Restart: systemctl restart php8.3-fpm"
-echo " 2. Test cron: php artisan modpackchecker:check"
+echo " 1. Deploy PHP files from repo (Chronicler)"
+echo " 2. Restart: systemctl restart php8.3-fpm"
+echo " 3. Test cron: php artisan modpackchecker:check"
echo ""
diff --git a/services/modpack-version-checker/blueprint-extension/views/server/ErrorBoundary.tsx b/services/modpack-version-checker/blueprint-extension/views/server/ErrorBoundary.tsx
new file mode 100644
index 0000000..886fd40
--- /dev/null
+++ b/services/modpack-version-checker/blueprint-extension/views/server/ErrorBoundary.tsx
@@ -0,0 +1,27 @@
+import React, { Component, ErrorInfo, ReactNode } from "react";
+
+interface Props { children?: ReactNode; fallback?: ReactNode; }
+interface State { hasError: boolean; }
+
+class ModpackErrorBoundary extends Component {
+ public state: State = { hasError: false };
+
+ public static getDerivedStateFromError(_: Error): State {
+ return { hasError: true };
+ }
+
+ public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
+ console.error("ModpackChecker Component Error:", error, errorInfo);
+ }
+
+ public render() {
+ if (this.state.hasError) {
+ return this.props.fallback || (
+ Modpack module unavailable.
+ );
+ }
+ return this.props.children;
+ }
+}
+
+export default ModpackErrorBoundary;