CI/CD Strategy
Project: Software Design Web Application | Team Size: 6 | Hosting: Gitea (University Server) | CI/CD Platform: Gitea Actions | Duration: 3 Months
1. Purpose
This document defines the Continuous Integration and Continuous Deployment (CI/CD) workflow that all team members must follow throughout the project.
- Ensure every feature is tested before integration.
- Detect bugs as early as possible.
- Prevent broken code from entering the shared codebase.
- Maintain a deployable project at all times.
- Encourage automated testing rather than manual verification.
- Improve software quality and team confidence.
The CI/CD pipeline is a mandatory quality gate for all code entering the repository.
2. What is CI/CD?
- Continuous Integration
- Continuous Deployment
Automatically building and testing the project whenever code is pushed or a Pull Request is opened, instead of waiting until the end of the project to discover problems.
CI answers: "Does this new code still work with the rest of the project?"
Automates delivery of the application after code passes all quality checks — to a development, testing, or demonstration environment. Production deployment (if applicable) should only occur from main.
3. Current CI Pipeline — what actually runs
CI is live via Gitea Actions, split into separate workflow files by concern rather than one monolithic pipeline — this keeps each job fast and relevant to what actually changed:
| Workflow file | What it does |
|---|---|
client-ci.yml | Lints and builds the frontend (client workspace) |
backend-ci.yml | Runs backend service tests |
docker.yml | Builds Docker images |
test.yml | A minimal sanity check (runs echo) confirming the Actions runner itself is working — not a quality gate, just infrastructure verification |
4. Development Workflow
- Pull the latest
develop. - Create a feature branch.
- Implement the assigned feature.
- Write tests for all new code.
- Run tests locally.
- Ensure the project builds successfully.
- Commit changes.
- Push the feature branch.
- Wait for CI pipeline to complete.
- Open a Pull Request only if all checks pass.
- Obtain code review.
- Merge into
develop.
5. Testing Policy
Testing is mandatory. Every feature must include appropriate automated tests. No feature is considered complete until its tests have been written.
If you write code, you must write tests for that code.
Minimum expectations for every new feature: unit tests, edge case testing, error handling tests. Where appropriate, also add integration, API, and UI/component tests.
6. Types of Tests, and the tooling actually in use
| Test Type | Purpose | Tooling in this project |
|---|---|---|
| Unit Tests | Test individual functions or classes in isolation | Backend: Node's built-in node:test + node:assert/strict, per service |
| Integration/API Tests | Verify multiple components / real HTTP routes work together | Backend: real in-process Express servers (app.listen(0)) hit with fetch, no Supertest |
| Component Tests | Verify frontend components behave correctly | Frontend: Vitest (vitest run) |
| End-to-End Tests (optional) | Simulate real user behaviour through the application | Not currently in use |
Backend services follow a dependency-injection pattern — service classes/functions take their dependencies (repository calls, cross-service HTTP clients) as constructor arguments with real defaults, so tests can substitute plain fake objects instead of trying to mock live module bindings. This exists specifically because Node's node:test + tsx's ESM-style module loading don't reliably support t.mock.method() against import * as X module namespaces in this setup — worth knowing before assuming that approach will work in a new service.
7. Local Testing Before Pushing
Before pushing, run the full test suite locally. The project should build successfully and pass all tests. Don't rely on CI to catch mistakes you could've caught locally.
- Project builds successfully
- All automated tests pass
- No linting errors (
npm run lint— oxlint on the frontend, ESLint-family tooling per backend service) - No debugging code remains
- No commented-out code remains
- New functionality includes tests
- Existing tests continue to pass
There's no npm run format:check script — Prettier is installed at the root but only runs via Husky's pre-commit hook (lint-staged, applying prettier --write to staged files automatically). This means formatting is fixed for you at commit time locally, rather than something CI verifies after the fact. If you commit without going through the normal Husky-enabled flow (e.g. --no-verify), nothing else catches unformatted code before merge.
8. Continuous Integration Pipeline — actual stages
The stages that genuinely run, per workflow file:
| Workflow | Stage | What happens |
|---|---|---|
client-ci.yml | Checkout, install | Standard actions/checkout + npm install |
client-ci.yml | Lint | Runs the client's lint script |
client-ci.yml | Build | tsc -b && vite build |
backend-ci.yml | Checkout, install | Standard actions/checkout + npm install |
backend-ci.yml | Test | Runs each backend service's npm run test (tsx --test) |
docker.yml | Build images | Builds Docker images per service, per the project's Dockerfiles |
There is currently no separate static-analysis-only stage, no coverage generation stage, and no formatting-check stage in CI — see Sections 7 and 11 for what's covered instead/elsewhere.
9. Pull Request Requirements
A Pull Request cannot be merged unless:
- CI pipeline succeeds (
client-ci.yml,backend-ci.yml, anddocker.ymlall pass for whatever they cover). - Required review is approved.
If any CI job fails, the Pull Request must remain open until resolved.
10. Branch Protection Rules
- main
- develop
- No direct pushes
- Pull Requests only
- Passing CI required
- Review approval required
- No direct pushes
- Pull Requests only
- Passing CI required
- Review approval required
These protections ensure that all shared code has been automatically validated before it reaches either branch.
11. Testing Policy
Every PR that adds or changes backend logic should include tests covering:
- The main success case
- At least one meaningful failure case (not just "throws an error" — the specific failure a user could actually trigger: wrong owner, missing resource, invalid input, already-in-a-terminal-state, etc.)
- Any ownership/permission check, tested from both sides (the allowed user succeeds, a different user is rejected)
Frontend components with real logic (not just static markup) should have at least a basic render/interaction test where practical.
What review actually checks, in place of a coverage number: does this PR's tests genuinely exercise the interesting cases for what changed, or only the happy path? Reviewers should say so explicitly in review comments if test coverage looks thin for what's being added — see Git Methodology's Code Review section.
12. Handling Failed Tests
- Read the error logs.
- Identify the failing test or build step.
- Fix the issue locally.
- Run the full test suite again.
- Commit the fix.
- Push the updated branch.
- Verify that the pipeline passes.
Do not merge code with failing tests.
13. Testing Responsibilities
Every developer is responsible for writing tests for their own code, updating existing tests when modifying functionality, keeping tests maintainable, avoiding unnecessary duplication, and keeping tests fast and deterministic. Reviewers verify adequate tests accompany each Pull Request.
14. Actual Project Structure
Backend services are independent, each with their own tests folder — there is no single shared tests/ directory at the repo root. See the Project Plan's folder structure section for the full layout; the CI-relevant pieces:
backend/services/<service_name>/
src/
tests/ # node:test files for this service only
package.json # "test": "tsx --test src/tests/*.test.ts"
client/
src/
(component tests colocated or in a test folder, run via `vitest run`)
.gitea/
workflows/
client-ci.yml
backend-ci.yml
docker.yml
test.yml
15. Example CI Pipeline — backend
16. Actual Gitea Workflow — backend-ci.yml (representative example)
name: backend-ci
on:
push:
branches:
- feature/**
- bugfix/**
- refactor/**
- docs/**
- test/**
pull_request:
branches:
- develop
- main
paths:
- "backend/**"
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
- name: Install Dependencies
run: npm install
- name: Run Lint
run: npm run lint --workspaces --if-present
- name: Run Tests
run: npm run test --workspaces --if-present
- name: Build
run: npm run build --workspaces --if-present
client-ci.yml follows the same shape, scoped to the client workspace only, without a test step (frontend tests are run separately via vitest run, not currently wired into this workflow — worth adding if frontend test coverage grows).
17. Definition of Done
A task is only complete when:
- The feature has been implemented.
- Tests have been written.
- All tests pass locally.
- CI pipeline passes (
client-ci.yml/backend-ci.yml/docker.yml, as relevant). - Pull Request has been reviewed.
- Pull Request has been approved.
- Pull Request has been merged.
18. Summary
| Rule | Requirement |
|---|---|
| Write tests for all new code | ✅ Required |
| Run tests before pushing | ✅ Required |
| CI pipeline must pass | ✅ Required |
| Pull Request required | ✅ Required |
| Code review required | ✅ Required |
No direct pushes to develop | ✅ Required |
No direct pushes to main | ✅ Required |
| Merge only after passing CI | ✅ Required |
| Fix failing tests immediately | ✅ Required |
| Coverage tooling / thresholds | ⚠️ Not configured |
| Automated formatting check in CI | ⚠️ Not configured — enforced locally via Husky pre-commit instead |
19. Team Agreement
By contributing to this repository, each team member agrees to:
- Write automated tests for all new functionality.
- Never merge code that fails the CI pipeline.
- Run tests locally before pushing changes.
- Review teammates' Pull Requests thoroughly.
- Help maintain a stable, deployable codebase throughout the project.
Following this CI/CD strategy will help ensure a reliable development process, reduce integration issues, and maintain high software quality from the first sprint to the final release.