NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Git for Laravel

Configuration

The published config/git.php holds one block per provider plus shared blocks for caching, logging, webhooks and batching. GitHub’s block in full, with GitLab and Bitbucket abbreviated where they repeat the same shape:

return [
    'providers' => [
        'github' => [
            'url' => env('GITHUB_API_URL', 'https://api.github.com'),
            'token' => env('GITHUB_TOKEN'),
            'webhook_secret' => env('GITHUB_WEBHOOK_SECRET'),
            'timeout' => env('GITHUB_TIMEOUT', 10),
            'retry' => [
                'times' => env('GITHUB_RETRY_TIMES', 1),
                'backoff' => env('GITHUB_RETRY_BACKOFF', 0),
            ],
            'rateLimits' => [
                'enabled' => env('GITHUB_RATELIMIT_ENABLED', true),
                'owner' => env('GITHUB_RATELIMIT_OWNER', 'app'),
                'maxAttempts' => env('GITHUB_RATELIMIT', 5000),
                'timespan' => env('GITHUB_RATELIMIT_TIMESPAN', 'hour'), // second | minute | hour | day
                'adaptive' => env('GITHUB_RATELIMIT_ADAPTIVE', true),
                'max_wait' => env('GITHUB_RATELIMIT_MAX_WAIT'),         // ms; null = wait/pace
                'jitter' => env('GITHUB_RATELIMIT_JITTER'),             // ms; null = none
            ],
            'options' => [
                'headers' => [
                    'User-Agent' => env('GIT_USER_AGENT', env('APP_NAME', 'GitHttp/1.0')),
                    'X-GitHub-Api-Version' => env('GITHUB_API_VERSION', '2022-11-28'),
                ],
            ],

            // GitHub App authentication (self-refreshing installation tokens).
            // When id, installation_id and private_key are all set, Git::github() mints
            // installation tokens automatically; otherwise it uses token.
            // Git::githubApp() needs only id + private_key.
            'app' => [
                'id' => env('GITHUB_APP_ID'),
                'installation_id' => env('GITHUB_APP_INSTALLATION_ID'),
                'private_key' => env('GITHUB_APP_PRIVATE_KEY'), // PEM string or file path
                'slug' => env('GITHUB_APP_SLUG'),               // builds the install URL
                'permissions' => [                               // default scope for a per-operation mint
                    'contents' => 'write',
                    'pull_requests' => 'write',
                    'metadata' => 'read',
                ],
            ],

            // OAuth credentials (self-refreshing access tokens).
            'oauth' => [
                'client_id' => env('GITHUB_OAUTH_CLIENT_ID'),
                'client_secret' => env('GITHUB_OAUTH_CLIENT_SECRET'),
                'token_url' => env('GITHUB_OAUTH_TOKEN_URL', 'https://github.com/login/oauth/access_token'),
            ],
        ],

        'gitlab' => [
            'url' => env('GITLAB_API_URL', 'https://gitlab.com'),
            'token' => env('GITLAB_TOKEN'),
            'webhook_secret' => env('GITLAB_WEBHOOK_SECRET'),
            // timeout, retry and options follow the same shape with GITLAB_* keys
            'rateLimits' => [
                'maxAttempts' => env('GITLAB_RATELIMIT', 10),
                'timespan' => env('GITLAB_RATELIMIT_TIMESPAN', 'second'),
                // enabled, owner, adaptive, max_wait, jitter as above
            ],
            'oauth' => [
                'client_id' => env('GITLAB_OAUTH_CLIENT_ID'),
                'client_secret' => env('GITLAB_OAUTH_CLIENT_SECRET'),
                'token_url' => env('GITLAB_OAUTH_TOKEN_URL', 'https://gitlab.com/oauth/token'),
            ],
        ],

        'bitbucket' => [
            'url' => env('BITBUCKET_API_URL', 'https://api.bitbucket.org'),
            'token' => env('BITBUCKET_TOKEN'),
            'webhook_secret' => env('BITBUCKET_WEBHOOK_SECRET'),
            // timeout, retry and options follow the same shape with BITBUCKET_* keys
            'rateLimits' => [
                'maxAttempts' => env('BITBUCKET_RATELIMIT', 1000),
                'timespan' => env('BITBUCKET_RATELIMIT_TIMESPAN', 'hour'),
            ],
        ],
    ],

    'cache' => [
        'enabled' => env('GIT_CACHE_ENABLED', false),
        'store' => env('GIT_CACHE_STORE'),
        'ttl' => env('GIT_CACHE_TTL', 3600),
    ],

    'logging' => [
        'enabled' => env('GIT_LOGGING_ENABLED', false),
        'channel' => env('GIT_LOGGING_CHANNEL'),
    ],

    'webhooks' => [
        // Handed over raw and parsed strictly where it is read: 1/true/on/yes enable the
        // route, 0/false/off/no keep it off, and anything else throws at boot.
        'enabled' => env('GIT_WEBHOOKS_ENABLED', false),
        'path' => env('GIT_WEBHOOKS_PATH', 'git/webhooks'),
        'middleware' => ['api'],
    ],

    'batch' => [
        'concurrency' => env('GIT_BATCH_CONCURRENCY', 25),
    ],
];

Every key

KeyDefaultPurpose
providers.<name>.urlForge API URLAPI base URL — api.github.com, gitlab.com, api.bitbucket.org. For GitHub Enterprise Server use https://ghe.example.com/api/v3; clone and install URLs are then built on the web host https://ghe.example.com. Unset or blank (GITHUB_API_URL=) uses the public API; a non-string value throws.
providers.<name>.tokennullDefault access token — Git::github() and friends use it when you pass no credential.
providers.<name>.webhook_secretnullVerifies inbound webhooks; also the default secret when registering a webhook. Unset or blank means no secret, so nothing verifies.
providers.<name>.timeout10HTTP request timeout in seconds, 0–3600 (0 = none).
providers.<name>.retry.times1Attempts (0–100) for a read that hits a dropped connection, a 429 or a 5xx; writes are never retried. retry may also be a plain int — the attempts.
providers.<name>.retry.backoff0Backoff between read retries, in ms (0–600000).
providers.<name>.rateLimits.enabledtrueClient-side throttling; false sends with no limiter at all.
providers.<name>.rateLimits.ownerappThrottle bucket key (git:<provider>:<owner>). Blank is not set (app); non-string throws.
providers.<name>.rateLimits.maxAttempts5000 / 10 / 1000Requests per timespan, at least 1 (GitHub / GitLab / Bitbucket).
providers.<name>.rateLimits.timespanhour / second / hoursecond, minute, hour or day; anything else throws (an unset or blank key means minute).
providers.<name>.rateLimits.adaptivetrueHonour the forge’s own Retry-After / X-RateLimit-* headers.
providers.<name>.rateLimits.max_waitnullMax defer in ms (0 or more) before failing fast; null or blank waits and paces instead.
providers.<name>.rateLimits.jitternullRandom jitter in ms (0 or more) added to each defer; null or blank adds none.
providers.<name>.options.headersUser-AgentExtra HTTP options sent with every request; GitHub also pins X-GitHub-Api-Version 2022-11-28.
providers.github.app.idnullGitHub App id; with installation_id and private_key set, Git::github() mints installation tokens. Unset or blank means no app is configured; a non-string value (an integer, say) throws naming the key instead of falling back to token.
providers.github.app.installation_idnullInstallation id for the default app credential.
providers.github.app.private_keynullApp private key — a PEM string or a file path.
providers.github.app.slugnullThe app’s public slug; required by installUrl().
providers.github.app.permissionscontents, pull_requests: write; metadata: readPermission set used by InstallationTokenScope::forRepositories().
providers.<github|gitlab>.oauth.client_idnullOAuth client id, read by OauthToken::forProvider().
providers.<github|gitlab>.oauth.client_secretnullOAuth client secret, read by OauthToken::forProvider().
providers.<github|gitlab>.oauth.token_urlForge token URLOAuth endpoint used to refresh access tokens.
cache.enabledfalseStore ETags and serve 304 Not Modified responses from cache.
cache.storeDefault storeCache store for conditional requests and for minted App/OAuth tokens. Blank is not set (default store); non-string throws.
cache.ttl3600Cached-response TTL in seconds, at least 1.
logging.enabledfalseLog method, URL, status and duration at debug level — never tokens or bodies.
logging.channelDefault channelLog channel for request logging. Blank is not set (default channel); non-string throws.
webhooks.enabledfalseRegister the POST {path}/{provider} webhook route. Unset or blank (GIT_WEBHOOKS_ENABLED=null, GIT_WEBHOOKS_ENABLED=) means off — for the route, the URL webhooks()->register() derives, and about alike.
webhooks.pathgit/webhooksBase path of the webhook route. Blank is not set and uses git/webhooks; surrounding slashes are trimmed (/hooks/ is hooks); a slash-only path or a non-string throws — never the site root.
webhooks.middleware['api']Middleware applied to the webhook route.
batch.concurrency25Max concurrent requests per pool (1–1000); larger inputs are chunked.

Environment

The common knobs are env-driven, so most apps never publish the config at all:

GITHUB_TOKEN=ghp_...
GITLAB_TOKEN=glpat-...
BITBUCKET_TOKEN=...

GITHUB_WEBHOOK_SECRET=...
GIT_WEBHOOKS_ENABLED=true

GIT_CACHE_ENABLED=true
GIT_CACHE_STORE=redis
GIT_LOGGING_ENABLED=true
  • Tokens and secrets: GITHUB_TOKEN, GITLAB_TOKEN, BITBUCKET_TOKEN and the matching *_WEBHOOK_SECRET.
  • Per-provider transport: *_API_URL, *_TIMEOUT, *_RETRY_TIMES, *_RETRY_BACKOFF and the *_RATELIMIT* keys.
  • GitHub App: GITHUB_APP_ID, GITHUB_APP_INSTALLATION_ID, GITHUB_APP_PRIVATE_KEY, GITHUB_APP_SLUG.
  • OAuth: GITHUB_OAUTH_CLIENT_ID, GITHUB_OAUTH_CLIENT_SECRET, GITHUB_OAUTH_TOKEN_URL and the GitLab equivalents.
  • Shared: GIT_CACHE_ENABLED, GIT_CACHE_STORE, GIT_CACHE_TTL, GIT_LOGGING_ENABLED, GIT_LOGGING_CHANNEL, GIT_WEBHOOKS_ENABLED, GIT_WEBHOOKS_PATH, GIT_BATCH_CONCURRENCY, GIT_USER_AGENT, GITHUB_API_VERSION.

.env delivers every value as a string, and the package reads them as what they spell: an integer key takes a numeric string (GITHUB_TIMEOUT=45 is 45 seconds) and throws package-toolkit’s InvalidConfigurationException naming the key when the value isn’t an integer in its range; a boolean key reads 1/true/on/yes as on and 0/false/off/no as off — so GIT_WEBHOOKS_ENABLED=off really is off — and throws the same exception for anything else (GIT_WEBHOOKS_ENABLED=disabled fails at boot rather than silently keeping the route off). The same goes for every other setting: a timespan outside second/minute/hour/day, a non-integer max_wait or jitter, and a non-string string setting all throw naming the key. Only a key that is not set — absent, null or blank (a host’s KEY=) — takes its default (or stays off, for an optional one).

App and OAuth tokens are cached so they survive across requests — point cache.store at a shared store (Redis, database, file) rather than the array driver when you use them.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.