Compare commits

...

6 Commits
main ... v2

6 changed files with 725 additions and 126 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
depot
apt-repo/*
secret

View File

@ -1,23 +1,37 @@
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV DEBIAN_FRONTEND=noninteractive
LABEL maintainer="Le Garage Numérique <contact@legaragenumerique.fr>" \
org.opencontainers.image.title="depot-apt" \
org.opencontainers.image.description="Dépôt APT Le Garage Numérique" \
org.opencontainers.image.source="https://gitea.legaragenumerique.fr/GARAGENUM/depot-apt"
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
gnupg \
dpkg-dev \
wget \
gzip \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
COPY app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/. .
RUN pip install --no-cache-dir gunicorn flask
RUN chmod +x entrypoint.sh
RUN groupadd -r aptrepo && useradd -r -g aptrepo aptrepo \
&& chown -R aptrepo:aptrepo /workspace
USER aptrepo
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/ || exit 1
EXPOSE 8000
CMD ["./entrypoint.sh"]

View File

@ -112,6 +112,10 @@ server {
}
```
## FOR V3
- [ ] function to read Packages and create packets cards in index.html
## REQUETES DE PAQUETS
Pour toute demande d'ajout de paquet Debian, Créer une [issue](https://git.legaragenumerique.fr/GARAGENUM/depot-apt/issues) avec le plus d'informations concernant le paquet (code source par exemple).

View File

@ -1,19 +1,59 @@
#!/bin/bash
set -e
# Configuration
GPG_KEY_NAME="example"
KEY_PUBLIC="/workspace/apt-repo/pgp-key.public"
# ============================================================
# CONFIGURATION — modifiez ces variables selon vos besoins
# ============================================================
DOMAIN="deb.legaragenumerique.fr" # adresse du dépôt
GPG_KEY_NAME="gn-depot"
EMAIL="admin@lgn.dev"
DIST_NAME="stable" # Nom de la distribution : stable, focal, bookworm, etc.
COMPONENT="main" # Composante : main, contrib, non-free, etc.
ARCH="amd64" # Architecture : amd64, arm64, all, etc.
REPO_ORIGIN="GN-depot" # Nom affiché dans le fichier Release
REPO_LABEL="GN-depot" # Label affiché dans le fichier Release
REPO_DESCRIPTION="Dépôt du garage numérique"
# Priorité du dépôt recommandée pour les clients (générée dans le fichier .pref)
# < 0 : jamais installé
# 100 : installé seulement si absent ailleurs
# 500 : priorité normale (dépôts standards)
# 990 : priorité élevée
# >1000 : installe même en régression de version
REPO_PRIORITY=500
# ============================================================
# CHEMINS (ne pas modifier sauf besoin spécifique)
# ============================================================
KEY_PUBLIC="./depot/pgp-key.public"
KEY_PRIVATE="/workspace/secret/pgp-key.private"
EMAIL="test@exemple.com"
POOL_DIR="./apt-repo/pool/$COMPONENT/binary-$ARCH"
DISTS_DIR="./apt-repo/dists/$DIST_NAME/$COMPONENT/binary-$ARCH"
# ============================================================
echo "📦 Création du dépôt APT"
mkdir -p ./apt-repo/pool/main/binary-amd64
mkdir -p ./apt-repo/dists/stable/main/binary-amd64
echo " Distribution : $DIST_NAME"
echo " Composante : $COMPONENT"
echo " Architecture : $ARCH"
echo " Priorité : $REPO_PRIORITY"
# Génération des clés seulement si elles n'existent pas
mkdir -p "$POOL_DIR"
mkdir -p "$DISTS_DIR"
# ------------------------------------------------------------
# Génération des clés GPG (seulement si absentes)
# ------------------------------------------------------------
make_keys() {
echo "🔐 Génération des clés GPG"
# S'assurer que le dossier destination existe
mkdir -p "$(dirname "$KEY_PUBLIC")"
mkdir -p "$(dirname "$KEY_PRIVATE")"
cat > example-pgp-key.batch <<EOF
Key-Type: RSA
Key-Length: 4096
@ -28,26 +68,165 @@ EOF
gpg --batch --gen-key example-pgp-key.batch
gpg --armor --export "$GPG_KEY_NAME" > "$KEY_PUBLIC"
gpg --armor --export-secret-keys "$GPG_KEY_NAME" > "$KEY_PRIVATE"
chmod 600 $KEY_PRIVATE
cp "$KEY_PUBLIC" ./apt-repo/pgp-key.public
chmod 600 "$KEY_PRIVATE"
}
sign_packages() {
cd ./apt-repo
dpkg-scanpackages --arch amd64 pool/ > dists/stable/main/binary-amd64/Packages
gzip -9 < dists/stable/main/binary-amd64/Packages > dists/stable/main/binary-amd64/Packages.gz
# ------------------------------------------------------------
# Génération du fichier Release
# ------------------------------------------------------------
generate_release() {
local base="$1" # = chemin vers ./apt-repo
local dist_path="$base/dists/$DIST_NAME"
local packages_file="$COMPONENT/binary-$ARCH/Packages"
local packages_gz="$COMPONENT/binary-$ARCH/Packages.gz"
# Les checksums sont calculés depuis dist_path
MD5_PKG=$(md5sum "$dist_path/$packages_file" | cut -d' ' -f1)
SHA1_PKG=$(sha1sum "$dist_path/$packages_file" | cut -d' ' -f1)
SHA256_PKG=$(sha256sum "$dist_path/$packages_file" | cut -d' ' -f1)
SIZE_PKG=$(wc -c < "$dist_path/$packages_file")
MD5_GZ=$(md5sum "$dist_path/$packages_gz" | cut -d' ' -f1)
SHA1_GZ=$(sha1sum "$dist_path/$packages_gz" | cut -d' ' -f1)
SHA256_GZ=$(sha256sum "$dist_path/$packages_gz" | cut -d' ' -f1)
SIZE_GZ=$(wc -c < "$dist_path/$packages_gz")
cat <<EOF
Origin: $REPO_ORIGIN
Label: $REPO_LABEL
Suite: $DIST_NAME
Codename: $DIST_NAME
Architectures: $ARCH
Components: $COMPONENT
Description: $REPO_DESCRIPTION
Date: $(date -Ru)
MD5Sum:
$MD5_PKG $SIZE_PKG $packages_file
$MD5_GZ $SIZE_GZ $packages_gz
SHA1:
$SHA1_PKG $SIZE_PKG $packages_file
$SHA1_GZ $SIZE_GZ $packages_gz
SHA256:
$SHA256_PKG $SIZE_PKG $packages_file
$SHA256_GZ $SIZE_GZ $packages_gz
EOF
}
# ------------------------------------------------------------
# Scan, compression et signature des paquets
# ------------------------------------------------------------
sign_packages() {
# Sauvegarder le répertoire de départ
local WORKDIR
WORKDIR=$(pwd)
cd ./apt-repo
echo "📋 Scan des paquets"
dpkg-scanpackages --arch "$ARCH" pool/ > "dists/$DIST_NAME/$COMPONENT/binary-$ARCH/Packages"
gzip -9 < "dists/$DIST_NAME/$COMPONENT/binary-$ARCH/Packages" \
> "dists/$DIST_NAME/$COMPONENT/binary-$ARCH/Packages.gz"
cd dists/stable
gpg --import /workspace/secret/pgp-key.private
echo "⚙️ Génération de Release"
/workspace/generate-release.sh > Release
echo "⚙️ Génération de Release"
generate_release "$(pwd)" > "dists/$DIST_NAME/Release"
echo "🔏 Signature du Release"
gpg --default-key "$GPG_KEY_NAME" -abs < Release > Release.gpg
gpg --default-key "$GPG_KEY_NAME" --clearsign < Release > InRelease
gpg --default-key "$GPG_KEY_NAME" -abs \
< "dists/$DIST_NAME/Release" \
> "dists/$DIST_NAME/Release.gpg"
gpg --default-key "$GPG_KEY_NAME" --clearsign \
< "dists/$DIST_NAME/Release" \
> "dists/$DIST_NAME/InRelease"
cd "$WORKDIR"
}
# ------------------------------------------------------------
# Génération du fichier de priorité pour les clients
# ------------------------------------------------------------
generate_client_pref() {
local pref_file="./apt-repo/gn-depot.pref"
cat > "$pref_file" <<EOF
# Copiez ce fichier dans /etc/apt/preferences.d/ sur vos machines clientes
# pour définir la priorité de ce dépôt.
#
# Valeurs utiles :
# < 0 → jamais installé
# 100 → seulement si absent ailleurs
# 500 → priorité normale (équivalent dépôts standards)
# 990 → priorité élevée (favorisé sur les autres dépôts)
# >1000 → installe même en cas de régression de version
Package: *
Pin: origin $DOMAIN
Pin-Priority: $REPO_PRIORITY
EOF
echo "📄 Fichier de priorité client généré : $pref_file"
}
# ------------------------------------------------------------
# Génération du sources.list pour les clients
# ------------------------------------------------------------
generate_client_sources() {
local sources_file="./apt-repo/gn-depot.list"
cat > "$sources_file" <<EOF
# Copiez ce fichier dans /etc/apt/sources.list.d/ sur vos machines clientes
deb [arch=$ARCH signed-by=/usr/share/keyrings/gn-depot.gpg] https://$DOMAIN $DIST_NAME $COMPONENT
EOF
echo "📄 Fichier sources.list client généré : $sources_file"
}
# ------------------------------------------------------------
# Génération du script d'installation du dépôt pour les clients
# ------------------------------------------------------------
generate_install_script() {
cat > ./apt-repo/install-repo.sh <<EOF
#!/bin/bash
set -e
DOMAIN="$DOMAIN"
DIST="$DIST_NAME"
COMPONENT="$COMPONENT"
ARCH="$ARCH"
PRIORITY="$REPO_PRIORITY"
LABEL="$REPO_LABEL"
echo "🔑 Ajout de la clé GPG..."
wget -qO /etc/apt/trusted.gpg.d/gn-depot.asc "https://\$DOMAIN/pgp-key.public"
echo "📋 Ajout du dépôt..."
echo "deb [arch=\$ARCH signed-by=/etc/apt/trusted.gpg.d/gn-depot.asc] https://\$DOMAIN \$DIST \$COMPONENT" \
> /etc/apt/sources.list.d/gn-depot.list
echo "⚙️ Définition de la priorité (\$PRIORITY)..."
cat > /etc/apt/preferences.d/gn-depot.pref <<PREF
Package: *
Pin: release l=\$LABEL
Pin-Priority: \$PRIORITY
PREF
echo "🔄 Mise à jour des dépôts..."
apt update
echo ""
echo "✅ Dépôt installé avec succès !"
echo " Priorité : \$PRIORITY"
echo " Utilisez : sudo apt install <paquet>"
EOF
chmod +x ./apt-repo/install-repo.sh
echo "📄 Script d'installation client généré : install-repo.sh"
}
# ============================================================
# MAIN
# ============================================================
if [ ! -f "$KEY_PUBLIC" ]; then
make_keys
else
@ -55,14 +234,21 @@ else
fi
echo "🚚 Copie des paquets"
cp ./depot/* ./apt-repo/pool/main/binary-amd64/ || true
cp ./depot/* "$POOL_DIR/" 2>/dev/null || true
sign_packages
generate_client_pref
generate_client_sources
generate_install_script
# Lancement serveur
echo "🚀 Lancement du serveur Gunicorn"
cd /workspace
cp index.html logo.png apt-repo/
gunicorn -b 0.0.0.0:8000 server:app
echo "📡 Dépôt APT disponible sur https://votre-domaine.tld"
echo "🔑 Clé publique disponible sur https://votre-domaine.tld/pgp-key.public"
echo ""
echo "✅ Dépôt APT disponible"
echo " 📡 URL : https://${DOMAIN}"
echo " 🔑 Clé pub : https://${DOMAIN}/pgp-key.public"
echo " 📋 Sources : https://${DOMAIN}/gn-depot.list"
echo " ⚙️ Priorité : https://${DOMAIN}/gn-depot.pref"

View File

@ -4,138 +4,531 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GN dépôt</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Sora:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0d1117;
--surface: #161b22;
--surface2: #21262d;
--border: #30363d;
--accent: #58a6ff;
--accent2: #3fb950;
--accent3: #f78166;
--text: #e6edf3;
--text-muted: #8b949e;
--radius: 12px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: sans-serif;
background-color: #f9f9f9;
color: #333;
margin: 2em;
font-family: 'Sora', sans-serif;
background-color: var(--bg);
color: var(--text);
line-height: 1.7;
min-height: 100vh;
}
body::before {
content: '';
position: fixed;
inset: 0;
background-image:
linear-gradient(rgba(88,166,255,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(88,166,255,0.03) 1px, transparent 1px);
background-size: 40px 40px;
pointer-events: none;
z-index: 0;
}
.wrapper {
position: relative;
z-index: 1;
max-width: 860px;
margin: 0 auto;
padding: 3em 1.5em 4em;
}
header {
text-align: center;
margin-bottom: 3em;
animation: fadeDown 0.6s ease both;
}
header img.logo {
max-width: 160px;
margin-bottom: 1.5em;
filter: drop-shadow(0 0 20px rgba(88,166,255,0.25));
}
header h1 {
font-size: 1.9em;
font-weight: 700;
color: var(--text);
letter-spacing: -0.02em;
}
header h1 span { color: var(--accent); }
header p {
color: var(--text-muted);
margin-top: 0.6em;
font-size: 1em;
font-weight: 300;
}
section {
margin-bottom: 2.5em;
animation: fadeUp 0.5s ease both;
}
section:nth-child(2) { animation-delay: 0.1s; }
section:nth-child(3) { animation-delay: 0.2s; }
section:nth-child(4) { animation-delay: 0.3s; }
.section-title {
font-size: 0.75em;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--accent);
margin-bottom: 1em;
display: flex;
align-items: center;
gap: 0.6em;
}
.section-title::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}
/* --- Bloc installation principale --- */
.install-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
}
.install-card-header {
padding: 1.2em 1.5em;
border-bottom: 1px solid var(--border);
}
.install-card-header p {
color: var(--text-muted);
font-size: 0.92em;
margin-bottom: 0.4em;
}
.install-card-header p:last-child { margin-bottom: 0; }
.badge-list {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
margin-top: 0.8em;
}
.badge {
font-size: 0.75em;
font-family: 'JetBrains Mono', monospace;
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 20px;
padding: 0.2em 0.7em;
color: var(--text-muted);
display: flex;
align-items: center;
gap: 0.4em;
}
.badge .dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--accent2);
}
/* Code blocks */
.code-block {
position: relative;
background: #0d1117;
}
pre {
font-family: 'JetBrains Mono', monospace;
font-size: 0.85em;
padding: 1.2em 1.4em;
padding-right: 5em;
overflow-x: auto;
color: #e6edf3;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: auto;
background: #fff;
padding: 2em;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 12px;
.copy-btn {
position: absolute;
top: 0.7em;
right: 0.7em;
background: var(--surface2);
border: 1px solid var(--border);
color: var(--text-muted);
border-radius: 6px;
padding: 0.3em 0.75em;
font-size: 0.75em;
font-family: 'Sora', sans-serif;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
}
img.logo {
max-width: 200px;
display: block;
margin: 0 auto 2em;
.copy-btn:hover { background: var(--accent); color: var(--bg); border-color: var(--accent); }
.copy-btn.copied { background: var(--accent2); color: var(--bg); border-color: var(--accent2); }
/* Détails manuels (accordéon) */
.manual-toggle {
width: 100%;
background: var(--surface2);
border: none;
border-top: 1px solid var(--border);
color: var(--text-muted);
font-family: 'Sora', sans-serif;
font-size: 0.82em;
padding: 0.8em 1.4em;
text-align: left;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5em;
transition: color 0.2s;
}
pre {
background: #f4f4f4;
padding: 1em;
border-left: 5px solid #007acc;
overflow-x: auto;
.manual-toggle:hover { color: var(--text); }
.manual-toggle .arrow {
margin-left: auto;
transition: transform 0.25s;
}
h1, h2 {
color: #007acc;
text-align: center;
.manual-toggle.open .arrow { transform: rotate(180deg); }
.manual-steps {
display: none;
border-top: 1px solid var(--border);
}
.manual-steps.open { display: block; }
.step {
border-bottom: 1px solid var(--border);
}
.step:last-child { border-bottom: none; }
.step-header {
display: flex;
align-items: center;
gap: 0.8em;
padding: 0.7em 1.2em;
background: var(--surface2);
border-bottom: 1px solid var(--border);
}
.step-num {
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--accent);
color: var(--bg);
font-size: 0.72em;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.step-label {
font-size: 0.83em;
font-weight: 600;
color: var(--text-muted);
}
/* Inline code */
code {
font-family: 'JetBrains Mono', monospace;
background: var(--surface2);
padding: 0.15em 0.45em;
border-radius: 5px;
font-size: 0.87em;
color: var(--accent);
border: 1px solid var(--border);
}
pre code {
background: none;
padding: 0;
border: none;
font-size: inherit;
color: inherit;
}
/* Syntax */
.kw { color: #ff7b72; }
.str { color: #a5d6ff; }
.cmt { color: #8b949e; font-style: italic; }
.cmd { color: #3fb950; }
.url { color: #79c0ff; }
/* Packages */
.packages {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1.5em;
margin-top: 1.5em;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1em;
}
.package {
border: 1px solid #e0e0e0;
border-radius: 12px;
padding: 1.2em;
background: #fafafa;
transition: transform 0.2s, box-shadow 0.2s;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 1.3em;
transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s;
display: flex;
flex-direction: column;
gap: 0.4em;
}
.package:hover {
transform: translateY(-4px);
box-shadow: 0 8px 20px rgba(0,0,0,0.08);
transform: translateY(-3px);
border-color: var(--accent);
box-shadow: 0 8px 24px rgba(88,166,255,0.1);
}
.package-icon {
font-size: 2.5em;
margin-bottom: 0.5em;
}
.package-icon { font-size: 2em; }
.package h3 { font-size: 1em; font-weight: 600; color: var(--text); }
.package p { font-size: 0.88em; color: var(--text-muted); flex: 1; }
.package h3 {
margin: 0.2em 0;
color: #007acc;
}
.package p {
font-size: 0.95em;
margin: 0.3em 0 0.8em;
}
.package code {
background: #eee;
padding: 0.2em 0.4em;
.package-install {
font-family: 'JetBrains Mono', monospace;
font-size: 0.8em;
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 6px;
padding: 0.4em 0.7em;
color: var(--accent2);
display: block;
margin-top: 0.4em;
}
.package a {
font-size: 0.82em;
color: var(--accent);
text-decoration: none;
margin-top: 0.3em;
display: inline-block;
}
.package a:hover { text-decoration: underline; }
/* Resources */
.resource-list {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
}
.resource-item {
display: flex;
align-items: center;
gap: 1em;
padding: 0.9em 1.3em;
border-bottom: 1px solid var(--border);
text-decoration: none;
color: var(--text);
transition: background 0.15s;
font-size: 0.9em;
}
.resource-item:last-child { border-bottom: none; }
.resource-item:hover { background: var(--surface2); }
.resource-item .ri-icon { font-size: 1.1em; width: 1.5em; text-align: center; }
.resource-item .ri-name { font-weight: 600; color: var(--accent); font-family: 'JetBrains Mono', monospace; font-size: 0.9em; }
.resource-item .ri-desc { color: var(--text-muted); font-size: 0.85em; margin-left: auto; }
@keyframes fadeDown {
from { opacity: 0; transform: translateY(-18px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
footer {
text-align: center;
color: var(--text-muted);
font-size: 0.8em;
margin-top: 3em;
padding-top: 1.5em;
border-top: 1px solid var(--border);
}
</style>
</head>
<body>
<div class="container">
<img src="logo.png" alt="Le Garage Numérique" class="logo" />
<div class="wrapper">
<h1>Bienvenue sur le dépôt APT du Garage Numérique</h1>
<p>Ce dépôt vous permet dinstaller facilement les paquets maintenus par <strong>Le Garage Numérique</strong> sur les distributions basées sur Debian.</p>
</div>
<header>
<img src="logo.png" alt="Le Garage Numérique" class="logo" />
<h1>Dépôt APT <span>Le Garage Numérique</span></h1>
<p>Paquets maintenus par Le Garage Numérique pour les distributions basées sur Debian</p>
</header>
<h2>Instructions d'utilisation</h2>
<!-- Installation -->
<section>
<div class="section-title">Installation du dépôt</div>
<div class="install-card">
<div class="container">
<p>Installer le dépôt du garage :</p>
<pre><code>sudo wget -O /etc/apt/trusted.gpg.d/gn-depot.asc https://deb.legaragenumerique.fr/pgp-key.public
echo 'deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/gn-depot.asc] https://deb.legaragenumerique.fr stable main' \
| sudo tee /etc/apt/sources.list.d/gn-depot.list
sudo apt update</code></pre>
<!-- Description -->
<div class="install-card-header">
<p>Une seule commande suffit pour configurer le dépôt, importer la clé GPG et définir la priorité automatiquement.</p>
<div class="badge-list">
<div class="badge"><span class="dot"></span>Clé GPG importée</div>
<div class="badge"><span class="dot"></span>sources.list configuré</div>
<div class="badge"><span class="dot"></span>Priorité 500 appliquée</div>
<div class="badge"><span class="dot"></span>apt update exécuté</div>
</div>
</div>
<p>Installer le paquet souhaité (ex. <code>geo</code>) :</p>
<pre><code>sudo apt install geo</code></pre>
<!-- Commande principale -->
<div class="code-block">
<pre id="cmd-main"><button class="copy-btn" onclick="copyCode('cmd-main', this)">Copier</button><span class="cmd">curl</span> -fsSL <span class="url">https://deb.legaragenumerique.fr/install-repo.sh</span> | <span class="cmd">sudo</span> bash</pre>
</div>
<p>Et voilà 🎉 Vous êtes prêt à utiliser notre dépôt !</p>
</div>
<!-- Accordéon installation manuelle -->
<button class="manual-toggle" id="toggle-manual" onclick="toggleManual()">
⚙️ Installation manuelle étape par étape
<span class="arrow"></span>
</button>
<h2>Paquets disponibles</h2>
<div class="manual-steps" id="manual-steps">
<div class="packages">
<div class="package">
<div class="package-icon">🌍</div>
<h3>Geographical Adventures</h3>
<p>Petit jeu sur la géographie.</p>
<code>sudo apt install geo</code><br>
<a href="https://sebastian.itch.io/geographical-adventures">Site web</a>
</div>
<div class="step">
<div class="step-header">
<div class="step-num">1</div>
<div class="step-label">Ajouter la clé publique GPG</div>
</div>
<div class="code-block">
<pre id="cmd1"><button class="copy-btn" onclick="copyCode('cmd1', this)">Copier</button><span class="cmd">sudo</span> wget -qO /etc/apt/trusted.gpg.d/gn-depot.asc \
<span class="url">https://deb.legaragenumerique.fr/pgp-key.public</span></pre>
</div>
</div>
<div class="package">
<div class="package-icon">🔫</div>
<h3>World of padman</h3>
<p>Petit FPS open source.</p>
<code>sudo apt install wop</code><br>
<a href="https://worldofpadman.net">Site web</a>
</div>
<div class="step">
<div class="step-header">
<div class="step-num">2</div>
<div class="step-label">Ajouter le dépôt à sources.list</div>
</div>
<div class="code-block">
<pre id="cmd2"><button class="copy-btn" onclick="copyCode('cmd2', this)">Copier</button><span class="kw">echo</span> <span class="str">'deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/gn-depot.asc] https://deb.legaragenumerique.fr stable main'</span> \
| <span class="cmd">sudo</span> tee /etc/apt/sources.list.d/gn-depot.list</pre>
</div>
</div>
<div class="package">
<div class="package-icon">✒️</div>
<h3>Scribus</h3>
<p>Scribus svn 1.7.3.</p>
<code>sudo apt install scribus</code><br>
<a href="https://www.scribus.net">Site web</a>
</div>
<div class="step">
<div class="step-header">
<div class="step-num">3</div>
<div class="step-label">Définir la priorité du dépôt</div>
</div>
<div class="code-block">
<pre id="cmd3"><button class="copy-btn" onclick="copyCode('cmd3', this)">Copier</button><span class="cmd">sudo</span> wget -qO /etc/apt/preferences.d/gn-depot.pref \
<span class="url">https://deb.legaragenumerique.fr/gn-depot.pref</span></pre>
</div>
</div>
<!-- div class="package">
<div class="package-icon">📦</div>
<h3>backup-gn</h3>
<p>Solution simple de sauvegarde automatisée.</p>
<code>sudo apt install backup-gn</code><br>
<a href="https://www.legaragenumerique.fr">Site web</a>
</div> -->
<div class="step">
<div class="step-header">
<div class="step-num">4</div>
<div class="step-label">Mettre à jour et installer</div>
</div>
<div class="code-block">
<pre id="cmd4"><button class="copy-btn" onclick="copyCode('cmd4', this)">Copier</button><span class="cmd">sudo</span> apt update
<span class="cmd">sudo</span> apt install &lt;nom-du-paquet&gt;</pre>
</div>
</div>
</div>
</div>
</section>
<!-- Packages -->
<section>
<div class="section-title">Paquets disponibles</div>
<div class="packages">
<div class="package">
<div class="package-icon">🌍</div>
<h3>Geographical Adventures</h3>
<p>Petit jeu de géographie interactif.</p>
<code class="package-install">sudo apt install geo</code>
<a href="https://sebastian.itch.io/geographical-adventures" target="_blank" rel="noopener">↗ Site web</a>
</div>
<div class="package">
<div class="package-icon">🔫</div>
<h3>World of Padman</h3>
<p>FPS open source léger et fun.</p>
<code class="package-install">sudo apt install wop</code>
<a href="https://worldofpadman.net" target="_blank" rel="noopener">↗ Site web</a>
</div>
<div class="package">
<div class="package-icon">✒️</div>
<h3>Scribus 1.7.3 SVN</h3>
<p>Logiciel de PAO — version de développement récente.</p>
<code class="package-install">sudo apt install scribus</code>
<a href="https://www.scribus.net" target="_blank" rel="noopener">↗ Site web</a>
</div>
</div>
</section>
<footer>
© Le Garage Numérique · Dépôt APT · <a href="https://deb.legaragenumerique.fr" style="color:var(--accent);text-decoration:none;">deb.legaragenumerique.fr</a>
</footer>
</div>
<script>
function copyCode(id, btn) {
const pre = document.getElementById(id);
const clone = pre.cloneNode(true);
// Supprimer le bouton du clone avant d'extraire le texte
clone.querySelector('.copy-btn')?.remove();
const text = clone.innerText.trim();
navigator.clipboard.writeText(text).then(() => {
btn.textContent = '✓ Copié';
btn.classList.add('copied');
setTimeout(() => {
btn.textContent = 'Copier';
btn.classList.remove('copied');
}, 2000);
});
}
function toggleManual() {
const btn = document.getElementById('toggle-manual');
const steps = document.getElementById('manual-steps');
btn.classList.toggle('open');
steps.classList.toggle('open');
}
</script>
</body>
</html>
</html>

View File

@ -4,7 +4,8 @@ services:
build: .
restart: always
volumes:
- ./depot:/workspace/depot
- ./secret:/workspace/secret
- ./apt-repo:/workspace/apt-repo
- ./depot:/workspace/depot
ports:
- "8000:8000"