Skip to content
Français
CI/CD & Automatisation medium

Getting started with GitHub Actions: CI/CD pipelines and security

20 min de lecture

Read this page in French

Before creating your first workflow, let us take the time to understand what a CI/CD pipeline is, how GitHub Actions works, and above all why security has to be your priority from day one.

What is a CI/CD pipeline?

Picture yourself working on an application. On every code change, you have to:

  1. Check that the code compiles and that the tests pass
  2. Build the application (an executable, a Docker image, and so on)
  3. Deploy it to an environment (staging, production, and so on)

Doing all of that by hand is slow, repetitive and error-prone. A CI/CD pipeline automates those steps.

What does CI/CD stand for?

  • CI (Continuous Integration): on every change, the code is tested and integrated automatically. Bugs surface very early.
  • CD (Continuous Delivery/Deployment): validated code is automatically prepared for, or pushed to, the target environments.

The idea is small frequent changes, validated automatically, rather than large risky releases once a month.

GitHub Actions: the engine behind your pipelines

GitHub Actions is the CI/CD service built into GitHub. When something happens on your repository (a push, a pull request, a release), GitHub Actions can run tasks automatically.

CI/CD pipeline with GitHub Actions

Concretely:

  1. An event triggers the pipeline (you push code)
  2. A workflow defines what to do (test, build, deploy)
  3. A runner executes the tasks (a virtual machine provided by GitHub)
  4. A result comes out (success or failure, artifacts, a deployment)

Why security comes before everything else

Attacks on CI/CD pipelines have exploded. In March 2025, a compromised GitHub action (tj-actions/changed-files) made it possible to exfiltrate the secrets of thousands of repositories within hours. The attack covered every version from v1 to v45, including the ones considered "stable".

The problem with mutable tags

When you write uses: actions/checkout@v4, you are trusting a Git tag. But a tag can be moved at any time by the maintainer.

Mutable tag versus immutable SHA

Here is what can happen:

  1. You use actions/checkout@v4 in your workflow
  2. The action works perfectly for months
  3. An attacker compromises the maintainer's account
  4. They move the v4 tag to point at malicious code
  5. Your next pipeline runs the attacker's code, without you changing anything at all

That is exactly what happened with tj-actions/changed-files.

The fix: pin by SHA

A SHA (Secure Hash Algorithm) is the cryptographic fingerprint of a commit. Unlike a tag, it is immutable: it cannot be changed without changing the hash itself.

SHA versus tag

# ❌ DANGEROUS: a mutable tag
- uses: actions/checkout@v4
# ✅ SECURE: a pinned SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

The # v4.1.1 comment keeps a readable trace of the matching version. Tools such as Renovate or Dependabot update those SHAs automatically while preserving the comment.

The three security reflexes

Adopt these practices from your very first workflow.

1. Pin the actions by SHA

# Always use the full SHA, never the tag
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

2. Declare minimal permissions

# By default, a workflow has FAR too many permissions
# Restrict them explicitly to what is strictly needed
permissions:
contents: read # Read-only on the code

3. Never print a secret into the logs

# ❌ DISASTER: the secret shows up in the logs
- run: echo "Token: ${{ secrets.API_TOKEN }}"
# ✅ Secrets stay masked when they are used correctly
- run: curl -H "Authorization: Bearer ${{ secrets.API_TOKEN }}" https://api.example.com

Your first workflow

Now that you understand the stakes, here is a secure workflow for a Python API:

.github/workflows/ci.yml
name: CI Pipeline
# Minimal permissions, declared at workflow level
permissions:
contents: read
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-24.04
steps:
# Actions pinned by SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Install Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: '3.12'
- name: Install the dependencies
run: pip install -r requirements.txt
- name: Run the tests
run: pytest

This workflow:

  • declares permissions: contents: read to limit what it can reach;
  • uses SHAs instead of tags for its actions;
  • does exactly what it is meant to do, testing the code.

The key concepts

Before going further, here is the essential vocabulary:

TermDefinitionExample
WorkflowA YAML file describing your pipeline.github/workflows/ci.yml
JobA group of tasks running on the same machinetest, build, deploy
StepAn individual task inside a job"Install Python", "Run pytest"
RunnerThe machine (VM) that executes the jobubuntu-24.04, windows-2025
ActionA reusable component, like a libraryactions/checkout, actions/setup-python
SHAThe unique, immutable identifier of a commitb4ffde65f46336ab88eb53be808477a3936bae11

What you will learn

This Foundations module gives you the basics to build and understand your first workflows safely:

Next steps

You now understand what a CI/CD pipeline is and why it helps, how GitHub Actions fits into your development workflow, why security belongs in the picture from day one, and the three essential reflexes: SHA, permissions, secrets.

Is this site useful to you?

Fewer than 1% of readers support this site.

I maintain more than 700 free guides, with no ads and no tracking. Any support, even a symbolic one, helps cover hosting and keeps these resources free. Thank you for the help.

The form does not show? Open Ko-fi in a new tab.

Subscribe and follow my DevSecOps work on LinkedIn