roadrunner-php/version-checker
Checks the installed RoadRunner version against required constraints. Provides greaterThan, lessThan, and equal methods, throwing UnsupportedVersionException or RoadrunnerNotInstalledException on mismatch. Requires PHP 8.0+.
Installation (package renamed in v1.4.0; v1.4.1 is a patch):
composer require roadrunner/version-checker
The package was renamed from roadrunner-php/version-checker to roadrunner/version-checker in v1.4.0. If you're adding it for local-only checks:
"require-dev": {
"roadrunner/version-checker": "^1.4"
}
If you already have the old package installed, run composer remove roadrunner-php/version-checker before requiring the new name — otherwise you can end up with two copies of the same classes in the autoloader.
Upgrading 1.4.0 → 1.4.1 is a straight composer update roadrunner/version-checker; it's a bug fix, not an API change.
Where to look first:
composer.json → confirm the vendor is now roadrunner/ and the constraint is ^1.4 (1.4.1 needs no constraint change).composer.lock / composer show roadrunner/roadrunner (new in v1.4.1) — the installed version is now detected from the roadrunner/roadrunner package. Confirm it's actually in your dependency tree before trusting getInstalledVersion().roadrunner-php/version-checker even though the Packagist name is roadrunner/version-checker; don't let that mismatch convince you the rename didn't happen.vendor/roadrunner/version-checker/src to confirm the namespace is still RoadRunner\VersionChecker (the vendor rename does not automatically imply a namespace change, but verify before doing a mass find-and-replace).Basic Usage:
use RoadRunner\VersionChecker\VersionChecker;
$checker = new VersionChecker();
$latestVersion = $checker->getLatestVersion(); // e.g. "v1.0.0"
$installedVersion = $checker->getInstalledVersion(); // resolved from roadrunner/roadrunner (v1.4.1)
$isLatest = $checker->isLatest();
First Use Case:
if (!$checker->isLatest()) {
echo "⚠️ Outdated RoadRunner version. Latest: {$checker->getLatestVersion()}";
}
Warn from a service provider (non-blocking, log-only):
public function boot()
{
$checker = new VersionChecker();
if (!$checker->isLatest()) {
Log::warning('RoadRunner version outdated', [
'installed' => $checker->getInstalledVersion(),
'latest' => $checker->getLatestVersion(),
]);
}
}
Keep this out of the hot path — see the caching tip below. Log both versions: as of v1.4.1 the installed side is derived from roadrunner/roadrunner, and a null/empty installed version is itself a useful signal (see Gotchas).
Artisan command as the team-facing entry point — far more discoverable than a boot-time log line:
php artisan rr:check-version
protected function handle()
{
$checker = new VersionChecker();
$latest = $checker->getLatestVersion();
$this->info("Latest RoadRunner version: {$latest}");
$this->line('Installed: ' . ($checker->getInstalledVersion() ?: 'unknown'));
return $checker->isLatest()
? $this->info('Up to date.')
: $this->warn('Update recommended!');
}
Scheduled check with notification:
$schedule->call(function () {
$checker = new VersionChecker();
if (! $checker->isLatest()) {
Notification::route('slack', config('services.slack.webhook'))
->notify(new RoadRunnerOutdated(
$checker->getInstalledVersion(),
$checker->getLatestVersion()
));
}
})->daily()->environments(['staging', 'production']);
Custom comparison logic — if you only care about minor/major drift:
// Illustrative: check the exact method name against the v1.4 README,
// since the API is not contractually stable across 1.x.
$checker->setVersionComparator(
fn ($installed, $latest) => version_compare($installed, $latest, '>=')
);
Explicit installed version for non-Composer runtimes — if your worker image gets RoadRunner from a tarball/curl installer rather than from Composer, roadrunner/roadrunner won't be present and detection will come up empty. Feed the version you already know from your image build:
$checker = new VersionChecker();
// e.g. from an env var baked into the image at build time
$checker->setInstalledVersion(env('RR_VERSION', 'v2025.1.0'));
rr get-version gives you the runtime version; since v1.4.1 the checker reads the installed version from the roadrunner/roadrunner Composer package. Comparing all three catches the case where your lockfile, your go/container image, and your vendor/ have drifted apart.$latest = Cache::remember('rr_latest_version', now()->addHours(6), fn () =>
(new VersionChecker())->getLatestVersion()
);
Only cache the remote half; the installed version comes from local Composer metadata and is cheap (and can change on deploy).^1.4 in composer.json and let the package resolve; 1.3 → 1.4 was a rename, not an API break, and 1.4.1 is a bug fix — a caret constraint carries you across both. Review the changelog on each minor, since 1.x is still moving.require-dev keeps it out of your production image.Package rename (v1.4.0) — still the main migration risk:
roadrunner-php/version-checker → roadrunner/version-checker.composer.json, any composer require lines in Dockerfiles/CI, and any docs or scripts that reference the old name."roadrunner-php/version-checker": "^1.0" will simply stop receiving v1.4+ updates — it won't error, which makes this a silent staleness bug.RoadRunner\VersionChecker\*, verify the namespace after upgrading with a quick grep -r "namespace" vendor/roadrunner/version-checker/src.roadrunner-php/version-checker (that's where the v1.4.1 changelog lives). Old-looking URLs are not evidence you're on the old package — check composer show.Installed-version detection now depends on roadrunner/roadrunner (v1.4.1):
getInstalledVersion() returns a real value rather than empty/null.roadrunner/roadrunner to require/require-dev, or set the version explicitly (see Implementation Patterns #5).Network dependency: getLatestVersion() reaches out to a remote API. Always guard it:
try {
$latest = $checker->getLatestVersion();
} catch (\Throwable $e) {
Log::notice('RoadRunner version check skipped: ' . $e->getMessage());
}
Never let a version check fail a deploy. Treat it as advisory.
Version format assumptions: the comparison presumes semver-ish tags (v1.2.3). Pre-release tags (-rc1, -dev), RoadRunner's v2025.x calendar-style tags, and custom schemes can produce surprising results — sanity-check with a fixture rather than trusting it blind.
No built-in caching: repeated calls hit the network each time. One scheduled job and one command per machine is fine; putting it in boot() on every request is not.
Documentation drift from the v1.4.0 refresh: the README examples were rewritten, so snippets copied from blog posts or older issues may reference methods that no longer exist — and 1.4.1's detection change means even recent 1.4.0-era snippets about reading the installed version may be stale. Trust the repo's README and changelog over anything else.
getInstalledVersion() and getLatestVersion() the first time you wire this up, not just the boolean.composer show roadrunner/roadrunner in the same environment (same container, same vendor/) tells you whether detection can work at all. If that command fails, the checker cannot know your installed version.$this->app->instance(VersionChecker::class, new FakeVersionChecker('v1.4.1'));
Constructor-inject VersionChecker rather than new-ing it inside a command for exactly this reason.Custom version source — useful for air-gapped or mirrored environments:
$checker->setVersionSource('https://internal.example.com/rr/latest.json');
(Confirm the setter name against the v1.4 README; the API has shifted across 1.x.)
Events over log lines — decouples detection from notification:
if (! $checker->isLatest()) {
event(new RoadRunnerOutdated(
$checker->getInstalledVersion(),
$checker->getLatestVersion()
));
}
Then a listener decides whether that's a Slack ping, a Jira ticket, or nothing at all.
Wrap, don't fork: put a thin RoadRunnerVersionService around the checker and inject that into your commands and listeners. When the package renames or reshuffles its API again (the vendor rename in 1.4.0 and the detection change in 1.4.1 are two examples in as many patches), you fix one class instead of every call site.
How can I help you explore Laravel packages today?