Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Core Composer Scaffold Laravel Package

drupal/core-composer-scaffold

Composer plugin used by Drupal core to scaffold essential project files such as .htaccess, robots.txt, and settings into the web root during install and update, with configurable file mappings and overwrite behavior.

Deep Wiki
Context7

Technical Evaluation

What the package actually is. drupal/core-composer-scaffold is a Composer plugin (type: composer-plugin), not a Laravel package and not a library with a PHP API. It subscribes to Composer's post-create-project-cmd, post-autoload-dump, post-install-cmd and post-update-cmd events and copies files out of dependency packages into a configured project root ("scaffold") location. Behavior is driven entirely by declarative config in the root composer.json under extra.drupal-scaffold: allowed-packages, file-mapping, locations (web-root), gitignore, omit-default-file-mappings.

Architecture fit: adjacent, not native. Laravel has no first-class equivalent. New projects are handled by composer create-project laravel/laravel (a skeleton), and post-creation boilerplate is normally either committed by hand, generated by an Artisan command we write ourselves, or spread across tooling (laravel/pint, PHPStan, Docker, CI). The plugin's real value to us is as a generic, config-driven file distributor: publish phpstan.neon, pint.json, .editorconfig, docker-compose.yml, .github/workflows/*, base .env.example, Makefile from one internal acme/laravel-scaffold package into N services, with per-project overrides via file-mapping modes (skip, append, prepend, overwrite). That is a genuine, if narrow, fit — it addresses config drift across a fleet of Laravel repos, which is a recurring TPM-visible problem.

Alternatives and why this one is still on the table:

Option Trade-off
Custom Artisan command in a shared package Full control, no GPL, no plugin code execution; but requires writing ~300–500 LOC of idempotent copy/conflict logic and installing the package first (chicken-and-egg)
Custom composer create-project skeleton Best for greenfield; useless for the 20 existing services we actually need to standardize
Manual PRs / repo template sync No new dependency; drift returns within two quarters
drupal/core-composer-scaffold Battle-tested at very large scale, zero code we own; but GPL-2.0, Drupal-centric docs, plugin executes at install time

Integration feasibility: yes, with conditions. Requires Composer 2.x and an explicit config.allow-plugins entry (drupal/core-composer-scaffold: true) — Composer 2.2+ blocks unlisted plugins by default, which is a deliberate hardening feature we would be opting out of for this one package. Setting locations.web-root to . maps files into the Laravel project root rather than web/, which is the only structural change needed. PHP 8.x and Laravel 10/11/12 impose no constraints.

Technical risk.

  • License (highest-severity item). GPL-2.0. For internal tooling and non-distributed services this is normally acceptable, but it will fail an automated license-compliance gate and requires a documented exception. If any Laravel codebase here is distributed to customers (on-prem, appliance, SDK), GPL-2.0 in the install-time toolchain needs legal sign-off, not a TPM decision.
  • Install-time code execution. A Composer plugin runs arbitrary PHP in CI and developer machines with the developer's privileges. This is a supply-chain surface that security review will want to see closed: pinned version, composer.lock committed, provenance verified.
  • Provenance ambiguity. Repository: unknown plus 54 stars and a 0.63 score is a reporting artifact, not necessarily a quality signal — the canonical source is Drupal.org's git with a GitHub mirror, and the package ships as a transitive dependency of effectively every Drupal 9/10/11 site, so real-world install volume is enormous. Still, we must confirm the exact origin and checksum before pinning; a low-signal registry entry is not something to pull into 20 repos on trust.
  • Overwrite semantics. The plugin's conflict handling is mode-based and protective by default, but its "don't clobber locally modified files" behavior is the single most important thing to validate in a spike — files like .env.example, phpunit.xml, and CI workflows are routinely edited per project, and surprising overwrites would be a trust-destroying incident.
  • Event ordering. It hooks post-autoload-dump, the same event Laravel uses for @php artisan package:discover. Ordering between the two is not something we control, so nothing scaffolded should be required during that step.

Key questions to resolve before committing.

  1. Is any Laravel codebase in scope distributed outside the company? (License gate.)
  2. Does the canonical composer.json for this package match what the mirror shows, and what is the pinned version + hash we are approving?
  3. Does the plugin refuse to overwrite locally modified files, and can we observe and log those skips in CI?
  4. Is a --no-plugins hardened production build in use anywhere? If so, the plugin is a no-op in exactly the environment where correctness matters most.
  5. Do we accept ownership of an internal acme/laravel-scaffold package, or is this a solution looking for a problem? Quantify current config drift first.

Integration Approach

Stack fit. Composer 2.2+, PHP 8.1+, Laravel 10–12. No framework coupling, no service provider, no runtime footprint — the plugin is inert outside Composer commands. That is the main architectural virtue: it cannot break request handling, queue workers, or Octane.

Configuration shape. Root composer.json per service:

"config": { "allow-plugins": { "drupal/core-composer-scaffold": true } },
"require-dev": { "acme/laravel-scaffold": "^1.0" },
"extra": {
  "drupal-scaffold": {
    "locations": { "web-root": "." },
    "allowed-packages": ["acme/laravel-scaffold"],
    "gitignore": false,
    "file-mapping": {
      "[project-root]/phpstan.neon": { "mode": "skip" },
      "[project-root]/.editorconfig": { "mode": "overwrite" },
      "[project-root]/.github/workflows/ci.yml": { "mode": "skip" }
    }
  }
}

skip for files teams legitimately customize; overwrite only for files that must never diverge. gitignore: false unless we want the plugin appending managed blocks to .gitignore.

Migration path. Greenfield-first, brownfield by exception.

  1. Spike (1 week, one engineer). Two throwaway Laravel repos, one greenfield and one representative legacy service. Verify: conflict behavior on locally modified files, interaction with package:discover, behavior under composer install --no-dev, and behavior when allow-plugins is absent (expect a hard failure we can tolerate). Output: a decision memo, not code.
  2. Governance (parallel, 1–2 weeks elapsed). License exception, security review of the pinned version, provenance confirmation. This is the long pole and is not engineering time — start it day one.
  3. Pilot (2–3 weeks). Build acme/laravel-scaffold with 5–8 genuinely shared files only. Apply to 2–3 willing services. Measure: files still diverging after adoption, CI failures attributable to the plugin, developer complaints.
  4. Rollout (4–6 weeks). Wave by team, not by repo count. Cap the shared file set; every addition needs a named owner.

Compatibility and sequencing cautions. Do the allow-plugins change and the require-dev addition in the same PR — an allow-plugins: true entry with no installed plugin is harmless, but a plugin present without the allowance fails the build with a confusing message. Land the internal scaffold package and publish a first tag before touching consumer repos. Keep the plugin in require-dev; production deploys should not need scaffolding.

Honest sequencing alternative. Run steps 1–2 in parallel with a one-week evaluation of the ~400-LOC custom-Artisan-command option. If the internal scaffold package is going to hold fewer than ~8 files and fewer than ~10 repos, the custom command wins on license, control, and readability. The Drupal plugin only pays off at fleet scale.

Operational Impact

Maintenance. Upstream tracks Drupal core's release cadence (minors plus security releases), which means a steady but low-volume stream of patch and minor updates. Version majors align to Drupal majors, so pinning to a caret range on a single major is stable. Our own maintenance burden is the internal scaffold package, not the plugin — but that package is now a shared dependency for every Laravel service, which converts a previously invisible concern (config drift) into a Tier-2 platform responsibility with an on-call implication we must staff explicitly.

Support. No Laravel-community support path. The issue queue is Drupal's and the documentation assumes web/ roots and drupal/core defaults. Expect the first non-obvious failure to be diagnosed internally from source. Budget one engineer as designated owner; without that, adoption stalls the first time someone hits a confusing skip.

Scaling. Runtime cost is zero. Install cost is milliseconds. The scaling constraint is organizational: every consuming repo carries a composer.json block that can drift, so the config itself becomes an artifact needing standardization — mitigate by generating the block from a template or by keeping it identical verbatim across repos and diffing it in CI.

Failure modes and detection.

Failure mode Blast radius Detection
Plugin blocked by missing allow-plugins Build fails closed (good) CI, immediate
GPL-2.0 flag in license gate Blocks all builds in that pipeline License scanner; add explicit exception or it will resurface every audit
Silent skip of a locally modified file Drift persists with false confidence Needs explicit CI assertion that key files match the source hash
Overwrite of a customized file Lost work, developer trust incident Spike must confirm the protective default; back up before first run in brownfield
--no-plugins in hardened deploy path Scaffolding absent in prod; usually harmless, occasionally not Audit deploy scripts before rollout
Upstream major bump changes mapping semantics Fleet-wide breakage on composer update Dependabot/Renovate with the internal package grouped; never auto-merge majors
Stale pinned version accumulates CVEs Toolchain exposure Standard dependency update cadence

Ramp-up. Technical ramp-up is genuinely low — one config block, half a day for a competent Laravel developer. Non-technical ramp-up is the real cost: license exception, security review, and provenance verification will consume more calendar time than the engineering. Plan for 3–5 weeks from decision to first production service, of which perhaps 3 days is code.

Verdict. The package is technically sound and proven at a scale Laravel shops rarely encounter, and the low star count is a registry artifact rather than a quality signal. But the fit is opportunistic — Laravel has no need for the Drupal scaffold model — and the costs (GPL-2.0, install-time code execution, provenance verification, platform ownership) are all incurred up front, while the benefit is contingent on having a real fleet-wide config-drift problem. Recommend: authorize the one-week spike and the governance review in parallel, and hold the rollout decision until both close. If the answer to "how many Laravel repos need to share files" is under ten, the plugin is the wrong tool and a thin internal Artisan command is the better product decision.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log