Skip to main content
  1. Posts/

Building a Complete CI/CD Pipeline: Introduction

Quentin Marques
Author
Quentin Marques
Future Architect in Cybersecurity
Table of Contents

Introduction
#

CI/CD β€” short for Continuous Integration and Continuous Deployment β€” is the backbone of modern DevOps.
It ensures that code changes are automatically built, tested, and delivered to production with minimal manual effort.

CI/CD is part of the Infrastructure as Code (IaC) philosophy: your pipelines, configuration, and deployments are written as code, version-controlled, and reproducible.

In this article, I will explain each step of a CI/CD pipeline, illustrating it with my self-hosted homelab setup using Gitea, Hugo, Docker, Semaphore, and Ansible.


🧠 Planning
#

The first phase is planning. Here you define what needs to be done and how.
Good planning reduces ambiguities for the next phases.

  • Decide on tasks and specifications.
  • Choose your tools: a Kanban board (GitLab, Trello) works well.

GitLab boards are great to organize and track features, bugs, and improvements.


πŸ’» Coding
#

The coding phase is where you implement your planned tasks.

  • Use an IDE to write your code efficiently.
  • Use Git for version control and a remote Git server (GitHub, GitLab, or Gitea) to store code and collaborate.

Each commit on your remote server can trigger pipelines that build, test, release, and deploy automatically.

For Gitea, this requires:

  1. A .gitea/workflows folder in your repository.
  2. A Gitea Runner configured to execute jobs.

Example pipeline structure:

name: My Pipeline
run-name: Build & Deploy
on:
  push:
    paths:
      - <folder-to-watch>
    branches:
      - main
jobs:
  build:
    runs-on: <runner>
    container:
      image: <build-image>
    steps:
      (...)
  test:
    runs-on: <runner>
    needs: build
    steps:
      (...)
  release:
    runs-on: <runner>
    needs: test
    steps:
      (...)
  deploy:
    runs-on: <runner>
    needs: release
    steps:
      (...)

In this article, I will use my blog as an example, though simpler setups exist.


πŸ—οΈ Build
#

During the build phase, your code is compiled, packaged, or generated.

For my Hugo blog:

hugo --minify
  • The generated files are stored as artifacts for later stages:
- name: Upload files
  uses: actions/upload-artifact@v3
  with:
    name: hugo-artifacts
    path: public/

Artifacts allow later jobs (release, deploy) to access the build output reliably.


πŸ§ͺ Test
#

Testing ensures your code is secure, functional, and maintainable.

  • Run unit tests, integration tests, linters, SAST/DAST, or custom checks.
  • For my blog, tests could include grammar checks or broken link detection.

πŸ“¦ Release
#

The release phase packages your artifacts for deployment.

  • First, download the artifacts:
- name: Download files
  uses: actions/download-artifact@v3
  with:
    name: hugo-artifacts
    path: public/
  • Then, create a Docker image with your files and push it to your registry:
- name: Push Docker image
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: |
      <GITEA_URL>/${{ secrets.REGISTRY_USER }}/blog:<version>

Notes:

  • Secrets store credentials securely (REGISTRY_USER in Gitea).
  • The version is automatically incremented from the last tag.

πŸš€ Deploy
#

Deployment can be automated using tools like ArgoCD or Semaphore.

  • In my setup, I trigger deployments via webhooks from the CI pipeline.
  • Manual deployment is also possible if automation is not required.

πŸ“ˆ Monitor
#

Monitoring ensures your application and infrastructure remain healthy and performant.

  • Define what to monitor (uptime, metrics, logs).
  • Choose your tools: dashboards, notifications, alerting.

In my homelab:

  • Kuma Uptime for availability.
  • Prometheus + Grafana for metrics, with Discord notifications via webhooks.

βš™οΈ Operate
#

Post-deployment operations ensure the system remains updated and consistent.

  • In my homelab, I use Ansible and Semaphore to manage configuration and updates automatically.

Conclusion
#

CI/CD is like an assembly line for software: each job handles a specific step, moving your code from development to production smoothly and automatically.


More Resources
#