Skip to content

Setup

Register the provider next to your cloud’s:

alchemy.run.ts
import * as GitHub from "alchemy/GitHub";
providers: Layer.mergeAll(Cloudflare.providers(), GitHub.providers()),

Run alchemy profile edit --add GitHub and pick between two local options:

  • gh CLI — shells out to gh auth token (recommended).
  • Stored PAT — a personal access token entered interactively, saved under ~/.alchemy/credentials/<profile>/.

When CI=true, profiles are bypassed and Alchemy reads GITHUB_ACCESS_TOKEN or GITHUB_TOKEN directly.

See Profiles for how credentials are stored and switched.

Both profile methods and CI environment credentials work against GitHub Enterprise Server and GitHub Enterprise Cloud with data residency. alchemy profile edit re-configure GitHub prompts for the host. Leave it blank for github.com, or enter your enterprise host (e.g. github.example.com or acme.ghe.com).

The host is normalized into the REST API base URL automatically:

  • github.example.comhttps://github.example.com/api/v3 (GHES)
  • acme.ghe.comhttps://api.acme.ghe.com (data residency)
  • an explicit URL like https://github.example.com/api/v3 is used as-is

Alternatively, set the host in the environment — GITHUB_BASE_URL, GITHUB_API_URL (set automatically on GitHub Actions runners), or GH_HOST — and every method picks it up without reconfiguring. On an enterprise host, CI environment resolution also honors the gh CLI’s GH_ENTERPRISE_TOKEN / GITHUB_ENTERPRISE_TOKEN before falling back to GITHUB_ACCESS_TOKEN / GITHUB_TOKEN, and the gh CLI method runs gh auth token --hostname <host> (run gh auth login --hostname <host> once first).

To fix the host for the whole app — independent of profile configuration or the environment — pass it to providers():

providers: GitHub.providers({ baseUrl: "github.example.com" }),

The auth provider sees the pinned host too: the configure flow stops prompting for it and authenticates against it directly.

Every GitHub resource also takes its own baseUrl prop, so a single stack can span github.com and an enterprise instance:

yield* GitHub.Repository("mirror", {
owner: "my-org",
name: "mirror",
baseUrl: "github.example.com",
});

Resolution order is: resource baseUrlproviders({ baseUrl }) → the host from the login profile or environment. Changing a resource’s baseUrl replaces it — the same name on a different GitHub instance is a different physical resource.

  • repo — always required.
  • workflow — when you manage Actions secrets and variables.
  • delete_repo — only when you opt a repository into deletion via destroy(). Repositories default to retain on removal, so most tokens never need it.

Skip profiles entirely by building the credentials layer from a token you already have in hand — useful in tests:

Effect.provide(GitHub.fromToken(token))

fromToken accepts a plain string or a Redacted value, plus an optional baseUrl for GitHub Enterprise:

Effect.provide(GitHub.fromToken(token, { baseUrl: "github.example.com" }))

To read from the environment instead, GitHub.fromEnv() builds the same layer from GITHUB_ACCESS_TOKEN or GITHUB_TOKEN (and resolves the enterprise host from GITHUB_BASE_URL / GITHUB_API_URL / GH_HOST when set).