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()),

The next alchemy login adds a GitHub step with three options:

  • gh CLI — shells out to gh auth token (recommended).
  • Environment variables — reads GITHUB_ACCESS_TOKEN, falling back to GITHUB_TOKEN (good for CI).
  • Stored PAT — a personal access token entered interactively, saved under ~/.alchemy/credentials/<profile>/.

When CI=true, the login step skips the prompt and selects the environment-variables method automatically.

See Profiles for how credentials are stored and switched.

All three methods work against GitHub Enterprise Server and GitHub Enterprise Cloud with data residency. alchemy login --configure 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, the environment-variables method 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: alchemy login 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).