Add Docker build/deploy scripts, .dockerignore, and registry skill
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
# ── Python ──
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.eggs/
|
||||||
|
*.whl
|
||||||
|
|
||||||
|
# ── Environnement virtuel ──
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
.venv/
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# ── Git ──
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.gitattributes
|
||||||
|
|
||||||
|
# ── IDE ──
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# ── Node (imago-admin) ──
|
||||||
|
imago-admin/node_modules/
|
||||||
|
imago-admin/dist/
|
||||||
|
|
||||||
|
# ── Données locales ──
|
||||||
|
data/
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
|
||||||
|
# ── Tests & CI ──
|
||||||
|
.pytest_cache/
|
||||||
|
.mypy_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
htmlcov/
|
||||||
|
.coverage
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# ── Docker ──
|
||||||
|
docker-compose.override.yml
|
||||||
|
docker-compose.production.yml
|
||||||
|
|
||||||
|
# ── OS ──
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Construit l'image Docker Imago backend via WSL Debian.
|
||||||
|
.DESCRIPTION
|
||||||
|
Build l'image Docker pour le backend Imago en utilisant Docker
|
||||||
|
sous WSL Debian avec BuildKit activé.
|
||||||
|
.PARAMETRES
|
||||||
|
-full : Construction complète sans cache (--no-cache).
|
||||||
|
.EXEMPLE
|
||||||
|
.\build-img.ps1 # Build avec cache
|
||||||
|
.\build-img.ps1 -full # Build complet sans cache
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[switch]$full
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
try {
|
||||||
|
$scriptDir = $PSScriptRoot
|
||||||
|
$projectRoot = (Get-Item (Join-Path $scriptDir '..')).FullName
|
||||||
|
|
||||||
|
# Convertir chemin Windows vers WSL
|
||||||
|
$wslProjectRoot = $projectRoot -replace '^([A-Za-z]):\\', '/mnt/$1/' -replace '\\', '/'
|
||||||
|
$wslProjectRoot = $wslProjectRoot.ToLower()
|
||||||
|
|
||||||
|
# Vérifier Docker dans WSL Debian
|
||||||
|
Write-Host "Vérification de Docker dans WSL Debian..." -ForegroundColor Cyan
|
||||||
|
$dockerCheck = wsl -d Debian bash -c "docker --version" 2>&1
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Docker n'est pas disponible dans WSL Debian"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host "Docker détecté" -ForegroundColor Green
|
||||||
|
|
||||||
|
$noCache = if ($full) { '--no-cache' } else { '' }
|
||||||
|
|
||||||
|
# Commande de build
|
||||||
|
$cdCmd = "cd `"$wslProjectRoot`""
|
||||||
|
$dockerCmd = "docker build $noCache --progress=plain -t imago-backend:latest -f Dockerfile ."
|
||||||
|
$innerCmd = "export DOCKER_BUILDKIT=1 && $cdCmd && $dockerCmd"
|
||||||
|
|
||||||
|
Write-Host "Construction de l'image imago-backend:latest..." -ForegroundColor Cyan
|
||||||
|
$buildResult = wsl -d Debian bash -lc $innerCmd 2>&1
|
||||||
|
$buildResult | ForEach-Object { Write-Host $_ }
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Erreur lors de la construction de l'image (code: $LASTEXITCODE)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Image imago-backend:latest construite avec succès." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Erreur : $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Exécute deploy-img.sh dans WSL Debian pour pousser Imago vers le registre.
|
||||||
|
.DESCRIPTION
|
||||||
|
Lance le script Bash deploy-img.sh sous WSL Debian qui tague et pousse
|
||||||
|
l'image vers dev.lab.home:5000 avec versioning semver.
|
||||||
|
.EXEMPLE
|
||||||
|
.\deploy-img.ps1
|
||||||
|
#>
|
||||||
|
|
||||||
|
param()
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
try {
|
||||||
|
$scriptDir = $PSScriptRoot
|
||||||
|
$wslScriptDir = $scriptDir -replace '^([A-Za-z]):\\', '/mnt/$1/' -replace '\\', '/'
|
||||||
|
$wslScriptDir = $wslScriptDir.ToLower()
|
||||||
|
|
||||||
|
Write-Host "Déploiement de l'image Imago vers le registre..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Rendre le script exécutable
|
||||||
|
wsl -d Debian -- chmod +x "$wslScriptDir/deploy-img.sh"
|
||||||
|
|
||||||
|
# Exécuter
|
||||||
|
$innerCmd = "cd '$wslScriptDir' && ./deploy-img.sh"
|
||||||
|
wsl -d Debian bash -lc $innerCmd
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Le déploiement a échoué (code: $LASTEXITCODE)"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Déploiement terminé avec succès." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Erreur : $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Script: deploy-img.sh
|
||||||
|
# Description: Pousse l'image Docker Imago vers le registre HTTP local,
|
||||||
|
# gère le versioning semver et la rétention.
|
||||||
|
# Adapté de: homelab-automation-api/docker/deploy-img.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# CONFIG #
|
||||||
|
#####################################
|
||||||
|
IMAGE_NAME="imago-backend"
|
||||||
|
REGISTRY_HOST="dev.lab.home"
|
||||||
|
REGISTRY_PORT="5000"
|
||||||
|
MAX_VERSIONS=5
|
||||||
|
LOCAL_TAG="latest"
|
||||||
|
PUSH_LATEST="yes"
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# DÉDUCTIONS #
|
||||||
|
#####################################
|
||||||
|
REGISTRY="${REGISTRY_HOST}:${REGISTRY_PORT}"
|
||||||
|
REMOTE_REPO="${REGISTRY}/${IMAGE_NAME}"
|
||||||
|
LOCAL_IMAGE="${IMAGE_NAME}:${LOCAL_TAG}"
|
||||||
|
TEMP_DIR="/tmp/imago-deploy-$(date +%s)"
|
||||||
|
CURL="curl -fsSL"
|
||||||
|
JQ_BIN="${JQ_BIN:-jq}"
|
||||||
|
ACCEPT_MANIFEST='application/vnd.docker.distribution.manifest.v2+json'
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# UTILITAIRES #
|
||||||
|
#####################################
|
||||||
|
info() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
||||||
|
warn() { echo -e "\033[1;33m[AVERT]\033[0m $*"; }
|
||||||
|
error() { echo -e "\033[1;31m[ERREUR]\033[0m $*" >&2; }
|
||||||
|
success() { echo -e "\033[1;32m[SUCCÈS]\033[0m $*"; }
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
set +e
|
||||||
|
[[ -d "$TEMP_DIR" ]] && rm -rf "$TEMP_DIR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
require_cmd() {
|
||||||
|
command -v "$1" >/dev/null 2>&1 || { error "Commande requise manquante: $1"; exit 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# PRÉ-VÉRIFICATIONS #
|
||||||
|
#####################################
|
||||||
|
require_cmd docker
|
||||||
|
require_cmd curl
|
||||||
|
require_cmd sed
|
||||||
|
require_cmd grep
|
||||||
|
require_cmd awk
|
||||||
|
require_cmd sort
|
||||||
|
|
||||||
|
info "Vérification de l'image locale '${LOCAL_IMAGE}'…"
|
||||||
|
if ! docker image inspect "${LOCAL_IMAGE}" >/dev/null 2>&1; then
|
||||||
|
error "L'image locale ${LOCAL_IMAGE} n'existe pas. Lance build-img.ps1 d'abord."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Vérification du registre (http://${REGISTRY}/v2/)…"
|
||||||
|
if ! ${CURL} "http://${REGISTRY}/v2/" >/dev/null 2>&1; then
|
||||||
|
error "Registre injoignable à http://${REGISTRY}/v2/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "${TEMP_DIR}"
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# RÉCUPÉRATION DES TAGS #
|
||||||
|
#####################################
|
||||||
|
get_all_tags() {
|
||||||
|
local tags_json
|
||||||
|
if ! tags_json="$(${CURL} "http://${REGISTRY}/v2/${IMAGE_NAME}/tags/list" 2>/dev/null)"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if command -v "${JQ_BIN}" >/dev/null 2>&1; then
|
||||||
|
echo "${tags_json}" | ${JQ_BIN} -r '.tags[]?' 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo "${tags_json}" \
|
||||||
|
| tr -d '\n' \
|
||||||
|
| sed -n 's/.*"tags":[[]\([^]]*\)[]].*/\1/p' \
|
||||||
|
| tr -d '"' \
|
||||||
|
| tr ',' '\n' \
|
||||||
|
| sed 's/^[[:space:]]*//; s/[[:space:]]*$//'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_semver() {
|
||||||
|
grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' || true
|
||||||
|
}
|
||||||
|
|
||||||
|
increment_patch() {
|
||||||
|
local tag="$1"
|
||||||
|
local major minor patch
|
||||||
|
IFS='.' read -r major minor patch <<< "${tag}"
|
||||||
|
echo "${major}.${minor}.$((patch+1))"
|
||||||
|
}
|
||||||
|
|
||||||
|
info "Récupération des tags existants…"
|
||||||
|
ALL_TAGS="$(get_all_tags || true)"
|
||||||
|
SEMVER_TAGS="$(printf '%s\n' "${ALL_TAGS}" | filter_semver | sort -V || true)"
|
||||||
|
|
||||||
|
if [[ -z "${SEMVER_TAGS}" ]]; then
|
||||||
|
NEW_TAG="2.0.0"
|
||||||
|
else
|
||||||
|
LATEST_TAG="$(printf '%s\n' "${SEMVER_TAGS}" | tail -n1)"
|
||||||
|
NEW_TAG="$(increment_patch "${LATEST_TAG}")"
|
||||||
|
fi
|
||||||
|
info "Tag retenu : ${NEW_TAG}"
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# TAG + PUSH #
|
||||||
|
#####################################
|
||||||
|
info "Taggage ${LOCAL_IMAGE} → ${REMOTE_REPO}:${NEW_TAG}"
|
||||||
|
docker tag "${LOCAL_IMAGE}" "${REMOTE_REPO}:${NEW_TAG}"
|
||||||
|
|
||||||
|
if [[ "${PUSH_LATEST}" == "yes" ]]; then
|
||||||
|
info "Taggage ${LOCAL_IMAGE} → ${REMOTE_REPO}:latest"
|
||||||
|
docker tag "${LOCAL_IMAGE}" "${REMOTE_REPO}:latest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Push ${REMOTE_REPO}:${NEW_TAG}…"
|
||||||
|
docker push --disable-content-trust "${REMOTE_REPO}:${NEW_TAG}"
|
||||||
|
|
||||||
|
if [[ "${PUSH_LATEST}" == "yes" ]]; then
|
||||||
|
info "Push ${REMOTE_REPO}:latest…"
|
||||||
|
docker push --disable-content-trust "${REMOTE_REPO}:latest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# RÉTENTION #
|
||||||
|
#####################################
|
||||||
|
delete_by_tag() {
|
||||||
|
local tag="$1"
|
||||||
|
local digest
|
||||||
|
digest="$(curl -fsSI -H "Accept: ${ACCEPT_MANIFEST}" "http://${REGISTRY}/v2/${IMAGE_NAME}/manifests/${tag}" \
|
||||||
|
| tr -d '\r' \
|
||||||
|
| awk -F': ' 'tolower($1)=="docker-content-digest"{print $2}' \
|
||||||
|
| tail -n1)"
|
||||||
|
if [[ -z "${digest}" ]]; then
|
||||||
|
warn "Digest introuvable pour ${tag}. Skip."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
info "Suppression ${tag} (digest: ${digest})…"
|
||||||
|
curl -fsS -X DELETE "http://${REGISTRY}/v2/${IMAGE_NAME}/manifests/${digest}" >/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
ALL_TAGS="$(get_all_tags || true)"
|
||||||
|
SEMVER_TAGS="$(printf '%s\n' "${ALL_TAGS}" | filter_semver | sort -V || true)"
|
||||||
|
|
||||||
|
if [[ -n "${SEMVER_TAGS}" ]]; then
|
||||||
|
COUNT="$(printf '%s\n' "${SEMVER_TAGS}" | wc -l | awk '{print $1}')"
|
||||||
|
if (( COUNT > MAX_VERSIONS )); then
|
||||||
|
TO_DELETE_COUNT=$(( COUNT - MAX_VERSIONS ))
|
||||||
|
OLDEST="$(printf '%s\n' "${SEMVER_TAGS}" | head -n "${TO_DELETE_COUNT}")"
|
||||||
|
while IFS= read -r old_tag; do
|
||||||
|
[[ -z "${old_tag}" ]] && continue
|
||||||
|
[[ "${old_tag}" == "${NEW_TAG}" ]] && continue
|
||||||
|
delete_by_tag "${old_tag}"
|
||||||
|
done <<< "${OLDEST}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# NETTOYAGE LOCAL #
|
||||||
|
#####################################
|
||||||
|
info "Nettoyage tags locaux temporaires…"
|
||||||
|
set +e
|
||||||
|
docker rmi "${REMOTE_REPO}:${NEW_TAG}" >/dev/null 2>&1
|
||||||
|
[[ "${PUSH_LATEST}" == "yes" ]] && docker rmi "${REMOTE_REPO}:latest" >/dev/null 2>&1
|
||||||
|
set -e
|
||||||
|
|
||||||
|
success "Déploiement terminé. Version publiée : ${NEW_TAG}"
|
||||||
|
success "Image disponible : ${REMOTE_REPO}:${NEW_TAG}"
|
||||||
|
success " : ${REMOTE_REPO}:latest"
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user