Aller au contenu

Mettre en place la publication CI/CD

Ce que vous allez construire : Un pipeline automatisé qui publie un paquet .deb sur Repod à chaque fois que vous poussez un tag de version.

Durée : ~20 minutes Prérequis : Repod démarré et accessible depuis votre runner CI, un dépôt sur GitHub ou GitLab


Étape 1 — Créer un token API

Les systèmes CI/CD ne doivent jamais utiliser vos identifiants admin personnels. Créez un token dédié avec le rôle minimum requis (uploader).

  1. Aller dans Paramètres → Tokens API
  2. Cliquer sur Créer un token
  3. Remplir :
    • Nom : gitlab-ci-prod (ou github-actions-prod)
    • Rôle : uploader
    • Expiration : laisser vide (ou définir une date de rotation annuelle)
  4. Cliquer Générer
  5. Copier le token immédiatement — il commence par repod_ et ne sera plus jamais affiché
# Se connecter d'abord en tant qu'admin
TOKEN=$(curl -s -X POST http://REPO_HOST:8000/auth/token \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"YourPassword1!"}' \
  | jq -r .access_token)

# Créer le token API
curl -s -X POST http://REPO_HOST:8000/auth/api-tokens \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"gitlab-ci-prod","roles":["uploader"]}' \
  | jq .

Sauvegardez la valeur token de la réponse — vous en aurez besoin à l'étape suivante.

Un token par pipeline

Créez un token séparé pour chaque système CI (GitHub, GitLab, Jenkins…). Ainsi vous pouvez en révoquer un sans affecter les autres, et voir quel système a effectué quel upload dans les journaux d'audit.


Étape 2 — Stocker le token comme secret CI

  1. Aller dans votre dépôt → Settings → Secrets and variables → Actions
  2. Cliquer sur New repository secret
  3. Nom : REPOD_TOKEN
  4. Valeur : repod_xxxxxxxxxx (votre token)
  5. Ajouter également REPOD_URL = https://repo.example.com (ou http://YOUR_HOST:8000)
  1. Aller dans votre projet → Settings → CI/CD → Variables
  2. Ajouter une variable :
    • Clé : REPOD_TOKEN
    • Valeur : repod_xxxxxxxxxx
    • Type : Variable
    • Protégée : ✅ (disponible uniquement sur les branches/tags protégés)
    • Masquée : ✅ (masquée dans les logs)
  3. Ajouter REPOD_URL de la même manière

Stockez le token dans le coffre de secrets de votre système CI et exposez-le via la variable d'environnement REPOD_TOKEN.


Étape 3 — Workflow GitHub Actions

Créer .github/workflows/publish.yml :

.github/workflows/publish.yml
name: Build and publish package

on:
  push:
    tags:
      - 'v*'           # triggers on v1.0.0, v2.3.1, etc.

jobs:
  publish:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        distribution: [jammy, focal, noble]   # publish to multiple distros

    steps:
      - uses: actions/checkout@v4

      - name: Build .deb package
        run: |
          # Replace with your actual build command
          make deb VERSION=${GITHUB_REF_NAME#v}
          ls -lh *.deb

      - name: Upload to Repod
        env:
          REPOD_TOKEN: ${{ secrets.REPOD_TOKEN }}
          REPOD_URL: ${{ secrets.REPOD_URL }}
        run: |
          DEB_FILE=$(ls *.deb | head -1)
          echo "Uploading $DEB_FILE to distribution ${{ matrix.distribution }}..."

          RESPONSE=$(curl -s -w "\n%{http_code}" \
            -X POST "$REPOD_URL/upload/" \
            -H "Authorization: Bearer $REPOD_TOKEN" \
            -F "file=@$DEB_FILE" \
            -F "distribution=${{ matrix.distribution }}")

          HTTP_CODE=$(echo "$RESPONSE" | tail -1)
          BODY=$(echo "$RESPONSE" | head -1)

          echo "Response: $BODY"

          if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "201" ]; then
            echo "Upload failed with HTTP $HTTP_CODE"
            exit 1
          fi

          STATUS=$(echo "$BODY" | jq -r .status)
          echo "Package status: $STATUS"

          if [ "$STATUS" = "pending_review" ]; then
            echo "⚠️  Package is pending CVE review by the security team."
            # Don't fail — the upload succeeded, review is expected
          fi

Stratégie de matrice

La stratégie matrix.distribution exécute le job d'upload une fois par distribution, en parallèle. Retirez les distributions que vous n'utilisez pas.


Étape 4 — Pipeline GitLab CI

Créer ou étendre .gitlab-ci.yml :

.gitlab-ci.yml
stages:
  - build
  - publish

variables:
  REPOD_URL: "https://repo.example.com"
  DISTRIBUTIONS: "jammy focal noble"

build-deb:
  stage: build
  image: debian:bookworm
  script:
    - apt-get update -qq && apt-get install -y build-essential devscripts
    - make deb VERSION=${CI_COMMIT_TAG#v}
    - ls -lh *.deb
  artifacts:
    paths:
      - "*.deb"
    expire_in: 1 day
  only:
    - tags

publish-to-repod:
  stage: publish
  image: curlimages/curl:latest
  needs: [build-deb]
  script:
    - DEB_FILE=$(ls *.deb | head -1)
    - |
      for DIST in $DISTRIBUTIONS; do
        echo "Publishing $DEB_FILE to $DIST..."
        curl -sf -X POST "$REPOD_URL/upload/" \
          -H "Authorization: Bearer $REPOD_TOKEN" \
          -F "file=@$DEB_FILE" \
          -F "distribution=$DIST" \
          || { echo "Failed to upload to $DIST"; exit 1; }
        echo "✅ Published to $DIST"
      done
  only:
    - tags
  environment:
    name: production

Stockez REPOD_TOKEN dans Settings → CI/CD → Variables (masquée + protégée).


Étape 5 — Script shell générique

Pour Jenkins, Drone, Woodpecker, ou tout autre système :

scripts/publish-deb.sh
#!/usr/bin/env bash
# Usage: ./scripts/publish-deb.sh mypackage_1.0.0_amd64.deb jammy
set -euo pipefail

DEB_FILE="${1:?Usage: $0 <file.deb> <distribution>}"
DISTRIBUTION="${2:?Usage: $0 <file.deb> <distribution>}"
REPOD_URL="${REPOD_URL:?REPOD_URL env var not set}"
REPOD_TOKEN="${REPOD_TOKEN:?REPOD_TOKEN env var not set}"

echo "📦 Publishing $DEB_FILE$DISTRIBUTION"

RESPONSE=$(curl -sf -X POST "$REPOD_URL/upload/" \
  -H "Authorization: Bearer $REPOD_TOKEN" \
  -F "file=@$DEB_FILE" \
  -F "distribution=$DISTRIBUTION")

STATUS=$(echo "$RESPONSE" | jq -r .status)
echo "✅ Upload complete — status: $STATUS"

if [ "$STATUS" = "pending_review" ]; then
  echo "⚠️  Security team review required before this package is published."
fi

Vérifier un upload réussi

Après l'exécution de votre pipeline, vérifiez que le paquet est disponible :

# Vérifier via l'API
curl -s -H "Authorization: Bearer $REPOD_TOKEN" \
  "$REPOD_URL/packages/?q=your-package-name" | jq .

# Vérifier le journal d'audit (dernière entrée d'upload)
curl -s -H "Authorization: Bearer $REPOD_TOKEN" \
  "$REPOD_URL/artifacts/audit/logs" | jq '.[-1]'

Dans l'interface web, allez dans Packages et recherchez le nom de votre paquet.


Bonnes pratiques

Pratique Pourquoi
Un token API par système CI Isole le rayon d'impact si un token fuite ; piste d'audit séparée par système
Utiliser uniquement le rôle uploader La CI n'a pas besoin d'approuver des CVE ou de gérer des utilisateurs
Définir une date d'expiration Faire tourner les tokens chaque année ; poser un rappel calendrier
Vérifier status dans la réponse pending_review signifie que des CVE ont été trouvées — alertez votre équipe sécurité
Ne jamais logger le token Masquez-le dans votre système CI ; utilisez des secrets, jamais de valeur en dur
Tester les uploads sur une distribution de staging d'abord Utilisez jammy-staging avant de promouvoir vers jammy

Étapes suivantes