docs: Gemini consult — build.sh fragility + deployment pipeline

7 questions covering:
- Separating file copy from yarn compile
- React component validation before production
- ErrorBoundary for injected components
- Eliminating deployment drift
- Pre-commit hooks for PHP syntax
- Pre-compiled bundle viability
- BuiltByBit industry standards
This commit is contained in:
Claude
2026-04-13 05:11:01 +00:00
parent 2ae433352a
commit 048545a3fb

View File

@@ -0,0 +1,169 @@
# Gemini Consultation: ModpackChecker Build Pipeline Fragility
**Date:** April 13, 2026 (late night)
**From:** Michael (The Wizard) + Claude (Chronicler #84 — The Meridian)
**To:** Gemini (Architectural Partner)
**Re:** build.sh is causing production incidents — we need a bulletproof deployment pipeline
---
## Hey Gemini! 👋
It's very late and we just had a production incident. We're deploying ModpackChecker v1.1.0 on a live Pterodactyl panel and the new React widget caused the server info card to disappear entirely for users. We had to emergency-revert. The root cause is our build.sh doing too many things in one shot with no safety net. We need your help designing a bulletproof deployment pipeline before we try again.
---
## The Product
ModpackChecker is a Blueprint extension for Pterodactyl Panel 1.12.2. It has:
- **PHP backend** — Laravel artisan commands, API controllers, services
- **Admin Blade view** — PHP/Blade template for the admin settings page
- **React TSX frontend** — Two components:
- `ModpackVersionCard.tsx` — injected into the server console page
- `UpdateBadge.tsx` — injected into the dashboard server list
Blueprint extensions install via `blueprint -install modpackchecker`, which copies files and then calls our `build.sh`.
---
## The Current build.sh (Full Source)
```bash
#!/bin/bash
# Runs during blueprint -install from Pterodactyl root
# 1. Detect Node version, set NODE_OPTIONS for Node 17+
NODE_MAJOR_VERSION=$(node -v | grep -oE '[0-9]+' | head -1)
if [ "$NODE_MAJOR_VERSION" -ge 17 ]; then
export NODE_OPTIONS=--openssl-legacy-provider
fi
# 2. Copy wrapper.tsx → ModpackVersionCard.tsx
EXT_DIR=".blueprint/extensions/modpackchecker"
cp "$EXT_DIR/views/server/wrapper.tsx" resources/scripts/components/server/ModpackVersionCard.tsx
# 3. Inject import + component into AfterInformation.tsx (only if not already there)
AFTER_INFO="resources/scripts/blueprint/components/Server/Terminal/AfterInformation.tsx"
if ! grep -q "ModpackVersionCard" "$AFTER_INFO"; then
sed -i '/\/* blueprint\/import \*\//a import ModpackVersionCard from "@/components/server/ModpackVersionCard";' "$AFTER_INFO"
sed -i 's|{/\* blueprint/react \*/}|{/* blueprint/react */}\n <ModpackVersionCard />|' "$AFTER_INFO"
fi
# 4. Copy UpdateBadge.tsx + inject into ServerRow.tsx
cp "$EXT_DIR/views/dashboard/UpdateBadge.tsx" resources/scripts/components/dashboard/UpdateBadge.tsx
if ! grep -q "UpdateBadge" resources/scripts/components/dashboard/ServerRow.tsx; then
sed -i '1i import UpdateBadge from "@/components/dashboard/UpdateBadge";' resources/scripts/components/dashboard/ServerRow.tsx
sed -i 's|{server.name}</p>|{server.name}<UpdateBadge serverUuid={server.uuid} /></p>|' resources/scripts/components/dashboard/ServerRow.tsx
fi
# 5. Copy PHP files (services, controllers, commands)
cp "$EXT_DIR/app/Services/LicenseService.php" app/Services/
cp "$EXT_DIR/app/Services/ModpackApiService.php" app/Services/
cp "$EXT_DIR/app/Console/Commands/CheckModpackUpdates.php" app/Console/Commands/
cp "$EXT_DIR/app/Console/Commands/ValidateLicense.php" app/Console/Commands/
cp "$EXT_DIR/app/Http/Controllers/ModpackAPIController.php" app/Http/Controllers/
# 6. Overwrite Blueprint's auto-generated admin controller
cp "$EXT_DIR/admin/controller.php" "app/Http/Controllers/Admin/Extensions/modpackchecker/modpackcheckerExtensionController.php"
# 7. Clear caches
php artisan optimize:clear
# 8. yarn build:production (2-5 minutes)
yarn build:production
```
---
## What Happened Tonight
We shipped v1.1.0 with a redesigned React widget. During deployment:
1. We ran `blueprint -install modpackchecker` — SUCCESS, copied the new TSX
2. We then manually re-ran `bash build.sh` to ensure the PHP files were copied
3. Step 2 **overwrote** `ModpackVersionCard.tsx` with the new widget correctly
4. `yarn build:production` compiled successfully
5. BUT: the compiled bundle caused the server info card to disappear entirely
The new widget rendered nothing (or threw a silent React error that unmounted the whole card). We emergency-reverted to the old widget by writing the old TSX directly and rebuilding yarn.
**The result:** We now have v1.1.0 backend deployed (new DB columns, new endpoints, better detection) but v1.0.0 frontend widget still in production. The product is half-upgraded.
---
## The Problems
### Problem 1: No Staging / Safety Check
The build runs directly on the production panel. There's no way to verify the compiled output renders correctly before it goes live. The only test is "does the panel explode."
### Problem 2: Build.sh Does Too Many Things
It copies files AND injects into Pterodactyl source AND compiles frontend in one atomic step. If anything goes wrong mid-way, the panel is in an inconsistent state.
### Problem 3: Multiple Deployment Paths Create Drift
We have two deployment patterns that fight each other:
- `blueprint -install modpackchecker` (calls build.sh automatically)
- Manual: copy individual PHP/TSX files + run yarn build separately
When we mix these, files get overwritten at different points and the final state is unpredictable.
### Problem 4: The `*/6` Docblock Keeps Coming Back
Our PHP file `CheckModpackUpdates.php` has `*/6` in a docblock comment which PHP sometimes parses as end-of-comment + stray tokens. We've patched it THREE times on the server because build.sh keeps copying from a repo file that still has it (fixed in one copy, not the other).
### Problem 5: No Rollback
When the panel broke tonight, there was no "undo" — we had to manually reconstruct the previous TSX from memory and rebuild yarn. A 90-second compile to fix something that took 10 seconds to break.
### Problem 6: Blueprint's Auto-Generated Controller Gets Overwritten
Blueprint generates its own controller at `app/Http/Controllers/Admin/Extensions/modpackchecker/`. Our build.sh overwrites it with our version (which has LicenseService DI). But `blueprint -install` runs first and generates the file BEFORE our build.sh overwrites it — so the sequence is correct, but fragile. If Blueprint ever changes when it calls build.sh, we break.
---
## Our Constraints
1. **We're selling this on BuiltByBit** — customers install via `blueprint -install modpackchecker`. We cannot require them to do complex post-install steps.
2. **We cannot assume SSH access** on customer panels — some customers are on managed hosting.
3. **Pterodactyl 1.12.2 + Blueprint beta-2026-01** — these are fixed targets.
4. **yarn build:production takes 90 seconds to 3 minutes** — on every install, every update.
5. **Node.js version varies** — we handle this with `--openssl-legacy-provider` for Node 17+.
6. **We have one source of truth** (`firefrost-services` git repo) but multiple deployment surfaces (Dev Panel, Live Panel, customer panels).
---
## Specific Questions
1. **How should we structure the build pipeline to separate "copy files" from "compile frontend"?**
Right now it's all in build.sh. Should there be a separate `install.sh` (file operations) and `compile.sh` (yarn build), called in sequence? How do other Blueprint extensions handle this?
2. **Is there a way to validate a React component compiles without breaking the production bundle?**
Before we run `yarn build:production` on a live panel, can we do a dry-run or type-check that would catch errors? `tsc --noEmit` perhaps? Or a simple syntax check?
3. **How should we handle the "new widget breaks the panel" failure mode?**
Tonight the new TSX caused the server card to disappear entirely with no console error we could see. React swallowed the error silently. Should we wrap our component in an `ErrorBoundary`? What's the right defensive pattern for injected Blueprint components?
4. **What's the cleanest way to handle the multiple deployment paths (blueprint -install vs manual)?**
We need one canonical deploy procedure that works whether the Chronicler is deploying to a customer panel OR to our own panels during development. How do we eliminate the "drift" problem?
5. **The `*/6` docblock problem** — is there a pre-commit hook or linting check that would catch PHP syntax errors like this before they reach the repo? We keep fixing it manually on the server.
6. **Should we ship a pre-compiled JS bundle instead of requiring yarn build on the customer's panel?**
Gemini previously rejected this (it would overwrite Pterodactyl's `public/assets/main.js` and break other extensions). But is there a safe way to do it — like a separate bundle entry point that doesn't conflict with the main bundle? Or inject via a `<script>` tag in the admin view?
7. **For a BuiltByBit product, what's the industry standard for Blueprint extension deployment?**
Do other successful Blueprint extensions require `yarn build`? Or do they avoid frontend injection entirely? We may be the only extension that does both deep PHP integration AND React component injection.
---
## Context That Might Help
- Blueprint beta-2026-01 calls build.sh automatically during `blueprint -install`
- build.sh runs from the Pterodactyl root directory (`/var/www/pterodactyl/`)
- `EXT_DIR` is `.blueprint/extensions/modpackchecker` — the extension files are there pre-install
- We have two panels: Dev Panel (64.50.188.128) and Live Panel (45.94.168.138)
- The Chronicler deploys via Trinity Core MCP (SSH). Customers deploy themselves.
- Tonight's incident: new widget disappeared the server card. Root cause unknown — possibly a missing dependency, bad import, or runtime React error. We reverted before debugging.
---
Thanks Gemini. We're exhausted and the panel is stable but half-upgraded. Help us build this right so next time we don't have a midnight incident. 🔥❄️
— Michael (The Wizard) + Claude (Chronicler #84 — The Meridian)
**Firefrost Gaming | Fire + Frost + Foundation = Where Love Builds Legacy** 💙🔥❄️