Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/check-blogs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Check that all blog URLs in guests.json return a successful HTTP response."""

import json
import sys
import urllib.request
import urllib.error

TIMEOUT = 10
SUCCESS_CODES = range(200, 400) # 2xx and 3xx considered reachable

guests = json.load(open("guests.json"))
errors = []

for g in guests:
url = g.get("blog", "")
if not url:
continue
if g.get("skip_blog_check"):
print(f"SKIP {g['name']} (bot-filter bypass)")
continue
try:
req = urllib.request.Request(url, method="HEAD", headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=TIMEOUT) as r:
if r.status in SUCCESS_CODES:
print(f"OK {g['name']} ({r.status})")
else:
errors.append(f"[{g['name']}] blog HTTP {r.status}: {url}")
except urllib.error.HTTPError as e:
if e.code in SUCCESS_CODES:
print(f"OK {g['name']} ({e.code})")
else:
errors.append(f"[{g['name']}] blog HTTP {e.code}: {url}")
except Exception as e:
errors.append(f"[{g['name']}] blog unreachable: {url} ({e})")

if errors:
print("\nErrors detected:")
for err in errors:
print(f" ✗ {err}")
sys.exit(1)
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
- name: Check avatar images
run: python3 .github/check-avatars.py

- name: Check blog availability
run: python3 .github/check-blogs.py

- name: Check OPML is up-to-date
run: |
node generate-opml.mjs /tmp/cuistops-guests-check.opml
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Toutes les données sont dans [`guests.json`](guests.json). Ajouter une entrée
| `github` | URL \| `null` | non | Profil GitHub |
| `blog` | URL \| `null` | non | Blog personnel — affiché en bouton mis en valeur sur la carte |
| `rss` | URL \| `null` | non | Flux RSS du blog |
| `skip_blog_check` | booléen | non | Si `true`, exclut le blog de la vérification automatique de disponibilité (utile si le site bloque les bots/scrapers) |
| `tags` | tableau de chaînes | oui | Mots-clés (2-4 recommandés) |

> Les champs optionnels absents ou à `null` n'affichent pas le bouton correspondant.
Expand Down
Binary file removed avatars/axellink_fr.webp
Binary file not shown.
Binary file removed avatars/horgix_fr.webp
Binary file not shown.
Binary file removed avatars/michael_tf.webp
Binary file not shown.
Binary file removed avatars/r_verchere_fr.webp
Binary file not shown.
Binary file removed avatars/smana_dev.webp
Binary file not shown.
3 changes: 2 additions & 1 deletion cuistops-guests.opml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<opml version="2.0">
<head>
<title>CuistOps — Blogs des potes</title>
<dateCreated>Thu, 07 May 2026 21:29:58 GMT</dateCreated>
<dateCreated>Thu, 07 May 2026 21:54:11 GMT</dateCreated>
</head>
<body>
<outline text="CuistOps">
Expand All @@ -16,6 +16,7 @@
<outline type="rss" text="Raphaël Pinson" title="Raphaël Pinson — " xmlUrl="https://dev.to/feed/raphink" htmlUrl="https://raphink.info"/>
<outline type="rss" text="Pascal MARTIN 😶‍🌫️" title="Pascal MARTIN 😶‍🌫️ — " xmlUrl="https://blog.pascal-martin.fr/index.xml" htmlUrl="https://blog.pascal-martin.fr"/>
<outline type="rss" text="Frédéric Léger - webofmars" title="Frédéric Léger - webofmars — " xmlUrl="https://webofmars.com/index.xml" htmlUrl="https://webofmars.com"/>
<outline type="rss" text="Rémi Verchère ❄️" title="Rémi Verchère ❄️ — " xmlUrl="https://www.vrchr.fr/index.xml" htmlUrl="https://vrchr.fr"/>
<outline type="rss" text="Seboss666" title="Seboss666 — Sysadmin dans l'âme" xmlUrl="https://blog.seboss666.info/feed/" htmlUrl="https://blog.seboss666.info"/>
</outline>
</body>
Expand Down
63 changes: 7 additions & 56 deletions guests.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"github": "https://github.com/julienhmmt",
"blog": "https://j.hommet.net",
"rss": "https://j.hommet.net/index.xml",
"skip_blog_check": true,
"tags": [
"Blog",
"Kubernetes",
Expand Down Expand Up @@ -170,19 +171,6 @@
"Blockchain"
]
},
{
"id": 9,
"name": "Smaine Kahlouch (Smana)",
"title": "",
"roles": [
"blog"
],
"bio": "SRE / DevOps / Cloud. Cloud native meetup and Kubernetes Community Days organizer. Loves surfing, skating, boxing and his family.",
"avatar": "avatars/smana_dev.webp",
"bluesky": "https://bsky.app/profile/smana.dev",
"tags": [],
"blog": "https://smana.dev"
},
{
"id": 10,
"name": "Aurélie Vache",
Expand Down Expand Up @@ -211,19 +199,6 @@
"blog": "https://raphink.info",
"rss": "https://dev.to/feed/raphink"
},
{
"id": 12,
"name": "Alexis \"Horgix\" Chotard",
"title": "",
"roles": [
"blog"
],
"bio": "Staff SRE at @payfiteng.bsky.social & Maître Raclettier — Mostly posting about conferences & tech — 🔧 Automation, ❤️ Open-Source, ⚙️ Rust, 📣🎙️ Conferences {organizer, speaker, frequent attendee} — Mandatory « I use Arch btw »",
"avatar": "avatars/horgix_fr.webp",
"bluesky": "https://bsky.app/profile/horgix.fr",
"tags": [],
"blog": "https://horgix.fr"
},
{
"id": 13,
"name": "Pascal MARTIN 😶‍🌫️",
Expand Down Expand Up @@ -263,46 +238,22 @@
"avatar": "avatars/bool_fr.webp",
"bluesky": "https://bsky.app/profile/bool.fr",
"tags": [],
"blog": "https://bool.fr"
"blog": "https://olivier.bonvalet.fr"
},
{
"id": 16,
"name": "Rémi Verchère ❄️",
"title": "",
"roles": [
"blog"
"blog",
"speaker"
],
"bio": "Cloud Native Infrastructure Consultant | Kubestronaut. Compte perso, troll bienveillant.",
"avatar": "avatars/r_verchere_fr.webp",
"avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:dnp6rvkryqwq43gwxxiudfkw/bafkreihjdpzrknbecngob5phutygbsqzl3stvrdbzizm4ehqvhs6fipb7y",
"bluesky": "https://bsky.app/profile/r.verchere.fr",
"tags": [],
"blog": "https://r.verchere.fr"
},
{
"id": 17,
"name": "Michael Todorovic",
"title": "",
"roles": [
"blog"
],
"bio": "Automating bugs deployment at scale and empowering developer experience Senior SRE @veepee_fr Food, music, Akita inu lover",
"avatar": "avatars/michael_tf.webp",
"bluesky": "https://bsky.app/profile/michael.tf",
"tags": [],
"blog": "https://michael.tf"
},
{
"id": 18,
"name": "Axel Vanzaghi",
"title": "",
"roles": [
"blog"
],
"bio": "Thoughts Are My Own | Réanimateur de machines (SRE) @OVHcloud | Tech addict | French and English",
"avatar": "avatars/axellink_fr.webp",
"bluesky": "https://bsky.app/profile/axellink.fr",
"tags": [],
"blog": "https://axellink.fr"
"blog": "https://vrchr.fr",
"rss": "https://www.vrchr.fr/index.xml"
},
{
"id": 19,
Expand Down
16 changes: 14 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@
<div class="logo-icon">🍳</div>
<span class="logo-text">Cuist<span>Ops</span></span>
</a>
<span class="header-subtitle">Le live DevOps francophone du lundi soir !</span>
<span class="header-subtitle">La communauté Française Dev|Ops !</span>
<a class="opml-btn" href="cuistops-guests.opml" download>
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M6.18 15.64a2.18 2.18 0 0 1 2.18 2.18C8.36 19.01 7.38 20 6.18 20C4.98 20 4 19.01 4 17.82a2.18 2.18 0 0 1 2.18-2.18M4 4.44A15.56 15.56 0 0 1 19.56 20h-2.83A12.73 12.73 0 0 0 4 7.27V4.44m0 5.66a9.9 9.9 0 0 1 9.9 9.9h-2.83A7.07 7.07 0 0 0 4 12.93V10.1z"/></svg>
Télécharger l'OPML
Expand All @@ -613,7 +613,7 @@
<main>
<div class="hero">
<h1>La <span>cuisine</span></h1>
<p>Découvrez la communauté CuistOps ( Guests, viewers, copains ).</p>
<p>Dev, Ops, Platform Engineer, SRE, DevRel... Retrouvez les acteurs de la communauté française ! </p>
</div>

<div class="search-wrapper">
Expand All @@ -638,6 +638,7 @@ <h1>La <span>cuisine</span></h1>

<div class="stats-bar">
<p class="guest-count"><strong id="count">0</strong> personne(s) affichée(s)</p>
<button class="view-btn" id="sort-btn" title="Trier A→Z / défaut" onclick="toggleSort()">A→Z</button>
<div class="view-toggle">
<button class="view-btn active" id="grid-btn" title="Vue grille" onclick="setView('grid')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
Expand Down Expand Up @@ -676,6 +677,14 @@ <h1>La <span>cuisine</span></h1>
let guests = [];
let currentView = 'grid';
let activeFilter = 'all';
let sortAlpha = false;

function toggleSort() {
sortAlpha = !sortAlpha;
const btn = document.getElementById('sort-btn');
btn.classList.toggle('active', sortAlpha);
applyFilters();
}

async function loadGuests() {
try {
Expand Down Expand Up @@ -792,6 +801,9 @@ <h3>Aucun guest trouvé</h3>
g.tags.some(t => t.toLowerCase().includes(q))
);
}
if (sortAlpha) {
filtered = [...filtered].sort((a, b) => a.name.localeCompare(b.name, 'fr'));
}
render(filtered);
}

Expand Down
Loading