Client Windows · GitHub Actions
Procédure : workflows, fichiers YAML, runner
Réponse courte : le client n’installe pas les runners hébergés GitHub. Il crée .github\workflows, y met le YAML, configure Secrets/Environments, et n’installe un Self-Hosted Runner Windows que pour déployer sur sa machine / son serveur.
À retenir pour le client
runs-on: ubuntu-latest → cloud GitHub, zéro install.
runs-on: self-hosted → installer le runner sur le Windows cible.
Faut-il installer le « hosted runner » GitHub ?
FAQ| GitHub-hosted | Self-hosted Windows | |
|---|---|---|
| Où ça tourne ? | Cloud GitHub | PC / serveur Windows du client |
| Installation chez le client ? | NON | OUI (si deploy on-prem/local) |
| Dans nos YAML | Job test | Job deploy-prod (cas local) |
| Cas Azure / AWS OIDC | Suffit souvent tout seul | Pas nécessaire |
| Comment l’avoir ? | Automatique dès Actions activé | Settings → Actions → Runners → New → Windows |
Phrase client : « GitHub fournit déjà des machines pour les tests. On installe un runner seulement sur la machine qui doit recevoir la production. »
Procédure Windows client (complète)
À remettreFichier kit :
examples/cicd/PROCEDURE-WINDOWS-CLIENT.md — aussi dans le ZIP.Installer Git + Python (PATH)
Pas de runner hosted à installer. Juste Git et Python 3.12.
Créer le repo GitHub
Public (Free) ou Pro si privé. Sans README si push du kit.
Créer les dossiers locaux
%USERPROFILE%\demo_dev (travail) et demo_prod (cible deploy).
Créer .github\workflows et coller 1 YAML
Ex. ci-cd-local-runner.yml pour la démo Windows.
Push + branches main / recette / prod
GitHub détecte automatiquement les workflows.
UI : Environment production + Ruleset + check test
Required reviewers = gate d’approbation.
Installer Self-Hosted Runner Windows + run.cmd
Statut Idle. Job test reste en cloud.
Valider Fail Fast → Merge → Approve → :8001
Preuve bout-en-bout.
# Procédure client Windows — GitHub Actions de A à Z
Public : client **Windows 10/11** (ou Windows Server)
Repo modèle : https://github.com/Thomasdsf/myndkor-chat-demo
À animer / remettre tel quel.
---
## 1. Ce qu’il faut comprendre avant d’installer quoi que ce soit
### Deux types de « runners »
| Type | C’est quoi ? | Faut-il l’installer chez le client ? |
|---|---|---|
| **GitHub-hosted** (`ubuntu-latest`, `windows-latest`) | Machines **fournies par GitHub** dans le cloud | **NON** — rien à installer. Dès qu’Actions est actif, ça tourne. |
| **Self-hosted** (`runs-on: self-hosted`) | Logiciel runner installé **sur le PC/serveur Windows du client** | **OUI** — seulement si on déploie **chez eux** (dossier local, IIS, serveur on-prem…). |
### Dans nos YAML Myndkor
```text
Job "test" → runs-on: ubuntu-latest → GitHub-hosted (aucun install)
Job "deploy" → runs-on: self-hosted → Runner Windows à installer sur le poste/serveur cible
```
**Donc :**
- Pour **tester** (pytest) → pas besoin d’installer de runner.
- Pour **déployer en prod locale / on-prem Windows** → il faut installer le **Self-Hosted Runner** sur la machine Windows de prod (ou le poste de démo).
- Pour **Azure Web App / AWS App Runner** (exemples cloud) → en général **pas** de self-hosted : tout passe en GitHub-hosted + login cloud (OIDC).
> « Hosted runner GitHub » = déjà inclus. On n’installe **pas** un produit « GitHub Hosted Runner » chez le client.
---
## 2. Prérequis logiciels (poste Windows client)
Installer **une fois** (PowerShell pour vérifier) :
| Logiciel | Pourquoi | Vérif |
|---|---|---|
| [Git for Windows](https://git-scm.com/download/win) | clone / commit / push | `git --version` |
| [Python 3.12](https://www.python.org/downloads/windows/) cocher **Add to PATH** | app + venv | `python --version` |
| Compte GitHub + droits admin sur le repo | Settings, Actions, Environments | — |
| (Démo chat) [Ollama](https://ollama.com/download/windows) + modèle | runtime IA | `ollama list` |
| Navigateur | UI GitHub | — |
```powershell
git --version
python --version
pip --version
```
Si `python` inconnu : réinstaller Python avec « Add to PATH », puis **fermer/rouvrir** PowerShell.
---
## 3. Créer le dépôt et l’arborescence (workflows inclus)
### 3.1 Créer le repo GitHub
1. https://github.com/new
2. Nom : ex. `myndkor-chat-demo`
3. Visibilité : **Public** si compte Free (sinon Environments/Rulesets incomplets) — ou Pro/Team si Privé
4. **Ne pas** cocher « Add README » si vous allez pousser le kit ensuite
### 3.2 Créer les dossiers locaux Windows
```powershell
# Dossier de travail (DEV)
$Dev = "$env:USERPROFILE\demo_dev"
New-Item -ItemType Directory -Force -Path $Dev | Out-Null
cd $Dev
# Dossier cible PROD locale (utilisé par le runner self-hosted)
$Prod = "$env:USERPROFILE\demo_prod"
New-Item -ItemType Directory -Force -Path $Prod | Out-Null
```
### 3.3 Mettre les fichiers du projet + créer `.github\workflows`
Depuis le **kit ZIP** décompressé (ex. `Downloads\kit`) :
```powershell
$Kit = "$env:USERPROFILE\Downloads\kit" # <-- adapter le chemin
# Fichiers applicatifs
Copy-Item "$Kit\app.py","$Kit\test_app.py","$Kit\requirements.txt","$Kit\.env.example","$Kit\.gitignore" -Destination $Dev -Force
Copy-Item "$Kit\Dockerfile","$Kit\.dockerignore" -Destination $Dev -Force -ErrorAction SilentlyContinue
# === CRÉER LE DOSSIER WORKFLOWS (obligatoire) ===
New-Item -ItemType Directory -Force -Path "$Dev\.github\workflows" | Out-Null
# === METTRE LE YAML ACTIF (1 seul pour commencer) ===
# Démo / prod locale Windows :
Copy-Item "$Kit\.github\workflows\ci-cd-local-runner.yml" `
-Destination "$Dev\.github\workflows\ci-cd-local-runner.yml" -Force
```
Arborescence attendue :
```text
C:\Users\<user>\demo_dev\
├── .github\
│ └── workflows\
│ └── ci-cd-local-runner.yml ← pipeline actif
├── app.py
├── test_app.py
├── requirements.txt
├── .env.example
├── .gitignore
└── Dockerfile (optionnel)
```
### 3.4 (Plus tard) Ajouter un autre exemple client
Les autres YAML sont dans `kit\examples\cicd\` — **ne les copiez pas tous** d’un coup.
```powershell
# Exemple : basculer vers Azure (désactive le local en le renommant)
Rename-Item "$Dev\.github\workflows\ci-cd-local-runner.yml" "ci-cd-local-runner.yml.disabled"
Copy-Item "$Kit\examples\cicd\ci-cd-azure-webapp.yml" `
-Destination "$Dev\.github\workflows\ci-cd-azure-webapp.yml"
```
> 1 YAML de deploy actif = 1 stratégie. Plusieurs YAML qui écoutent `push` sur `prod` = plusieurs deploys en parallèle.
---
## 4. Premier commit / push + branches
```powershell
cd $Dev
git init -b main
git add .
git commit -m "chore: bootstrap app + github actions workflow"
git remote add origin https://github.com/<ORG>/<REPO>.git
# ex: https://github.com/Thomasdsf/myndkor-chat-demo.git
git push -u origin main
git branch recette
git push -u origin recette
git branch prod
git push -u origin prod
```
Vérifier dans GitHub : onglet **Actions** — le workflow apparaît.
---
## 5. Configurer GitHub (UI) — avant le runner
### 5.1 Activer Actions (si besoin)
Repo → **Settings → Actions → General**
- « Allow all actions and reusable workflows »
- Save
### 5.2 Environment `production` (gate d’approbation)
**Settings → Environments → New environment → `production`**
- ✅ **Required reviewers** : le DevOps / vous
- **Environment secrets** : `OLLAMA_API_KEY` (valeur factice OK en lab)
- **Environment variables** : `OLLAMA_HOST`, `APP_TITLE`
### 5.3 Ruleset sur `prod`
**Settings → Rules → Rulesets → New**
- Target : branche `prod`
- Require pull request before merging
- Require status checks to pass → ajouter le check nommé **`test`**
- Block force pushes
### 5.4 (Public Free) Secret Scanning + Push Protection
**Settings → Code security**
---
## 6. Installer le Self-Hosted Runner Windows (pour le job deploy)
### Quand l’installer ?
- ✅ Déploiement vers `%USERPROFILE%\demo_prod`, serveur Windows, IIS, dossier local
- ❌ Uniquement Azure/AWS cloud via OIDC → **pas nécessaire**
### 6.1 Télécharger depuis GitHub
1. Repo → **Settings → Actions → Runners → New self-hosted runner**
2. Choisir **Windows** / **x64**
3. Laisser la page ouverte : elle affiche les commandes + un **token** temporaire
Lien type : `https://github.com/<ORG>/<REPO>/settings/actions/runners/new`
### 6.2 Commandes PowerShell (souvent en Administrateur)
```powershell
# Dossier runner (séparé de demo_dev)
mkdir $env:USERPROFILE\actions-runner
cd $env:USERPROFILE\actions-runner
# Coller ICI les 3 commandes affichées par GitHub :
# 1) Invoke-WebRequest ... actions-runner-win-x64-....zip
# 2) Extract archive
# 3) .\config.cmd --url https://github.com/<ORG>/<REPO> --token <TOKEN_UI>
```
Pendant `config.cmd` :
- Runner group : défaut
- Runner name : ex. `win-demo-01`
- Labels : laisser défaut (`self-hosted`, `Windows`, `X64`) — **important** car le YAML dit `runs-on: self-hosted`
- Work folder : défaut `_work`
### 6.3 Démarrer le runner
**Pendant un atelier / test :**
```powershell
cd $env:USERPROFILE\actions-runner
.\run.cmd
```
Laisser la fenêtre ouverte. Statut GitHub : **Idle** (vert).
**En production Windows (service permanent) :**
```powershell
# PowerShell Administrateur, dans actions-runner
.\svc.cmd install
.\svc.cmd start
.\svc.cmd status
```
### 6.4 Ce que GitHub-hosted fait pendant ce temps
Rien à faire : le job `test` sur `ubuntu-latest` démarre tout seul dans le cloud GitHub (minutes Actions du compte).
---
## 7. Lancer l’app DEV (optionnel mais utile pour la démo)
```powershell
cd $env:USERPROFILE\demo_dev
python -m venv .venv
.\.venv\Scripts\Activate.ps1
# Si erreur scripts : Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
pip install -r requirements.txt
Copy-Item .env.example .env
notepad .env
# APP_ENV=DEV
# APP_TITLE=Chat DEV
# OLLAMA_HOST=http://127.0.0.1:11434
uvicorn app:app --host 0.0.0.0 --port 8000
# Navigateur : http://127.0.0.1:8000
```
La PROD (`:8001` dans `demo_prod`) sera démarrée **par le runner** après Approve.
---
## 8. Scénario de validation (Fail Fast → Deploy)
```powershell
cd $env:USERPROFILE\demo_dev
git checkout recette
# Casser le test volontairement
(Get-Content app.py) -replace 'REQUIRED_MODEL = "gemma4:32"','REQUIRED_MODEL = "WRONG"' |
Set-Content app.py -Encoding utf8
git add -A
git commit -m "feat: break model for fail-fast"
git push origin recette
```
1. Ouvrir PR `recette` → `prod`
2. Job **test** (GitHub-hosted) → **rouge** → Merge bloqué
3. Corriger :
```powershell
(Get-Content app.py) -replace 'REQUIRED_MODEL = "WRONG"','REQUIRED_MODEL = "gemma4:32"' |
Set-Content app.py -Encoding utf8
git add app.py
git commit -m "fix: restore REQUIRED_MODEL"
git push origin recette
```
4. Test **vert** → **Merge**
5. Job deploy → **Waiting** (environment production)
6. **Review deployments → Approve and deploy**
7. Le **self-hosted Windows** copie vers `demo_prod` et démarre `:8001`
8. http://127.0.0.1:8001 → bandeau PROD
---
## 9. Checklist « client Windows prêt »
### Logiciels
- [ ] Git + Python 3.12 (PATH)
- [ ] (si besoin) Ollama
### Repo GitHub
- [ ] Fichiers app poussés
- [ ] Dossier `.github\workflows\` créé
- [ ] **Un** YAML actif collé dedans
- [ ] Branches `main` / `recette` / `prod`
- [ ] Environment `production` + reviewers + secrets
- [ ] Ruleset `prod` + check `test`
### Runners
- [ ] Compris : **test = GitHub-hosted** (rien à installer)
- [ ] **Self-hosted Windows** installé + Idle (si deploy local/on-prem)
- [ ] Labels OK (`self-hosted`)
### Preuve
- [ ] PR Fail Fast rouge puis vert
- [ ] Approve environment
- [ ] App PROD répond
---
## 10. FAQ rapide (à dire au client)
**Q : Je dois installer GitHub Actions Hosted Runner ?**
R : Non. Les runners hébergés GitHub sont dans le cloud. Vous ne les installez pas.
**Q : Alors qu’est-ce qu’on installe ?**
R : Uniquement le **Self-Hosted Runner** sur la machine Windows qui doit recevoir le déploiement (ou pour une démo locale).
**Q : Où créer les fichiers YAML ?**
R : Toujours
`\.github\workflows\mon-pipeline.yml`
GitHub les détecte automatiquement après push.
**Q : Docker / Drone ?**
R : Autre procédure (`examples/drone/PROCEDURE-DRONE.md`). Pas de dossier `.github/workflows` : fichier `.drone.yml` à la racine + runner Drone Docker.
**Q : Compte Free + repo privé ?**
R : Environments / Rulesets limités → passer **Public** ou GitHub Pro.
Les commandes essentielles (antisèche)
PowerShell$Dev = "$env:USERPROFILE\demo_dev"
$Prod = "$env:USERPROFILE\demo_prod"
$Kit = "$env:USERPROFILE\Downloads\kit" # adapter
New-Item -ItemType Directory -Force -Path $Dev,$Prod | Out-Null
cd $Dev
Copy-Item "$Kit\app.py","$Kit\test_app.py","$Kit\requirements.txt","$Kit\.env.example","$Kit\.gitignore" -Destination . -Force
# Créer le dossier workflows GitHub Actions
New-Item -ItemType Directory -Force -Path ".\github\workflows" | Out-Null
# Mettre LE yaml actif (un seul)
Copy-Item "$Kit\.github\workflows\ci-cd-local-runner.yml" `
-Destination ".\github\workflows\ci-cd-local-runner.yml" -Force
# (Plus tard) autre cas client depuis examples\cicd\*.yml — un seul à la fois
cd $env:USERPROFILE\demo_dev
git init -b main
git add .
git commit -m "chore: bootstrap app + workflow"
git remote add origin https://github.com/Thomasdsf/myndkor-chat-demo.git
git push -u origin main
git branch recette; git push -u origin recette
git branch prod; git push -u origin prod
# UI : Repo → Settings → Actions → Runners → New self-hosted runner → Windows x64
# Coller les commandes GitHub (download + unzip + config.cmd)
mkdir $env:USERPROFILE\actions-runner
cd $env:USERPROFILE\actions-runner
# ... commandes affichées par GitHub (URL + TOKEN) ...
.\run.cmd
# OU service permanent (Admin) :
# .\svc.cmd install
# .\svc.cmd start
# Vérifier statut Idle sur la page Runners
# Rappel : le job test utilise ubuntu-latest (cloud) — RIEN à installer pour ça
1. https://github.com/Thomasdsf/myndkor-chat-demo/settings/actions → Allow actions
2. https://github.com/Thomasdsf/myndkor-chat-demo/settings/environments → New "production"
- Required reviewers
- Secret OLLAMA_API_KEY
- Vars OLLAMA_HOST, APP_TITLE
3. https://github.com/Thomasdsf/myndkor-chat-demo/settings/rules → Ruleset branche prod
- Require PR
- Require status check "test"
4. https://github.com/Thomasdsf/myndkor-chat-demo/settings/actions/runners → runner Idle
YAML actif démo (Self-hosted Windows)
.github/workflows# =============================================================================
# CAS 1 — CI/CD Local via GitHub Self-Hosted Runner (DÉMO LIVE — Windows)
# =============================================================================
# Job test : PR + push → pytest (fail-fast) sur ubuntu-latest (cloud GitHub)
# Job deploy: merge sur branche prod → environment: production (approbation
# manuelle) → copie vers %USERPROFILE%\demo_prod + port 8001
#
# Poste formateur (LIVE) : Windows 10/11 + runner self-hosted Windows
# Dossiers :
# %USERPROFILE%\demo_dev → travail / port 8000
# %USERPROFILE%\demo_prod → cible déploiement / port 8001
#
# Prérequis GitHub :
# - Environnement "production" + secret OLLAMA_API_KEY
# - Variables : OLLAMA_HOST, APP_TITLE
# =============================================================================
name: CI/CD Local Self-Hosted Runner
on:
pull_request:
branches: [prod, recette, main]
push:
branches: [prod, recette, main]
permissions:
contents: read
checks: write
concurrency:
group: local-cicd-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run pytest (Fail Fast)
env:
MODEL_NAME: gemma4:32
APP_ENV: RECETTE
APP_TITLE: Myndkor Chat Demo
OLLAMA_HOST: http://127.0.0.1:11434
run: pytest -q
deploy-prod:
name: deploy-prod
needs: test
if: github.ref == 'refs/heads/prod' && github.event_name == 'push'
runs-on: self-hosted
environment:
name: production
url: http://127.0.0.1:8001
steps:
- name: Checkout
uses: actions/checkout@v4
# ---------- LIVE DÉMO : Windows PowerShell ----------
- name: Déployer vers demo_prod (Windows — LIVE)
if: runner.os == 'Windows'
shell: powershell
env:
OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }}
OLLAMA_HOST: ${{ vars.OLLAMA_HOST }}
APP_TITLE: ${{ vars.APP_TITLE }}
run: |
$ErrorActionPreference = "Stop"
$Target = Join-Path $env:USERPROFILE "demo_prod"
New-Item -ItemType Directory -Force -Path $Target | Out-Null
Write-Host "Cible PROD = $Target"
$exclude = @('.git', '.venv', '__pycache__', '.env', 'app.pid', 'app.log', 'app.err')
Get-ChildItem -Force | Where-Object { $exclude -notcontains $_.Name } | ForEach-Object {
Copy-Item $_.FullName -Destination $Target -Recurse -Force
}
$hostVal = if ($env:OLLAMA_HOST) { $env:OLLAMA_HOST } else { "http://127.0.0.1:11434" }
$titleVal = if ($env:APP_TITLE) { $env:APP_TITLE } else { "Myndkor Chat PROD" }
@(
"OLLAMA_HOST=$hostVal"
"OLLAMA_API_KEY=$($env:OLLAMA_API_KEY)"
"MODEL_NAME=gemma4:32"
"APP_ENV=PROD"
"APP_TITLE=$titleVal"
) | Set-Content -Path (Join-Path $Target ".env") -Encoding ascii
Set-Location $Target
python -m venv .venv
& .\.venv\Scripts\python.exe -m pip install -q --upgrade pip
& .\.venv\Scripts\python.exe -m pip install -q -r requirements.txt
# Libère le port 8001 si une ancienne instance tourne
Get-NetTCPConnection -LocalPort 8001 -ErrorAction SilentlyContinue |
ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }
Start-Sleep -Seconds 1
Get-Content .env | Where-Object { $_ -and $_ -notmatch '^\s*#' } | ForEach-Object {
$k, $v = $_ -split '=', 2
Set-Item -Path "Env:$k" -Value $v
}
$uvicorn = Join-Path $Target ".venv\Scripts\uvicorn.exe"
if (Test-Path "app.log") { Remove-Item "app.log" -Force -ErrorAction SilentlyContinue }
if (Test-Path "app.err") { Remove-Item "app.err" -Force -ErrorAction SilentlyContinue }
Start-Process -FilePath $uvicorn -ArgumentList "app:app --host 0.0.0.0 --port 8001" `
-WorkingDirectory $Target `
-RedirectStandardOutput "app.log" -RedirectStandardError "app.err" -WindowStyle Hidden
Start-Sleep -Seconds 3
Invoke-RestMethod http://127.0.0.1:8001/health
Write-Host "PROD locale OK → http://127.0.0.1:8001"
# ---------- Variante Linux/macOS/WSL (hors live Windows) ----------
- name: Déployer vers demo_prod (Linux/macOS/WSL)
if: runner.os != 'Windows'
env:
OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }}
OLLAMA_HOST: ${{ vars.OLLAMA_HOST }}
APP_TITLE: ${{ vars.APP_TITLE }}
run: |
set -euo pipefail
TARGET="${HOME}/demo_prod"
mkdir -p "$TARGET"
rsync -a --delete \
--exclude '.git' --exclude '.venv' --exclude '__pycache__' \
--exclude '.env' --exclude '*.pid' --exclude '*.log' \
./ "$TARGET/"
umask 077
{
echo "OLLAMA_HOST=${OLLAMA_HOST:-http://127.0.0.1:11434}"
echo "OLLAMA_API_KEY=${OLLAMA_API_KEY}"
echo "MODEL_NAME=gemma4:32"
echo "APP_ENV=PROD"
echo "APP_TITLE=${APP_TITLE:-Myndkor Chat PROD}"
} > "$TARGET/.env"
chmod 600 "$TARGET/.env"
cd "$TARGET"
python3 -m venv .venv
. .venv/bin/activate
pip install -q -r requirements.txt
if [ -f app.pid ] && kill -0 "$(cat app.pid)" 2>/dev/null; then
kill "$(cat app.pid)" || true
sleep 1
fi
if command -v fuser >/dev/null 2>&1; then fuser -k 8001/tcp 2>/dev/null || true; fi
set -a; . ./.env; set +a
nohup uvicorn app:app --host 0.0.0.0 --port 8001 > app.log 2>&1 &
echo $! > app.pid
sleep 2
curl -sf "http://127.0.0.1:8001/health"
echo
echo "PROD locale OK → http://127.0.0.1:8001"
# Exemples CI/CD clé en main (à donner au client)
> **Procédure Windows client (recommandée) :**
> [`PROCEDURE-WINDOWS-CLIENT.md`](./PROCEDURE-WINDOWS-CLIENT.md)
> → créer `.github\workflows`, coller le YAML, GitHub-hosted vs Self-hosted, installer le runner.
Ces YAML sont **volontairement hors** de `.github/workflows/` pour ne pas
se déclencher tous en même temps sur le repo de démo.
## Utilisation chez le client
1. Choisir **un** fichier selon l’infra cible (voir tableau ci-dessous).
2. Le copier dans `.github/workflows/` du projet client.
3. Renseigner les Secrets / Variables documentés en tête du YAML.
4. Créer l’Environment GitHub `production` (et `recette` si besoin).
5. Désactiver / ne pas copier les autres exemples.
Le workflow **live formateur** reste :
`../../.github/workflows/ci-cd-local-runner.yml` (Self-Hosted Windows).
## Catalogue
| Fichier | Cas | Stack typique |
|---|---|---|
| `ci-cd-azure-webapp.yml` | PaaS Azure | Web App + OIDC |
| `ci-cd-windows-server.yml` | On-prem Windows | Service / NSSM |
| `ci-cd-docker-ghcr.yml` | Conteneurs | Docker → GHCR |
| `ci-cd-linux-ssh.yml` | VPS / VM Linux | SSH + rsync + systemd |
| `ci-cd-aws-ecr-apprunner.yml` | PaaS AWS | ECR + App Runner OIDC |
| `ci-cd-kubernetes.yml` | Orchestration | GHCR + kubectl |
| `ci-cd-recette-puis-prod.yml` | Gouvernance 2 env | recette → prod |
## Hors GitHub Actions
| Dossier | Cas |
|---|---|
| [`../drone/`](../drone/) | **Docker + Drone CI** (`.drone.yml`, compose serveur/runner, compose app) |
Fichiers support dans le kit racine :
- `Dockerfile`
- `deploy/myndkor-chat.service` (systemd pour l’exemple SSH)
- `.dockerignore`
Si le client est plutôt Docker/Drone
Autre procédurePas de dossier
.github/workflows : fichier .drone.yml à la racine + runner Drone (Docker). Voir procédure dédiée.# Procédure client — Docker + Drone CI (clé en main)
Repo de référence : https://github.com/Thomasdsf/myndkor-chat-demo
Pack source : `examples/drone/` du kit formation.
Cette procédure est **indépendante** de la démo GitHub Actions Windows.
À remettre au client tel quel, ou à animer en atelier secondaire.
---
## Objectif final
```
Code → Drone test (pytest)
→ Build/Push image Docker (branche prod)
→ Promote manuel "production"
→ Conteneur démarré sur le serveur cible
```
---
## Phase 0 — Prérequis machine
À installer avant toute chose :
| Outil | Vérification |
|---|---|
| Git | `git --version` |
| Docker Engine + Compose v2 | `docker version` / `docker compose version` |
| Accès au dépôt Git (GitHub/Gitea/GitLab) | clone OK |
| (Optionnel lab) Ports libres `8080` (Drone UI) et `8000` (app) | |
Windows : Docker Desktop. Linux : Docker Engine officiel.
---
## Phase 1 — Créer l’arborescence projet
Dans un PowerShell / bash, depuis le dossier où vous voulez le projet client :
### Option A — Partir du kit Myndkor (recommandé)
```powershell
# Windows PowerShell
$Root = "$env:USERPROFILE\projets\myndkor-drone-demo"
New-Item -ItemType Directory -Force -Path $Root | Out-Null
cd $Root
# Copier depuis le kit (adapter le chemin si ZIP décompressé ailleurs)
$Kit = "$env:USERPROFILE\Downloads\kit" # <-- à ajuster
Copy-Item "$Kit\app.py","$Kit\test_app.py","$Kit\requirements.txt","$Kit\.env.example","$Kit\.gitignore","$Kit\Dockerfile","$Kit\.dockerignore" -Destination . -Force
New-Item -ItemType Directory -Force -Path "deploy","docs" | Out-Null
Copy-Item "$Kit\deploy\myndkor-chat.service" -Destination "deploy\" -Force
# Fichiers Drone → racine + docs
Copy-Item "$Kit\examples\drone\.drone.yml" -Destination ".\.drone.yml" -Force
Copy-Item "$Kit\examples\drone\docker-compose.app.yml","$Kit\examples\drone\docker-compose.drone.yml","$Kit\examples\drone\drone-secrets.example.env","$Kit\examples\drone\README.md","$Kit\examples\drone\PROCEDURE-DRONE.md" -Destination "docs\" -Force
```
```bash
# Linux / macOS / WSL
ROOT="$HOME/projets/myndkor-drone-demo"
KIT="$HOME/Downloads/kit" # <-- à ajuster
mkdir -p "$ROOT"/{deploy,docs} && cd "$ROOT"
cp "$KIT"/{app.py,test_app.py,requirements.txt,.env.example,.gitignore,Dockerfile,.dockerignore} .
cp "$KIT/deploy/myndkor-chat.service" deploy/
cp "$KIT/examples/drone/.drone.yml" ./.drone.yml
cp "$KIT/examples/drone/"{{docker-compose.app.yml,docker-compose.drone.yml,drone-secrets.example.env,README.md,PROCEDURE-DRONE.md}} docs/
```
### Option B — Projet déjà existant
```text
mon-projet/
├── app.py
├── test_app.py
├── requirements.txt
├── Dockerfile
├── .dockerignore
├── .drone.yml ← À CRÉER ICI (racine, obligatoire)
├── .gitignore
├── .env.example
└── docs/
├── docker-compose.app.yml
├── docker-compose.drone.yml
└── drone-secrets.example.env
```
**Point critique :** le fichier s’appelle `.drone.yml` et est à la **racine** du dépôt (pas dans `.github/workflows/`).
---
## Phase 2 — Initialiser Git + branches
```powershell
cd $Root # ou cd mon-projet
git init -b main
git add .
git commit -m "chore: bootstrap app + drone pipeline"
# Créer le remote (GitHub public/privé selon le contrat client)
git remote add origin https://github.com/<ORG>/<REPO>.git
git push -u origin main
git branch recette
git push -u origin recette
git branch prod
git push -u origin prod
```
Convention Myndkor : `main` → `recette` → `prod`.
Le build Docker Drone se déclenche sur **`prod`** (voir `.drone.yml`).
---
## Phase 3 — Choisir le mode Drone
### Mode 3A — Drone déjà fourni par le client (prod réelle)
1. Admin Drone : **Sync** / activer le dépôt.
2. Repository Settings → **Trusted** si besoin de Docker socket (selon politique).
3. Passer à la **Phase 4** (secrets).
### Mode 3B — Lab formateur / PoC (compose inclus)
```powershell
# Secrets OAuth GitHub (créer une OAuth App)
# Homepage : http://localhost:8080
# Callback : http://localhost:8080/login
$env:DRONE_GITHUB_CLIENT_ID = "xxxxxxxx"
$env:DRONE_GITHUB_CLIENT_SECRET = "yyyyyyyy"
$env:DRONE_RPC_SECRET = -join ((1..32) | ForEach-Object { "{0:x}" -f (Get-Random -Max 16) })
$env:DRONE_SERVER_HOST = "localhost"
cd docs # là où se trouve docker-compose.drone.yml
docker compose -f docker-compose.drone.yml up -d
```
```bash
export DRONE_GITHUB_CLIENT_ID=xxxxxxxx
export DRONE_GITHUB_CLIENT_SECRET=yyyyyyyy
export DRONE_RPC_SECRET=$(openssl rand -hex 16)
export DRONE_SERVER_HOST=localhost
cd docs
docker compose -f docker-compose.drone.yml up -d
```
UI : http://localhost:8080 → login GitHub → activer le repo.
Vérifier :
```powershell
docker ps --filter name=drone
```
---
## Phase 4 — Créer les secrets Drone
Dans Drone → Repository → **Settings → Secrets**
(ou Org secrets si mutualisés).
| Nom secret | Exemple / usage |
|---|---|
| `docker_username` | user registry (GHCR / Harbor / Docker Hub) |
| `docker_password` | token `write:packages` ou mot de passe registry |
| `ssh_host` | IP/hostname serveur de prod |
| `ssh_user` | `deploy` |
| `ssh_key` | clé privée OpenSSH (contenu complet) |
| `ollama_api_key` | clé API (factice OK en lab) |
| `ollama_host` | `http://127.0.0.1:11434` ou URL cloud |
Modèle : `docs/drone-secrets.example.env` (ne jamais committer les vraies valeurs).
Adapter aussi dans `.drone.yml` la ligne :
```yaml
repo: ghcr.io/thomasdsf/myndkor-chat-demo
```
→ `ghcr.io/<org>/<image>` du client.
---
## Phase 5 — Valider le Dockerfile en local (avant CI)
```powershell
cd $Root
Copy-Item .env.example .env
# Éditer APP_ENV=DEV, OLLAMA_* si besoin
docker build -t myndkor-chat:local .
docker run --rm -p 8000:8000 --env-file .env myndkor-chat:local
# Autre terminal :
curl http://127.0.0.1:8000/health
```
Ou via Compose app :
```powershell
cd docs
docker compose -f docker-compose.app.yml up -d --build
curl http://127.0.0.1:8000/health
```
---
## Phase 6 — Premier pipeline CI (Fail Fast)
1. Créer une branche / PR ou push sur `recette` :
```powershell
git checkout recette
# Casser volontairement pour montrer l’échec :
# (Get-Content app.py) -replace 'REQUIRED_MODEL = "gemma4:32"','REQUIRED_MODEL = "WRONG"' | Set-Content app.py
git commit --allow-empty -m "ci: trigger drone test"
git push origin recette
```
2. Dans Drone UI : build **test** vert (ou rouge si Fail Fast).
3. Corriger si besoin, re-push.
> Sur `pull_request` / `push`, seul le pipeline `test-and-build` tourne.
> L’étape `docker` (push image) ne part que sur **branche `prod`**.
---
## Phase 7 — Build & push image (branche prod)
```powershell
git checkout prod
git merge recette # ou PR recette → prod selon gouvernance GitHub/Gitea
git push origin prod
```
Dans Drone :
1. Step `test` OK
2. Step `docker` build + push `ghcr.io/...:<sha>` et `:latest`
Vérifier l’image :
```powershell
docker pull ghcr.io/<org>/<image>:latest
```
---
## Phase 8 — Mise en production (Promote)
Équivalent GitHub « Review deployments → Approve » :
1. Ouvrir le build `prod` réussi dans Drone.
2. Bouton **Promote** (ou Deploy).
3. Target : **`production`** (doit matcher `trigger.target` du 2ᵉ pipeline).
4. Confirmer.
Le pipeline `deploy-production` démarre :
- SSH sur le serveur
- `docker login` + `docker pull`
- stop/rm ancien conteneur
- `docker run` avec `APP_ENV=PROD` + secrets
- `curl /health`
Navigateur / smoke test :
```text
http://<serveur>:8000
Bandeau vert PROD + titre injecté
```
---
## Phase 9 — Checklist de recette client
- [ ] `.drone.yml` à la racine (pas dans `.github/`)
- [ ] Dockerfile + `.dockerignore` présents
- [ ] Repo activé dans Drone + runner online
- [ ] Secrets registry + SSH + Ollama créés
- [ ] Test rouge volontaire puis vert (Fail Fast démontré)
- [ ] Image visible dans le registry après push `prod`
- [ ] Promote `production` → conteneur UP + `/health` OK
- [ ] Aucun secret dans Git (`git log -p` clean)
---
## Phase 10 — Désactiver / cohabiter avec GitHub Actions
Si le même repo a aussi des workflows Actions :
| Stratégie | Action |
|---|---|
| Démo Actions seule | Ne pas activer le repo dans Drone |
| Démo Drone seule | Renommer `.github/workflows/*.yml` → `*.yml.disabled` |
| Les deux en PoC | Branches séparées ou repos séparés |
---
## Dépannage rapide
| Symptôme | Cause fréquente | Correctif |
|---|---|---|
| Drone ignore le repo | Non activé / mauvais remote | Activate + Sync |
| Step docker fail auth | Mauvais `docker_password` | Token packages:write |
| Promote ne lance rien | Target ≠ `production` | Exact match du YAML |
| App down après deploy | Port / Ollama host | Vérifier `-e OLLAMA_HOST` et firewall |
| pytest fail dans Drone | Dépendance manquante | Aligner `requirements.txt` |
---
## Récap commandes « anti-sèche » (Windows)
```powershell
# 1) Arborescence + fichiers (Phase 1)
# 2) git init / push branches (Phase 2)
# 3) Lab Drone (optionnel)
cd docs
$env:DRONE_GITHUB_CLIENT_ID="..."
$env:DRONE_GITHUB_CLIENT_SECRET="..."
$env:DRONE_RPC_SECRET="..."
$env:DRONE_SERVER_HOST="localhost"
docker compose -f docker-compose.drone.yml up -d
# 4) Secrets dans UI Drone
# 5) git push origin prod → test + docker
# 6) UI → Promote → production
# 7) curl http://<host>:8000/health
```
# =============================================================================
# EXEMPLE CLIENT — Drone CI + Docker (clé en main)
# =============================================================================
# Emplacement : à copier à la RACINE du dépôt client sous le nom `.drone.yml`
#
# Flux :
# 1) test — toujours (PR + push) : pytest dans une image Python
# 2) docker — push branche prod : build Dockerfile + push registry
# 3) deploy — uniquement après PROMOTION manuelle target=production
# (équivalent GitHub Environment "Required reviewers")
#
# Secrets Drone (repository ou org) :
# docker_username / docker_password
# ssh_host / ssh_user / ssh_key (si deploy SSH)
# ollama_api_key / ollama_host
#
# Docs Drone : https://docs.drone.io/
# =============================================================================
---
kind: pipeline
type: docker
name: test-and-build
concurrency:
limit: 1
# Déclencheurs : PR + push (ignore tags)
trigger:
event:
- push
- pull_request
steps:
# ---------------------------------------------------------------------------
# 1. Fail Fast — mêmes garanties que le job GitHub Actions "test"
# ---------------------------------------------------------------------------
- name: test
image: python:3.12-slim
environment:
MODEL_NAME: gemma4:32
APP_ENV: RECETTE
APP_TITLE: Myndkor Chat Demo
OLLAMA_HOST: http://127.0.0.1:11434
commands:
- pip install --no-cache-dir -r requirements.txt
- pytest -q
# ---------------------------------------------------------------------------
# 2. Build & push image (uniquement branche prod, après tests OK)
# Adaptez "repo" à votre registry (ghcr.io/org/app, harbor, ECR…)
# ---------------------------------------------------------------------------
- name: docker
image: plugins/docker
settings:
repo: ghcr.io/thomasdsf/myndkor-chat-demo
registry: ghcr.io
username:
from_secret: docker_username
password:
from_secret: docker_password
tags:
- ${DRONE_COMMIT_SHA}
- latest
dockerfile: Dockerfile
# purge: true # optionnel : nettoyer le cache builder
when:
branch:
- prod
event:
- push
---
# =============================================================================
# Pipeline de DÉPLOIEMENT — déclenché manuellement via Promote
# UI Drone → build prod réussi → Promote → target: production
# =============================================================================
kind: pipeline
type: docker
name: deploy-production
trigger:
event:
- promote
target:
- production
steps:
# Exemple A — déploiement SSH + docker compose sur un VPS
- name: deploy-ssh
image: appleboy/drone-ssh
settings:
host:
from_secret: ssh_host
username:
from_secret: ssh_user
key:
from_secret: ssh_key
port: 22
script:
- set -euo pipefail
- export IMAGE=ghcr.io/thomasdsf/myndkor-chat-demo:${DRONE_COMMIT_SHA}
- echo "$DOCKER_PASSWORD" | docker login ghcr.io -u "$DOCKER_USERNAME" --password-stdin
- docker pull "$IMAGE"
- docker stop myndkor-chat || true
- docker rm myndkor-chat || true
- |
docker run -d --name myndkor-chat --restart unless-stopped \
-p 8000:8000 \
-e APP_ENV=PROD \
-e MODEL_NAME=gemma4:32 \
-e APP_TITLE="Myndkor Chat PROD" \
-e OLLAMA_HOST="$OLLAMA_HOST" \
-e OLLAMA_API_KEY="$OLLAMA_API_KEY" \
"$IMAGE"
- docker ps --filter name=myndkor-chat
- curl -sf http://127.0.0.1:8000/health
environment:
DOCKER_USERNAME:
from_secret: docker_username
DOCKER_PASSWORD:
from_secret: docker_password
OLLAMA_API_KEY:
from_secret: ollama_api_key
OLLAMA_HOST:
from_secret: ollama_host
# Exemple B (désactivé) — kubectl set image
# Décommentez et commentez deploy-ssh si le client est full K8s.
# - name: deploy-k8s
# image: bitnami/kubectl:latest
# environment:
# KUBE_CONFIG:
# from_secret: kube_config
# commands:
# - mkdir -p ~/.kube
# - echo "$KUBE_CONFIG" | base64 -d > ~/.kube/config
# - kubectl -n myndkor set image deploy/myndkor-chat app=ghcr.io/thomasdsf/myndkor-chat-demo:${DRONE_COMMIT_SHA}
# - kubectl -n myndkor rollout status deploy/myndkor-chat --timeout=180s
Guide formateur (complément)
PREPA-WINDOWS# Prérequis poste Windows — Démo live myndkor-chat-demo
Compte : [Thomasdsf](https://github.com/Thomasdsf)
Repo : https://github.com/Thomasdsf/myndkor-chat-demo (**public**)
Formation : https://myndkor.com/formations/github-enterprise-cicd/
Poste formateur = **Windows 10/11**. Tous les blocs ci-dessous sont en **PowerShell**.
---
## 1. Logiciels à installer (une seule fois)
### 1.1 Git for Windows
1. Télécharger : https://git-scm.com/download/win
2. Installer avec les options par défaut.
3. Cocher **Git from the command line and also from 3rd-party software**.
Vérification :
```powershell
git --version
```
### 1.2 Python 3.12
1. Télécharger : https://www.python.org/downloads/windows/
2. **Cocher « Add python.exe to PATH »** pendant l’install.
3. Fermer / rouvrir PowerShell.
Vérification :
```powershell
python --version
pip --version
```
### 1.3 Ollama (Windows) + modèle `gemma4:32`
1. Télécharger : https://ollama.com/download/windows
2. Installer et laisser Ollama démarrer (icône barre des tâches).
3. Dans PowerShell :
```powershell
ollama pull gemma4:32
ollama list
# API locale attendue :
Invoke-RestMethod http://127.0.0.1:11434/api/tags
```
> Si tu utilises Ollama Cloud / un autre host : note l’URL pour la variable GitHub `OLLAMA_HOST`.
### 1.4 (Optionnel) GitHub CLI
https://cli.github.com/ — utile pour `gh repo` / auth, pas obligatoire pour la démo.
---
## 2. Dossiers DEV / PROD
```powershell
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\demo_dev" | Out-Null
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\demo_prod" | Out-Null
```
| Dossier | Rôle | Port |
|---|---|---|
| `%USERPROFILE%\demo_dev` | Travail formateur (git, live coding) | **8000** |
| `%USERPROFILE%\demo_prod` | Cible du runner après Approve | **8001** |
---
## 3. Cloner / pousser le kit dans le repo
Si le repo GitHub est encore vide, depuis le ZIP formation ou une copie locale du kit :
```powershell
cd $env:USERPROFILE\demo_dev
# Copier ici app.py, test_app.py, requirements.txt, .gitignore, .env.example, .github\...
git init -b main
git add .
git commit -m "chore: bootstrap myndkor chat demo"
git remote add origin https://github.com/Thomasdsf/myndkor-chat-demo.git
git push -u origin main
git branch recette
git push -u origin recette
git branch prod
git push -u origin prod
```
Sinon, simple clone :
```powershell
git clone https://github.com/Thomasdsf/myndkor-chat-demo.git $env:USERPROFILE\demo_dev
cd $env:USERPROFILE\demo_dev
```
---
## 4. Environnement Python DEV (port 8000)
```powershell
cd $env:USERPROFILE\demo_dev
python -m venv .venv
.\.venv\Scripts\Activate.ps1
# Si erreur d'exécution de scripts :
# Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
pip install -r requirements.txt
Copy-Item .env.example .env
notepad .env
# APP_ENV=DEV
# APP_TITLE=Chat DEV Myndkor
# OLLAMA_HOST=http://127.0.0.1:11434
# OLLAMA_API_KEY= (vide en local, ou factice)
# MODEL_NAME=gemma4:32
uvicorn app:app --host 0.0.0.0 --port 8000
```
Navigateur : http://127.0.0.1:8000 — bandeau **bleu DEV**.
Test unitaire :
```powershell
pytest -q
```
---
## 5. Self-Hosted Runner Windows (obligatoire pour le deploy live)
1. Ouvre : https://github.com/Thomasdsf/myndkor-chat-demo/settings/actions/runners
2. **New self-hosted runner** → **Windows** → **x64**.
3. Dans PowerShell **en Administrateur** (recommandé pour le service) :
```powershell
mkdir $env:USERPROFILE\actions-runner ; cd $env:USERPROFILE\actions-runner
# Colle les 3 commandes affichées par GitHub (download + extract + config)
# Exemple de forme :
# Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/vX.Y.Z/actions-runner-win-x64-X.Y.Z.zip -OutFile actions-runner-win-x64.zip
# Add-Type -AssemblyName System.IO.Compression.FileSystem
# [System.IO.Compression.ZipFile]::ExtractToDirectory("$PWD\actions-runner-win-x64.zip", "$PWD")
# .\config.cmd --url https://github.com/Thomasdsf/myndkor-chat-demo --token <TOKEN_UI>
```
Lancer le runner pendant toute la séance :
```powershell
.\run.cmd
```
Ou l’installer comme service Windows (reste online après reboot) :
```powershell
# PowerShell Administrateur, dans le dossier actions-runner
.\svc.cmd install
.\svc.cmd start
```
Vérifie le statut **Idle** sur la page Runners.
> Le job `test` tourne sur `ubuntu-latest` (cloud). Seul `deploy-prod` utilise ton runner Windows.
---
## 6. Réglages GitHub (UI) — à faire avant la séance
1. **Code security** → Secret Scanning + Push Protection
https://github.com/Thomasdsf/myndkor-chat-demo/settings/security_analysis
2. **Environments → `production`**
https://github.com/Thomasdsf/myndkor-chat-demo/settings/environments
- Required reviewers : ton compte (ou un 2ᵉ compte DevOps)
- Secret : `OLLAMA_API_KEY` (valeur factice OK, ex. `demo_key_myndkor`)
- Variables : `OLLAMA_HOST=http://127.0.0.1:11434`, `APP_TITLE=Chat PROD Myndkor`
3. **Ruleset sur `prod`**
https://github.com/Thomasdsf/myndkor-chat-demo/settings/rules
- Require pull request
- Require status checks → `test`
- Block force pushes
---
## 7. Checklist « J-0 / 5 min avant »
- [ ] Repo **public**
- [ ] Runner Windows **Idle**
- [ ] `demo_dev` ouvert dans VS Code / terminal, branche `recette`
- [ ] App DEV sur http://127.0.0.1:8000
- [ ] Ollama up + `gemma4:32`
- [ ] Rien d’autre n’occupe le port **8001**
- [ ] Onglets : repo, Actions, PR, `:8000`, `:8001`
- [ ] Environment `production` + Ruleset OK
---
## 8. Commandes Fail Fast (PowerShell) pendant le live
```powershell
cd $env:USERPROFILE\demo_dev
git checkout recette
# Casser volontairement le test
(Get-Content app.py) -replace 'REQUIRED_MODEL = "gemma4:32"','REQUIRED_MODEL = "WRONG"' | Set-Content app.py -Encoding utf8
git add -A
git commit -m "feat: break model for fail-fast demo"
git push origin recette
# → Ouvrir PR recette → prod : check test ROUGE, Merge bloqué
# Corriger
(Get-Content app.py) -replace 'REQUIRED_MODEL = "WRONG"','REQUIRED_MODEL = "gemma4:32"' | Set-Content app.py -Encoding utf8
git add app.py
git commit -m "fix: restore REQUIRED_MODEL gemma4:32"
git push origin recette
# → test VERT → Merge → Review deployments → Approve → refresh :8001
```