Compare commits
10 Commits
8b65643cb1
...
2107eb829f
| Author | SHA1 | Date | |
|---|---|---|---|
| 2107eb829f | |||
| f7cdbd1b5c | |||
| ee809bb2e2 | |||
| 853361cc6d | |||
| 3fde00c6ee | |||
| 6d5ab9f23a | |||
| 3695a35232 | |||
| f1d4755b6d | |||
| 94ea7e280b | |||
| 3fa1189030 |
+12
@@ -0,0 +1,12 @@
|
|||||||
|
# Claude Code — paramètres locaux et historique de sessions
|
||||||
|
.claude/
|
||||||
|
|
||||||
|
# Fichiers de travail personnels (notes, brouillons)
|
||||||
|
claude_code_skills_installer_guidelines_md.md
|
||||||
|
future-list_skill.md
|
||||||
|
|
||||||
|
# Fichiers temporaires système
|
||||||
|
*.tmp
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,541 @@
|
|||||||
|
# Tutoriel Gitea — Installation, Configuration et Utilisation
|
||||||
|
|
||||||
|
**Sources officielles :**
|
||||||
|
- [Documentation Gitea](https://docs.gitea.com)
|
||||||
|
- [Installation binaire Linux](https://docs.gitea.com/installation/install-from-binary)
|
||||||
|
- [Configuration SSH dans Gitea](https://www.techaddressed.com/tutorials/add-verify-ssh-keys-gitea/)
|
||||||
|
- [API Gitea](https://docs.gitea.com/development/api-usage)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Installation sur Debian/Ubuntu
|
||||||
|
|
||||||
|
### 1.1 Prérequis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt install -y git curl wget
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Créer un utilisateur système dédié
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo adduser --system --shell /bin/bash --gecos 'Gitea' \
|
||||||
|
--group --disabled-password --home /home/gitea gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Créer l'arborescence des dossiers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /var/lib/gitea/{custom,data,log}
|
||||||
|
sudo chown -R gitea:gitea /var/lib/gitea/
|
||||||
|
sudo chmod -R 750 /var/lib/gitea/
|
||||||
|
sudo mkdir -p /etc/gitea
|
||||||
|
sudo chown root:gitea /etc/gitea
|
||||||
|
sudo chmod 770 /etc/gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.4 Télécharger Gitea
|
||||||
|
|
||||||
|
Récupère la dernière version depuis [github.com/go-gitea/gitea/releases](https://github.com/go-gitea/gitea/releases) :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Remplace x.y.z par la version actuelle
|
||||||
|
GITEA_VERSION="1.22.0"
|
||||||
|
wget -O /tmp/gitea "https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64"
|
||||||
|
sudo mv /tmp/gitea /usr/local/bin/gitea
|
||||||
|
sudo chmod +x /usr/local/bin/gitea
|
||||||
|
gitea --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.5 Créer le service systemd
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/systemd/system/gitea.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Contenu du fichier :
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Gitea (Git with a cup of tea)
|
||||||
|
After=network.target
|
||||||
|
After=mysqld.service
|
||||||
|
Wants=mysqld.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
RestartSec=2s
|
||||||
|
Type=simple
|
||||||
|
User=gitea
|
||||||
|
Group=gitea
|
||||||
|
WorkingDirectory=/var/lib/gitea/
|
||||||
|
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
||||||
|
Restart=always
|
||||||
|
Environment=USER=gitea HOME=/home/gitea GITEA_WORK_DIR=/var/lib/gitea
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Activer et démarrer :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now gitea
|
||||||
|
sudo systemctl status gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Configuration initiale (interface web)
|
||||||
|
|
||||||
|
Ouvre `http://ton-serveur:3000` dans un navigateur.
|
||||||
|
|
||||||
|
### 2.1 Paramètres recommandés
|
||||||
|
|
||||||
|
| Paramètre | Valeur recommandée |
|
||||||
|
|-----------|-------------------|
|
||||||
|
| Base de données | SQLite (simple) ou MariaDB (production) |
|
||||||
|
| Titre du site | `mes_skills` ou ton nom |
|
||||||
|
| URL de base | `https://gitea.maison43.duckdns.org` |
|
||||||
|
| Port SSH | `22` (ou `2222` si conflit) |
|
||||||
|
| Port HTTP | `3000` |
|
||||||
|
| Dossier des dépôts | `/var/lib/gitea/data/repositories` |
|
||||||
|
|
||||||
|
### 2.2 Création du compte administrateur
|
||||||
|
|
||||||
|
Remplis les champs "Compte administrateur" pendant l'installation. Ce compte aura tous les droits.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Configuration SSH
|
||||||
|
|
||||||
|
### 3.1 Générer une clé SSH sur ta machine locale (si pas déjà fait)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keygen -t ed25519 -C "ton@email.com"
|
||||||
|
# Clé publique générée dans : ~/.ssh/id_ed25519.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Ajouter la clé dans Gitea
|
||||||
|
|
||||||
|
1. Connecte-toi à Gitea
|
||||||
|
2. **Paramètres** (icône profil) → **Paramètres** → **Clés SSH / GPG**
|
||||||
|
3. Cliquer **Ajouter une clé**
|
||||||
|
4. Coller le contenu de `~/.ssh/id_ed25519.pub`
|
||||||
|
|
||||||
|
### 3.3 Tester la connexion SSH
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -T git@gitea.maison43.duckdns.org
|
||||||
|
# Réponse attendue :
|
||||||
|
# Hi gilles! You've successfully authenticated, but Gitea does not provide shell access.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 Configurer `~/.ssh/config` (optionnel mais recommandé)
|
||||||
|
|
||||||
|
```
|
||||||
|
Host gitea.maison43.duckdns.org
|
||||||
|
HostName gitea.maison43.duckdns.org
|
||||||
|
User git
|
||||||
|
IdentityFile ~/.ssh/id_ed25519
|
||||||
|
Port 22
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Créer et utiliser un dépôt
|
||||||
|
|
||||||
|
### 4.1 Créer un dépôt via l'interface
|
||||||
|
|
||||||
|
1. Cliquer **+** → **Nouveau dépôt**
|
||||||
|
2. Nom : `mes_skills`
|
||||||
|
3. Visibilité : Privé ou Public selon ton choix
|
||||||
|
4. Initialiser avec un README : optionnel
|
||||||
|
5. Valider
|
||||||
|
|
||||||
|
### 4.2 Cloner un dépôt existant
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Via SSH (recommandé)
|
||||||
|
git clone git@gitea.maison43.duckdns.org:gilles/mes_skills.git
|
||||||
|
|
||||||
|
# Via HTTPS
|
||||||
|
git clone https://gitea.maison43.duckdns.org/gilles/mes_skills.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 Configurer le remote sur un dépôt local existant
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote add origin git@gitea.maison43.duckdns.org:gilles/mes_skills.git
|
||||||
|
git push -u origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 Workflow git quotidien
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add fichier.md
|
||||||
|
git commit -m "feat: ajout skill debugging"
|
||||||
|
git push
|
||||||
|
|
||||||
|
# Récupérer les modifications distantes
|
||||||
|
git pull
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Tokens d'accès API
|
||||||
|
|
||||||
|
Utile pour les scripts bash, l'installeur de skills, ou l'automatisation.
|
||||||
|
|
||||||
|
### 5.1 Créer un token
|
||||||
|
|
||||||
|
1. **Paramètres** → **Applications** → **Générer un nouveau token**
|
||||||
|
2. Nom : `mes_skills_installer`
|
||||||
|
3. Durée : illimitée ou 1 an
|
||||||
|
4. Permissions : `Repository` → Read (pour lire) / Write (pour pousser)
|
||||||
|
5. **Copier le token immédiatement** — il ne sera plus affiché
|
||||||
|
|
||||||
|
### 5.2 Utiliser le token dans un script bash
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GITEA_TOKEN="ton_token_ici"
|
||||||
|
GITEA_URL="https://gitea.maison43.duckdns.org"
|
||||||
|
|
||||||
|
# Lister les dépôts
|
||||||
|
curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
"${GITEA_URL}/api/v1/repos/search" | jq '.data[].name'
|
||||||
|
|
||||||
|
# Créer un fichier dans un dépôt
|
||||||
|
curl -s -X POST \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"message":"feat: nouveau skill","content":"'"$(base64 -w0 mon_fichier.md)"'"}' \
|
||||||
|
"${GITEA_URL}/api/v1/repos/gilles/mes_skills/contents/skills/dev/mon-skill/claude-code.md"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 Stocker le token de façon sécurisée
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dans ~/.config/mes_skills/config (permissions 600)
|
||||||
|
mkdir -p ~/.config/mes_skills
|
||||||
|
echo "GITEA_TOKEN=ton_token_ici" > ~/.config/mes_skills/config
|
||||||
|
chmod 600 ~/.config/mes_skills/config
|
||||||
|
|
||||||
|
# Charger dans un script
|
||||||
|
source ~/.config/mes_skills/config
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Accès depuis l'extérieur (DuckDNS + reverse proxy)
|
||||||
|
|
||||||
|
### 6.1 DuckDNS (DNS dynamique)
|
||||||
|
|
||||||
|
Ton instance est accessible via `gitea.maison43.duckdns.org`.
|
||||||
|
|
||||||
|
Pour maintenir le DNS à jour (si IP dynamique) :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Cron toutes les 5 minutes
|
||||||
|
*/5 * * * * curl -s "https://www.duckdns.org/update?domains=maison43&token=TON_TOKEN_DUCKDNS&ip=" > /dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Reverse proxy Nginx (HTTPS)
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name gitea.maison43.duckdns.org;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/gitea.maison43.duckdns.org/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/gitea.maison43.duckdns.org/privkey.pem;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:3000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Certificat Let's Encrypt :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install certbot python3-certbot-nginx
|
||||||
|
sudo certbot --nginx -d gitea.maison43.duckdns.org
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Utilisation avec `mes_skills`
|
||||||
|
|
||||||
|
### 7.1 Pousser des skills vers Gitea
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/Documents/vscode/mes_skills/mes_skills
|
||||||
|
|
||||||
|
# Ajouter un nouveau skill
|
||||||
|
cp templates/claude-code.md skills/dev/mon-nouveau-skill/claude-code.md
|
||||||
|
# Éditer le fichier...
|
||||||
|
|
||||||
|
git add skills/dev/mon-nouveau-skill/
|
||||||
|
git commit -m "feat: ajout skill mon-nouveau-skill"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Installer les skills depuis Gitea
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Installation standard (clone depuis Gitea)
|
||||||
|
curl -fsSL https://gitea.maison43.duckdns.org/gilles/mes_skills/raw/branch/main/install.sh | bash
|
||||||
|
|
||||||
|
# Installation depuis le dépôt local (développement)
|
||||||
|
SKILLS_REPO=/chemin/local bash install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 URL utiles de ton instance
|
||||||
|
|
||||||
|
| Usage | URL |
|
||||||
|
|-------|-----|
|
||||||
|
| Interface web | `https://gitea.maison43.duckdns.org` |
|
||||||
|
| Dépôt mes_skills | `https://gitea.maison43.duckdns.org/gilles/mes_skills` |
|
||||||
|
| install.sh brut | `https://gitea.maison43.duckdns.org/gilles/mes_skills/raw/branch/main/install.sh` |
|
||||||
|
| API | `https://gitea.maison43.duckdns.org/api/v1` |
|
||||||
|
| Swagger API | `https://gitea.maison43.duckdns.org/api/swagger` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Workflow git pas à pas — pour débutant
|
||||||
|
|
||||||
|
### Le cycle de travail en 4 étapes
|
||||||
|
|
||||||
|
```
|
||||||
|
Modifier des fichiers
|
||||||
|
↓
|
||||||
|
git add ← "je veux inclure ces fichiers dans mon enregistrement"
|
||||||
|
↓
|
||||||
|
git commit ← "j'enregistre une version avec un message"
|
||||||
|
↓
|
||||||
|
git push ← "j'envoie mon travail vers Gitea"
|
||||||
|
```
|
||||||
|
|
||||||
|
Et dans l'autre sens :
|
||||||
|
|
||||||
|
```
|
||||||
|
git pull ← "je récupère les modifications de Gitea vers ma machine"
|
||||||
|
↓
|
||||||
|
Modifier des fichiers
|
||||||
|
↓
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Étape 0 — Vérifier où on en est (toujours commencer par là)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status
|
||||||
|
```
|
||||||
|
|
||||||
|
Ce que ça dit :
|
||||||
|
- **"nothing to commit"** → tout est propre, rien à faire
|
||||||
|
- **"Changes not staged"** → tu as modifié des fichiers mais pas encore fait `git add`
|
||||||
|
- **"Changes to be committed"** → tu as fait `git add`, prêt pour `git commit`
|
||||||
|
- **"Your branch is ahead of origin"** → tu as des commits locaux à `push`
|
||||||
|
|
||||||
|
> 💡 **Règle d'or :** lance `git status` avant de faire quoi que ce soit. C'est ton tableau de bord.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Étape 1 — `git add` — Préparer les fichiers à enregistrer
|
||||||
|
|
||||||
|
`git add` ne sauvegarde rien encore. Il dit juste à git : *"ces fichiers feront partie de mon prochain enregistrement"*.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ajouter un fichier précis
|
||||||
|
git add skills/dev/debugging/claude-code.md
|
||||||
|
|
||||||
|
# Ajouter tous les fichiers modifiés d'un dossier
|
||||||
|
git add skills/dev/debugging/
|
||||||
|
|
||||||
|
# Ajouter tous les fichiers modifiés du projet (à utiliser avec précaution)
|
||||||
|
git add .
|
||||||
|
```
|
||||||
|
|
||||||
|
Après un `git add`, refais `git status` pour voir ce qui est "en attente de commit" (en vert).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Étape 2 — `git commit` — Enregistrer une version
|
||||||
|
|
||||||
|
`git commit` crée un point de sauvegarde local avec un message qui explique **ce que tu as fait et pourquoi**.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "feat: ajout skill debugging pour claude-code"
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 Un bon message de commit répond à la question : *"Si je lis ça dans 6 mois, je comprends ce qui a changé ?"*
|
||||||
|
|
||||||
|
**Conventions de messages (optionnel mais utile) :**
|
||||||
|
|
||||||
|
| Préfixe | Usage |
|
||||||
|
|---------|-------|
|
||||||
|
| `feat:` | Nouveau skill, nouvelle fonctionnalité |
|
||||||
|
| `fix:` | Correction d'une erreur |
|
||||||
|
| `docs:` | Modification de documentation |
|
||||||
|
| `chore:` | Maintenance, nettoyage |
|
||||||
|
|
||||||
|
À ce stade, ton travail est **sauvegardé localement** mais **pas encore sur Gitea**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Étape 3 — `git push` — Envoyer vers Gitea
|
||||||
|
|
||||||
|
`git push` envoie tes commits locaux vers ton dépôt Gitea. C'est à ce moment que les autres (ou toi depuis une autre machine) peuvent voir ton travail.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Premier push sur une nouvelle branche
|
||||||
|
git push -u origin main
|
||||||
|
|
||||||
|
# Tous les push suivants (plus court)
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
Si ça demande un mot de passe → tu utilises HTTPS. Passe en SSH (voir section 3) pour ne plus jamais avoir à saisir de mot de passe.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Étape 4 — `git pull` — Récupérer les modifications de Gitea
|
||||||
|
|
||||||
|
`git pull` télécharge les modifications faites sur Gitea (par toi depuis une autre machine, ou par quelqu'un d'autre) et les intègre à ta copie locale.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Bonne habitude :** fais toujours `git pull` avant de commencer à travailler, pour être sûr d'avoir la version la plus récente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Exemple complet — Ajouter un skill et le pousser
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Récupérer les dernières modifications de Gitea
|
||||||
|
git pull
|
||||||
|
|
||||||
|
# 2. Vérifier l'état du dépôt
|
||||||
|
git status
|
||||||
|
|
||||||
|
# 3. Créer ton nouveau skill
|
||||||
|
cp templates/claude-code.md skills/jardinage/arrosage/claude-code.md
|
||||||
|
# Éditer le fichier avec ton éditeur...
|
||||||
|
|
||||||
|
# 4. Vérifier ce qui a changé
|
||||||
|
git status
|
||||||
|
# → "Untracked files: skills/jardinage/arrosage/claude-code.md"
|
||||||
|
|
||||||
|
# 5. Préparer le fichier pour l'enregistrement
|
||||||
|
git add skills/jardinage/arrosage/claude-code.md
|
||||||
|
|
||||||
|
# 6. Vérifier (optionnel mais recommandé)
|
||||||
|
git status
|
||||||
|
# → "Changes to be committed: new file: skills/jardinage/..."
|
||||||
|
|
||||||
|
# 7. Enregistrer la version avec un message
|
||||||
|
git commit -m "feat: ajout skill arrosage pour jardinage"
|
||||||
|
|
||||||
|
# 8. Envoyer vers Gitea
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
Résultat : ton skill est visible sur `https://gitea.maison43.duckdns.org/gilles/mes_skills`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Voir l'historique de tes commits
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Compact (une ligne par commit)
|
||||||
|
git log --oneline -10
|
||||||
|
|
||||||
|
# Avec dates et auteur
|
||||||
|
git log --oneline --graph -10
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Annuler des erreurs courantes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# J'ai fait git add par erreur sur un fichier → l'enlever de la sélection
|
||||||
|
git restore --staged fichier.md
|
||||||
|
|
||||||
|
# J'ai modifié un fichier et je veux revenir à la version du dernier commit
|
||||||
|
git restore fichier.md
|
||||||
|
|
||||||
|
# Je veux voir ce que j'ai modifié avant de committer
|
||||||
|
git diff
|
||||||
|
|
||||||
|
# Je veux voir ce qui est dans le "add" (staged)
|
||||||
|
git diff --staged
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Commandes git essentielles
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Statut du dépôt
|
||||||
|
git status
|
||||||
|
|
||||||
|
# Historique compact
|
||||||
|
git log --oneline -10
|
||||||
|
|
||||||
|
# Voir les modifications avant commit
|
||||||
|
git diff
|
||||||
|
|
||||||
|
# Annuler les modifications d'un fichier (non commité)
|
||||||
|
git restore fichier.md
|
||||||
|
|
||||||
|
# Créer une branche
|
||||||
|
git checkout -b ma-branche
|
||||||
|
|
||||||
|
# Revenir sur main
|
||||||
|
git checkout main
|
||||||
|
|
||||||
|
# Fusionner une branche
|
||||||
|
git merge ma-branche
|
||||||
|
|
||||||
|
# Voir les remotes
|
||||||
|
git remote -v
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Mise à jour de Gitea
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl stop gitea
|
||||||
|
|
||||||
|
# Télécharger la nouvelle version
|
||||||
|
wget -O /tmp/gitea "https://dl.gitea.com/gitea/NOUVELLE_VERSION/gitea-NOUVELLE_VERSION-linux-amd64"
|
||||||
|
sudo mv /tmp/gitea /usr/local/bin/gitea
|
||||||
|
sudo chmod +x /usr/local/bin/gitea
|
||||||
|
|
||||||
|
sudo systemctl start gitea
|
||||||
|
gitea --version
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Dépannage courant
|
||||||
|
|
||||||
|
| Problème | Solution |
|
||||||
|
|----------|----------|
|
||||||
|
| Port 3000 inaccessible | `sudo ufw allow 3000` ou vérifier le firewall |
|
||||||
|
| Push SSH refusé | Vérifier la clé SSH dans Gitea, tester avec `ssh -T git@...` |
|
||||||
|
| `git push` demande mot de passe | Utiliser SSH au lieu de HTTPS, ou configurer un credential helper |
|
||||||
|
| Gitea ne démarre pas | `sudo journalctl -u gitea -n 50` pour voir les logs |
|
||||||
|
| Clone lent | Vérifier la connexion réseau, utiliser `--depth=1` pour les gros dépôts |
|
||||||
Executable
+506
@@ -0,0 +1,506 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# install.sh — Installeur interactif de skills IA
|
||||||
|
# Dépôt : https://gitea.maison43.duckdns.org/gilles/mes_skills
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Couleurs Gruvbox Dark 256 ──────────────────────────────────────
|
||||||
|
GRV_FG='\033[38;5;223m'
|
||||||
|
GRV_RED='\033[38;5;167m'
|
||||||
|
GRV_GREEN='\033[38;5;142m'
|
||||||
|
GRV_YELLOW='\033[38;5;214m'
|
||||||
|
GRV_BLUE='\033[38;5;109m'
|
||||||
|
GRV_PURPLE='\033[38;5;175m'
|
||||||
|
GRV_AQUA='\033[38;5;108m'
|
||||||
|
GRV_ORANGE='\033[38;5;208m'
|
||||||
|
GRV_GRAY='\033[38;5;245m'
|
||||||
|
RESET='\033[0m'
|
||||||
|
|
||||||
|
# ── Thème fzf Gruvbox Dark ────────────────────────────────────────
|
||||||
|
export FZF_DEFAULT_OPTS="
|
||||||
|
--color=bg+:#3c3836,bg:#282828,spinner:#fb4934,hl:#928374
|
||||||
|
--color=fg:#ebdbb2,header:#928374,info:#8ec07c,pointer:#fb4934
|
||||||
|
--color=marker:#fb4934,fg+:#ebdbb2,prompt:#fb4934,hl+:#fb4934
|
||||||
|
--border=rounded --height=80% --layout=reverse
|
||||||
|
--header-lines=2
|
||||||
|
"
|
||||||
|
|
||||||
|
# ── Icônes ────────────────────────────────────────────────────────
|
||||||
|
ICO_OK="✓"
|
||||||
|
ICO_UPD="↑"
|
||||||
|
ICO_NEW="+"
|
||||||
|
ICO_NA="·"
|
||||||
|
ICO_LOCAL="●L"
|
||||||
|
ICO_GLOBAL="●G"
|
||||||
|
ICO_SKIP="○"
|
||||||
|
|
||||||
|
# ── Configuration ─────────────────────────────────────────────────
|
||||||
|
REPO_URL="https://gitea.maison43.duckdns.org/gilles/mes_skills.git"
|
||||||
|
REPO_DIR="/tmp/mes_skills_$$"
|
||||||
|
_CLONED_REPO_DIR=""
|
||||||
|
STATE_FILE="/tmp/skills_state_$$"
|
||||||
|
|
||||||
|
SKILLS_DEBUG="${SKILLS_DEBUG:-0}"
|
||||||
|
SKILLS_DRY_RUN="${SKILLS_DRY_RUN:-0}"
|
||||||
|
SKILLS_REPO="${SKILLS_REPO:-}"
|
||||||
|
SKILLS_TAG="${SKILLS_TAG:-}"
|
||||||
|
SKILLS_AGENT="${SKILLS_AGENT:-}"
|
||||||
|
|
||||||
|
# ── Helpers couleur ───────────────────────────────────────────────
|
||||||
|
ok() { echo -e "${GRV_GREEN}${ICO_OK} $*${RESET}"; }
|
||||||
|
err() { echo -e "${GRV_RED}✗ $*${RESET}" >&2; }
|
||||||
|
info() { echo -e "${GRV_BLUE}→ $*${RESET}"; }
|
||||||
|
warn() { echo -e "${GRV_ORANGE}⚠ $*${RESET}"; }
|
||||||
|
debug() { [[ "$SKILLS_DEBUG" == "1" ]] && echo -e "${GRV_GRAY}[DBG] $*${RESET}" || true; }
|
||||||
|
header() { echo -e "\n${GRV_PURPLE}╔══ $* ══╗${RESET}\n"; }
|
||||||
|
|
||||||
|
# ── Nettoyage automatique ─────────────────────────────────────────
|
||||||
|
cleanup() {
|
||||||
|
debug "Nettoyage $_CLONED_REPO_DIR et $STATE_FILE"
|
||||||
|
[[ -n "$_CLONED_REPO_DIR" && -d "$_CLONED_REPO_DIR" ]] && rm -rf "$_CLONED_REPO_DIR"
|
||||||
|
[[ -f "$STATE_FILE" ]] && rm -f "$STATE_FILE"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# ── Installation fzf ──────────────────────────────────────────────
|
||||||
|
_install_fzf_binary() {
|
||||||
|
local fzf_url="https://github.com/junegunn/fzf/releases/latest/download/fzf-linux_amd64.tar.gz"
|
||||||
|
local tmp_fzf="/tmp/fzf_$$.tar.gz"
|
||||||
|
info "Téléchargement fzf depuis GitHub Releases..."
|
||||||
|
curl -fsSL "$fzf_url" -o "$tmp_fzf"
|
||||||
|
mkdir -p "$HOME/.local/bin"
|
||||||
|
tar -xzf "$tmp_fzf" -C "$HOME/.local/bin/" fzf
|
||||||
|
rm -f "$tmp_fzf"
|
||||||
|
export PATH="$HOME/.local/bin:$PATH"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_fzf() {
|
||||||
|
warn "fzf non trouvé."
|
||||||
|
echo -e " ${GRV_FG}Installer fzf ? [o/N]${RESET} \c"
|
||||||
|
read -r answer
|
||||||
|
[[ "$answer" != "o" && "$answer" != "O" ]] && err "fzf requis. Abandon." && exit 1
|
||||||
|
if command -v apt-get &>/dev/null; then
|
||||||
|
debug "Installation via apt"
|
||||||
|
apt-get install -y fzf 2>/dev/null || _install_fzf_binary
|
||||||
|
else
|
||||||
|
_install_fzf_binary
|
||||||
|
fi
|
||||||
|
command -v fzf &>/dev/null && ok "fzf installé." || { err "Impossible d'installer fzf."; exit 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Vérification des dépendances ──────────────────────────────────
|
||||||
|
check_deps() {
|
||||||
|
header "Vérification des dépendances"
|
||||||
|
command -v git &>/dev/null || { err "git non trouvé. Installer git et relancer."; exit 1; }
|
||||||
|
ok "git $(git --version | awk '{print $3}')"
|
||||||
|
command -v fzf &>/dev/null || install_fzf
|
||||||
|
ok "fzf $(fzf --version | awk '{print $1}')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Détection des agents IA ───────────────────────────────────────
|
||||||
|
DETECTED_AGENTS=()
|
||||||
|
|
||||||
|
detect_agents() {
|
||||||
|
header "Détection des agents IA"
|
||||||
|
|
||||||
|
_add_agent() {
|
||||||
|
local name="$1"
|
||||||
|
if [[ -n "$SKILLS_AGENT" && "$SKILLS_AGENT" != "$name" ]]; then
|
||||||
|
debug "Agent $name ignoré (SKILLS_AGENT=$SKILLS_AGENT)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
DETECTED_AGENTS+=("$name")
|
||||||
|
ok "Agent détecté : $name"
|
||||||
|
}
|
||||||
|
|
||||||
|
_skip_agent() {
|
||||||
|
local name="$1"
|
||||||
|
if [[ -z "$SKILLS_AGENT" || "$SKILLS_AGENT" == "$name" ]]; then
|
||||||
|
echo -e "${GRV_GRAY}${ICO_NA} Agent absent : $name${RESET}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_detect_gemini() {
|
||||||
|
command -v gemini &>/dev/null && return 0
|
||||||
|
local prefix
|
||||||
|
prefix=$(npm config get prefix 2>/dev/null) || return 1
|
||||||
|
[[ -n "$prefix" && -f "${prefix}/bin/gemini" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# claude-code
|
||||||
|
if [[ -d "$HOME/.claude" ]] || command -v claude &>/dev/null; then
|
||||||
|
_add_agent "claude-code"
|
||||||
|
else
|
||||||
|
_skip_agent "claude-code"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# gemini-cli
|
||||||
|
if _detect_gemini; then
|
||||||
|
_add_agent "gemini-cli"
|
||||||
|
else
|
||||||
|
_skip_agent "gemini-cli"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# codex
|
||||||
|
if command -v codex &>/dev/null || [[ -f "$HOME/.npm-global/bin/codex" ]]; then
|
||||||
|
_add_agent "codex"
|
||||||
|
else
|
||||||
|
_skip_agent "codex"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# hermes
|
||||||
|
if command -v hermes &>/dev/null || [[ -f "$HOME/.local/bin/hermes" ]]; then
|
||||||
|
_add_agent "hermes"
|
||||||
|
else
|
||||||
|
_skip_agent "hermes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#DETECTED_AGENTS[@]} -eq 0 ]]; then
|
||||||
|
warn "Aucun agent IA détecté. L'installation continuera mais aucun skill ne sera filtré."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Clone du dépôt ────────────────────────────────────────────────
|
||||||
|
clone_repo() {
|
||||||
|
header "Récupération du dépôt"
|
||||||
|
if [[ -n "$SKILLS_REPO" ]]; then
|
||||||
|
REPO_DIR="$SKILLS_REPO"
|
||||||
|
info "Utilisation du dépôt local : $REPO_DIR"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
info "Clonage depuis $REPO_URL..."
|
||||||
|
git clone --depth=1 "$REPO_URL" "$REPO_DIR" &>/dev/null
|
||||||
|
_CLONED_REPO_DIR="$REPO_DIR"
|
||||||
|
ok "Dépôt cloné dans $REPO_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Gestion des versions ──────────────────────────────────────────
|
||||||
|
get_frontmatter_field() {
|
||||||
|
local file="$1" field="$2"
|
||||||
|
grep "^${field}:" "$file" 2>/dev/null | head -1 | awk '{print $2}' | tr -d "\"'"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Retourne 0 (succès) si ver2 est plus récente que ver1
|
||||||
|
version_is_newer() {
|
||||||
|
local ver1="$1" ver2="$2"
|
||||||
|
[[ "$ver1" == "$ver2" ]] && return 1
|
||||||
|
local newest
|
||||||
|
newest=$(printf '%s\n%s' "$ver1" "$ver2" | sort -V | tail -1)
|
||||||
|
[[ "$newest" == "$ver2" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Chemins de destination ────────────────────────────────────────
|
||||||
|
get_dest_path() {
|
||||||
|
local cat="$1" skill="$2" agent="$3" scope="$4"
|
||||||
|
local base
|
||||||
|
case "$agent" in
|
||||||
|
claude-code) [[ "$scope" == "global" ]] && base="$HOME/.claude" || base=".claude" ;;
|
||||||
|
gemini-cli) [[ "$scope" == "global" ]] && base="$HOME/.gemini" || base=".gemini" ;;
|
||||||
|
codex) [[ "$scope" == "global" ]] && base="$HOME/.codex" || base=".codex" ;;
|
||||||
|
hermes) [[ "$scope" == "global" ]] && base="$HOME/.hermes" || base=".hermes" ;;
|
||||||
|
esac
|
||||||
|
echo "${base}/skills/${cat}/${skill}/SKILL.md"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_local_version() {
|
||||||
|
local cat="$1" skill="$2" agent="$3"
|
||||||
|
local dest
|
||||||
|
dest=$(get_dest_path "$cat" "$skill" "$agent" "local")
|
||||||
|
[[ -f "$dest" ]] && get_frontmatter_field "$dest" "version" || echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Scan des skills disponibles ───────────────────────────────────
|
||||||
|
# Format : "cat|skill|agent|etat|repo_version|local_version"
|
||||||
|
SKILLS_LIST=()
|
||||||
|
|
||||||
|
scan_skills() {
|
||||||
|
header "Scan des skills disponibles"
|
||||||
|
SKILLS_LIST=()
|
||||||
|
|
||||||
|
while IFS= read -r skill_file; do
|
||||||
|
local rel="${skill_file#${REPO_DIR}/skills/}"
|
||||||
|
local agent_file="${rel##*/}"
|
||||||
|
local agent="${agent_file%.md}"
|
||||||
|
local skill_path="${rel%/*}"
|
||||||
|
local cat="${skill_path%%/*}"
|
||||||
|
local skill="${skill_path#*/}"
|
||||||
|
|
||||||
|
# Filtre agent
|
||||||
|
local agent_detected=0
|
||||||
|
if [[ ${#DETECTED_AGENTS[@]} -gt 0 ]]; then
|
||||||
|
for a in "${DETECTED_AGENTS[@]}"; do
|
||||||
|
[[ "$a" == "$agent" ]] && agent_detected=1
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
[[ "$agent_detected" -eq 0 && ${#DETECTED_AGENTS[@]} -gt 0 ]] && continue
|
||||||
|
|
||||||
|
# Filtre tag
|
||||||
|
if [[ -n "$SKILLS_TAG" ]]; then
|
||||||
|
grep -q "$SKILLS_TAG" "$skill_file" || continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
local repo_ver; repo_ver=$(get_frontmatter_field "$skill_file" "version")
|
||||||
|
local local_ver; local_ver=$(get_local_version "$cat" "$skill" "$agent")
|
||||||
|
local etat
|
||||||
|
|
||||||
|
if [[ -z "$local_ver" ]]; then
|
||||||
|
etat="new"
|
||||||
|
elif version_is_newer "$local_ver" "$repo_ver"; then
|
||||||
|
etat="upd"
|
||||||
|
else
|
||||||
|
etat="ok"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SKILLS_LIST+=("${cat}|${skill}|${agent}|${etat}|${repo_ver}|${local_ver}")
|
||||||
|
debug "Skill trouvé : $cat/$skill [$agent] état=$etat"
|
||||||
|
done < <(find "${REPO_DIR}/skills" -name "*.md" | sort)
|
||||||
|
|
||||||
|
ok "${#SKILLS_LIST[@]} skill(s) trouvé(s)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Clé d'état normalisée ────────────────────────────────────────
|
||||||
|
make_key() {
|
||||||
|
# Entrée : "cat|skill|agent|..." — Sortie : clé normalisée pour STATE_FILE
|
||||||
|
local entry="$1"
|
||||||
|
local cat skill agent
|
||||||
|
IFS='|' read -r cat skill agent _ <<< "$entry"
|
||||||
|
# Normalise en remplaçant - et / par _ pour éviter les collisions
|
||||||
|
printf '%s_%s_%s' "${cat//-/_}" "${skill//-/_}" "${agent//-/_}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── État du menu ──────────────────────────────────────────────────
|
||||||
|
state_init() {
|
||||||
|
: > "$STATE_FILE"
|
||||||
|
for entry in "${SKILLS_LIST[@]}"; do
|
||||||
|
local key; key=$(make_key "$entry")
|
||||||
|
echo "${key}=local" >> "$STATE_FILE"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
state_get() {
|
||||||
|
grep "^${1}=" "$STATE_FILE" 2>/dev/null | cut -d'=' -f2
|
||||||
|
}
|
||||||
|
|
||||||
|
state_cycle() {
|
||||||
|
local key="$1" etat="$2"
|
||||||
|
local current; current=$(state_get "$key")
|
||||||
|
local next
|
||||||
|
case "$current" in
|
||||||
|
local) next="global" ;;
|
||||||
|
global) next="skip" ;;
|
||||||
|
skip) [[ "$etat" == "upd" ]] && next="update" || next="local" ;;
|
||||||
|
update) next="local" ;;
|
||||||
|
*) next="local" ;;
|
||||||
|
esac
|
||||||
|
sed -i "s|^${key}=.*|${key}=${next}|" "$STATE_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Formatage ligne menu ──────────────────────────────────────────
|
||||||
|
format_skill_line() {
|
||||||
|
local entry="$1"
|
||||||
|
local cat skill agent etat repo_ver local_ver
|
||||||
|
IFS='|' read -r cat skill agent etat repo_ver local_ver <<< "$entry"
|
||||||
|
local key; key=$(make_key "$entry")
|
||||||
|
local action; action=$(state_get "$key")
|
||||||
|
|
||||||
|
local ico_etat color_etat
|
||||||
|
case "$etat" in
|
||||||
|
ok) ico_etat="$ICO_OK"; color_etat="$GRV_GREEN" ;;
|
||||||
|
upd) ico_etat="$ICO_UPD"; color_etat="$GRV_YELLOW" ;;
|
||||||
|
new) ico_etat="$ICO_NEW"; color_etat="$GRV_AQUA" ;;
|
||||||
|
*) ico_etat="$ICO_NA"; color_etat="$GRV_GRAY" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local ico_action color_action
|
||||||
|
case "$action" in
|
||||||
|
local) ico_action="$ICO_LOCAL"; color_action="$GRV_GREEN" ;;
|
||||||
|
global) ico_action="$ICO_GLOBAL"; color_action="$GRV_BLUE" ;;
|
||||||
|
skip) ico_action="$ICO_SKIP"; color_action="$GRV_GRAY" ;;
|
||||||
|
update) ico_action="$ICO_UPD"; color_action="$GRV_YELLOW" ;;
|
||||||
|
*) ico_action="$ICO_SKIP"; color_action="$GRV_GRAY" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local ver_info=""
|
||||||
|
[[ "$etat" == "upd" ]] && ver_info=" (${local_ver}→${repo_ver})"
|
||||||
|
[[ "$etat" == "new" ]] && ver_info=" (v${repo_ver})"
|
||||||
|
|
||||||
|
printf "${color_etat}%s${RESET} %-35s ${GRV_GRAY}[%s]${RESET} ${color_action}%s${RESET}%s\n" \
|
||||||
|
"$ico_etat" "${cat}/${skill}" "$agent" "$ico_action" "$ver_info"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Menu fzf principal ────────────────────────────────────────────
|
||||||
|
run_menu() {
|
||||||
|
header "Sélection des skills"
|
||||||
|
state_init
|
||||||
|
|
||||||
|
local cycle_script="/tmp/skills_cycle_$$.sh"
|
||||||
|
local list_script="/tmp/skills_list_$$.sh"
|
||||||
|
local fns_file="/tmp/skills_fns_$$.sh"
|
||||||
|
|
||||||
|
# Script de cycle d'état (appelé par fzf via execute-silent)
|
||||||
|
cat > "$cycle_script" << 'CYCLE_EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
STATE_FILE="$1"
|
||||||
|
SKILLS_FNS="$2"
|
||||||
|
LINE_NUM="$3" # numéro de ligne fzf (1-based)
|
||||||
|
|
||||||
|
source "$SKILLS_FNS"
|
||||||
|
|
||||||
|
# Récupère l'entrée correspondante (0-based dans SKILLS_LIST)
|
||||||
|
idx=$(( LINE_NUM - 1 ))
|
||||||
|
entry="${SKILLS_LIST[$idx]:-}"
|
||||||
|
[[ -z "$entry" ]] && exit 0
|
||||||
|
|
||||||
|
key=$(make_key "$entry")
|
||||||
|
etat=$(echo "$entry" | cut -d'|' -f4)
|
||||||
|
|
||||||
|
current=$(grep "^${key}=" "$STATE_FILE" 2>/dev/null | cut -d'=' -f2)
|
||||||
|
case "$current" in
|
||||||
|
local) next="global" ;;
|
||||||
|
global) next="skip" ;;
|
||||||
|
skip) [[ "$etat" == "upd" ]] && next="update" || next="local" ;;
|
||||||
|
update) next="local" ;;
|
||||||
|
*) next="local" ;;
|
||||||
|
esac
|
||||||
|
sed -i "s|^${key}=.*|${key}=${next}|" "$STATE_FILE"
|
||||||
|
CYCLE_EOF
|
||||||
|
chmod +x "$cycle_script"
|
||||||
|
|
||||||
|
# Exporter fonctions et variables dans un fichier source
|
||||||
|
{
|
||||||
|
declare -f format_skill_line state_get make_key
|
||||||
|
declare -p GRV_GREEN GRV_YELLOW GRV_AQUA GRV_GRAY GRV_BLUE RESET \
|
||||||
|
ICO_OK ICO_UPD ICO_NEW ICO_NA ICO_LOCAL ICO_GLOBAL ICO_SKIP
|
||||||
|
echo "STATE_FILE='$STATE_FILE'"
|
||||||
|
echo "SKILLS_LIST=($(printf '"%s" ' "${SKILLS_LIST[@]}"))"
|
||||||
|
} > "$fns_file"
|
||||||
|
|
||||||
|
# Script générateur de liste pour fzf --reload
|
||||||
|
cat > "$list_script" << LIST_EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
source "$fns_file"
|
||||||
|
for entry in "\${SKILLS_LIST[@]}"; do
|
||||||
|
format_skill_line "\$entry"
|
||||||
|
done
|
||||||
|
LIST_EOF
|
||||||
|
chmod +x "$list_script"
|
||||||
|
|
||||||
|
local legend
|
||||||
|
legend=$(echo -e "${GRV_GRAY}État: ${GRV_GREEN}✓ installé ${GRV_YELLOW}↑ MAJ ${GRV_AQUA}+ nouveau Action: ${GRV_GREEN}●L local ${GRV_BLUE}●G global ${GRV_GRAY}○ ignorer TAB=changer ENTER=confirmer ESC=quitter${RESET}")
|
||||||
|
|
||||||
|
fzf \
|
||||||
|
--ansi \
|
||||||
|
--prompt="Skills > " \
|
||||||
|
--header="$legend" \
|
||||||
|
--bind="tab:execute-silent($cycle_script '$STATE_FILE' '$fns_file' {n})+reload($list_script)" \
|
||||||
|
< <(bash "$list_script") > /dev/null || true
|
||||||
|
|
||||||
|
rm -f "$cycle_script" "$list_script" "$fns_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Installation ──────────────────────────────────────────────────
|
||||||
|
install_selected() {
|
||||||
|
header "Installation"
|
||||||
|
local count_install=0 count_update=0 count_skip=0
|
||||||
|
|
||||||
|
for entry in "${SKILLS_LIST[@]}"; do
|
||||||
|
local cat skill agent etat repo_ver local_ver
|
||||||
|
IFS='|' read -r cat skill agent etat repo_ver local_ver <<< "$entry"
|
||||||
|
local key; key=$(make_key "$entry")
|
||||||
|
local action; action=$(state_get "$key")
|
||||||
|
|
||||||
|
if [[ "$action" == "skip" ]]; then
|
||||||
|
(( count_skip++ )) || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
local scope="$action"
|
||||||
|
[[ "$action" == "update" ]] && scope="local"
|
||||||
|
|
||||||
|
local src="${REPO_DIR}/skills/${cat}/${skill}/${agent}.md"
|
||||||
|
local dest; dest=$(get_dest_path "$cat" "$skill" "$agent" "$scope")
|
||||||
|
|
||||||
|
debug "Copie $src → $dest"
|
||||||
|
|
||||||
|
if [[ "$SKILLS_DRY_RUN" == "1" ]]; then
|
||||||
|
info "[DRY-RUN] cp $src → $dest"
|
||||||
|
else
|
||||||
|
mkdir -p "$(dirname "$dest")"
|
||||||
|
cp "$src" "$dest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$action" == "update" ]]; then
|
||||||
|
ok "Mis à jour : ${cat}/${skill} [${agent}] ${local_ver}→${repo_ver}"
|
||||||
|
(( count_update++ )) || true
|
||||||
|
else
|
||||||
|
ok "Installé : ${cat}/${skill} [${agent}] → ${scope}"
|
||||||
|
(( count_install++ )) || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "\n${GRV_PURPLE}╔══ Bilan ══╗${RESET}"
|
||||||
|
echo -e " ${GRV_GREEN}${ICO_OK} $count_install installé(s)${RESET}"
|
||||||
|
echo -e " ${GRV_YELLOW}${ICO_UPD} $count_update mis à jour${RESET}"
|
||||||
|
echo -e " ${GRV_GRAY}${ICO_SKIP} $count_skip ignoré(s)${RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Récapitulatif final ───────────────────────────────────────────
|
||||||
|
print_summary() {
|
||||||
|
local shown=()
|
||||||
|
|
||||||
|
echo -e "\n${GRV_PURPLE}╔══ Tester vos skills ══╗${RESET}\n"
|
||||||
|
for entry in "${SKILLS_LIST[@]}"; do
|
||||||
|
local cat skill agent etat repo_ver local_ver
|
||||||
|
IFS='|' read -r cat skill agent etat repo_ver local_ver <<< "$entry"
|
||||||
|
local key; key=$(make_key "$entry")
|
||||||
|
local action; action=$(state_get "$key")
|
||||||
|
[[ "$action" == "skip" ]] && continue
|
||||||
|
|
||||||
|
local already=0
|
||||||
|
if [[ ${#shown[@]} -gt 0 ]]; then
|
||||||
|
for s in "${shown[@]}"; do [[ "$s" == "${skill}|${agent}" ]] && already=1; done
|
||||||
|
fi
|
||||||
|
[[ "$already" -eq 1 ]] && continue
|
||||||
|
shown+=("${skill}|${agent}")
|
||||||
|
|
||||||
|
case "$agent" in
|
||||||
|
claude-code) echo -e " ${GRV_AQUA}claude \"utilise le skill ${skill}\" --print${RESET}" ;;
|
||||||
|
gemini-cli) echo -e " ${GRV_AQUA}gemini -p \"utilise le skill ${skill}\"${RESET}" ;;
|
||||||
|
codex) echo -e " ${GRV_AQUA}codex \"\$${skill}\"${RESET}" ;;
|
||||||
|
hermes) echo -e " ${GRV_AQUA}hermes \"utilise le skill ${skill}\"${RESET}" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "\n${GRV_PURPLE}╔══ Documentation agents ══╗${RESET}\n"
|
||||||
|
for agent in "${DETECTED_AGENTS[@]}"; do
|
||||||
|
case "$agent" in
|
||||||
|
claude-code) echo -e " ${GRV_BLUE}Claude Code${RESET} → https://code.claude.com/docs/en/skills" ;;
|
||||||
|
gemini-cli) echo -e " ${GRV_BLUE}Gemini CLI ${RESET} → https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/skills.md" ;;
|
||||||
|
codex) echo -e " ${GRV_BLUE}Codex ${RESET} → https://developers.openai.com/codex/skills" ;;
|
||||||
|
hermes) echo -e " ${GRV_BLUE}Hermes ${RESET} → https://hermes-agent.nousresearch.com/docs/user-guide/features/skills/" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Point d'entrée ────────────────────────────────────────────────
|
||||||
|
main() {
|
||||||
|
echo -e "\n${GRV_PURPLE}╔══════════════════════════════════════╗${RESET}"
|
||||||
|
echo -e "${GRV_PURPLE}║ mes_skills — Installeur de skills ║${RESET}"
|
||||||
|
echo -e "${GRV_PURPLE}╚══════════════════════════════════════╝${RESET}\n"
|
||||||
|
|
||||||
|
check_deps
|
||||||
|
detect_agents
|
||||||
|
clone_repo
|
||||||
|
scan_skills
|
||||||
|
|
||||||
|
if [[ ${#SKILLS_LIST[@]} -eq 0 ]]; then
|
||||||
|
warn "Aucun skill compatible trouvé. Vérifier les agents détectés ou SKILLS_TAG."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_menu
|
||||||
|
install_selected
|
||||||
|
print_summary
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Executable
+170
@@ -0,0 +1,170 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Tests unitaires pour install.sh
|
||||||
|
# Usage : cd tests && bash test_install.sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
assert_eq() {
|
||||||
|
local desc="$1" expected="$2" actual="$3"
|
||||||
|
if [[ "$expected" == "$actual" ]]; then
|
||||||
|
echo " ✓ $desc"
|
||||||
|
(( PASS++ )) || true
|
||||||
|
else
|
||||||
|
echo " ✗ $desc"
|
||||||
|
echo " attendu : '$expected'"
|
||||||
|
echo " obtenu : '$actual'"
|
||||||
|
(( FAIL++ )) || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_true() {
|
||||||
|
local desc="$1"
|
||||||
|
shift
|
||||||
|
if eval "$@" 2>/dev/null; then
|
||||||
|
echo " ✓ $desc"
|
||||||
|
(( PASS++ )) || true
|
||||||
|
else
|
||||||
|
echo " ✗ $desc"
|
||||||
|
(( FAIL++ )) || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_false() {
|
||||||
|
local desc="$1"
|
||||||
|
shift
|
||||||
|
if ! eval "$@" 2>/dev/null; then
|
||||||
|
echo " ✓ $desc"
|
||||||
|
(( PASS++ )) || true
|
||||||
|
else
|
||||||
|
echo " ✗ $desc"
|
||||||
|
(( FAIL++ )) || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Charge install.sh sans exécuter main()
|
||||||
|
load_install() {
|
||||||
|
local tmp; tmp=$(mktemp)
|
||||||
|
# Supprime l'appel final à main et desactive set -e pour le sourcing
|
||||||
|
grep -v '^main "\$@"' ../install.sh > "$tmp"
|
||||||
|
set +e
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$tmp"
|
||||||
|
set -e
|
||||||
|
rm -f "$tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "══════════════════════════════════"
|
||||||
|
echo " Tests install.sh"
|
||||||
|
echo "══════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
load_install
|
||||||
|
# Désactive le trap cleanup() hérité de install.sh (évite l'erreur nounset sur STATE_FILE)
|
||||||
|
trap - EXIT
|
||||||
|
|
||||||
|
# ── 1. Comparaison de versions ────────────────────────────────────
|
||||||
|
echo "1. version_is_newer()"
|
||||||
|
assert_true "1.2.0 > 1.0.0" "version_is_newer '1.0.0' '1.2.0'"
|
||||||
|
assert_false "1.0.0 n'est pas > 1.2.0" "version_is_newer '1.2.0' '1.0.0'"
|
||||||
|
assert_false "1.0.0 == 1.0.0 → false" "version_is_newer '1.0.0' '1.0.0'"
|
||||||
|
assert_true "2.0.0 > 1.9.9" "version_is_newer '1.9.9' '2.0.0'"
|
||||||
|
assert_true "1.0.1 > 1.0.0" "version_is_newer '1.0.0' '1.0.1'"
|
||||||
|
|
||||||
|
# ── 2. Parsing frontmatter ────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "2. get_frontmatter_field()"
|
||||||
|
|
||||||
|
TMP_SKILL=$(mktemp --suffix=.md)
|
||||||
|
cat > "$TMP_SKILL" << 'SKILL_EOF'
|
||||||
|
---
|
||||||
|
name: test-skill
|
||||||
|
version: 2.1.0
|
||||||
|
description: Skill de test pour vérification
|
||||||
|
agents: [claude-code]
|
||||||
|
category: dev
|
||||||
|
tags: [bash, test]
|
||||||
|
---
|
||||||
|
# Contenu du skill
|
||||||
|
SKILL_EOF
|
||||||
|
|
||||||
|
assert_eq "version" "2.1.0" "$(get_frontmatter_field "$TMP_SKILL" "version")"
|
||||||
|
assert_eq "name" "test-skill" "$(get_frontmatter_field "$TMP_SKILL" "name")"
|
||||||
|
assert_eq "category" "dev" "$(get_frontmatter_field "$TMP_SKILL" "category")"
|
||||||
|
assert_eq "champ absent → vide" "" "$(get_frontmatter_field "$TMP_SKILL" "nonexistent")"
|
||||||
|
rm -f "$TMP_SKILL"
|
||||||
|
|
||||||
|
# ── 3. Chemins de destination ─────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "3. get_dest_path()"
|
||||||
|
|
||||||
|
assert_eq "claude global" \
|
||||||
|
"$HOME/.claude/skills/dev/debugging/SKILL.md" \
|
||||||
|
"$(get_dest_path "dev" "debugging" "claude-code" "global")"
|
||||||
|
|
||||||
|
assert_eq "claude local" \
|
||||||
|
".claude/skills/dev/debugging/SKILL.md" \
|
||||||
|
"$(get_dest_path "dev" "debugging" "claude-code" "local")"
|
||||||
|
|
||||||
|
assert_eq "gemini local" \
|
||||||
|
".gemini/skills/dev/debugging/SKILL.md" \
|
||||||
|
"$(get_dest_path "dev" "debugging" "gemini-cli" "local")"
|
||||||
|
|
||||||
|
assert_eq "codex global" \
|
||||||
|
"$HOME/.codex/skills/jardinage/arrosage/SKILL.md" \
|
||||||
|
"$(get_dest_path "jardinage" "arrosage" "codex" "global")"
|
||||||
|
|
||||||
|
assert_eq "hermes local" \
|
||||||
|
".hermes/skills/electronique/arduino/SKILL.md" \
|
||||||
|
"$(get_dest_path "electronique" "arduino" "hermes" "local")"
|
||||||
|
|
||||||
|
# ── 4. Gestion d'état ─────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "4. state_get() / state_cycle() / make_key()"
|
||||||
|
|
||||||
|
STATE_FILE=$(mktemp)
|
||||||
|
export STATE_FILE
|
||||||
|
|
||||||
|
# Teste make_key — prend une entrée "cat|skill|agent|etat|repo_ver|local_ver"
|
||||||
|
assert_eq "make_key basique" \
|
||||||
|
"dev_debugging_claude_code" \
|
||||||
|
"$(make_key "dev|debugging|claude-code|new|1.0.0|")"
|
||||||
|
|
||||||
|
assert_eq "make_key avec tirets" \
|
||||||
|
"dev_git_workflow_claude_code" \
|
||||||
|
"$(make_key "dev|git-workflow|claude-code|new|1.0.0|")"
|
||||||
|
|
||||||
|
# Initialise un état
|
||||||
|
echo "dev_debugging_claude_code=local" > "$STATE_FILE"
|
||||||
|
|
||||||
|
assert_eq "state_get local" "local" "$(state_get "dev_debugging_claude_code")"
|
||||||
|
|
||||||
|
state_cycle "dev_debugging_claude_code" "ok"
|
||||||
|
assert_eq "cycle local→global" "global" "$(state_get "dev_debugging_claude_code")"
|
||||||
|
|
||||||
|
state_cycle "dev_debugging_claude_code" "ok"
|
||||||
|
assert_eq "cycle global→skip" "skip" "$(state_get "dev_debugging_claude_code")"
|
||||||
|
|
||||||
|
state_cycle "dev_debugging_claude_code" "ok"
|
||||||
|
assert_eq "cycle skip→local (état ok)" "local" "$(state_get "dev_debugging_claude_code")"
|
||||||
|
|
||||||
|
# Teste le cycle avec état upd
|
||||||
|
echo "dev_debugging_claude_code=skip" > "$STATE_FILE"
|
||||||
|
state_cycle "dev_debugging_claude_code" "upd"
|
||||||
|
assert_eq "cycle skip→update (état upd)" "update" "$(state_get "dev_debugging_claude_code")"
|
||||||
|
|
||||||
|
state_cycle "dev_debugging_claude_code" "upd"
|
||||||
|
assert_eq "cycle update→local" "local" "$(state_get "dev_debugging_claude_code")"
|
||||||
|
|
||||||
|
rm -f "$STATE_FILE"
|
||||||
|
unset STATE_FILE || true
|
||||||
|
|
||||||
|
# ── Bilan ─────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "══════════════════════════════════"
|
||||||
|
printf " Résultats : %d passés, %d échoués\n" "$PASS" "$FAIL"
|
||||||
|
echo "══════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
[[ "$FAIL" -eq 0 ]]
|
||||||
Reference in New Issue
Block a user