sculpin/sculpin-theme-composer-plugin
A Composer plugin that installs and manages Sculpin theme dependencies, letting Composer pull theme packages and wire them into your Sculpin site's theme directory so theme assets and templates are handled during install and update.
composer --version (2.x expected) and the PHP version the package declares under require in its composer.json. If you are still on Composer 1 you can keep using the previous 1.0.x line, but the 2.x line is where ongoing fixes will land.composer require vendor/package:^1.0
On an existing app that already has the package pinned to an older constraint, use composer update vendor/package --with-all-dependencies — a bare composer update will churn the whole lockfile and make the diff unreviewable.php artisan vendor:publish --provider="Vendor\Package\PackageServiceProvider"
Run it with --tag=config first if you only want to see the defaults before taking on migrations/assets.composer.json (require, extra.laravel.providers, extra.laravel.aliases), the service provider's register()/boot() methods, the published config file (every knob you can turn is there), then the README's quick-start block. The provider tells you what is bound in the container; the config tells you what is safe to change.app()), run one operation against a real or fake backend, and assert the result. Doing this in a throwaway artisan command or a single feature test is the fastest way to flush out a mis-resolved config key or an unpublished migration before it hits a feature branch.Constructor injection, always. Bind the package's contract in a constructor and let Laravel resolve it. Reach for the facade only in Blade templates or one-off glue code. This keeps your own classes testable without booting the package.
public function __construct(private \Vendor\Package\Contracts\Client $client) {}
Configuration lives in config/, not in code. Publish the config file, then drive it from env() inside that file (never env() from application code — see Gotchas). This is the intended extension point for endpoints, credentials, timeouts, and queue names.
Swap implementations via a service provider. If the package ships an interface, rebind it in your AppServiceProvider for tests or for a fake/local driver:
$this->app->bind(\Vendor\Package\Contracts\Client::class, FakeClient::class);
This is more robust than mocking a concrete class and survives minor releases.
Wrap it in your own thin adapter. For anything beyond a single call site, write a one-class wrapper (App\Services\Thing) that owns the package dependency. When a future release renames a method, you have one file to change instead of forty.
Long-running work goes on the queue. Anything network-bound or slow should be pushed to a job rather than executed in a request. If the package ships its own job or command, prefer it — it will already handle retries and backoff correctly.
Testing. Use Http::fake() if the package is HTTP-based; use the package's own fake or an in-memory driver if it offers one. For Laravel package integration tests, orchestra/testbench is the standard harness. Always clean up published config in setUp/tearDown so tests don't leak env state between runs.
CI, now that Composer 2 is supported. Pin to Composer 2 explicitly and cache the right directory:
- uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
tools: composer:v2
- run: composer install --prefer-dist --no-interaction --no-progress
Commit composer.lock for applications; do not commit it for libraries. Run composer validate --strict as a cheap gate — it catches malformed constraints before they bite someone else.
Production builds. composer install --no-dev --optimize-autoloader, then php artisan config:cache && php artisan route:cache. In Docker, copy composer.json and composer.lock first, run composer install, then copy the source — otherwise every source edit invalidates the dependency layer.
1.0.3 specifically — do not mix lockfile generations. A composer.lock written by Composer 1 and one written by Composer 2 differ in plugin-api-version and churn content-hash on unrelated packages. Regenerate the lock once with Composer 2, commit it alone in its own commit, and only then bump the package. Otherwise the real change is buried in a 400-line diff.
Composer 2.2+ prompts for allow-plugins. If the package (or anything in its tree) ships a Composer plugin, you will be asked to allow it interactively — and CI will simply fail because it cannot answer. Pre-declare it:
"config": { "allow-plugins": { "vendor/plugin-name": true } }
Composer 1 plugins do not load under Composer 2. If you maintain a plugin of your own (installer, script hook), it must declare composer-plugin-api: ^2.0 and be updated to the v2 plugin API. A plugin that silently no-ops is the classic symptom.
env() outside config files returns null after config:cache. This is the single most common "works locally, broken in prod" bug in Laravel packages. Only call env() from files in config/; read everything else via config().
composer diagnose and composer why are your first debugging tools. composer why vendor/package explains who pulled a dependency in; composer show --tree shows the full resolution; composer diagnose catches bad repo config, proxy issues, and connectivity problems in one shot. composer outdated --direct keeps the tree honest.
Memory and timeouts. Composer 2 is much lighter than 1, but large trees in constrained containers still OOM. COMPOSER_MEMORY_LIMIT=-1 composer update is the escape hatch; do not bake it into CI permanently.
Platform drift. If your CI image and your local machine differ in PHP extension set, set config.platform.php in composer.json so resolution is deterministic across both. Otherwise the lock file is only valid on whichever machine generated it.
--no-scripts and --no-plugins. Useful for auditing what a package does at install time, and for reproducing a broken post-install hook. If composer install fails inside a script, these two flags tell you fast whether the problem is the package or your environment.
Extension points to prefer, in order of stability: published config → container bindings for interfaces → published migrations/assets → package events. Avoid subclassing package classes or overriding vendor code; those are the first things to break on a minor bump.
Pin sensibly. ^1.0 is right for this package; 1.0.3 exact is only justified if you have a documented reason. When 1.0.3 fixes your Composer 2 install, a caret constraint gets you the fix automatically — an exact pin means the next developer hits the same wall.
How can I help you explore Laravel packages today?