diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4d4bcf7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +node_modules +dist +.git +.github +*.md +.env +.env.* +!.env.example +!.env.deploy.example +coverage +.vscode +.cursor +terminals +**/*.log diff --git a/.gitignore b/.gitignore index b50664c..f53c221 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ dist-ssr .env .env.* !.env.example +.env.deploy # Editor directories and files .vscode/* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..03a3265 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +# Build front Vite (variables VITE_* et JIRA_DOMAIN figées au build) +FROM node:22-alpine AS build +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . + +# Origine Jira pour les liens « ouvrir le ticket » (__JIRA_ORIGIN__ dans vite.config.js) +ARG JIRA_DOMAIN= +ENV JIRA_DOMAIN=$JIRA_DOMAIN + +# Obligatoire en prod : URL de ton proxy HTTPS vers Jira (même origine ou sous-chemin) +ARG VITE_JIRA_BASE_URL= +ENV VITE_JIRA_BASE_URL=$VITE_JIRA_BASE_URL + +ARG VITE_JIRA_BROWSE_BASE_URL= +ENV VITE_JIRA_BROWSE_BASE_URL=$VITE_JIRA_BROWSE_BASE_URL +ARG VITE_JIRA_EPIC_KEY= +ENV VITE_JIRA_EPIC_KEY=$VITE_JIRA_EPIC_KEY +ARG VITE_JIRA_PAGE_SIZE= +ENV VITE_JIRA_PAGE_SIZE=$VITE_JIRA_PAGE_SIZE +ARG VITE_JIRA_BOARD_ID= +ENV VITE_JIRA_BOARD_ID=$VITE_JIRA_BOARD_ID +ARG VITE_JIRA_SPRINT_FIELD= +ENV VITE_JIRA_SPRINT_FIELD=$VITE_JIRA_SPRINT_FIELD +ARG VITE_JIRA_STORY_POINTS_FIELD= +ENV VITE_JIRA_STORY_POINTS_FIELD=$VITE_JIRA_STORY_POINTS_FIELD +ARG VITE_MY_JIRA_ACCOUNT_ID= +ENV VITE_MY_JIRA_ACCOUNT_ID=$VITE_MY_JIRA_ACCOUNT_ID +ARG VITE_MY_JIRA_EMAIL= +ENV VITE_MY_JIRA_EMAIL=$VITE_MY_JIRA_EMAIL + +RUN npm run build + +FROM nginx:alpine +COPY docker/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build /app/dist /usr/share/nginx/html +EXPOSE 80 diff --git a/deploy/hooks.json.example b/deploy/hooks.json.example new file mode 100644 index 0000000..341bca9 --- /dev/null +++ b/deploy/hooks.json.example @@ -0,0 +1,31 @@ +[ + { + "id": "jira-descours-deploy", + "execute-command": "/config/deploy.sh", + "command-working-directory": "/", + "trigger-rule": { + "and": [ + { + "match": { + "type": "value", + "value": "refs/heads/main", + "parameter": { + "source": "payload", + "name": "ref" + } + } + }, + { + "match": { + "type": "value", + "value": "CHANGEME_SECRET_TOKEN", + "parameter": { + "source": "url", + "name": "token" + } + } + } + ] + } + } +] diff --git a/deploy/nas-deploy.sh.example b/deploy/nas-deploy.sh.example new file mode 100644 index 0000000..152dc3c --- /dev/null +++ b/deploy/nas-deploy.sh.example @@ -0,0 +1,8 @@ +#!/bin/sh +# Copier vers le NAS (ex. /volume1/docker/jira-descours/deploy.sh), chmod +x, +# adapter APP_DIR et redémarrer le conteneur « webhook » qui l’invoque. +set -e +APP_DIR="/volume1/docker/jira-descours/src" +cd "$APP_DIR" +git pull --ff-only +docker compose --env-file .env.deploy -f docker-compose.yml up -d --build diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..25a83d9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +# Déploiement NAS : variables dans un fichier `.env.deploy` à côté de ce fichier (non versionné). +# Voir docs/DEPLOY_SYNOLOGY_GITEA.md + +services: + web: + build: + context: . + dockerfile: Dockerfile + args: + JIRA_DOMAIN: ${JIRA_DOMAIN:-} + VITE_JIRA_BASE_URL: ${VITE_JIRA_BASE_URL:-} + VITE_JIRA_BROWSE_BASE_URL: ${VITE_JIRA_BROWSE_BASE_URL:-} + VITE_JIRA_EPIC_KEY: ${VITE_JIRA_EPIC_KEY:-} + VITE_JIRA_PAGE_SIZE: ${VITE_JIRA_PAGE_SIZE:-} + VITE_JIRA_BOARD_ID: ${VITE_JIRA_BOARD_ID:-} + VITE_JIRA_SPRINT_FIELD: ${VITE_JIRA_SPRINT_FIELD:-} + VITE_JIRA_STORY_POINTS_FIELD: ${VITE_JIRA_STORY_POINTS_FIELD:-} + VITE_MY_JIRA_ACCOUNT_ID: ${VITE_MY_JIRA_ACCOUNT_ID:-} + VITE_MY_JIRA_EMAIL: ${VITE_MY_JIRA_EMAIL:-} + image: jira-descours:local + ports: + - "${HOST_PORT:-8080}:80" + restart: unless-stopped diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..e389242 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,18 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + + location / { + try_files $uri $uri/ /index.html; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 7d; + add_header Cache-Control "public, immutable"; + } +} diff --git a/docs/DEPLOY_SYNOLOGY_GITEA.md b/docs/DEPLOY_SYNOLOGY_GITEA.md new file mode 100644 index 0000000..4d801db --- /dev/null +++ b/docs/DEPLOY_SYNOLOGY_GITEA.md @@ -0,0 +1,227 @@ +# Déploiement automatique : Gitea + Docker sur Synology NAS + +Ce dépôt contient un **`Dockerfile`** et un **`docker-compose.yml`** pour servir le build Vite avec **Nginx**. +L’objectif : à chaque **push sur `main`**, le NAS **tire** le code et **reconstruit** le conteneur. + +--- + +## Vue d’ensemble + +| Étape | Rôle | +|--------|------| +| 1–3 | Cloner le dépôt sur le NAS, créer `.env.deploy` | +| 4 | Premier déploiement manuel (`docker compose up`) | +| 5–7 | Conteneur **webhook** qui exécute un script au push Gitea | +| 8 | Configurer le **webhook** dans Gitea (URL + secret en query) | +| 9–10 | **Proxy inverse** Synology + HTTPS pour l’accès extérieur | +| 11–12 | Vérifications et dépannage | + +Chemins d’exemple : `/volume1/docker/jira-descours/` — à adapter à ton volume DSM. + +--- + +## 1. Préparer un dossier sur le NAS + +En **SSH** (utilisateur admin ou compte avec droits `docker`) : + +```sh +mkdir -p /volume1/docker/jira-descours +cd /volume1/docker/jira-descours +``` + +--- + +## 2. Cloner le dépôt Gitea (une fois) + +Utilise l’URL **SSH** ou **HTTPS** de ton Gitea. + +**Dépôt privé (recommandé)** : créer une **clé SSH** sur le NAS, enregistrer la clé **publique** dans Gitea +(*Paramètres du dépôt → Clés de déploiement* ou compte utilisateur → Clés SSH). + +```sh +cd /volume1/docker/jira-descours +git clone git@GITEA_HOST:UTILISATEUR/jira-descours.git src +cd src +``` + +Le dossier `src` contiendra le `docker-compose.yml` à la racine du dépôt après clone. + +--- + +## 3. Fichier d’environnement de build `.env.deploy` + +Sur le NAS, dans **`src/`** (racine du clone) : + +```sh +cp .env.deploy.example .env.deploy +nano .env.deploy +``` + +Renseigne au minimum : + +- **`JIRA_DOMAIN`** — sous-domaine Atlassian (comme en dev). +- **`VITE_JIRA_BASE_URL`** — URL **HTTPS** de ton **proxy Jira** (obligatoire en prod : le navigateur n’utilise pas le proxy Vite du `npm run dev`). + +Optionnel : `VITE_JIRA_*` comme dans `.env.example`. + +**`HOST_PORT`** : port sur lequel le NAS écoute (ex. `8080`). Tu le brancheras ensuite sur le **proxy inverse** DSM. + +Ne **commit pas** `.env.deploy` (déjà ignoré par `.gitignore`). + +--- + +## 4. Premier build manuel + +Toujours dans `src/` : + +```sh +docker compose --env-file .env.deploy up -d --build +``` + +Teste en local (LAN) : `http://IP_DU_NAS:8080` (ou le port choisi). + +--- + +## 5. Script de déploiement appelé par le webhook + +Toujours sur le NAS, crée un script **hors** du dépôt (pour ne pas l’écraser au `git pull`), par ex. : + +```sh +sudo tee /volume1/docker/jira-descours/deploy.sh <<'EOF' +#!/bin/sh +set -e +APP_DIR="/volume1/docker/jira-descours/src" +cd "$APP_DIR" +git pull --ff-only +docker compose --env-file .env.deploy -f docker-compose.yml up -d --build +EOF +sudo chmod +x /volume1/docker/jira-descours/deploy.sh +``` + +Adapte **`APP_DIR`** si ton chemin diffère. + +--- + +## 6. Fichier `hooks.json` pour [adnanh/webhook](https://github.com/adnanh/webhook) + +Copie `deploy/hooks.json.example` vers le NAS, par ex. : + +`/volume1/docker/jira-descours/hooks.json` + +1. Remplace **`CHANGEME_SECRET_TOKEN`** par un **secret long** (génère-en un et garde-le pour Gitea). +2. Vérifie que **`refs/heads/main`** correspond à ta branche de prod (sinon change la valeur). + +--- + +## 7. Lancer le récepteur webhook (Docker) + +Le conteneur webhook doit pouvoir exécuter **`docker`** : montage du socket Docker. + +**Choisir une IP joignable depuis le conteneur Gitea** : + +- Gitea **hors Docker** sur le NAS → souvent `http://127.0.0.1:PORT` depuis le NAS… mais le webhook doit écouter sur une interface que Gitea peut appeler. +- Gitea **dans Docker** sur le même hôte → souvent `http://172.17.0.1:9888` (bridge Docker vers l’hôte) ou **l’IP LAN du NAS** (`http://192.168.x.x:9888`). + +Exemple (adapter chemins et tag d’image) : + +```sh +docker run -d --name gitea-deploy-hook --restart unless-stopped \ + -p 127.0.0.1:9888:9000 \ + -v /volume1/docker/jira-descours/hooks.json:/etc/webhook/hooks.json:ro \ + -v /volume1/docker/jira-descours/deploy.sh:/config/deploy.sh:ro \ + -v /var/run/docker.sock:/var/run/docker.sock \ + ghcr.io/adnanh/webhook:2.8.2 \ + -verbose -hooks=/etc/webhook/hooks.json +``` + +- **`-p 127.0.0.1:9888:9000`** : le hook n’est pas exposé sur toute la carte réseau (plus sûr). Gitea sur le **même hôte** doit joindre `127.0.0.1:9888` **depuis l’hôte** ; si Gitea est **dans un autre conteneur**, utilise plutôt l’IP LAN du NAS et `-p 9888:9000` **avec pare-feu** ou réseau Docker partagé. + +Vérifie la doc de ton image `webhook` pour le chemin des hooks (`-hooks=...`). + +Test manuel (depuis le NAS) : + +```sh +curl "http://127.0.0.1:9888/hooks/jira-descours-deploy?token=CHANGEME_SECRET_TOKEN" \ + -X POST -H "Content-Type: application/json" \ + -d '{"ref":"refs/heads/main"}' +``` + +Le hook doit s’exécuter (voir logs du conteneur `gitea-deploy-hook`). + +--- + +## 8. Webhook dans Gitea + +1. Ouvre le dépôt → **Paramètres** → **Webhooks** → **Ajouter un webhook** → **Gitea**. +2. **URL cible** (exemple si hook sur le NAS, port 9888, secret en query) : + + `http://IP_LAN_DU_NAS:9888/hooks/jira-descours-deploy?token=TON_SECRET_LONG` + + Si Gitea et le hook sont sur le **même** OS Docker, teste d’abord avec l’IP LAN ; ajuste selon ce qui fonctionne chez toi. + +3. **Déclencher sur** : « Push » (événements de push). +4. Branche : si l’UI le permet, limite à **`main`** ; sinon le filtre est déjà dans `hooks.json` (`ref`). + +Enregistre, puis **« Test de livraison »** ou un **push** sur `main` : le site doit se reconstruire après quelques minutes. + +--- + +## 9. Accès depuis l’extérieur (HTTPS) + +1. **Nom de domaine** ou **DDNS Synology** pointant vers l’IP publique de ta box. +2. **Box internet** : redirection **TCP 443** (et éventuellement **80**) vers l’**IP du NAS** (même ports ou ceux de ton reverse proxy). +3. Sur DSM : **Panneau de configuration** → **Portail de connexion** → **Proxy inverse** : + - **Nom d’hôte** : ex. `jira-descours.mondomaine.fr` + - **Destination** : `http://127.0.0.1:8080` (ou le `HOST_PORT` défini dans `.env.deploy`) + - **HTTPS** activé, certificat Let’s Encrypt pour ce nom d’hôte. + +4. Accès : `https://jira-descours.mondomaine.fr` + +**Alternative sans ouvrir les ports** : [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) sur le NAS (très courant en homelab). + +--- + +## 10. Sécurité (rappel court) + +- Secret **long** dans l’URL du webhook ; ne le commite pas. +- Préférer le hook en **127.0.0.1** si Gitea appelle depuis la même machine ; sinon **pare-feu** DSM limitant le port du hook aux IP de confiance. +- Mettre à jour DSM, Gitea et les images Docker régulièrement. + +--- + +## 11. Check-list après un push + +- Gitea → **Paramètres du dépôt** → **Webhooks** : dernière livraison en vert. +- Sur le NAS : `docker logs gitea-deploy-hook` puis `docker ps` : conteneur `web` à jour. +- Site public : hard refresh (Ctrl+F5) pour éviter le cache du navigateur. + +--- + +## 12. Dépannage + +| Problème | Piste | +|----------|--------| +| `git pull` échoue | Clé SSH / droits dépôt ; URL du `origin`. | +| Build Docker OOM | NAS peu de RAM : build sur une machine CI puis `docker pull` (évolution). | +| Page blanche / erreur Jira | `VITE_JIRA_BASE_URL` absent ou incorrect au **build** ; refaire `up --build` après correction de `.env.deploy`. | +| Webhook jamais reçu | URL depuis le conteneur Gitea (test `curl` depuis **dedans** le conteneur Gitea vers l’URL du hook). | +| 403 Let’s Encrypt | Ports 80/443 bien redirigés ; nom DNS correct. | + +--- + +## Fichiers utiles dans ce dépôt + +| Fichier | Rôle | +|---------|------| +| `Dockerfile` | Build Node + image Nginx | +| `docker-compose.yml` | Service `web` + args de build | +| `docker/nginx.conf` | SPA `try_files` → `index.html` | +| `.env.deploy.example` | Modèle pour le NAS | +| `deploy/hooks.json.example` | Modèle webhook | +| `deploy/nas-deploy.sh.example` | Ancienne variante (script dans le dépôt) ; le guide privilégie `deploy.sh` **sur le NAS** | + +--- + +## Variante : Gitea Actions + runner sur le NAS + +Si tu préfères tout voir dans l’UI Gitea : active **Actions**, installe **act_runner** sur le NAS, enregistre-le sur ton instance, puis ajoute un workflow `.gitea/workflows/deploy.yml` qui exécute les mêmes commandes que `deploy.sh`. C’est plus lourd à configurer (dont l’accès sécurisé à `docker.sock`) mais très propre à long terme.