What happens when you open a pull request
The checks that run on a pull request, what each one is actually testing, the extra approval step for forks, and the test image you get to try the change for real.
Opening a pull request against main starts four workflows. This page explains what each one checks, so a red tick tells you something useful rather than just that something went wrong.
The four workflows
Workflow | What it does | When it runs |
|---|---|---|
Test | Builds, tests, lints and format-checks both the server and the agent | Every pull request |
Code quality | Biome on the frontend | Every pull request, unless the change only touches |
Security scan | CodeQL, secret scanning, Go vulnerability scanning | Every pull request |
Build and Push Docker Images | Builds a real image you can run | Every pull request from the main repository. Fork pull requests need approval first, see below |
There is also an npm audit, but it only runs when you have touched package.json, package-lock.json or frontend/package.json.
Test
Eight jobs run in parallel, four for the server and four for the agent:
Job | Equivalent locally |
|---|---|
Test (server) |
|
Lint (server) | golangci-lint |
Format Check (server) |
|
Build (server) |
|
Agent Tests |
|
Agent Format Check |
|
Agent Lint | golangci-lint |
Agent Build |
|
Tests run with -race, so a data race fails the build even when the test itself passes. Race conditions that never show up on your machine will surface here.
Both test jobs upload an HTML coverage report as a build artefact, which you can download from the workflow run page.
Running make check in server-source-code and agent-source-code covers the format, vet, lint and test jobs in one go. If that passes locally, Test should pass in CI.
One thing that surprises people: the four server jobs each build the frontend first, with npm ci and a Vite build, before touching Go. That is not redundancy for its own sake. The server embeds the compiled frontend into the binary with Go's embed, so the embed directory has to exist or the Go build fails. Even the lint and format jobs need it.
Code quality
Runs Biome across the repository in CI mode. Locally the equivalent is:
cd frontend
npx biome check --write src/
npx biome check src/A pre-commit hook runs Biome too, so this rarely fails in CI unless the hook was bypassed.
This workflow skips changes that only touch docker/.
Security scan
Three jobs:
- CodeQL static analysis, in four passes: the frontend TypeScript, the server Go, the agent Go, and the GitHub Actions workflows themselves. It uses the
security-extendedquery set, which is broader than the default. - Gitleaks scans for committed secrets: API keys, tokens, private keys.
- govulncheck checks the Go dependencies of both the server and the agent against the Go vulnerability database.
Findings appear under Security → Code scanning in the repository. The default view shows the default branch, so use the branch dropdown to select your pull request branch.
govulncheck is the one most likely to fail on a change you did not make: it reports newly disclosed vulnerabilities in existing dependencies. If it fails on a pull request that does not touch dependencies, that is usually what has happened, and it is not your fault.
The Docker test image
This is the useful one. Your change gets built into a real multi-architecture image and pushed to a staging registry, and a bot comments on the pull request with a ready-to-run compose snippet:
services:
patchmon-server:
image: cr.dev.patchmon.cloud/pr/patchmon-server:pr-123The comment also gives a digest-pinned reference if you want an exact, reproducible pull.
Notes on how it behaves:
- Pull request images go to a separate
/pr/namespace, so a test build can never be mistaken for a production image. - Nothing is pushed to the public GitHub registry from a pull request. Only
mainand release tags go there. - Pushing more commits updates the same comment rather than adding a new one, so the thread does not fill up with near-identical bot messages.
- The image reports the next patch as a pre-release, for example
2.0.3-rc.61, rather than the last released version. See How versioning works.
Fork pull requests need approval
If you are contributing from a fork, the image build does not run straight away. A maintainer has to add the approved-for-build label first.
This is a security boundary, not a judgement on your change. The image build needs registry credentials, so it runs with access to repository secrets. Running that automatically against code from any fork would hand those credentials to anyone who opened a pull request. A maintainer reads the diff, then labels it.
The other three workflows run on fork pull requests immediately, because they need no secrets. So you get test, lint and security feedback right away, and the image once someone has looked.
If your fork pull request seems to be missing the Docker build, that is why. Ask on Discord if it has been sitting a while.
When something fails
- Open the failing job from the Checks tab and read the step that went red.
- Reproduce locally.
make checkcovers most Test failures,npx biome check src/covers Code quality. - For CodeQL, look under Security → Code scanning and switch the branch dropdown to your branch.
- Push a fix. Everything reruns automatically, and the image comment updates in place.
A failure in a workflow you did not touch is worth mentioning in the pull request rather than force-pushing repeatedly at it. Dependency vulnerability alerts in particular are often nothing to do with your change.
Where to go next
- How work gets planned and shipped, for what happens either side of the pull request
- Building PatchMon locally
- How versioning works
- How a release happens
- Contributing code for the review process itself
Was this helpful?
Your feedback shapes what we write next.
