The Linagora engineering blog — bilingual (EN / FR) technical articles, statically generated from Markdown files.
- Astro 5 — static site generator
- Tailwind CSS 4 — styling via CSS tokens
- MDX — enriched Markdown for articles
- Shiki — syntax highlighting
- Pagefind — client-side full-text search
- TypeScript strict throughout
nvm use 22 # Astro 5 requires Node 20+
npm install
npm run dev # http://localhost:4321
npm run build # static build + Pagefind index in dist/
npm run preview # local server to test the buildIn
devmode the Pagefind search index is not generated — the search modal shows a fallback. To test it locally, runnpm run build && npm run preview.
src/content/posts/<slug>.<lang>.md
<slug>— URL-friendly, kebab-case, identical between EN and FR to link the translations.<lang>—enorfr.
Examples:
src/content/posts/migrating-to-astro.en.md
src/content/posts/migrating-to-astro.fr.md
src/content/posts/post-mortem-may-incident.en.md # EN-only, OK
An article that exists in only one language will not appear in the other language's listing and the language switcher button will be disabled.
---
title: My great article
description: A short summary (~160 chars) used for SEO and cards.
date: 2026-05-04 # ISO date (required)
updated: 2026-06-01 # optional
authors: [linagora-team] # IDs of files in src/content/authors/ (at least one)
tags: [astro, devops] # optional
cover: /covers/my-article.jpg # optional — path under public/
coverAlt: Description # required if `cover` is set
draft: false # optional, defaults to false
---Plain Markdown or MDX. Syntax highlighting is automatic:
```ts
const hello = (name: string) => `Hello ${name}!`;
```src/content/authors/<id>.md
---
name: Jane Doe
title: Senior Software Engineer
bio: A short bio.
avatar: /authors/jane-doe.jpg # optional, in public/
github: janedoe # optional
linkedin: janedoe # optional (LinkedIn slug)
mastodon: https://floss.social/@janedoe
website: https://janedoe.dev
---Reference the author in the article frontmatter: authors: [jane-doe].
- Fork the repository (or create a branch if you have direct access)
- Add your
.mdfile(s) - Test locally:
npm run build - Open a Pull Request against
main
CI validates the build and the type-check. After merging to main, the Docker image is published to GHCR.
src/
├── content/
│ ├── posts/ # ← articles (.md, one per language)
│ └── authors/ # ← author profiles
├── content.config.ts # Zod schemas (frontmatter)
├── i18n/ # UI strings + routing helpers
├── layouts/ # Base + Post layouts
├── components/ # reusable Astro components
├── pages/[lang]/ # dynamic per-language pages
├── lib/posts.ts # collection query helpers
├── styles/global.css # CSS tokens + prose styles
└── site.config.ts # name, URL, GitHub repo info
| What | Where |
|---|---|
| Blog name, GitHub repo, social URLs | src/site.config.ts |
| Colour tokens, typography, dark mode | src/styles/global.css |
| UI strings (EN/FR) | src/i18n/ui.ts |
| About page copy | src/pages/[lang]/about.astro |
| Logo / favicon | public/favicon.svg, src/components/LinagoraLogo.astro |
| Default OG image | public/og-default.svg (or replace with a 1200×630 .png) |
| Auto-generated cover gradients | src/components/AutoCover.astro |
docker build -t linagora/twake-engineering-blog:latest .
docker run -p 8080:8080 linagora/twake-engineering-blog:latestThe image serves the site on port 8080 via nginx.
The .github/workflows/deploy.yml workflow builds and pushes the image to GHCR (ghcr.io/<owner>/<repo>) on every push to main.
To wire on Linagora infrastructure (left as a TODO in the workflow):
- Either SSH +
docker compose pull && docker compose up -d - Or
kubectl set imageon a Kubernetes Deployment - Or a webhook to an internal orchestrator
| File | What |
|---|---|
src/site.config.ts |
Canonical site URL (edit SITE.url), GitHub org/repo for the "Edit on GitHub" links |
astro.config.ts |
i18n config, redirects, integrations |
Pagefind indexes content tagged with data-pagefind-body (= each article body). The index is generated at the end of npm run build under dist/pagefind/.
Language filtering is automatic: Pagefind respects the lang attribute of the page's <html> tag.
Shortcut: ⌘K / Ctrl+K.
The blog code is licensed under AGPL-3.0. Published articles belong to their authors and are released under CC BY-SA 4.0 unless the frontmatter states otherwise (license: ...).