Skip to main content

CI/CD

AgentHiFive uses GitHub Actions for continuous integration and continuous deployment. The CI pipeline runs on every pull request and push to main. The CD pipeline builds Docker images and deploys to Azure on manual dispatch.

CI Pipeline

Workflow: .github/workflows/ci.yml Triggers: Manual dispatch (workflow_dispatch) Concurrency: Grouped by ref with cancel-in-progress enabled

Jobs

The CI pipeline runs two parallel jobs: lint-typecheck-test (main CI with PostgreSQL) and docs-build (Docusaurus build verification). The main job has six stages:

Install --> Build Packages --> Apply Migrations --> Check Drift --> Lint + Typecheck --> Test

Stages

1. Install Dependencies

- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile

Dependencies are installed with --frozen-lockfile to ensure reproducible builds. The pnpm cache is stored between runs for faster installs.

2. Build Shared Packages

- run: pnpm turbo run build --filter='./packages/*'

Shared packages (contracts, security, sdk, oauth-connectors) are built before linting or testing, since apps import from their build output.

3. Apply Migrations and Check Drift

- run: pnpm --filter @agenthifive/api run migrate-apply
- run: pnpm --filter @agenthifive/api exec drizzle-kit generate --name ci-drift-check

Migration files are applied to the test database, then drizzle-kit generate verifies no schema drift. If new migration files are generated, the build fails — run make migrate-generate locally and commit.

4. Lint and Typecheck

- run: pnpm turbo run lint
- run: pnpm turbo run typecheck

These commands run across the entire monorepo via Turborepo.

5. Test

- run: pnpm turbo run test
env:
DATABASE_URL: postgresql://test:test_password@localhost:5433/agenthifive_test

Tests run against a PostgreSQL 15 service container provisioned by GitHub Actions:

services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_DB: agenthifive_test
POSTGRES_USER: test
POSTGRES_PASSWORD: test_password
ports:
- 5433:5432
options: >-
--health-cmd "pg_isready -U test -d agenthifive_test"
--health-interval 2s
--health-timeout 5s
--health-retries 10

Timeout

The CI job has a 15-minute timeout to prevent hung builds from consuming runner minutes.

CD Pipeline

Workflow: .github/workflows/cd.yml Triggers: Manual dispatch (workflow_dispatch) Concurrency: Grouped by ref, cancel-in-progress disabled (deployments should complete)

Overview

The CD pipeline builds Docker images and static SPAs, then deploys to the Azure integration environment. Authentication uses OIDC federation (no stored credentials).

Architecture

Build Phase (parallel):
├── Build API Image → Push to Azure Container Registry (acrah5.azurecr.io)
├── Build Web SPA → Upload artifact
├── Build Admin SPA → Upload artifact
└── Build Docs → Upload artifact

Deploy Phase (sequential):
1. Update migration job image → Run DB migration
2. Update API container app revision → Wait for healthy
3. Upload Web SPA to Azure Storage
4. Upload Docs to Azure Storage
5. Upload Admin SPA to Azure Storage

Authentication

- uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
- run: az acr login --name acrah5
Required Variables

Three repository variables (not secrets) must be configured in GitHub:

  • AZURE_CLIENT_ID — Service principal client ID (OIDC federation)
  • AZURE_TENANT_ID — Azure AD tenant ID
  • AZURE_SUBSCRIPTION_ID — Azure subscription ID

No stored secrets are needed — OIDC federation handles authentication.

Build and Push Images

The API Docker image is built and tagged with both the commit SHA and latest:

ServiceDockerfileImage
APIapps/api/Dockerfileacrah5.azurecr.io/api
- uses: docker/build-push-action@v5
with:
context: .
file: apps/api/Dockerfile
push: true
tags: |
acrah5.azurecr.io/api:${{ github.sha }}
acrah5.azurecr.io/api:latest
build-args: |
BUILD_NUMBER=${{ env.BUILD_NUMBER }}
BUILD_DATE=${{ env.BUILD_DATE }}
GIT_SHA=${{ env.GIT_SHA }}
cache-from: type=gha
cache-to: type=gha,mode=max

Web, Docs, and Admin SPAs are built as static exports and uploaded to Azure Storage accounts (not containerized).

Deploy to Integration

The deploy job runs after all builds succeed:

  1. Database migration — Updates the Azure Container Apps migration job with the new image and runs it. Polls for completion (max 5 min).
  2. API revision — Updates the Azure Container Apps API service with the new image. Waits for the revision to report healthy.
  3. SPA uploads — Uploads Web, Docs, and Admin static exports to their respective Azure Storage $web containers.

Promotion

A separate promote workflow (.github/workflows/promote.yml) promotes a successful integration build to staging or production without rebuilding.

Timeout

Individual jobs have timeouts: 20 min (API build), 15 min (SPA builds), 15 min (deploy).

Local Equivalents

CI StepLocal Command
Installpnpm install
Build packagespnpm turbo run build --filter='./packages/*'
Lintpnpm lint
Typecheckpnpm typecheck
Testcd apps/api && bash run-tests.sh
Docker builddocker build -f apps/api/Dockerfile .