Deployment Pipeline - bcgov/eagle-dev-guides GitHub Wiki
Deployment Pipeline
This document describes the CI/CD pipeline used by Eagle applications for building, testing, and deploying to OpenShift.
Quick Reference
Deploy to Test/Prod requires semantic version tag (v1.2.3 format):
# Check existing tags
git ls-remote --tags origin | grep -E 'v[0-9]+\.[0-9]+\.[0-9]+$' | tail -3
# Deploy to test (workflow creates Git tag and GitHub release automatically)
gh workflow run "Deploy to Test" --repo bcgov/eagle-api -f version=v2.1.1
# Deploy to prod (same version)
gh workflow run "Deploy to Prod" --repo bcgov/eagle-api -f version=v2.1.1
IMPORTANT: Do NOT create Git tags manually. The "Deploy to Test" workflow creates them automatically if they don't exist.
Repository Deployment Differences
All Eagle repositories now use a consistent deployment pattern:
| Repository | Deployment Type | Managed By | Workflow Pattern |
|---|---|---|---|
| eagle-public | Deployment | Helm | Build → Scan → Push → Helm upgrade |
| eagle-admin | Deployment | Helm | Build → Scan → Push → Helm upgrade |
| eagle-api | Deployment | Helm | Build → Scan → Push → Helm upgrade |
| eao-nginx (rproxy) | Deployment | Helm | Build → Scan → Push → Helm upgrade |
Note: eagle-api (Feb 2026) and eao-nginx (Feb 2026) were migrated from DeploymentConfig + S2I builds to the modern Helm + Dockerfile pattern.
Repository-Specific Details
eagle-api:
- Node Version: Node 24 (eagle-public also uses Node 24)
- Test Framework: Mocha (eagle-public uses Vitest, eagle-admin uses Karma)
- Dependencies: Modern packages - axios ^1.8.2, bcryptjs, jwks-rsa ^3.2.2, sharp ~0.33.5
eao-nginx (rproxy):
- Base Image: nginx:1.27-alpine (routes to eagle-public, eagle-admin, eagle-api, penguin-analytics)
- See eao-nginx README for configuration details
Pipeline Overview (eagle-public / eagle-admin / eagle-api)
flowchart TD
subgraph PR["Pull Request Phase"]
A[Push to feature branch] --> B[PR Checks Workflow]
B --> C{Lint}
B --> D{Test}
B --> E{Build}
C & D & E -->|All Pass| F[Ready for Review]
end
subgraph Dev["Deploy to Dev"]
G[Merge to develop] --> H[Install Dependencies]
H --> I[Lint + Test + Build]
I --> J[🔒 Trivy Security Scan]
J -->|CRITICAL/HIGH found| X[❌ Deployment Blocked]
J -->|Clean| K[Push to Registry]
K --> L[Helm Deploy to Dev]
L --> M[Tag: dev, ci-latest, SHA]
end
subgraph Test["Deploy to Test"]
N[Manual Trigger] --> O[Input Version: v1.2.3]
O --> P{Git Tag Exists?}
P -->|No| Q[Create Git Tag]
Q --> R[Create GitHub Release]
R --> S[Tag ci-latest → v1.2.3]
P -->|Yes| T[Verify Image Exists]
S --> U[Tag v1.2.3 → test]
T --> U
U --> V[Helm Upgrade]
end
subgraph Prod["Deploy to Prod"]
W[Manual Trigger] --> Y[Input Version: v1.2.3]
Y --> Z[Verify Git Tag Exists]
Z --> AA[Verify Image Tag Exists]
AA --> AB[Tag v1.2.3 → prod]
AB --> AC[Rollout Restart]
end
F -.->|Merge| G
M -.->|Manual| N
V -.->|Manual| W
Workflow Details
1. PR Checks (pr.yaml)
Trigger: Pull request to develop branch
| Job | Purpose | Node Version |
|---|---|---|
| Install | Cache dependencies | 24.x (eagle-public), 22.x (eagle-admin/api) |
| Lint | ESLint code quality | 24.x (eagle-public), 22.x (eagle-admin/api) |
| Test | Unit tests: Vitest (eagle-public), Karma (eagle-admin), Mocha (eagle-api) | 24.x (eagle-public), 22.x (eagle-admin/api) |
| Build | Production build | 24.x (eagle-public), 22.x (eagle-admin/api) |
All jobs must pass before merge is allowed.
2. Deploy to Dev (deploy-to-dev.yaml)
Trigger: Push to develop branch (or manual dispatch)
flowchart LR
subgraph Jobs
Install --> Lint & Test & Build
Lint & Test & Build --> Scan
Scan --> Push
Push --> Deploy
end
| Job | Description |
|---|---|
| Install | Install and cache yarn dependencies |
| Lint | Run ESLint |
| Test | Run unit tests |
| Build | Build production bundle |
| Scan | 🔒 Trivy vulnerability scan (blocks on CRITICAL/HIGH) |
| Push | Push image with tags: dev, ci-latest, {SHA} |
| Deploy | Helm upgrade to dev namespace + tag images in ImageStream |
Security Scanning
The Trivy scan step blocks deployment if CRITICAL or HIGH severity vulnerabilities are found:
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.IMAGE_NAME }}:${{ steps.short-sha.outputs.SHA }}
exit-code: '1'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
3. Deploy to Test (deploy-to-test.yaml)
Trigger: Manual workflow dispatch with version input
Input Required:
version: Semantic version tag (e.g.,v1.2.3)
Workflow Logic
flowchart TD
A[Input: v1.2.3] --> B{Validate Format}
B -->|Invalid| X[❌ Error: Must be v#.#.#]
B -->|Valid| C{Check if Tag Exists}
C -->|New Version| D[Create Git Tag]
D --> E[Create GitHub Release]
E --> F[Tag ci-latest → v1.2.3]
C -->|Existing Version| G[Verify Image Exists]
G -->|Not Found| Y[❌ Error: Image missing]
G -->|Found| H[Rollback Mode]
F --> I[Tag v1.2.3 → test]
H --> I
I --> J[Helm Upgrade to Test]
New Deployment Flow:
- Validates version format (
v1.2.3) - Creates Git tag on current
developHEAD - Creates GitHub Release with auto-generated notes
- Tags
ci-latestimage asv1.2.3 - Tags
v1.2.3astest - Installs Helm
- Runs
helm upgrade --installwithvalues-test.yaml - Updates deployment configuration and pulls new image
Rollback Flow (existing version):
- Validates version format
- Detects tag already exists
- Verifies image tag exists in registry
- Tags existing
v1.2.3astest - Installs Helm
- Runs
helm upgrade --installto ensure correct configuration - Deploys existing version
Why Helm in Test Workflow?
Unlike dev (which runs Helm on every merge), test deployments are infrequent. Using Helm ensures:
- Reproducibility: Deployment can be fully recreated if deleted
- Configuration Consistency: Environment-specific values always applied
- Self-healing: Corrects any manual configuration drift
This is especially important for eagle-api which has complex secret references that differ between environments.
4. Deploy to Prod (deploy-to-prod.yaml)
Trigger: Manual workflow dispatch with version input
Input Required:
version: Semantic version tag (e.g.,v1.2.3)
Requirements
- Git tag must exist (created during test deployment)
- Image tag must exist in registry
flowchart TD
A[Input: v1.2.3] --> B{Validate Format}
B -->|Invalid| X[❌ Error]
B -->|Valid| C{Git Tag Exists?}
C -->|No| Y[❌ Error: Deploy to test first]
C -->|Yes| D{Image Tag Exists?}
D -->|No| Z[❌ Error: Image not found]
D -->|Yes| E[Tag v1.2.3 → prod]
E --> F[Rollout Restart]
Image Tagging Strategy
flowchart LR
subgraph "Dev Deploy"
A[Build] --> B[dev]
A --> C[ci-latest]
A --> D[abc1234]
end
subgraph "Test Deploy"
C --> E[v1.2.3]
E --> F[test]
end
subgraph "Prod Deploy"
E --> G[prod]
end
| Tag | When Created | Mutable | Purpose |
|---|---|---|---|
dev |
Every dev deploy | Yes | Latest dev build |
ci-latest |
Every dev deploy | Yes | Promotion candidate |
{SHA} |
Every dev deploy | No | Audit trail |
v1.2.3 |
Test deploy (new) | No | Release version |
test |
Every test deploy | Yes | Current test image |
prod |
Every prod deploy | Yes | Current prod image |
Environment URLs
| Environment | Public | Admin | API |
|---|---|---|---|
| Dev | eagle-dev.apps.silver... | /admin/ | /api/ |
| Test | eagle-test.apps.silver... | /admin/ | /api/ |
| Prod | projects.eao.gov.bc.ca | /admin/ | /api/ |
Running Workflows
Deploy to Dev
Automatic on merge to develop, or manual:
- Go to Actions → Deploy to Dev
- Click Run workflow
- Select branch (usually
develop)
Deploy to Test
- Go to Actions → Deploy to Test
- Click Run workflow
- Enter version (e.g.,
v1.2.3) - Click Run workflow
Deploy to Prod
- Go to Actions → Deploy to Prod
- Click Run workflow
- Enter same version deployed to test (e.g.,
v1.2.3) - Click Run workflow
Troubleshooting
Trivy Scan Failing
The build is blocked due to security vulnerabilities. Options:
- Update base image or dependencies to patch vulnerabilities
- Add
apk upgrade --no-cachein Dockerfile to get latest patches - Review vulnerabilities - if false positives, update
.trivyignore
Deploy Failing - Deployment Not Found
Test workflows now use helm upgrade --install which will automatically create the deployment if it doesn't exist. This ensures reproducibility - if the deployment is accidentally deleted, the next workflow run will recreate it with the correct configuration.
For first-time setup:
- Ensure required secrets exist in the namespace (see Helm Charts)
- Run the deploy-to-test workflow with a version tag
- Helm will create all resources (Deployment, Service, Route, ConfigMap, PVC)
Version Tag Already Exists
This is normal for rollbacks. The workflow will:
- Skip release creation
- Verify the image tag exists
- Deploy the existing version
Related Documentation
- Helm Charts - Kubernetes deployment configuration
- Rollback Procedures - Step-by-step rollback guide
- Architecture Overview - System architecture