Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 81a954429e | |||
| 93a36be238 | |||
| 2e3ac4bce7 | |||
| 458f944e61 | |||
| 432c1a626f | |||
| 62f0a86693 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
depot
|
||||
apt-repo/*
|
||||
secret
|
||||
26
Dockerfile
26
Dockerfile
@ -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"]
|
||||
@ -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).
|
||||
@ -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"
|
||||
587
app/index.html
587
app/index.html
@ -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 d’installer 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 <nom-du-paquet></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>
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user