dánwòdánwò

Identity

GitHub

Sign in with GitHub, GitHub Apps and their installations, the REST API for repositories, issues, pull requests and checks, and signed webhooks.

danwo's GitHub emulator answers github.com for sign-in and api.github.com for the REST API. Octokit works against it unchanged, with an OAuth token, a personal access token or a GitHub App's installation token.

Set up

Add githubService with the repositories your app works on and a token for it:

danwo.config.ts
import { appEnvironment, defineConfig, githubService } from "@danwo/sdk"

const token = "ghp_danwo"

export default defineConfig({
  testDir: "tests",
  readiness: { mode: "page+backend", settle: true },
  environment: () =>
    appEnvironment({
      app: { command: "npm run dev", url: "http://localhost:3000", env: { GITHUB_TOKEN: token } },
      services: [
        githubService({
          seed: {
            users: [{ login: "ada", name: "Ada Lovelace" }],
            orgs: [{ login: "acme", members: ["ada"] }],
            repos: [{ repo: "acme/app", files: { "README.md": "# App" } }],
            tokens: [{ token, login: "ada", scopes: ["repo"] }],
          },
        }),
      ],
    }),
})

Seed it

FieldWhat it seeds
users, orgsAccounts by login, with names, emails and members.
reposRepositories by owner/name, with files on the default branch, topics and privacy.
issuesIssues already open.
tokensPersonal access tokens, with their scopes.
oauthAppsOAuth Apps, with their callback URLs.
appsGitHub Apps: slug, owner, private key, webhook URL and secret, permissions and events.
installationsWhere each app is installed, and on which repositories.
hooksRepository webhooks.

Use it in a test

Report a bug in the app, then check the issue it opened:

tests/report-bug.danwo.ts
import { type GithubFixtures, githubRepo, test as base } from "@danwo/sdk"

const test = base.extend<GithubFixtures>()

test("opens an issue for a bug report", async ({ app, screen, expect, github }) => {
  await app.open("/feedback")
  await screen.getByLabel("What went wrong?").fill("The export button does nothing")
  await screen.getByRole("button", { name: "Report bug" }).click()
  await expect(screen.getByText("Thanks, we're on it")).toBeVisible()

  const [issue] = githubRepo(github, "acme/app").issues()

  expect(issue?.title).toBe("The export button does nothing")
  expect(issue?.labels).toEqual(["bug"])
})
HelperWhat it's for
githubRepo(github, "owner/name")A repository's issues, comments, check runs, files and commits.
installGithubApp, uninstallGithubAppInstall or remove an app as a person would, with its webhook.
githubAppKey(github, slug)An app's private key, for signing its JWT.
githubSignIns(github)Who signed in.

What's covered

AreaCovered
Sign-inOAuth Apps and GitHub Apps, with expiring user tokens.
AppsInstallations and installation access tokens.
RepositoriesRepos, branches, commits, file contents and refs.
IssuesIssues, comments and labels.
Pull requestsFiles, commits, reviews and merging.
ChecksCheck runs and their annotations.
WebhooksRepository hooks, pings and App webhooks.
UsersThe signed-in user, their emails, and public profiles.

Every call checks the token's scopes or the app's permissions and answers with GitHub's error shapes.

Faults

CallFaults the matrix tries
A write, such as POST …/issues/:number/comments403, 429, 502, 503, a 10-second delay, connection reset, answer lost after GitHub acted, 422
A read403, 429, 502, 503, a 10-second delay, connection reset
A contents write or a mergeadds a 409, as when the branch moved
POST github.com/login/oauth/access_tokena 200 with bad_verification_code, 502, a delay, connection reset
Every webhook deliveryarrives twice, arrives 10 minutes late, forged signature, 1 hour stale

The 403 and 429 are GitHub's secondary rate limit, with Retry-After: 60.

Webhooks

Deliveries carry X-GitHub-Event and X-Hub-Signature-256, made with the webhook's secret, so @octokit/webhooks verifies them.

Fidelity

Public endpoints only. danwo fidelity github reads a public repository and an unknown one from GitHub's API. Both match.

Not covered yet

  • GraphQL
  • Actions, releases, packages and deployments
  • Git over HTTPS: clone and push

Next

On this page

Run it on every pull request

A GitHub check and one comment per pull request, with the flight records attached.

Set up CI