Bloom uses GitHub Actions for CI. All workflows run on pull requests targeting main. Merging requires a passing CI run.
Workflows
The .github/workflows/ directory contains 15 workflow files:
| Workflow file | Name | Trigger |
|---|
unit_tests_api.yml | Backend Unit Tests | PR to main |
integration_tests_api.yml | Backend Integration | PR to main |
unit_tests_sites_public.yml | Public Unit | PR to main |
unit_tests_sites_partners.yml | Partners Unit | PR to main |
unit_tests_shared_helpers.yml | Shared Helpers Unit | PR to main |
cypress_public.yml | Public Cypress | PR to main |
cypress_partners.yml | Partners Cypress | PR to main |
cypress_partners_listingapproval.yml | Partners Cypress (LA) | PR to main |
lint.yml | Lint | PR to main |
docker_compose_ci.yml | Docker Compose CI | Push/PR to main |
docker_image_build.yml | Docker Image Build | Push to main, manual |
infra_ci.yml | Infra CI | Push/PR to main (infra/) |
gitleaks.yml | gitleaks | Every push |
codeql.yml | CodeQL | Scheduled / PR |
missing-translations.yml | Missing translations | PR to main |
All application CI workflows (unit tests, integration tests, Cypress, lint) use paths-ignore: ['infra/**']. Infrastructure changes trigger the dedicated infra_ci.yml workflow instead.
Test workflows
API unit tests (unit_tests_api.yml)
Runs the NestJS unit test suite against the API.
- Runner:
ubuntu-latest, Node.js 22
- No database required (mocked in unit tests)
- Command:
yarn test from ./api
API integration tests (integration_tests_api.yml)
Runs end-to-end API tests that exercise the full request/response cycle against a real PostgreSQL database.
- Runner:
ubuntu-latest, Node.js 22
- Spins up a PostgreSQL service container
- Command:
yarn test:e2e from ./api
- Key env vars:
DATABASE_URL, TEST_DATABASE_URL, PORT=3100
Public site unit tests (unit_tests_sites_public.yml)
Runs the Jest unit test suite for sites/public.
- Runner:
ubuntu-latest, Node.js 22
- Installs dependencies in root,
sites/public
- Command:
yarn test:app:public:unit
Partners site unit tests (unit_tests_sites_partners.yml)
Runs the Jest unit test suite for sites/partners.
- Runner:
ubuntu-latest, Node.js 22
- Installs dependencies in root,
sites/public, sites/partners
- Command:
yarn test:app:partners:unit
Shared helpers unit tests (unit_tests_shared_helpers.yml)
Runs the unit tests for the shared-helpers package.
- Runner:
ubuntu-latest, Node.js 22
- Command:
yarn test:shared-helpers
Public site Cypress tests (cypress_public.yml)
Runs the full Cypress end-to-end test suite against the public site.
- Runner:
ubuntu-latest, Node.js 22
- Starts a PostgreSQL service container, seeds the database
- Builds the public site with
yarn build, starts both the API and Next.js dev server
- Waits for
http://localhost:3100/jurisdictions before running tests
- Uses cypress-io/github-action@v5
Partners site Cypress tests (cypress_partners.yml)
Runs the Cypress end-to-end suite for sites/partners covering the cypress/e2e/default/*.spec.ts test files.
- Runner:
ubuntu-latest, Node.js 22
- Starts a PostgreSQL service container, seeds the database
- Builds and starts the partners site and API
- Waits for
http://localhost:3100/
Partners Cypress — listing approval (cypress_partners_listingapproval.yml)
A separate Cypress job for the partners site that runs listing-approval-specific tests (cypress/e2e/listings-approval/*.spec.ts) with FEATURE_LISTINGS_APPROVAL=TRUE.
Linting
The lint.yml workflow runs ESLint and Prettier on every PR to main:
Docker workflows
Docker Compose CI (docker_compose_ci.yml)
Runs on every push and PR to main. Validates the full Docker Compose stack by running several smoke tests in sequence:
- build —
docker compose build --no-cache
- dbinitcheck — Runs
dbinit twice to verify idempotency (the init script must succeed on both a fresh and an already-initialized database).
- api-smoke-test — Starts the API with
docker compose up --wait api and checks it becomes healthy.
- dbreadonlycheck — Runs the readonly DB check using the
ci Compose profile.
- sites-smoke-test — Starts the full stack and curls both
http://127.0.0.1:3000 and http://127.0.0.1:3001 to confirm the sites are accessible.
Docker image build (docker_image_build.yml)
Runs on every push to main (and can be triggered manually). Builds and pushes all container images to the GitHub Container Registry (ghcr.io) in parallel using a build matrix:
| Container | Dockerfile | Platforms |
|---|
api | api/Dockerfile | linux/amd64 |
dbseed | api/Dockerfile.dbseed | linux/amd64 |
dbinit | api/dbinit/Dockerfile | linux/amd64 |
partners | Dockerfile.sites.partners | linux/amd64 |
public | Dockerfile.sites.public | linux/amd64 |
infra | infra/Dockerfile | linux/amd64, linux/arm64 |
infra-dev | infra/Dockerfile.dev | linux/amd64, linux/arm64 |
Each image is tagged with both latest and gitsha-<sha>. Layer caching is stored in the registry to speed up subsequent builds.
Infrastructure CI (infra_ci.yml)
Runs on pushes to main and on PRs that modify infra/** or the workflow file itself. Verifies that all OpenTofu files are correctly formatted:
tofu fmt -check -diff -recursive
Runs via the ghcr.io/bloom-housing/bloom/infra-dev container image.
Secret scanning with GitLeaks
The gitleaks.yml workflow runs on every push (not just PRs) using GitLeaks. It scans the full git history (fetch-depth: 0) for accidentally committed secrets such as API keys, tokens, and passwords.
- name: run gitleaks scan
uses: gitleaks/gitleaks-action@v2
GitLeaks requires a GITLEAKS_LICENSE secret to be configured in the repository for use beyond the free tier.
Dependency updates with Dependabot
Dependabot is configured in .github/dependabot.yml to scan npm dependencies weekly across all packages in the monorepo:
| Directory | Description |
|---|
/ | Root workspace dependencies |
/api | API dependencies |
/shared-helpers | Shared helpers package |
/sites/public | Public site dependencies |
/sites/partners | Partners site dependencies |
Update grouping
Dependabot groups minor and patch updates into a single weekly PR to reduce noise:
minor-versions group — All minor and patch version updates are combined into one PR per scan cycle.
minor-security group — Minor and patch security updates are similarly grouped.
- Major version upgrades — Not grouped; each gets its own PR so the breaking change can be reviewed in isolation.
Cypress Cloud
Cypress test runs record to Cypress Cloud when enabled. Recording is disabled by default:
record: false # set this value to true to record videos in cypress cloud
# for debugging purposes - skip passing tests
To enable recording for a run, set record: true in the relevant workflow file. The CYPRESS_RECORD_KEY for each site (PUBLIC_CYPRESS_RECORD_KEY, PARTNERS_CYPRESS_RECORD_KEY) must be configured as a repository secret.
Branch protection
The main branch is protected. Pull requests must have all required CI checks pass before merging. The following checks are effectively required by the workflow configuration:
- Backend Unit Tests
- Backend Integration
- Public Unit / Partners Unit / Shared Helpers Unit
- Public Cypress / Partners Cypress / Partners Cypress (LA)
- Lint
- Docker Compose CI
- GitLeaks
Conventional commits
Commit messages are validated against the Conventional Commits specification via commitlint. This enforces a consistent commit history and enables automated changelog generation.