Pipelines
How code gets checked, built and released. The workflows live in .github/workflows/; the task definitions they call live in the moon projects (crates/graphpg/moon.yml, e2e/moon.yml), so anything CI runs can be run locally with the same command.
| Trigger | Workflow | What happens | Publishes? |
|---|---|---|---|
| Pull request (any branch) | CI | static checks, tests, example, container build | no |
Push to main | Publish (runs CI first) | everything above, then build, scan, tag, sign | image :edge, :sha-… |
Tag vX.Y.Z | Publish (runs CI first) | as for main, plus tag verification and a GitHub Release | image :X.Y.Z, :X.Y, :X, :latest |
Push to main touching the docs | Pages | build the documentation website, deploy it | the site on GitHub Pages |
| Weekly | Dependabot | opens PRs for Cargo, npm, GitHub Actions and Docker updates | no |
Branches never publish an image: a pull request only builds and tests.
Jobs run on the runners named by the repository variable CI_RUNS_ON (GitHub-hosted when it is unset). Pull requests from forks always run on GitHub-hosted runners, whatever it says, so code from outside the repository never runs on self-hosted machines.
CI (ci.yml)
Runs on pull requests and manually, and is called by Publish before anything is released.
- check runs first and everything else waits for it, so a formatting or type error fails in about a minute rather than after the long jobs. It runs
moon run :typecheck :lint :format:cargo check,clippy -D warningsandcargo fmt --checkfor Rust,tscand Biome for TypeScript. Locally:pnpm verify, andpnpm fixto apply the fixes. - test runs the Rust integration tests against a real PostgreSQL service container.
- e2e runs
moon run e2e:typecheck e2e:test. moon installs the pinned toolchains (.moon/toolchains.yml), builds thegraphpgbinary, then the vitest suite uses Drizzle to migrate and seed the database, starts the binary and sends GraphQL requests to it. - example starts graphpg against
examples/blogand checks the documented query returns data. - image (pull requests only) builds the production Dockerfile without pushing, runs the container against PostgreSQL and checks it serves and shuts down cleanly. It catches a broken Dockerfile before it reaches
main.
Publish (publish.yml)
Runs on pushes to main and on v* tags. A newer push to a branch cancels the run in progress; tag runs are never cancelled.
- CI must pass. Nothing below runs if it fails.
- verify-tag (tags only) refuses a tag that is not on
mainor whose version differs fromCargo.toml. - plan chooses the platforms:
linux/amd64always, andlinux/arm64only when the repository is public (arm64 runners are free for public repositories only). - build builds one image per platform on a native runner and pushes it by digest, without a tag. The image is scanned with Trivy for fixable HIGH and CRITICAL vulnerabilities before any tag exists; a finding fails the run and nothing is tagged.
- merge combines the per-platform images into one multi-arch image, applies the tags and signs it with keyless cosign (the workflow's OIDC identity).
- release (tags only) creates the GitHub Release with the image digest, the
cosign verifycommand and notes generated from merged pull requests, grouped by label (release.yml).
Tags: main gets :edge and :sha-<short hash>. A release tag vX.Y.Z gets :X.Y.Z, :X.Y, :X and :latest; a pre-release suffix (v1.1.0-rc.1) gets a GitHub pre-release and only its exact version tag.
Container build caching
The Dockerfile compiles dependencies in their own layer, against stand-in sources, before copying the real source. That layer changes only when Cargo.toml or Cargo.lock does, and the GitHub Actions layer cache (cache-from/cache-to type=gha) keeps it between runs, so a source-only change recompiles just the graphpg crate instead of every dependency.
Pages (pages.yml)
Builds the documentation website in packages/website and deploys it to GitHub Pages. The site is VitePress: a features page of its own, and the guides, introduction and changelog copied from docs/, README.md and CHANGELOG.md at build time (packages/website/sync.mjs), so they have one source. Links between those pages stay on the site; links to the rest of the repository go to GitHub.
- It runs on pushes to
mainthat change the docs, the README, the changelog, the version inCargo.tomlor the site, and manually. Pull requests that touch them build the site without deploying, which catches a dead link or a page VitePress cannot compile. - The site is served under
/<repository>/. For a custom domain, set the repository variablePAGES_BASEto/. - Once, in the repository settings: Pages → Source: GitHub Actions.
- Locally:
moon run website:devserves it with reload;moon run website:buildbuilds it intopackages/website/src/.vitepress/dist.
Dependabot (dependabot.yml)
Weekly update PRs for Cargo (minor and patch grouped), npm (minor and patch grouped), GitHub Actions (grouped) and the Docker base images. Security updates are enabled for the repository. Dependabot PRs run the same CI as any other pull request. Actions are pinned by commit SHA and base images by digest, and Dependabot keeps the pins current.
pnpm-workspace.yaml overrides esbuild to a patched version because drizzle-kit pins older ones with known advisories; remove the override once drizzle-kit ships a patched esbuild.
Releasing
See CONTRIBUTING.md: bump the version in Cargo.toml, merge to main, tag that commit.