Skip to main content

CI/CD Pipelines

Overview

This article documents the continuous integration and deployment pipeline for the uzdavines.ai platform. The pipeline uses GitHub Actions to validate pull requests, build applications, and trigger deployments to Render -- ensuring that only tested, passing code reaches production.

  • Why it matters: Automated CI/CD eliminates manual deployment steps, reduces human error, and provides a consistent, auditable path from code change to production. Path-filtered workflows ensure that only affected applications are rebuilt, saving time and resources.
  • What you will learn: The end-to-end pipeline flow from PR validation through production deployment, how path filtering targets specific applications, the relationship between GitHub Actions workflows and Render deploy hooks, and key design decisions like disabled auto-deploy and squash merge strategy.

Deployments are fully automated through GitHub Actions. Each application has two workflows: a PR check (validation on pull requests) and a deploy (production deployment on merge to main).

Pipeline Flow

Developer pushes branch

v
Open Pull Request

v
pr-check-{app}.yml <-- Runs on PR to main
┌─────────────────┐ (path-filtered)
│ Checkout code │
│ Setup Node 20 │
│ npm ci │
│ npm run build │
└─────────────────┘

v
PR approved + merged

v
deploy-{app}.yml <-- Runs on push to main
┌─────────────────┐ (path-filtered)
│ Checkout code │
│ Setup Node 20 │
│ npm ci │
│ npm run build │
│ curl deploy hook│ ───────> Render builds + deploys
└─────────────────┘

Path Filtering

Each workflow only triggers when relevant files change:

WorkflowTriggers On
deploy-homepage.ymlapps/homepage/**, packages/shared/**
deploy-docs.ymlapps/docs/**, packages/shared/**
deploy-resume.ymlapps/resume/**, packages/shared/**

Changes to packages/shared/** trigger all workflows since shared code affects every application.

Workflow Files

PR Check

Validates that the application builds successfully. Runs on pull requests targeting main.

name: PR Check — {App}

on:
pull_request:
branches: [main]
paths:
- "apps/{app}/**"
- "packages/shared/**"

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npm run build

Production Deploy

Builds the application and triggers a Render deploy hook. Runs when changes are pushed to main.

name: Deploy {App}

on:
push:
branches: [main]
paths:
- "apps/{app}/**"
- "packages/shared/**"

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npm run build
- name: Trigger Render Deploy
run: curl -X POST "${{ secrets.RENDER_{APP}_DEPLOY_HOOK }}"

Render Deploy Hooks

Deploy hooks are stored as GitHub repository secrets:

Secret NameService
RENDER_HOMEPAGE_DEPLOY_HOOKHomepage (Web Service)
RENDER_DOCS_DEPLOY_HOOKDocs (Static Site)
RENDER_RESUME_DEPLOY_HOOKResume (Web Service)

When the deploy hook is called, Render pulls the latest code from the repository and runs its own build process.

Render Build Configuration

ApplicationBuild CommandOutput
Homepagenpm install && npm run build.next/
Docsnpm install && npm run buildbuild/
Resumenpm install && npm run build.next/

Key Design Decisions

  • Render auto-deploy is disabled — GitHub Actions controls all deployments for consistency and visibility
  • Path filtering prevents unnecessary builds when unrelated apps change
  • Squash merge keeps main history clean with one commit per feature
  • onBrokenLinks: 'throw' in Docusaurus catches broken documentation links at build time, preventing deployment of broken docs