Continuous integration¶
This guide explains how HARP’s GitHub Actions CI/CD pipeline works and how to troubleshoot failures.
Overview¶
HARP uses GitHub Actions for continuous integration. The CI pipeline is defined in .github/workflows/cicd.yml and runs automatically on:
Every push to any branch
Every pull request
Semantic version tags (e.g.,
0.9.0,0.9.0-rc1)
The pipeline consists of multiple jobs that run in parallel to provide fast feedback.
CI jobs¶
Test jobs¶
All test jobs run directly on the runner using uv (no Docker containers):
- test-backend-core
Tests the core
harp/directoryRuns
uv run pytest harp -n auto -m 'not subprocess'Uses parallel execution (
-n auto)Skips subprocess-marked tests
Python 3.13
- test-backend-apps
Tests the
harp_apps/directoryRuns
uv run pytest harp_apps -n 1 -m 'not subprocess'Single worker (
-n 1) due to testcontainersIncludes tests for dashboard, proxy, storage, etc.
Python 3.13
- test-backend-e2e
Tests the
tests/directory (end-to-end tests)Runs
uv run pytest tests -n 1 -m 'not subprocess'Single worker (
-n 1)Tests complete workflows
Python 3.13
- test-frontend-unit
Runs frontend unit tests with Vitest
Uses pnpm and Node.js 24
Runs
pnpm test:unitinharp_apps/dashboard/frontend
Documentation and UI jobs¶
- build-documentation
Builds Sphinx documentation
Runs
make docsOutputs to
harp-docartifact (30 day retention)Continues on error (won’t fail the build)
- build-storybook
Builds Ladle (Storybook) UI component explorer
Runs
pnpm ui:buildOutputs to
harp-uiartifact (30 day retention)Continues on error (won’t fail the build)
Build and release jobs¶
- build-python-package
Builds the Python wheel
Runs
make clean-dist wheelBundles frontend assets
Validates with twine
Uploads wheel artifact (7 day retention)
- all-checks-passed
Consolidation job ensuring all tests pass
Depends on all test jobs and package build
Tests wheel installation on Python 3.13
Must pass before any releases
- build-docker-image-development
Builds Docker image for development branches
Only runs for non-tag pushes
Uses local wheel from build
Pushes to GHCR for all commits
Version branches (e.g.,
0.9) also push to Docker Hub as<version>-git
- publish-to-testpypi
Publishes to Test PyPI
Only runs for release tags
Uses trusted publishing (OIDC)
- publish-to-pypi
Publishes to PyPI
Runs after TestPyPI succeeds
Uses trusted publishing (OIDC)
- build-docker-image-release
Builds Docker image from PyPI release
Only runs for release tags
Uses
Dockerfile.pypito install from PyPIMainline releases (
X.Y.Z) get multiple tags:X,X.Y,X.Y.ZPre-releases (
X.Y.Z-rc1) get exact tag only0.9.xreleases also tagged as:latest
- create-github-release
Creates GitHub release
Converts changelog RST to Markdown
Attaches wheel artifact
Marks pre-releases appropriately
How tests run in CI¶
Test execution¶
Tests run directly on the GitHub Actions runner using uv:
# Install uv
uses: astral-sh/setup-uv@v4
# Create required directories
mkdir -p harp_apps/dashboard/web
# Install dependencies
uv sync
# Run tests
uv run pytest harp -n auto -m 'not subprocess'
This approach is simpler and faster than using Docker containers.
Testcontainers support¶
Tests that use testcontainers (for PostgreSQL, MySQL, etc.) work automatically on GitHub Actions runners because:
Docker is pre-installed on ubuntu-latest runners
Tests have access to the Docker socket
TESTCONTAINERS_RYUK_DISABLED=trueis set for faster cleanup
Environment variables¶
Key environment variables set by the CI:
CI: Automatically set by GitHub ActionsTESTCONTAINERS_RYUK_DISABLED: Set totruefor faster cleanupDOCKER_BUILDKIT: Enables BuildKit for Docker builds
Test parallelization¶
Core tests (
harp/): Run with-n autofor maximum parallelismApp tests (
harp_apps/): Run with-n 1due to testcontainers database conflictsE2E tests (
tests/): Run with-n 1for stability
Frontend tests¶
Frontend tests use pnpm and Node.js 24:
cd harp_apps/dashboard/frontend
pnpm install --ignore-workspace
pnpm test:unit
Troubleshooting CI failures¶
Common issues¶
Test failures with testcontainers
If database tests fail, check:
Docker service is running on the runner
No port conflicts between parallel tests (use
-n 1if needed)Testcontainers can access the Docker socket
Frontend test failures
If frontend tests fail:
Check Node.js version matches (24)
Ensure pnpm version matches (10)
Verify
harp_apps/dashboard/webdirectory exists
Build failures
If wheel build fails:
Check that frontend assets are bundled correctly
Verify version validation passes for tags
Check twine validation output
Debugging locally¶
To reproduce CI failures locally:
# 1. Ensure web directory exists
mkdir -p harp_apps/dashboard/web
# 2. Install dependencies (matches CI)
uv sync
# 3. Run tests exactly as CI does
uv run pytest harp -n auto -m 'not subprocess'
uv run pytest harp_apps -n 1 -m 'not subprocess'
uv run pytest tests -n 1 -m 'not subprocess'
# 4. Frontend tests
cd harp_apps/dashboard/frontend
pnpm install --ignore-workspace
pnpm test:unit
Running specific job tests¶
To simulate specific CI jobs:
# test-backend-core
uv run pytest harp -n auto -m 'not subprocess'
# test-backend-apps
uv run pytest harp_apps -n 1 -m 'not subprocess'
# test-backend-e2e
uv run pytest tests -n 1 -m 'not subprocess'
# test-frontend-unit
cd harp_apps/dashboard/frontend && pnpm test:unit
Checking CI logs¶
Use GitHub CLI to view logs:
# List recent runs
gh run list --limit 10
# View specific run
gh run view <run-id>
# View failed job logs
gh run view <run-id> --log-failed
# Watch a running build
gh run watch <run-id>
CI/CD best practices¶
For contributors¶
Run tests locally before pushing
uv run pytest harp harp_apps tests -n auto -m 'not subprocess'
Run the same commands as CI
Match the exact CI commands to catch failures early.
Check CI logs immediately after pushing
Failed CI checks block merging, so fix issues quickly.
Ensure ``harp_apps/dashboard/web`` exists
Create it with
mkdir -p harp_apps/dashboard/webif missing.
For maintainers¶
Keep CI fast
Use parallelism for core tests (
-n auto)Run sequential tests in parallel jobs
Use GitHub Actions caching
Make failures actionable
CI output should clearly show what failed
Error messages should suggest fixes
Document common issues in this guide
Version compatibility
Currently testing Python 3.13
Keep dependencies in sync with
uv.lockTest wheel installation before release
CI configuration reference¶
Key files¶
.github/workflows/cicd.yml: Main CI/CD workflowMakefile: Build and test automationDockerfile: Production image (uses local wheel)Dockerfile.pypi: Release image (installs from PyPI)pyproject.toml: Python dependencies and tool configuv.lock: Locked Python dependenciesharp_apps/dashboard/frontend/package.json: Frontend dependenciesbin/validate_version: Version validation script for releases
GitHub Actions secrets and variables¶
Required configuration:
GITHUB_TOKEN: Automatic, used for GHCR push and releasesDOCKERHUB_USERNAME(variable): Docker Hub usernameDOCKERHUB_TOKEN(secret): Docker Hub tokenPyPI trusted publishing configured for
harp-proxypackage
Workflow triggers¶
on:
push:
branches: ['**'] # All branches
tags:
- '*.*.*' # Semantic versions (0.9.0)
- '*.*.*-*' # Pre-releases (0.9.0-rc1)
pull_request: # All PRs
Release conditions¶
Different jobs run based on push type:
- Development images (
build-docker-image-development): All non-tag pushes
Version branches (e.g.,
0.9) also push to Docker Hub as<version>-git
- PyPI releases (
publish-to-pypi): Only semver tags (
0.9.0,0.9.0-rc1, etc.)
- Release images (
build-docker-image-release): Only after PyPI publish succeeds
Installs from PyPI, not local wheel
Further reading¶
Makefile - Complete Makefile reference