salahhusa9/laravel-updater
Laravel Updater lets you update your Laravel project with a single command or click. Supports Laravel 11, 12, and 13, with an Artisan command and web interface for running updates, so deployments and version upgrades stay quick and painless.
composer require salahhusa9/laravel-updater
php artisan vendor:publish --tag="laravel-updater-config"
config/updater.php before you run anything. This single file is the package: it holds your app's current version, the Git/repository settings the updater pulls from, and the before / after pipeline arrays that define what actually happens during an update. If a key name differs from what you expect, trust the published file over any blog post — the config has evolved across v1.x → v2.x.php artisan list | grep -i update
The command is the entry point for everything (CLI, scheduler, or a button in your admin panel).Updater service class (how it sequences pipelines) → Contracts/Pipeline (what a custom pipeline must implement) → CHANGELOG.md (the repo's only in-tree documentation; the real docs live at salahhusa9.com/laravel-updater).main is the deploy branch. Run the update command once by hand, watch each pipeline step's output, and record the exact sequence. That output becomes your checklist for every later automation.Git-pull deploy behind a webhook. The intended pattern is: push to a release branch/tag → webhook hits your app → job runs the updater → pipelines run migrations, clear caches, restart workers. Wrap the call so failures surface:
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
Cache::lock('app-update', 600)->block(5, function () {
Artisan::call('update'); // use the signature you confirmed above
Log::info('Update output', ['out' => Artisan::output()]);
});
Custom pipelines are where the package earns its keep. Write small, single-purpose classes implementing the package's pipeline contract — e.g. EnableMaintenanceMode, RunMigrations, ClearConfigCache, RestartHorizon, NotifySlackOnSuccess — then register them in the config's before/after arrays. Since v1.2.4 the updater merges your before/after pipelines with the package defaults, so you add steps without having to re-declare the built-ins. Keep the defaults and append; don't rewrite the array wholesale.
Order pipelines by blast radius. Before-update: maintenance mode on, snapshot/backup, stop queue workers. After-update: composer install --no-dev, migrations, config:cache/route:cache/view:cache, asset build if you build on-server, restart workers, maintenance mode off. Anything that writes config should come after anything that reads config.
Expose a version display, not just a command. Store the deployed version in config (or an env var) and surface it in your admin footer/health page. It turns "did the update land?" into a one-glance check and gives you a cheap cache-buster key for the frontend.
Treat pipelines as idempotent steps. Each one will eventually be re-run after a partial failure, so migrate over migrate:fresh, cache:clear over manual deletes, and no "create this record" logic that can't be repeated safely.
Test pipelines locally. You can't safely exercise the git pull on your laptop, but you can instantiate and call your pipeline classes directly in a feature test, and you can run the full command against a disposable staging checkout. Do both before wiring the webhook.
PATH. Git, composer, php, and (if your pipelines use them) npm/node must all resolve without a login shell. Since v1.2.1 the package no longer uses Symfony's executable finder, so a binary that only exists in a user's .bashrc will fail under www-data/forge/cron. Verify with sudo -u www-data which git composer.max_execution_time or PHP memory limits, and a timeout mid-pipeline leaves the app in a half-updated state. There is no transactional rollback — mitigate with maintenance mode around the whole run and a manual verification step afterward.config:cache is rebuilt. Never place config:cache in the before list, and remember the running PHP process has already loaded the old config for the current request.RuntimeExceptions (v1.0.1). Catch them explicitly around Artisan::call, log Artisan::output() for context, and return a non-zero exit code from any wrapping command so CI/schedulers notice.composer update across a major version is an infrastructure change, not a dependency bump — pin with a caret and review the CHANGELOG.md before crossing majors.release/* branch or tag over main. Updating from a moving branch means the "same" command produces different results on every run. Point the updater at something immutable per deploy and you keep the ability to say what's actually running.How can I help you explore Laravel packages today?