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;