On part de zéro et on arrive à un vrai cycle de travail : tu modifies du code sur ton poste, tu le pousses sur GitLab, ton propre runner exécute le pipeline, et tu passes par une branche et une merge request avant de déployer.
SSH
- Un compte GitLab : gitlab.com ou ton instance. Partout où tu vois
gitlab.com, remplace par ton adresse si tu as ta propre instance. - Une VM Debian 12 ou 13 pour le runner, avec accès Internet. Celle du cours VM Debian sur Proxmox avec Terraform convient parfaitement.
- Un poste Linux, macOS ou WSL pour Git.
Installer et configurer Git
sudo apt update && sudo apt install -y git
git --versionGit signe chaque commit avec un nom et un e-mail. Mets les mêmes que sur ton compte GitLab :
git config --global user.name "Jocelin Rey"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
git config --global --listgit config --global --list affiche bien user.name, user.email et init.defaultbranch=main.
Ajouter ta clé SSH à GitLab
SSH évite de taper un mot de passe à chaque push. Si tu n'as pas encore de clé :
ssh-keygen -t ed25519 -C "jocelin@poste"
cat ~/.ssh/id_ed25519.pubCopie toute la ligne affichée (elle commence par ssh-ed25519). Dans GitLab : Ton avatar (en haut à droite) › Edit profile › Access › SSH keys › Add new key.
Teste la connexion :
ssh -T [email protected]Welcome to GitLab, @jrey!SSH demande de confirmer l'empreinte du serveur : réponds yes. Elle est ensuite mémorisée dans ~/.ssh/known_hosts.
Créer le projet sur GitLab
+ (en haut de la barre latérale) › New project/repository › Create blank project.
GitLab ouvre la page du projet avec un premier commit (le README) :

Cloner le projet sur ton poste
Bouton Code (ou Clone selon la version), puis copie l'adresse Clone with SSH :

cd ~
git clone [email protected]:jrey/mon-site.git
cd mon-site
lsTu as maintenant une copie complète du dépôt, avec son historique. origin est le nom donné automatiquement au dépôt GitLab d'où tu as cloné.
Faire ton premier commit
Crée le fichier index.html à la racine du projet :
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Mon site</title>
</head>
<body>
<h1>Bonjour depuis GitLab CI</h1>
</body>
</html>Un fichier passe par trois zones avant d'arriver sur GitLab :
| Zone | Ce que c'est | Commande pour y aller |
|---|---|---|
| Répertoire de travail | Tes fichiers tels que tu les édites | (tu modifies) |
| Index (staging) | Ce qui partira dans le prochain commit | git add |
| Dépôt local | L'historique des commits, sur ton poste | git commit |
| Dépôt distant | Le projet sur GitLab | git push |
git statusUntracked files:
(use "git add <file>..." to include in what will be committed)
index.htmlgit add index.html
git commit -m "Ajoute la page d'accueil"
git pushDans GitLab, la page du projet affiche index.html et ton message de commit. git status répond nothing to commit, working tree clean.
Installer le runner sur la VM Debian
Le runner est le programme qui exécute les jobs. On utilise l'exécuteur Docker : chaque job tourne dans un conteneur neuf, jeté à la fin. La VM reste propre.
Docker
curl -fsSL https://get.docker.com | sudo sh
sudo docker run --rm hello-worldGitLab Runner
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt install -y gitlab-runner
gitlab-runner --versionhello-world affiche Hello from Docker! et gitlab-runner --version renvoie un numéro de version.
Créer le runner dans GitLab
Dans le projet : Settings › CI/CD › Runners (Expand) › Create project runner.
Choisis Linux. GitLab affiche la commande d'enregistrement avec le token du runner :
Garde la page ouverte jusqu'à la fin de l'étape suivante. Si tu la fermes, supprime le runner et recrées-en un.
Enregistrer le runner
Sur la VM, colle ton token à la place de glrt-... :
sudo gitlab-runner register --non-interactive \
--url "https://gitlab.com" \
--token "glrt-t1_xxxxxxxxxxxxxxxxxxxx" \
--executor "docker" \
--docker-image "alpine:3.22" \
--name "runner-debian-01"Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!Le service tourne déjà (installé par le paquet). Vérifie :
sudo gitlab-runner list
sudo gitlab-runner verify
systemctl status gitlab-runner --no-pagerDans GitLab, Settings › CI/CD › Runners : ton runner apparaît avec une pastille verte Online. Sa fiche indique le dernier contact et la version :

Gitlab.com fournit des instance runners qui pourraient prendre tes jobs à la place du tien. Dans la même page Runners, désactive Turn on instance runners for this project. Comme le pipeline met le tag debian sur tous les jobs, c'est ton runner qui travaille.
Écrire le pipeline
Crée .gitlab-ci.yml à la racine du projet (le point au début du nom est obligatoire) :
stages:
- build
- test
- deploy
default:
image: alpine:3.22
tags:
- debian
build-job:
stage: build
script:
- echo "Construction du site pour le commit $CI_COMMIT_SHORT_SHA"
- mkdir -p public
- cp index.html public/
- echo "$CI_COMMIT_SHORT_SHA" > public/version.txt
artifacts:
paths:
- public/
expire_in: 1 week
test-html:
stage: test
script:
- test -f public/index.html
- grep -q "<title>" public/index.html
test-version:
stage: test
script:
- test -s public/version.txt
- cat public/version.txt
deploy-prod:
stage: deploy
script:
- echo "Déploiement de la version $(cat public/version.txt) sur $CI_ENVIRONMENT_NAME"
- echo "Le jeton vaut $DEPLOY_TOKEN"
- tar -czf site.tar.gz -C public .
artifacts:
paths:
- site.tar.gz
environment: production
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual| Mot-clé | Rôle |
|---|---|
stages | Ordre des étapes. Tous les jobs d'une étape doivent réussir pour passer à la suivante |
default | Réglages appliqués à tous les jobs : image Docker alpine et tag debian (donc ton runner) |
script | Les commandes du job, exécutées ligne par ligne. Une erreur arrête le job |
artifacts | Fichiers conservés après le job et transmis aux étapes suivantes (ici le dossier public/) |
Deux jobs en test | test-html et test-version tournent en parallèle |
$CI_COMMIT_SHORT_SHA | Variable fournie par GitLab : l'identifiant court du commit |
environment | Rattache le job à l'environnement production, visible dans Operate › Environments |
rules | deploy-prod n'existe que sur la branche principale, et doit être lancé à la main (when: manual) |
Une ligne de script qui contient : (deux-points suivi d'un espace) casse le YAML. Écris echo "Version 1" plutôt que echo "Version : 1", ou mets toute la ligne entre apostrophes.
Build › Pipeline editor affiche le fichier, signale les erreurs de syntaxe et dessine le pipeline (onglet Visualize).
Pousser et suivre le pipeline
git add .gitlab-ci.yml
git commit -m "Ajoute le pipeline CI"
git pushDès le push, GitLab crée un pipeline. Ouvre Build › Pipelines :

Clique sur le pipeline pour voir le graphe des étapes :

Clique sur un job pour lire son journal :

| Statut | Signification |
|---|---|
| Pending | Le job attend un runner. S'il y reste, voir « Si ça bloque » |
| Running | Le runner l'exécute |
| Passed | Toutes les commandes ont réussi |
| Failed | Une commande a renvoyé une erreur : le journal montre laquelle |
| Manual | Job à lancer à la main (notre deploy-prod) |
build-job, test-html et test-version sont en Passed, deploy-prod attend en Manual, et la page de build-job propose Browse / Download pour l'artefact public/.
Ajouter une variable secrète
Un mot de passe ou un token ne doit jamais être écrit dans le dépôt. On le range dans GitLab : Settings › CI/CD › Variables (Expand) › Add variable.
Le job deploy-prod l'utilise déjà avec $DEPLOY_TOKEN. Masquée, elle n'apparaîtra jamais en clair dans les journaux :
$ echo "Le jeton vaut $DEPLOY_TOKEN"
Le jeton vaut [MASKED]Lancer le déploiement manuel
Dans Build › Pipelines, le bouton ▶ de la ligne du pipeline liste les jobs manuels. Choisis deploy-prod :

$ echo "Déploiement de la version $(cat public/version.txt) sur $CI_ENVIRONMENT_NAME"
Déploiement de la version 892bc2aa sur production
$ echo "Le jeton vaut $DEPLOY_TOKEN"
Le jeton vaut [MASKED]
$ tar -czf site.tar.gz -C public .
Job succeededOperate › Environments affiche maintenant production avec la version déployée et le commit correspondant.
Travailler sur une branche
On ne modifie pas main directement : on crée une branche, on y travaille, et on la fusionne une fois validée.
git switch main
git pull
git switch -c feature/nouveau-titreDans index.html, remplace le titre Bonjour depuis GitLab CI par Bienvenue sur mon site. Puis vérifie ce que tu as changé :
git status --short
git diff- <h1>Bonjour depuis GitLab CI</h1>
+ <h1>Bienvenue sur mon site</h1>git add index.html
git commit -m "Change le titre de la page"
git push -u origin feature/nouveau-titre-u relie ta branche locale à celle de GitLab : les prochains git push et git pull sur cette branche n'auront plus besoin d'arguments. GitLab répond avec un lien direct pour créer la merge request :
remote: To create a merge request for feature/nouveau-titre, visit:
remote: https://gitlab.com/jrey/mon-site/-/merge_requests/new?merge_request%5Bsource_branch%5D=feature%2Fnouveau-titreUn pipeline se lance sur la branche avec 3 jobs seulement : deploy-prod n'existe pas hors de main, grâce à rules.
Ouvrir et fusionner une merge request
Ouvre le lien affiché par le push (ou Create merge request dans le bandeau du projet). Donne un titre, laisse Delete source branch coché :

Sur la page de la merge request : l'onglet Changes montre le diff, et le pipeline de la branche s'affiche en haut. Quand il est vert, clique sur Merge.

Le code est testé par le pipeline avant d'arriver sur main, quelqu'un peut le relire et commenter, et l'historique garde la trace de chaque changement et de sa raison.
Récupérer le résultat sur ton poste
git switch main
git pull
git branch -d feature/nouveau-titre
git fetch --prune
git log --oneline --graph -5* 35a477d Merge branch 'feature/nouveau-titre' into 'main'
|\
| * 009afc5 Change le titre de la page
|/
* 892bc2a Ajoute le pipeline CI| Commande | Rôle |
|---|---|
git pull | Récupère le commit de merge fait sur GitLab |
git branch -d | Supprime ta branche locale, devenue inutile (refuse si elle n'a pas été fusionnée) |
git fetch --prune | Oublie les branches supprimées côté GitLab |
Le pipeline de main propose à nouveau deploy-prod : lance-le pour mettre en production la nouvelle version. La boucle est complète.
Corriger une erreur
| Situation | Commande |
|---|---|
| Annuler une modification pas encore ajoutée | git restore index.html |
Retirer un fichier de l'index (après git add) | git restore --staged index.html |
| Corriger le message ou ajouter un oubli au dernier commit, pas encore poussé | git commit --amend |
| Annuler un commit déjà poussé | git revert <id-du-commit> (crée un commit inverse, sans réécrire l'historique) |
| Mettre de côté des modifications pour changer de branche | git stash puis git stash pop |
--amend, reset ou rebase sur des commits déjà sur GitLab obligent à forcer le push et cassent le travail des autres. Après un push, utilise git revert.
Aide-mémoire Git
| Commande | Ce qu'elle fait |
|---|---|
git clone <url> | Copie un dépôt distant sur ton poste |
git status | État des fichiers : modifiés, ajoutés, non suivis |
git diff | Ce qui a changé depuis le dernier commit (--staged : ce qui est dans l'index) |
git add <fichier> | Prépare un fichier pour le commit (git add . : tout) |
git commit -m "message" | Enregistre un instantané dans l'historique local |
git push | Envoie tes commits sur GitLab |
git pull | Récupère et intègre les commits de GitLab |
git switch -c <branche> | Crée une branche et s'y place |
git switch <branche> | Change de branche |
git branch -a | Liste les branches locales et distantes |
git log --oneline --graph | Historique compact, avec les branches |
git show <id> | Détail d'un commit |
git remote -v | Adresse du dépôt distant |
git tag v1.0 && git push --tags | Marque une version |
Si ça bloque

| Symptôme | Cause | Solution |
|---|---|---|
Permission denied (publickey) au push | Clé SSH absente de GitLab ou mauvaise clé | Refais l'étape 2 et teste avec ssh -T [email protected] |
| Job bloqué en Pending : no runners that match all of the job's tags | Tag du job différent de celui du runner, ou runner hors ligne | Même tag des deux côtés (debian) ; sudo gitlab-runner verify sur la VM |
| Runner gris (offline) | Service arrêté ou VM sans accès à GitLab | sudo systemctl restart gitlab-runner puis journalctl -u gitlab-runner -n 50 |
| Pipeline invalide (yaml invalid) | Indentation ou deux-points dans un script | Build › Pipeline editor pointe la ligne en erreur |
Cannot connect to the Docker daemon | Docker absent ou arrêté sur la VM | sudo systemctl enable --now docker |
! [rejected] ... (fetch first) au push | Quelqu'un a poussé avant toi | git pull --rebase puis git push |
Push refusé sur main | Branche protégée | Passe par une branche et une merge request (étapes 13 et 14) |
$DEPLOY_TOKEN vide sur une branche | Variable protégée : uniquement sur les branches protégées | Normal : elle ne sert qu'au déploiement depuis main |