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

Laravel Version Laravel Package

othercode/laravel-version

A small Laravel package for managing and displaying your application's version information. Use it to keep a single version source, expose the current version in your app, and reference it from config, views, or health and about endpoints.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal steps

  1. composer require othercode/laravel-version — Laravel package auto-discovery should register the service provider; if not, add it manually to config/app.php.
  2. Publish the config: php artisan vendor:publish --tag=version-config (confirm the exact tag from the README — with a package this small, the README is the documentation).
  3. Decide your source precedence and put it in config/version.php. The whole point of the package is that this list is data, not code: ['env', 'file', 'git', 'config'] for production containers, ['git', 'file', 'config'] for local dev.
  4. Resolve the version once and expose it: add a route or extend your existing /health//up endpoint to return version + which source won.

Where to look first (in this order)

  • config/version.php after publishing — this is the entire public API surface in practice.
  • The service provider's register() method — that's where the resolver is bound and where the adapter contract (the "port") lives. That contract is the only thing you should ever code against.
  • The adapters themselves (VersionFileSource, EnvSource, GitTagSource, ConfigSource or similar) — read these before trusting them in a deploy pipeline. They're small by design; you can audit all four in ten minutes.
  • The README's "Custom sources" section, if it exists — that's your extension point.

First use case

Deployment verification. A version endpoint is the cheapest possible smoke test:

// routes/api.php
Route::get('/version', fn () => response()->json([
    'version' => app(VersionResolver::class)->resolve(),
    'source'  => app(VersionResolver::class)->sourceName(), // confirm method name
]));

Then in CI, after kubectl rollout status / forge deploy, curl https://app/api/version and assert the tag matches what you just built. This catches the classic "deploy silently no-op'd because the image tag didn't change" failure, and it catches it in seconds instead of in a bug report three days later.


Implementation Patterns

Bind once, resolve once, cache

The resolver should be a singleton and its result should be memoized. Do not let it re-read the filesystem or shell out to git per request:

// AppServiceProvider::register()
$this->app->singleton(AppVersion::class, function ($app) {
    $resolver = $app->make(VersionResolver::class);
    return new AppVersion(
        $resolver->resolve(),      // string
        $resolver->sourceName(),   // 'git' | 'env' | 'file' | 'config'
    );
});

Wrap it in your own value object (AppVersion) rather than injecting the package's resolver throughout your codebase. This is the single highest-leverage thing you can do with a zero-star dependency: it makes the package a swappable detail instead of a load-bearing wall.

Source precedence is your deployment contract

Think of the sources as a chain of responsibility with different jobs in different environments:

Environment Precedence Why
Local dev git → config Always correct, zero setup, changes with every commit
CI build stage env (APP_VERSION set by pipeline) The pipeline knows the release tag before the image exists
Production container file (VERSION baked at build) → env No .git in the image; VERSION is immutable and greppable
Tests config (hardcoded) Deterministic; assertions don't break when you tag a release

Bake the version at build time so it's identical everywhere:

ARG APP_VERSION=0.0.0-dev
RUN echo "${APP_VERSION}" > /var/www/html/VERSION

Middleware for a version header on every response

Cheap and enormously useful when debugging from a browser devtools panel or a customer's HAR file:

class AddVersionHeader
{
    public function __construct(private AppVersion $version) {}

    public function handle($request, Closure $next)
    {
        $response = $next($request);
        $response->headers->set('X-App-Version', $this->version->toString());
        return $response;
    }
}

Only do this on non-public responses if exposing your version is a concern (see Gotchas).

Asset cache busting

Laravel's mix()/vite() already handles manifest hashing, but if you serve assets from a CDN or have long-lived caches keyed by app version, feed the resolved version into config(['app.asset_version' => ...]) at boot, or append it as a query string. One version number to correlate "which release broke the CSS" with "which release broke the API."

Release tracking in error reporting

// Sentry / Bugsnag / Rollbar init
'release' => app(AppVersion::class)->toString(),

This is where a version resolver stops being decorative. Every exception report now carries the release, so you can say "this started in 2.4.1" instead of "around Tuesday."

Testing

Bind a fixed version in TestCase::setUp() or a dedicated trait:

$this->app->instance(AppVersion::class, new AppVersion('1.2.3-test', 'config'));

Then assert on the version endpoint without depending on the developer's git state or a VERSION file existing on the CI runner. Without this, your version tests will pass locally and fail in CI — a classic shallow-clone problem (see Gotchas).

Health check integration

Most teams end up with more than one health check: a liveness probe (cheap, no dependencies) and a readiness/deploy-verification one. Version belongs on the second one, alongside a migration count or queue depth — it's diagnostic, not a liveness signal. Don't put git invocation behind a liveness probe that's hit every two seconds.


Gotchas and Tips

The shallow-clone trap. git describe / git tag resolution fails in CI. GitHub Actions checks out with fetch-depth: 1 by default, so no tags exist and the git source silently returns nothing — or throws. Fix: fetch-depth: 0 in actions/checkout, or set APP_VERSION from github.ref_name, or just accept the fallback. But always verify what the resolver returns in CI, because a wrong-but-plausible version is worse than a missing one. If you can't get tags, the resolver should fall back to a 0.0.0+sha style value rather than an empty string.

.dockerignore deleting your VERSION file. You spend an hour wiring up the file source and the Docker build silently drops it. Grep your .dockerignore and then docker run --rm <image> cat VERSION as a build-time assertion.

config:cache and boot-time resolution. If you resolve at boot and the value depends on a runtime env var, php artisan config:cache can freeze a stale value into the cached config. Keep version resolution out of config/version.php's env() calls where the value must be dynamic — or better, accept that version is a build artifact and should be frozen. Be deliberate about which you want.

Sources disagreeing is the main failure mode. Four sources means four opinions. Always log or expose which source won — that single string (source: 'file') turns a two-hour debugging session into a two-second one. If two sources are configured and disagree, decide explicitly whether that's an error (fail loud) or a precedence rule (fail quiet). For a package this small, that behavior is whichever the adapter code says — read it.

It's a string, not a semver object. Despite the name, don't assume this parses or validates semver. It returns whatever the source contained, trailing newline and all — .trim() defensively. Don't build comparison logic (is version > x?) on top of it without a real semver library.

0 stars, 0 dependents, no ecosystem. This is the honest caveat: there is no community, no CVE feed, no Stack Overflow answers, and the bus factor is one. It's a tiny package — that's the mitigating factor — but treat it accordingly:

  • Pin the exact version in composer.json.
  • Read all of the source before adopting (it's an hour, at most).
  • Keep your AppVersion wrapper so removal is a one-file change.
  • If the resolver turns out to be 80 lines, seriously consider vendoring it into app/Support/ rather than taking the dependency. For a package this size, a local copy you fully understand often beats a remote one you don't.

Extension points: write your own source. The pluggable-source design is the package's actual value proposition. Implement the contract and register it in config for anything non-standard — a /build-info.json written by your CI, a database-stored release record, a cloud provider's deployment metadata API (careful: that's a network call at boot, so cache it). The moment you need a fifth source, do it via the contract rather than forking.

Don't leak version detail on public surfaces. A precise version + commit SHA on an unauthenticated endpoint tells an attacker exactly which CVEs to try. Prefer "which release" on internal health endpoints, and if you must expose publicly, expose only a major.minor and only behind auth. The X-App-Version header on every response is a convenience — make sure that trade-off is a decision, not an accident.

Debugging checklist when the version looks wrong, in order: (1) does VERSION exist in the running container? (2) is APP_VERSION set in that environment? (3) does git describe --tags work as the app user in that directory? (4) is config:cache serving a stale config? (5) which source does the resolver report as the winner? Almost every bug is answered by (5), which is why exposing it is worth the two lines.

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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment