owenvoke/gitea
A Laravel/PHP client for the Gitea API, providing a clean SDK and Laravel integration to work with repositories, issues, users, organizations, and other Gitea endpoints.
Architecture fit. The package is a thin, framework-agnostic Saloon connector over Gitea's REST API. That is the right shape for Laravel: no service provider, no facade, no config to publish means nothing to unwind later, and the container binding is a four-line AppServiceProvider change. It fits cleanly into the layer we would otherwise build ourselves — token auth, request construction, response parsing across users, orgs, repos, issues, and webhooks. It does not fit, and should not be used, as a public API gateway or a latency-critical path; it is an internal-tooling dependency.
Integration feasibility. High for a time-boxed spike. A one-day proof — create an issue, read a repository — exercises auth, JSON POST, and response parsing and gives a go/no-go signal. The realistic estimate shift is "weeks of HTTP plumbing" down to "days of application logic," which is the entire argument for funding this now rather than deferring a quarter.
Technical risk. Concentrated in the dependency, not the code. Zero dependents, negligible stars, an opportunity score of ~22 — this is a lowest-tier maintenance signal and a bus factor of one. Concretely:
src/Resources/ is the de facto feature list. Anything not there is our code, and DTO coverage in src/Data/ is partial.null-on-404 failures are our responsibility, not the package's.X-Total-Count may be absent; loop termination must be empty-page-based with a runaway cap.Key questions for the team.
Stack fit. PHP 8+, Composer, Laravel's container and queue layers, Saloon as the transport. The absence of a service provider is a feature — it means the wiring is ours and therefore documented, testable, and replaceable. Required wiring is three pieces: a config/services.php entry (URL, token, webhook secret, timeout), a singleton binding in AppServiceProvider, and an explicit ->throw() discipline or an always-throw connector setting.
Migration path. Wrap from day one. One narrow domain service per noun (GiteaIssues, GiteaRepositories, GiteaOrgs), injected by the container, returning our own value objects and scalars — never a Saloon Response and never a raw array reaching a controller or Blade view. That wrapper is the swap seam: if upstream goes quiet, we replace method bodies and nothing else moves. Pin the exact version, no floating constraints, and keep a PATCHES.md.
Compatibility. Three known traps to design around, not discover:
/api/v1; a trailing-slash or bare-host mismatch produces 404s that read like auth failures. Log the resolved URL once at boot during the spike.Testing must use Saloon's MockClient, not Http::fake() — the latter bypasses Saloon entirely and lets real requests through silently, which is a genuinely dangerous failure mode in CI.
Sequencing.
->json(); use ->dto() only where src/Data/ actually ships a class.gitea:sync that walks pages and repairs the local mirror. This is both a feature and the recovery mechanism for every other failure mode; it costs a day and makes the rest survivable.Maintenance. We own materially more of this integration than we would on GitHub or GitLab: pagination termination, retry and backoff policy, error semantics, webhook verification, DTO gaps, and version-compatibility review. That ownership belongs on the roadmap as a named cost line, not buried in "dependency is free." Minimum viable governance is a named owner, a pinned version, a PATCHES.md, and a scheduled compatibility check after each Gitea upgrade.
Support. Community only. No SLA, no security responsiveness contact, no escalation path other than fork-and-vendor with a bus factor of one. This is acceptable for internal admin tooling and disqualifying for anything a customer or regulator depends on. Tier it as a niche integration: funded as a time-boxed spike when a team asks, not staffed as an always-on first-class surface.
Scaling. The package has no opinion on throughput; we supply it. Bound queue worker concurrency so 429s don't thundering-herd, set explicit per-connector and per-job timeouts (the default is generous enough to pin workers when Gitea hangs), cache short-TTL reads that a user is waiting on, and push everything else to a queue. Gitea latency is our latency — no interactive calls in the request cycle.
Failure modes to plan for.
null on non-2xx because ->throw() is not automatic; the downstream error appears somewhere unrelated.Http::fake(). Use MockClient exclusively.Ramp-up. Low. A Laravel developer is productive in half a day provided they read src/Resources/ and src/Data/ first — those two directories answer "what is supported" and "where will ->dto() actually work" faster than any documentation. The onboarding risk is not the learning curve; it is the undocumented gaps, which is precisely why the wrapper layer and the reconciliation command are non-negotiable from sprint one.
How can I help you explore Laravel packages today?