Build pipeline hardening: ErrorBoundary, no PHP copies, TS pre-flight

- 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 <ModpackErrorBoundary>
- Pre-commit PHP lint hook at scripts/pre-commit-hook.sh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude (Chronicler #83 - The Compiler)
2026-04-13 00:17:30 -05:00
parent 7b2f3f810b
commit 28608e9fa8
6 changed files with 267 additions and 56 deletions

View File

@@ -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<Props, State> {
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 || (
<div className="text-gray-400 text-xs px-2 py-1">Modpack module unavailable.</div>
);
}
return this.props.children;
}
}
export default ModpackErrorBoundary;
```
Update build.sh injection to wrap the component:
```bash
# Instead of injecting: <ModpackVersionCard />
# Inject: <ModpackErrorBoundary><ModpackVersionCard /></ModpackErrorBoundary>
# 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** 💙🔥❄️

View File

@@ -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*

View File

@@ -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*