1 · Dependencies & assets
Objective — get the frozen vendor snapshot ready to boot: authenticate Composer to dodge the rate limit, install PHP and JS dependencies without clobbering vendor patches, audit .env.example for fail-loud placeholders, then build and commit frontend assets.
Background
Section titled “Background”The first boot starts with dependencies — but with three traps: Composer’s anonymous rate limit, the vendor’s patched vendor/ tree, and a .env.example full of real-looking placeholders that fail silently instead of loudly.
1. Authenticate Composer first
Section titled “1. Authenticate Composer first”Give Composer a GitHub token before installing — anonymous pulls hit a 60-request/hour limit that fails large CodeCanyon installs partway through.
-
Hand Composer a GitHub token so it never hits the anonymous limit.
Terminal window composer config -g github-oauth.github.com "$(gh auth token)"# Expected: no output (the token is written to Composer's global auth.json)- ✅ The token is set without ever printing on screen.
-
Confirm it landed with an exit-code check — never by reading the file.
Terminal window composer config -g github-oauth.github.com >/dev/null 2>&1 && echo "✅ token set"# Expected: ✅ token set- ✅
✅ token setprints.
- ✅
2. Install PHP dependencies
Section titled “2. Install PHP dependencies”Pull the locked PHP packages — but gate on vendor patches first if the ZIP shipped a pre-built vendor/.
-
If a shipped
vendor/exists, diff it before any install (skip ifvendor/came from a prior lock-file install)./tech-stack/laravel/codecanyon/build/playbooks/setup-new/02-code-repo/03-extract-and-snapshot/ if [ -d vendor ] && [ -f composer.lock ]; thenecho "Shipped vendor/ detected — run the P2 extract diff or P3 page-6 procedure before composer install"# or page 6: /tech-stack/laravel/codecanyon/build/playbooks/setup-new/03-local-dev/06-commit-and-secure/fi- ✅ Either no shipped
vendor/exists, or author patches are documented in_CUSTOMIZATIONS.mdbefore the install below.
- ✅ Either no shipped
-
Install from the lock file.
Terminal window composer install# Expected: "Generating optimized autoload files" with no errors- ✅
vendor/is populated and Composer exits cleanly.
- ✅
-
If Composer dies with
Allowed memory size … exhausted, lift the limit for the run.Terminal window COMPOSER_MEMORY_LIMIT=-1 composer install# Expected: the install completes without the memory error- ✅ Install completes under the lifted memory limit (if needed).
-
Capture the required PHP extensions — the list the server must enable in Phase 4/9.
Terminal window composer check-platform-reqs 2>/dev/null | grep -E "^ext-" | awk '{print $1}' | sort# Expected: a sorted list of ext-* requirements — save it for server setup- ✅ The
ext-*requirements are recorded for staging/production provisioning.
- ✅ The
3. Audit .env.example for fail-loud placeholders
Section titled “3. Audit .env.example for fail-loud placeholders”A .env.example that ships with plausible-but-fake values (APP_KEY=base64:abc…, DB_DATABASE=laravel) is dangerous: a teammate copies it, the app boots against the wrong database, and the failure surfaces hours later as corrupt data. Placeholders must fail loud — empty or obviously-fake — so a missing value crashes immediately.
-
Surface the risky keys.
Terminal window grep -E "^(APP_KEY|DB_DATABASE|DB_USERNAME|DB_PASSWORD|MAIL_|STRIPE_)" .env.example || true# Expected: a line per key — flag any with a real-looking value- ✅ The keys carrying plausible-but-fake values are visible.
-
Replace any real-looking value with a loud placeholder.
Key Loud placeholder APP_KEY=leave blank — key:generatefills itDB_DATABASE="REPLACE_ME_DB_NAME"DB_USERNAME="REPLACE_ME_DB_USER"DB_PASSWORD="REPLACE_ME_DB_PASS"STRIPE_KEY=/STRIPE_SECRET=leave blank - ✅ Every placeholder is blank or
REPLACE_ME_*— nothing that could silently boot the wrong way.
- ✅ Every placeholder is blank or
4. Build & commit frontend assets
Section titled “4. Build & commit frontend assets”Frontend assets are built on your machine and committed, so the production server never needs Node.js (the build-locally strategy). Install JS deps, then build.
-
Install JS deps and build.
Terminal window npm installnpm run build # outputs to public/build/# Expected: a clean build with files written under public/build/- ✅ The build finishes and
public/build/is populated.
- ✅ The build finishes and
-
Force-track the build output — it’s often gitignored by default, but the server relies on it.
Terminal window git add -f public/build/ls public/build/manifest.json # must exist# Expected: the manifest.json path prints- ✅
public/build/manifest.jsonexists and is staged in git.
- ✅
5. Run a security & dev-dependency audit
Section titled “5. Run a security & dev-dependency audit”Capture a vulnerability baseline now — it’s the reference point for Phase 9 hardening. Don’t chase every advisory mid-setup; record the counts and move on unless something is critical in a runtime path.
-
Record the audit baseline.
Terminal window npm audit --omit=dev || truecomposer audit || true# Expected: advisory counts printed — note them, don't chase them all now- ✅ A baseline of npm + Composer advisories is recorded.
-
Confirm
composer.jsondoesn’t autoload dev files in production — a"files"array underautoload-devbreaks the production boot (composer install --no-devskips it). The full pre-deploy check lives on page 5.- ✅ No production-required file is hiding under
autoload-dev.
- ✅ No production-required file is hiding under
6. Detect Stripe billing architecture
Section titled “6. Detect Stripe billing architecture”CodeCanyon Laravel apps ship wildly different Stripe wiring — canonical Laravel Cashier subscriptions, ad-hoc invoice-per-cycle code, or both architectures in the same codebase (a “dual-subsystem” pattern found in several Froiden/ResiDoro-family apps). Knowing which pattern is live gates every Stripe decision in Phase 6 (Task 62). Run the greps now, while vendor/ is fresh, and lock the answer into _CUSTOMIZATIONS.md.
-
Check for canonical Laravel Cashier use (subscription model via the
Billabletrait).Terminal window grep -rn "use.*Billable\|->newSubscription\|->subscriptions()\|Cashier::" \app/ --include="*.php" | head -20# Expected: lines containing Billable trait, newSubscription(), or Cashier:: facade — or no output- ✅ If output exists: canonical Cashier is present (at least partially). Note the files.
- ✅ If no output: no Cashier subscriptions — the app uses ad-hoc invoices only.
-
Check for ad-hoc / custom Stripe webhook controller (invoice-per-cycle telltale).
Terminal window grep -rn "StripeWebhookController\|stripe.*invoice\|invoice.*stripe" \app/ --include="*.php" -i | head -20# Expected: custom webhook handler or invoice references — or no output- ✅ A custom
StripeWebhookControllerconfirms ad-hoc billing is present.
- ✅ A custom
-
Check for dual-subsystem billing (SuperAdmin operator billing vs tenant-level billing — two separate Stripe flows).
Terminal window grep -rn "SuperAdmin\|superadmin\|super_admin" routes/ app/ \--include="*.php" | grep -i "stripe\|subscription\|billing" | head -20# Expected: SuperAdmin-namespaced billing routes signal the dual-subsystem pattern- ✅ If SuperAdmin-scoped Stripe routes appear alongside non-SuperAdmin Cashier use: dual-subsystem confirmed.
-
Record the findings in
_CUSTOMIZATIONS.md.Use the decision table below to classify what you found, then replace the
PROVISIONALblock (written in Phase 1B) with aVERIFIEDblock:What greps showed Architecture Stripe account strategy Cashier traits throughout, no custom webhook controller Single-tenant Cashier One Stripe account; restricted key rk_test_app_*safeCustom StripeWebhookController, no Cashier traitsAd-hoc invoices only Single Stripe account; standard key; webhook endpoint required Both Cashier traits AND custom controller Dual-subsystem Prefer a single Stripe account (Option C); both subsystems share one account SuperAdmin-scoped + non-SuperAdmin Cashier Dual-subsystem (operator + tenant) Prefer Option C; scope rk_test_app_*carefullyNo Stripe references found No Stripe Skip; note no_stripe: true# Run from the project root — updates _CUSTOMIZATIONS.md in-placeimport pathlib, datetimetoday = datetime.date.today().isoformat()f = pathlib.Path("_CUSTOMIZATIONS.md")if not f.exists():print("⚠️ _CUSTOMIZATIONS.md missing — create it first (Phase 1/2), then re-run.")raise SystemExit(1)text = f.read_text()old = "## Stripe account strategy — PROVISIONAL"new = f"## Stripe account strategy — VERIFIED {today}"if old in text:text = text.replace(old, new)# Append verification note after the header linenote = (f"\n\n> **Verified {today}** via Phase-3 architecture detection greps.\n""> Grep results: <!-- paste summary here -->\n""> Architecture class: <!-- Cashier / Ad-hoc / Dual-subsystem / None -->\n")text = text.replace(new, new + note, 1)f.write_text(text)print(f"✅ Updated: PROVISIONAL → VERIFIED {today}")else:print("⚠️ No PROVISIONAL block found — add the Stripe strategy block manually.")# Expected: "✅ Updated: PROVISIONAL → VERIFIED <date>"- ✅
_CUSTOMIZATIONS.mdnow has aVERIFIEDStripe strategy block with the grep summary and architecture class filled in.
- ✅
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🤖 Composer authenticated — no rate-limit failures.
- 🤖 Dependencies installed —
composer installandnpm installsucceeded. - 🤖 Vendor patches noted — any shipped-
vendor/patches flagged for the page-6 comparison. - 🤖 Placeholders loud —
.env.examplevalues are blank orREPLACE_ME_*. - 🤖 Build force-tracked —
public/build/manifest.jsonbuilt andgit add -f’d. - 🤖 Audit baseline recorded —
npm audit/composer auditcounts captured. - 🤖 Stripe architecture verified —
_CUSTOMIZATIONS.mdupdated with VERIFIED strategy, or skipped andno_stripe: truenoted.