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

Ocular Laravel Package

uma/ocular

uma/ocular is a lightweight Laravel package that gives you a clear view into your application’s internals. It offers a simple, Laravel-friendly way to inspect and debug key runtime behavior, with minimal setup and no heavy dependencies.

View on GitHub
Deep Wiki
Context7

Getting Started

uma/ocular is a narrow-purpose tool: it takes an already-generated coverage report (Clover/Cobertura-style XML is the common case) and pushes it to an external dashboard so coverage is visible outside the CI job that produced it. It does not run your tests or instrument code — that remains PHPUnit/Pest's job. Keep that boundary in mind, because it dictates where in the pipeline it belongs.

Minimal setup:

  1. Add it as a dev dependency so it never ships to production:
    composer require --dev uma/ocular
    
  2. Confirm the entry point after install. Low-star CLI packages typically expose either a vendor/bin/ocular binary or an artisan command; check vendor/bin/ and the package's composer.json bin section first, then the README. This is the one place you should not guess — the invocation signature is the whole surface area of the tool.
  3. Look for a publishable config (php artisan vendor:publish --tag=ocular or equivalent) and read it before writing any CI YAML. Endpoint, token, project key, and report-format detection usually live there.
  4. Verify the format it accepts by generating a sample report locally and running the upload with the most verbose/dry-run flag available.

Your first use case should be the smallest possible: one Laravel app, one test suite, coverage already generated, uploaded from a CI step. Prove the round-trip locally against the real endpoint before touching GitHub Actions.

Implementation Patterns

Generate first, upload second, in separate steps. Give coverage generation its own CI step with an explicit output path (--coverage-clover=build/clover.xml for PHPUnit/Pest), and let Ocular consume that artifact. Coupling generation and upload into one shell line makes failures ambiguous and prevents you from re-running just the upload after a transient network error.

Treat the upload as a post-test, fail-open step. Tests should gate the build; the coverage upload should not. Wrap the invocation so a non-zero exit logs a warning instead of failing the job:

- name: Upload coverage
  if: always() && hashFiles('build/clover.xml') != ''
  continue-on-error: true
  run: vendor/bin/ocular upload build/clover.xml

Pass identity explicitly. For a dashboard to attribute a report to the right commit and branch, the tool needs either environment inference (GitHub Actions, GitLab CI variables) or explicit flags. Prefer explicit values — --commit=$GITHUB_SHA --branch=$GITHUB_REF_NAME or the config equivalents — because inference breaks the moment you run from a scheduled job, a fork PR, or a container without the expected env vars. Idempotency matters: the same commit pushed twice should overwrite, not duplicate.

Monorepo and multi-language aggregation. The phrase "external code coverage" is the reason this package exists: your Laravel app may be exercised by a Pest suite, a Vitest suite hitting Inertia pages, and a Cypress run. Each produces a different report format. The pattern is to emit every suite's report into a common build/coverage/ directory and invoke Ocular once per file, tagging each with a source or suite label. Aggregate on the dashboard side rather than trying to merge XML locally.

Config over flags for anything stable. Endpoint URL, project identifier, and default report format belong in a committed config file. Only per-run values (commit SHA, branch, PR number) belong on the command line. This keeps the CI YAML readable and means a dashboard migration is a one-line config diff.

Local dry-runs. Give yourself a way to validate the report without hitting the network — typically a dry-run/verbose flag that parses and prints what would be uploaded. Use it whenever the dashboard shows an unexpected percentage; it isolates whether the problem is report generation or report submission.

Inject a fake HTTP client in tests if you need to assert on upload behavior in your own test suite. Well-built upload tools expose the transport as a dependency; if this one doesn't, wrap the binary in a small service class and mock that instead of shelling out in tests.

Gotchas and Tips

  • Pin the version exactly. Two stars and minimal dependents means the CLI surface is not stabilized. Use "uma/ocular": "dev-main#<sha>" or a specific tag; a floating constraint will eventually break a green pipeline on a commit you didn't make.
  • --no-dev in CI is a trap. If the job that runs the upload installs with composer install --no-dev --optimize-autoloader, the binary won't be there. Either run the upload in the job that has dev dependencies, or install the tool separately.
  • Missing report files fail silently or confusingly. A zero-byte or absent clover.xml often yields either a "0% uploaded" success or an opaque parse error. Guard with a file-existence check and a minimum-size check before invoking the uploader.
  • Path normalization is the classic breakage. Coverage reports embed absolute paths from the machine that generated them. If the dashboard shows every file as uncovered, or files it can't resolve, you're looking at a path-prefix mismatch between the CI workspace and the repo root. Run generation and upload from the same working directory, and check --base-path-style options if the dashboard still can't map files.
  • Don't let the token leak. Pass it via an environment variable or secret reference, never as a literal flag — process arguments show up in CI logs and ps output. If the tool accepts both, prefer the env var.
  • Exit codes are your only signal. There is no API to query upload status. Capture stdout/stderr into the job log and treat unexpected exit codes as warnings, not failures, per the fail-open pattern above.
  • Coverage percentages disagree between local and CI. This is almost always a test-suite selection difference (excluding slow integration tests in one environment), not a bug in Ocular. Diff the generated clover.xml before blaming the uploader.
  • Extension points are limited. Expect a single hard-coded report parser and a single transport. If you need a format it doesn't support, the realistic path is to convert to a supported format beforehand (e.g., lcov → clover via a small script) rather than patching the package. Forking a 2-star dependency is a maintenance liability.
  • Vendor the invocation in a shell script (bin/ci-coverage.sh) rather than scattering the command across workflow files. When the flag surface changes on a new release, you have one place to fix, and the script doubles as documentation for the next developer.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource