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

Flysystem Github Laravel Package

potherca/flysystem-github

Flysystem adapter for GitHub. Use a GitHub repository as a filesystem backend, reading, writing, listing, and deleting files through Flysystem's simple API while the package handles the underlying GitHub API calls. Useful for storing content, configs, or docs directly in a repo.

Deep Wiki
Context7

Getting Started

potherca/flysystem-github is a Flysystem 1.x adapter that maps Flysystem's read/write/listContents/delete calls onto the GitHub Contents API. In day-to-day Laravel terms: it lets you treat a GitHub repository (or a subdirectory in it) as a storage disk.

Before you install anything, check the constraints. Last tagged release was April 2016, which means:

  • league/flysystem must be ^1.0. Flysystem 2/3 renamed and re-typed the adapter interfaces (FilesystemAdapter, StorageAttributes, etc.) and this package will not load against them.
  • The package predates PHP 7.1+ type declarations and will likely need a shim or a fork to run cleanly on PHP 8.x. Expect deprecation noise at minimum.
  • On a modern Laravel app you will almost certainly hit a Composer conflict between league/flysystem ^1.0 and the framework's own constraint. Realistically this package is usable on Laravel 5.x/6.x-era apps, or in a standalone script/service, not on Laravel 10/11 without forking.

Install and configure:

composer require potherca/flysystem-github

You need a GitHub personal access token with repo scope (or public_repo if you only ever touch public repos). Keep it in .env, never in config/filesystems.php.

Where to look first, in this order:

  1. The package's README.md and composer.json — confirm the PHP and Flysystem constraints before writing any code.
  2. The adapter class (src/…/GithubAdapter.php) — the constructor signature and the exact config keys (username, token, repository, branch, and any path/prefix option) are defined there, not in documentation. Verify them; don't trust blog posts.
  3. The client wrapper class — this is where HTTP calls to api.github.com happen and where you'd add logging or a Guzzle handler for debugging.

First use case to try: a read-mostly content disk. Point the adapter at a docs/content repo and read markdown or JSON out of it:

$adapter = new GithubAdapter($client, [
    'repository' => 'acme/content',
    'branch'     => 'main',
    'prefix'     => 'articles', // verify key name against source
]);

$filesystem = new League\Flysystem\Filesystem($adapter);

$markdown = $filesystem->read('2024/hello.md');

If that works, you understand the surface area. If read() returns false, jump to the Gotchas section — path prefix, branch name, and token scope are the three usual culprits.

Implementation Patterns

Wrap it in a disk via a service provider

Don't instantiate the adapter inline. Register a singleton and expose it as a Flysystem disk so the rest of the app uses Storage::disk('github') and never sees GitHub-specific code.

// config/filesystems.php
'disks' => [
    'github' => [
        'driver'     => 'github',
        'username'   => env('GITHUB_USERNAME'),
        'token'      => env('GITHUB_TOKEN'),
        'repository' => env('GITHUB_REPOSITORY'), // owner/name
        'branch'     => env('GITHUB_BRANCH', 'main'),
    ],
],

Then in a service provider, extend the Storage manager with a custom driver that builds the adapter and returns a Filesystem. That keeps the dependency at the container boundary — the day you replace this package with direct API calls, only the provider changes.

Read pattern: content repo as a source of truth

The most durable use is reading. Load files on demand, and cache hard:

  • Wrap the adapter in Flysystem 1.x's League\Flysystem\Cached\CachedAdapter (backed by Laravel's cache store) so listContents() and read() don't burn API quota on every request.
  • If you need freshness, cache for minutes, not forever — commit SHAs change without notice.
  • For a CMS-like scenario, pull the repo into a local mirror on a schedule (queued job / artisan command) and serve from local disk. Treat GitHub as the write target and a local copy as the read cache.

Write pattern: one file = one commit

This is the single most important behavioural fact. The Contents API creates a commit per file mutation, so:

  • Writing 50 files produces 50 commits. There is no batching in this adapter; if you need an atomic multi-file commit you must drop to the Git Data API (blobs → tree → commit) yourself.
  • Updates require the blob sha, which the adapter fetches for you. That makes writes a read-then-write cycle and introduces race conditions (see Gotchas).
  • Use a dedicated branch and a bot account/token so the commit history stays legible and you can revert a bad sync in one git revert.
// Scheduled publish job — one commit, one file, retried on conflict
Storage::disk('github')->put("generated/{$date}.json", $payload);

Integration tips

  • Scope with a prefix. Put the disk on a subdirectory (content/, data/) so nobody accidentally writes to the repo root or the .github/ folder.
  • Test with a different adapter. Storage::fake() won't know this driver. Test your domain code against League\Flysystem\Memory\InMemoryFilesystemAdapter (or a local disk) and keep the GitHub adapter behind an interface; only smoke-test the real adapter in one integration test.
  • Guard the token. Give CI a token with access to exactly one repo. Rotate it; the adapter sends it on every request.
  • Add observability. Wrap the injected HTTP client with a middleware/handler that logs method, path, status, and rate-limit headers. Without this, failures are indistinguishable from each other.

Gotchas and Tips

Compatibility is the headline risk. Flysystem 1.x only; the project is unmaintained since 2016. Before committing to it, confirm it actually runs on your PHP version — and budget for forking. A 60-line class using Illuminate\Support\Facades\Http against the Contents API is a legitimate alternative and probably easier to maintain than a patched abandoned dependency.

Branch names. Older GitHub repos default to master, newer ones to main. A wrong branch gives 404s that look like missing files. Make the branch explicit in config and validate it at boot.

Real directory limits. Git has no directories. The adapter synthesizes them from file paths, so empty directories cannot exist, and createDir() may be a no-op or a fake success. Never rely on directory existence as state.

listContents() is expensive. It maps to one API call per directory level, paginated at 100 entries. Listing a large repo recursively can take dozens of requests. Always list a narrow prefix, cache the result, and be aware that pagination beyond the first page may need explicit handling depending on how the 2016 code was written.

Rate limits. Authenticated calls are 5,000/hour. A single page render that lists a directory and reads ten files is eleven requests. Without caching you will hit the ceiling in production, and GitHub returns 403 with a X-RateLimit-Remaining: 0 header — which the adapter may surface as a generic failure.

Size and encoding. The Contents API returns base64-encoded content and only inlines files below roughly 1 MB; larger files need the blob API. Anything binary or large is a bad fit. Keep the disk to text: markdown, JSON, YAML, generated HTML.

Write conflicts. Read-then-write means two concurrent writers can race and produce a 409 Conflict. Wrap writes in a retry loop with jitter, and keep the set of writers as small as possible. Never write from a web request path that users can trigger concurrently.

Errors become false. Flysystem 1.x conventions mean many failures return false rather than throwing. Check return values, or use the League\Flysystem\Filesystem methods that throw (write, read throw on failure in most cases — but verify per method). Enable Guzzle debug output or your logging handler to see the underlying 4xx.

Commit attribution and CI loops. Commits are authored by the token owner and will trigger repo webhooks and Actions. If you push generated files from a workflow that is itself triggered by pushes, you can create a loop. Guard with [skip ci] in the commit message if the adapter lets you set one, or filter by actor in the workflow.

Extension points. The client wrapper is the seam to subclass: override the HTTP call method to add retries, timeouts, a different base_uri (GitHub Enterprise), or logging. Subclass the adapter if you want custom getUrl() behaviour — verify whether it returns html_url, raw.githubusercontent.com, or nothing at all, since public URL generation matters for anything user-facing.

Migration note for new projects. If you're starting fresh, skip the package. Use knplabs/github-api or Laravel's HTTP client behind a small repository class, or simply git clone/git push in a queued job for bulk syncs — it's faster, atomic, and avoids the 1 MB / rate-limit / one-commit-per-file trade-offs entirely. Reserve this adapter for small, low-frequency, read-mostly integrations where the Flysystem API itself is the value.

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.
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
uni-method/symfony-lib