diff --git a/.cursor/rules/05-template-governance.mdc b/.cursor/rules/05-template-governance.mdc new file mode 100644 index 00000000..72a0a64b --- /dev/null +++ b/.cursor/rules/05-template-governance.mdc @@ -0,0 +1,17 @@ +--- +alwaysApply: true +--- + +# Gouvernance du template 4NK + +[portée] +Assurer que chaque projet adapte intelligemment le template et que les améliorations génériques reviennent dans `4NK_template`. + +[directives] +- Conserver `security-audit` et `release-guard` dans tous projets. +- Adapter la CI, les docs et `AGENTS.md` au contexte local. +- En cas d'amélioration générique : ouvrir une issue "Template Feedback", prototyper, valider CI, mettre à jour `CHANGELOG.md`/`TEMPLATE_VERSION`. + +[validation] +- Refuser un push/tag si l'adaptation a retiré les vérifications minimales (sécurité, tests, build, version/changelog/tag). +- Exiger une documentation claire dans `docs/TEMPLATE_ADAPTATION.md` et `docs/TEMPLATE_FEEDBACK.md`. \ No newline at end of file diff --git a/.cursor/rules/98-explain-complex-commands b/.cursor/rules/98-explain-complex-commands new file mode 100644 index 00000000..610e6ca5 --- /dev/null +++ b/.cursor/rules/98-explain-complex-commands @@ -0,0 +1,5 @@ +--- +alwaysApply: true +--- + +quand tu fais une commande ou un requète complexe, explique là avant de la lancer \ No newline at end of file diff --git a/.cursor/rules/99-lint-markdow.mdc b/.cursor/rules/99-lint-markdow.mdc new file mode 100644 index 00000000..6924c297 --- /dev/null +++ b/.cursor/rules/99-lint-markdow.mdc @@ -0,0 +1,9 @@ +--- +description: +globs: +alwaysApply: true +--- + +# Lint + +respecter strictement les règles de lint du markdown diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 00000000..56e5c352 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,14 @@ +{ + "MD013": { + "line_length": 200, + "code_blocks": false, + "tables": false, + "headings": false + }, + "MD007": { + "indent": 2 + }, + "MD024": { + "siblings_only": true + } +} diff --git a/TEMPLATE_VERSION b/TEMPLATE_VERSION new file mode 100644 index 00000000..264fc296 --- /dev/null +++ b/TEMPLATE_VERSION @@ -0,0 +1 @@ +v2025.08.5 \ No newline at end of file diff --git a/docs/templates/API.md b/docs/templates/API.md new file mode 100644 index 00000000..431560f3 --- /dev/null +++ b/docs/templates/API.md @@ -0,0 +1,8 @@ +# Référence API — Template + +- Vue d’ensemble +- Authentification/permissions +- Endpoints par domaine (schémas, invariants) +- Codes d’erreur +- Limites et quotas +- Sécurité et conformité diff --git a/docs/templates/ARCHITECTURE.md b/docs/templates/ARCHITECTURE.md new file mode 100644 index 00000000..42b78b2d --- /dev/null +++ b/docs/templates/ARCHITECTURE.md @@ -0,0 +1,8 @@ +# Architecture — Template + +- Contexte et objectifs +- Découpage en couches (UI, services, données) +- Flux principaux +- Observabilité +- CI/CD +- Contraintes et SLA diff --git a/docs/templates/CONFIGURATION.md b/docs/templates/CONFIGURATION.md new file mode 100644 index 00000000..3506069d --- /dev/null +++ b/docs/templates/CONFIGURATION.md @@ -0,0 +1,6 @@ +# Configuration — Template + +- Variables d’environnement (nom, type, défaut, portée) +- Fichiers de configuration (format, validation) +- Réseau et sécurité (ports, TLS, auth) +- Observabilité (logs, métriques, traces) diff --git a/docs/templates/INDEX.md b/docs/templates/INDEX.md new file mode 100644 index 00000000..be566c0c --- /dev/null +++ b/docs/templates/INDEX.md @@ -0,0 +1,12 @@ +# Index — Templates de documentation (pour projets dérivés) + +Utilisez ces squelettes pour démarrer la documentation de votre projet. + +- API.md — squelette de référence API +- ARCHITECTURE.md — squelette d’architecture +- CONFIGURATION.md — squelette de configuration +- USAGE.md — squelette d’usage +- TESTING.md — squelette de stratégie de tests +- SECURITY_AUDIT.md — squelette d’audit sécurité +- RELEASE_PLAN.md — squelette de plan de release +- OPEN_SOURCE_CHECKLIST.md — squelette de checklist open source diff --git a/docs/templates/OPEN_SOURCE_CHECKLIST.md b/docs/templates/OPEN_SOURCE_CHECKLIST.md new file mode 100644 index 00000000..8406e387 --- /dev/null +++ b/docs/templates/OPEN_SOURCE_CHECKLIST.md @@ -0,0 +1,7 @@ +# Checklist open source — Template + +- Gouvernance: LICENSE, CONTRIBUTING, CODE_OF_CONDUCT +- CI/CD: workflows, tests, security-audit, release-guard +- Documentation: README, INDEX, guides essentiels +- Sécurité: secrets, permissions, audit +- Publication: tag, changelog, release notes diff --git a/docs/templates/README.md b/docs/templates/README.md new file mode 100644 index 00000000..fe4d4bb7 --- /dev/null +++ b/docs/templates/README.md @@ -0,0 +1,29 @@ +# README — Template de projet + +## Présentation + +Décrivez brièvement l’objectif du projet, son périmètre et ses utilisateurs cibles. + +## Démarrage rapide + +- Prérequis (langages/outils) +- Étapes d’installation +- Commandes de démarrage + +## Documentation + +- Index: `docs/INDEX.md` +- Architecture: `docs/ARCHITECTURE.md` +- Configuration: `docs/CONFIGURATION.md` +- Tests: `docs/TESTING.md` +- Sécurité: `docs/SECURITY_AUDIT.md` +- Déploiement: `docs/DEPLOYMENT.md` + +## Contribution + +- GUIDE: `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` +- Processus de PR et revues + +## Licence + +- Indiquez la licence choisie (MIT/Apache-2.0/GPL) diff --git a/docs/templates/RELEASE_PLAN.md b/docs/templates/RELEASE_PLAN.md new file mode 100644 index 00000000..ab912bfd --- /dev/null +++ b/docs/templates/RELEASE_PLAN.md @@ -0,0 +1,7 @@ +# Plan de release — Template + +- Vue d’ensemble, objectifs, date cible +- Préparation (docs/CI/tests/sécurité) +- Communication (annonces, canaux) +- Lancement (checklist, tagging) +- Post‑lancement (support, retours) diff --git a/docs/templates/SECURITY_AUDIT.md b/docs/templates/SECURITY_AUDIT.md new file mode 100644 index 00000000..3876d6a9 --- /dev/null +++ b/docs/templates/SECURITY_AUDIT.md @@ -0,0 +1,7 @@ +# Audit de sécurité — Template + +- Menaces et surfaces d’attaque +- Contrôles préventifs et détectifs +- Gestion des secrets +- Politique de dépendances +- Vérifications CI (security-audit) diff --git a/docs/templates/TESTING.md b/docs/templates/TESTING.md new file mode 100644 index 00000000..81a4b510 --- /dev/null +++ b/docs/templates/TESTING.md @@ -0,0 +1,6 @@ +# Tests — Template + +- Pyramide: unit, integration, connectivity, external, performance +- Structure des répertoires +- Exécution et rapports +- Intégration CI diff --git a/docs/templates/USAGE.md b/docs/templates/USAGE.md new file mode 100644 index 00000000..8cad2e97 --- /dev/null +++ b/docs/templates/USAGE.md @@ -0,0 +1,7 @@ +# Usage — Template + +- Démarrage quotidien +- Opérations courantes +- Tests (référence vers TESTING.md) +- Sécurité (référence vers SECURITY_AUDIT.md) +- Déploiement (référence vers DEPLOYMENT.md) diff --git a/scripts/checks/version_alignment.sh b/scripts/checks/version_alignment.sh old mode 100644 new mode 100755 diff --git a/scripts/deploy/setup.sh b/scripts/deploy/setup.sh new file mode 100755 index 00000000..8908ea95 --- /dev/null +++ b/scripts/deploy/setup.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENV_DIR="${HOME}/.4nk_template" +ENV_FILE="${ENV_DIR}/.env" +TEMPLATE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +TEMPLATE_IN_REPO="${TEMPLATE_ROOT}/scripts/env/.env.template" + +usage() { + cat < [--dest DIR] [--force] + +Actions: + 1) Provisionne ~/.4nk_template/.env (si absent) + 2) Clone le dépôt cible si le dossier n'existe pas + 3) Copie la structure normative 4NK_template dans le projet cible: + - .gitea/** (workflows, templates issues/PR) + - AGENTS.md + - .cursor/rules/** (si présent) + - scripts/agents/**, scripts/env/ensure_env.sh, scripts/deploy/setup.sh + - docs/templates/** et docs/INDEX.md (table des matières) + 4) Ne remplace pas les fichiers existants sauf si --force + +Exemples: + $0 https://git.example.com/org/projet.git + $0 git@host:org/projet.git --dest ~/work --force +USAGE +} + +GIT_URL="${1:-}" +DEST_PARENT="$(pwd)" +FORCE_COPY=0 +shift || true +while [[ $# -gt 0 ]]; do + case "$1" in + --dest) + DEST_PARENT="${2:-}"; shift 2 ;; + --force) + FORCE_COPY=1; shift ;; + -h|--help) + usage; exit 0 ;; + *) + echo "Option inconnue: $1" >&2; usage; exit 2 ;; + esac +done + +if [[ -z "${GIT_URL}" ]]; then + usage; exit 2 +fi + +mkdir -p "${ENV_DIR}" +chmod 700 "${ENV_DIR}" || true + +if [[ ! -f "${ENV_FILE}" ]]; then + if [[ -f "${TEMPLATE_IN_REPO}" ]]; then + cp "${TEMPLATE_IN_REPO}" "${ENV_FILE}" + else + cat >"${ENV_FILE}" <<'EOF' +# Fichier d'exemple d'environnement pour 4NK_template +# Copiez ce fichier vers ~/.4nk_template/.env puis complétez les valeurs. +# Ne committez jamais de fichier contenant des secrets. + +# OpenAI (agents IA) +OPENAI_API_KEY= +OPENAI_MODEL= +OPENAI_API_BASE=https://api.openai.com/v1 +OPENAI_TEMPERATURE=0.2 + +# Gitea (release via API) +BASE_URL=https://git.4nkweb.com +RELEASE_TOKEN= +EOF + fi + chmod 600 "${ENV_FILE}" || true + echo "Fichier créé: ${ENV_FILE}. Complétez les valeurs requises (ex: OPENAI_API_KEY, OPENAI_MODEL, RELEASE_TOKEN)." >&2 +fi + +# 2) Clonage du dépôt si nécessaire +repo_name="$(basename -s .git "${GIT_URL}")" +target_dir="${DEST_PARENT%/}/${repo_name}" +if [[ ! -d "${target_dir}" ]]; then + echo "Clonage: ${GIT_URL} → ${target_dir}" >&2 + git clone --depth 1 "${GIT_URL}" "${target_dir}" +else + echo "Dossier existant, pas de clone: ${target_dir}" >&2 +fi + +copy_item() { + local src="$1" dst="$2" + if [[ ! -e "$src" ]]; then return 0; fi + if [[ -d "$src" ]]; then + mkdir -p "$dst" + if (( FORCE_COPY )); then + cp -a "$src/." "$dst/" + else + (cd "$src" && find . -type f -print0) | while IFS= read -r -d '' f; do + if [[ ! -e "$dst/$f" ]]; then + mkdir -p "$(dirname "$dst/$f")" + cp -a "$src/$f" "$dst/$f" + fi + done + fi + else + if [[ -e "$dst" && $FORCE_COPY -eq 0 ]]; then return 0; fi + mkdir -p "$(dirname "$dst")" && cp -a "$src" "$dst" + fi +} + +# 3) Copie de la structure normative +copy_item "${TEMPLATE_ROOT}/.gitea" "${target_dir}/.gitea" +copy_item "${TEMPLATE_ROOT}/AGENTS.md" "${target_dir}/AGENTS.md" +copy_item "${TEMPLATE_ROOT}/.cursor" "${target_dir}/.cursor" +copy_item "${TEMPLATE_ROOT}/.cursorignore" "${target_dir}/.cursorignore" +copy_item "${TEMPLATE_ROOT}/.gitignore" "${target_dir}/.gitignore" +copy_item "${TEMPLATE_ROOT}/.markdownlint.json" "${target_dir}/.markdownlint.json" +copy_item "${TEMPLATE_ROOT}/LICENSE" "${target_dir}/LICENSE" +copy_item "${TEMPLATE_ROOT}/CONTRIBUTING.md" "${target_dir}/CONTRIBUTING.md" +copy_item "${TEMPLATE_ROOT}/CODE_OF_CONDUCT.md" "${target_dir}/CODE_OF_CONDUCT.md" +copy_item "${TEMPLATE_ROOT}/SECURITY.md" "${target_dir}/SECURITY.md" +copy_item "${TEMPLATE_ROOT}/TEMPLATE_VERSION" "${target_dir}/TEMPLATE_VERSION" +copy_item "${TEMPLATE_ROOT}/security" "${target_dir}/security" +copy_item "${TEMPLATE_ROOT}/scripts" "${target_dir}/scripts" +copy_item "${TEMPLATE_ROOT}/docs/templates" "${target_dir}/docs/templates" + +# Génération docs/INDEX.md dans le projet cible (si absent ou --force) +INDEX_DST="${target_dir}/docs/INDEX.md" +if [[ ! -f "${INDEX_DST}" || $FORCE_COPY -eq 1 ]]; then + mkdir -p "$(dirname "${INDEX_DST}")" + cat >"${INDEX_DST}" <<'IDX' +# Documentation du projet + +Cette table des matières oriente vers: +- Documentation spécifique au projet: `docs/project/` +- Modèles génériques à adapter: `docs/templates/` + +## Sommaire +- À personnaliser: `docs/project/README.md`, `docs/project/INDEX.md`, `docs/project/ARCHITECTURE.md`, `docs/project/USAGE.md`, etc. + +## Modèles génériques +- Voir: `docs/templates/` +IDX +fi + +echo "Template 4NK appliqué à: ${target_dir}" >&2 +exit 0 diff --git a/scripts/deploy_first_install_no_certs.sh b/scripts/deploy_first_install_no_certs.sh old mode 100644 new mode 100755 diff --git a/scripts/deploy_first_install_with_certs.sh b/scripts/deploy_first_install_with_certs.sh old mode 100644 new mode 100755 diff --git a/scripts/dev/run_container.sh b/scripts/dev/run_container.sh new file mode 100755 index 00000000..2d543cb9 --- /dev/null +++ b/scripts/dev/run_container.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -euo pipefail + +IMAGE_NAME="4nk-template-dev:debian" +DOCKERFILE="docker/Dockerfile.debian" + +echo "[build] ${IMAGE_NAME}" +docker build -t "${IMAGE_NAME}" -f "${DOCKERFILE}" . + +echo "[run] launching container and executing agents" +docker run --rm -it \ + -v "${PWD}:/work" -w /work \ + "${IMAGE_NAME}" \ + "scripts/agents/run.sh; ls -la tests/reports/agents || true" + diff --git a/scripts/dev/run_project_ci.sh b/scripts/dev/run_project_ci.sh new file mode 100755 index 00000000..d92d96b1 --- /dev/null +++ b/scripts/dev/run_project_ci.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Build et lance le conteneur unifié (runner+agents) sur ce projet +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$ROOT_DIR" + +# Build image +docker compose -f docker-compose.ci.yml build + +# Exécuter agents par défaut +RUNNER_MODE="${RUNNER_MODE:-agents}" BASE_URL="${BASE_URL:-}" REGISTRATION_TOKEN="${REGISTRATION_TOKEN:-}" \ + docker compose -f docker-compose.ci.yml up --remove-orphans --abort-on-container-exit diff --git a/scripts/env/ensure_env.sh b/scripts/env/ensure_env.sh new file mode 100755 index 00000000..64358191 --- /dev/null +++ b/scripts/env/ensure_env.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +TEMPLATE_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.env.template" +ENV_DIR="${HOME}/.4nk_template" +ENV_FILE="${ENV_DIR}/.env" + +mkdir -p "${ENV_DIR}" +chmod 700 "${ENV_DIR}" || true + +if [[ ! -f "${ENV_FILE}" ]]; then + if [[ -f "${TEMPLATE_FILE}" ]]; then + cp "${TEMPLATE_FILE}" "${ENV_FILE}" + chmod 600 "${ENV_FILE}" || true + echo "Fichier d'environnement créé: ${ENV_FILE}" >&2 + echo "Veuillez renseigner les variables requises (OPENAI_API_KEY, OPENAI_MODEL, etc.)." >&2 + exit 3 + else + echo "Modèle d'environnement introuvable: ${TEMPLATE_FILE}" >&2 + exit 2 + fi +fi + +# Charger pour validation +set -a +. "${ENV_FILE}" +set +a + +MISSING=() +for var in OPENAI_API_KEY OPENAI_MODEL; do + if [[ -z "${!var:-}" ]]; then + MISSING+=("$var") + fi +done + +if (( ${#MISSING[@]} > 0 )); then + echo "Variables manquantes dans ${ENV_FILE}: ${MISSING[*]}" >&2 + exit 4 +fi + +echo "Environnement valide: ${ENV_FILE}" >&2 diff --git a/scripts/release/guard.sh b/scripts/release/guard.sh old mode 100644 new mode 100755 diff --git a/scripts/scripts/auto-ssh-push.sh b/scripts/scripts/auto-ssh-push.sh old mode 100644 new mode 100755 diff --git a/scripts/scripts/init-ssh-env.sh b/scripts/scripts/init-ssh-env.sh old mode 100644 new mode 100755 diff --git a/scripts/scripts/setup-ssh-ci.sh b/scripts/scripts/setup-ssh-ci.sh old mode 100644 new mode 100755 diff --git a/scripts/security/audit.sh b/scripts/security/audit.sh old mode 100644 new mode 100755 diff --git a/scripts/utils/check_md024.ps1 b/scripts/utils/check_md024.ps1 new file mode 100644 index 00000000..000c6d19 --- /dev/null +++ b/scripts/utils/check_md024.ps1 @@ -0,0 +1,47 @@ +Param( + [string]$Root = "." +) + +$ErrorActionPreference = "Stop" + +$files = Get-ChildItem -Path $Root -Recurse -Filter *.md | Where-Object { $_.FullName -notmatch '\\archive\\' } +$had = $false +foreach ($f in $files) { + try { + $lines = Get-Content -LiteralPath $f.FullName -Encoding UTF8 -ErrorAction Stop + } catch { + Write-Warning ("Impossible de lire: {0} — {1}" -f $f.FullName, $_.Exception.Message) + continue + } + $map = @{} + $firstMap = @{} + $dups = @{} + for ($i = 0; $i -lt $lines.Count; $i++) { + $line = $lines[$i] + if ($line -match '^\s{0,3}#{1,6}\s+(.*)$') { + $t = $Matches[1].Trim() + $norm = ([regex]::Replace($t, '\s+', ' ')).ToLowerInvariant() + if ($map.ContainsKey($norm)) { + if (-not $dups.ContainsKey($norm)) { + $dups[$norm] = New-Object System.Collections.ArrayList + $firstMap[$norm] = $map[$norm] + } + [void]$dups[$norm].Add($i + 1) + } else { + $map[$norm] = $i + 1 + } + } + } + if ($dups.Keys.Count -gt 0) { + $had = $true + Write-Output "=== $($f.FullName) ===" + foreach ($k in $dups.Keys) { + $first = $firstMap[$k] + $others = ($dups[$k] -join ', ') + Write-Output ("Heading: '{0}' first@{1} duplicates@[{2}]" -f $k, $first, $others) + } + } +} +if (-not $had) { + Write-Output "No duplicate headings detected." +}