CI/CD Pipeline
How It Works
Every deployment runs on a GitHub Actions ubuntu-latest runner and follows this sequence:
| Step | Action | Detail |
|---|---|---|
| 1 | Checkout | Clones the repo at the triggering commit |
| 2 | Setup Node 22 | Installs Node.js with npm cache enabled |
| 3 | Install deps | Runs npm ci — exact versions from package-lock.json |
| 4 | Build | Runs npm run build — VITE_* secrets injected here from GitHub Environment secrets |
| 5 | Extract commit | Reads last commit subject for the Slack message |
| 6 | Deploy | Uploads dist/ to Cloudflare Pages via cloudflare/pages-action@v1 |
| 7 | Notify Slack | Sends success (green) or failure (red) message |
A full run takes 35–50 seconds.
How Secrets Are Injected
Vite replaces all import.meta.env.VITE_* references at build time with their literal values:
ts
// Source code
const client = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
})
// After build (staging)
const client = axios.create({
baseURL: "https://staging-arc.semalink.africa",
})GitHub supplies the values from the scoped Environment secrets. The workflow declares environment: staging (or test / prod), which loads that environment's secrets for the run. No secret from one environment is ever available to another.
Triggers
Each workflow triggers on:
- Push to the corresponding branch (
staging,test,prod) - Manual dispatch via GitHub → Actions → Run workflow
Production vs Preview Deployments
Cloudflare Pages has two deployment types:
- Production — served on the custom domain (
staging-app.semalink.africa) - Preview — served on a one-off
*.pages.devURL only
All three workflows use branch: main and each project has production_branch set to main via the Cloudflare API. This ensures every CI upload goes to production, not a preview.