From b47b193b205b648c98d04af752dfa011cb0fe8de Mon Sep 17 00:00:00 2001 From: Bruno Carlin Date: Fri, 20 Jun 2025 02:33:45 +0200 Subject: [PATCH] feat: translate the site in french --- archetypes/default.md | 18 +- .../assets => assets}/static/css/bcarlin.css | 40 +- .../assets => assets}/static/css/pico.min.css | 0 .../static/css/remixicon.css | 0 .../assets => assets}/static/js/bcarlin.js | 0 config/_default/hugo.yaml | 39 ++ hugo.yaml => config/_default/menus.en.yaml | 34 +- config/_default/menus.fr.yaml | 36 ++ config/development/hugo.yaml | 8 + content/{_index.md => _index.en.md} | 0 content/_index.fr.md | 12 + .../index.en.md} | 9 +- .../001-setup-nginx-for-mediawiki/index.fr.md | 47 ++ .../index.en.md} | 9 +- .../002-build-pgpool-on-debian/index.fr.md | 35 ++ .../index.en.md} | 6 +- .../index.fr.md | 93 ++++ .../index.en.md} | 65 +-- content/blog/004-locking-buzhug/index.fr.md | 177 ++++++++ .../index.en.md} | 20 +- .../index.fr.md | 39 ++ .../index.en.md} | 16 +- .../006-discourse-without-docker/index.fr.md | 402 ++++++++++++++++++ .../index.en.md} | 8 +- .../index.fr.md | 136 ++++++ content/blog/_index.md | 6 - content/categories/outils/_index.fr.md | 5 + content/categories/tooling/_index.en.md | 4 + i18n/.gitkeep | 0 i18n/en.yaml | 20 + i18n/fr.yaml | 20 + layouts/.gitkeep | 0 .../_markup/render-heading.html | 2 +- .../_markup/render-link.html | 0 .../layouts => layouts}/_partials/footer.html | 11 +- .../layouts => layouts}/_partials/head.html | 18 +- .../_partials/head/css.html | 0 .../_partials/head/js.html | 2 + layouts/_partials/header.html | 22 + .../layouts => layouts}/_partials/icon.html | 0 .../layouts => layouts}/_partials/menu.html | 0 layouts/_partials/tags.html | 9 + layouts/baseof.html | 54 +++ .../layouts => layouts}/blog/list-item.html | 2 +- .../layouts => layouts}/blog/list.html | 0 .../layouts => layouts}/blog/single.html | 17 +- {themes/bcarlin/layouts => layouts}/home.html | 2 +- {themes/bcarlin/layouts => layouts}/page.html | 5 +- .../bcarlin/layouts => layouts}/section.html | 0 .../layouts => layouts}/section.rss.xml | 0 layouts/shortcodes/note.html | 7 + layouts/shortcodes/warning.html | 7 + .../bcarlin/layouts => layouts}/taxonomy.html | 0 {themes/bcarlin/layouts => layouts}/term.html | 2 +- .../static => static}/apple-touch-icon.png | Bin .../static => static}/favicon-96x96.png | Bin {themes/bcarlin/static => static}/favicon.ico | Bin {themes/bcarlin/static => static}/favicon.svg | 0 .../static => static}/site.webmanifest | 0 .../static/css/syntax-dark.css | 0 .../static/css/syntax-light.css | 0 .../static => static}/static/favicon.ico | Bin .../static/fonts/GentiumPlus-Bold.woff2 | Bin .../static/fonts/GentiumPlus-BoldItalic.woff2 | Bin .../static/fonts/GentiumPlus-Italic.woff2 | Bin .../static/fonts/GentiumPlus-Regular.woff2 | Bin .../static/fonts/NunitoSans-Italic.woff2 | Bin .../static/fonts/NunitoSans.woff2 | Bin .../static/fonts/remixicon.woff2 | Bin .../bcarlin/static => static}/static/logo.png | Bin .../bcarlin/static => static}/static/logo.svg | 0 .../web-app-manifest-192x192.png | Bin .../web-app-manifest-512x512.png | Bin themes/bcarlin/archetypes/default.md | 5 - themes/bcarlin/content/_index.md | 9 - themes/bcarlin/hugo.toml | 24 -- themes/bcarlin/layouts/_partials/header.html | 20 - themes/bcarlin/layouts/_partials/tags.html | 10 - themes/bcarlin/layouts/baseof.html | 38 -- themes/bcarlin/layouts/shortcodes/note.html | 4 - .../bcarlin/layouts/shortcodes/warning.html | 4 - 81 files changed, 1327 insertions(+), 251 deletions(-) rename {themes/bcarlin/assets => assets}/static/css/bcarlin.css (94%) rename {themes/bcarlin/assets => assets}/static/css/pico.min.css (100%) rename {themes/bcarlin/assets => assets}/static/css/remixicon.css (100%) rename {themes/bcarlin/assets => assets}/static/js/bcarlin.js (100%) create mode 100644 config/_default/hugo.yaml rename hugo.yaml => config/_default/menus.en.yaml (60%) create mode 100644 config/_default/menus.fr.yaml create mode 100644 config/development/hugo.yaml rename content/{_index.md => _index.en.md} (100%) create mode 100644 content/_index.fr.md rename content/blog/{001-setup-nginx-for-mediawiki.md => 001-setup-nginx-for-mediawiki/index.en.md} (96%) create mode 100644 content/blog/001-setup-nginx-for-mediawiki/index.fr.md rename content/blog/{002-build-pgpool-on-debian.md => 002-build-pgpool-on-debian/index.en.md} (86%) create mode 100644 content/blog/002-build-pgpool-on-debian/index.fr.md rename content/blog/{003-aptana-eclipse-and-xulrunner.md => 003-aptana-eclipse-and-xulrunner/index.en.md} (97%) create mode 100644 content/blog/003-aptana-eclipse-and-xulrunner/index.fr.md rename content/blog/{004-locking-buzhug.md => 004-locking-buzhug/index.en.md} (73%) create mode 100644 content/blog/004-locking-buzhug/index.fr.md rename content/blog/{005-automatically-open-sublime-text-projects-in-a-directory.md => 005-automatically-open-sublime-text-projects-in-a-directory/index.en.md} (71%) create mode 100644 content/blog/005-automatically-open-sublime-text-projects-in-a-directory/index.fr.md rename content/blog/{006-discourse-without-docker.md => 006-discourse-without-docker/index.en.md} (96%) create mode 100644 content/blog/006-discourse-without-docker/index.fr.md rename content/blog/{007-prepare-for-the-next-internet-outage.md => 007-prepare-for-the-next-internet-outage/index.en.md} (98%) create mode 100644 content/blog/007-prepare-for-the-next-internet-outage/index.fr.md delete mode 100644 content/blog/_index.md create mode 100644 content/categories/outils/_index.fr.md create mode 100644 content/categories/tooling/_index.en.md delete mode 100644 i18n/.gitkeep create mode 100644 i18n/en.yaml create mode 100644 i18n/fr.yaml delete mode 100644 layouts/.gitkeep rename {themes/bcarlin/layouts => layouts}/_markup/render-heading.html (62%) rename {themes/bcarlin/layouts => layouts}/_markup/render-link.html (100%) rename {themes/bcarlin/layouts => layouts}/_partials/footer.html (58%) rename {themes/bcarlin/layouts => layouts}/_partials/head.html (82%) rename {themes/bcarlin/layouts => layouts}/_partials/head/css.html (100%) rename {themes/bcarlin/layouts => layouts}/_partials/head/js.html (93%) create mode 100644 layouts/_partials/header.html rename {themes/bcarlin/layouts => layouts}/_partials/icon.html (100%) rename {themes/bcarlin/layouts => layouts}/_partials/menu.html (100%) create mode 100644 layouts/_partials/tags.html create mode 100644 layouts/baseof.html rename {themes/bcarlin/layouts => layouts}/blog/list-item.html (86%) rename {themes/bcarlin/layouts => layouts}/blog/list.html (100%) rename {themes/bcarlin/layouts => layouts}/blog/single.html (52%) rename {themes/bcarlin/layouts => layouts}/home.html (86%) rename {themes/bcarlin/layouts => layouts}/page.html (73%) rename {themes/bcarlin/layouts => layouts}/section.html (100%) rename {themes/bcarlin/layouts => layouts}/section.rss.xml (100%) create mode 100644 layouts/shortcodes/note.html create mode 100644 layouts/shortcodes/warning.html rename {themes/bcarlin/layouts => layouts}/taxonomy.html (100%) rename {themes/bcarlin/layouts => layouts}/term.html (82%) rename {themes/bcarlin/static => static}/apple-touch-icon.png (100%) rename {themes/bcarlin/static => static}/favicon-96x96.png (100%) rename {themes/bcarlin/static => static}/favicon.ico (100%) rename {themes/bcarlin/static => static}/favicon.svg (100%) rename {themes/bcarlin/static => static}/site.webmanifest (100%) rename {themes/bcarlin/static => static}/static/css/syntax-dark.css (100%) rename {themes/bcarlin/static => static}/static/css/syntax-light.css (100%) rename {themes/bcarlin/static => static}/static/favicon.ico (100%) rename {themes/bcarlin/static => static}/static/fonts/GentiumPlus-Bold.woff2 (100%) rename {themes/bcarlin/static => static}/static/fonts/GentiumPlus-BoldItalic.woff2 (100%) rename {themes/bcarlin/static => static}/static/fonts/GentiumPlus-Italic.woff2 (100%) rename {themes/bcarlin/static => static}/static/fonts/GentiumPlus-Regular.woff2 (100%) rename {themes/bcarlin/static => static}/static/fonts/NunitoSans-Italic.woff2 (100%) rename {themes/bcarlin/static => static}/static/fonts/NunitoSans.woff2 (100%) rename {themes/bcarlin/static => static}/static/fonts/remixicon.woff2 (100%) rename {themes/bcarlin/static => static}/static/logo.png (100%) rename {themes/bcarlin/static => static}/static/logo.svg (100%) rename {themes/bcarlin/static => static}/web-app-manifest-192x192.png (100%) rename {themes/bcarlin/static => static}/web-app-manifest-512x512.png (100%) delete mode 100644 themes/bcarlin/archetypes/default.md delete mode 100644 themes/bcarlin/content/_index.md delete mode 100644 themes/bcarlin/hugo.toml delete mode 100644 themes/bcarlin/layouts/_partials/header.html delete mode 100644 themes/bcarlin/layouts/_partials/tags.html delete mode 100644 themes/bcarlin/layouts/baseof.html delete mode 100644 themes/bcarlin/layouts/shortcodes/note.html delete mode 100644 themes/bcarlin/layouts/shortcodes/warning.html diff --git a/archetypes/default.md b/archetypes/default.md index 25b6752..2ce999e 100644 --- a/archetypes/default.md +++ b/archetypes/default.md @@ -1,5 +1,13 @@ -+++ -date = '{{ .Date }}' -draft = true -title = '{{ replace .File.ContentBaseName "-" " " | title }}' -+++ +--- +title: '{{ replace .File.ContentBaseName "-" " " | title }}' +slug: '{{ .File.ContentBaseName }}' +date: '{{ .Date }}' +draft: true +categories: [] +tags: +- +summary: | + entrer le résumé +description: | + entrer la description +--- diff --git a/themes/bcarlin/assets/static/css/bcarlin.css b/assets/static/css/bcarlin.css similarity index 94% rename from themes/bcarlin/assets/static/css/bcarlin.css rename to assets/static/css/bcarlin.css index 15c66a1..e1ee0e5 100644 --- a/themes/bcarlin/assets/static/css/bcarlin.css +++ b/assets/static/css/bcarlin.css @@ -217,6 +217,10 @@ body > footer p { margin-right: 0; margin-left: 18em; } + + .metadata p { + display: inline; + } } @@ -224,6 +228,15 @@ body > footer p { * Content Styling */ +.translations { + text-align: end; + margin-bottom: 0.3em; +} + +.translations a { + text-decoration: none; +} + main > article { text-align: justify; } @@ -232,11 +245,6 @@ main > article > header { margin-bottom: calc(2 * var(--pico-block-spacing-vertical)); } -.metadata { - display: flex; - gap: 0.5em; -} - ul.tags, ul.tags li { list-style-type: none; display: inline; @@ -244,10 +252,6 @@ ul.tags, ul.tags li { margin-bottom: 0; } -.tags li:not(:last-child)::after { - content: ", "; -} - ul > li { list-style-type: disc; } @@ -261,11 +265,6 @@ h1 { font-size: 1.7rem; } -h1::before { - content: "# "; - color: var(--markup-color); -} - h2 { font-size: 1.3rem; } @@ -313,10 +312,21 @@ h6::before { article header h1, article header p { margin-bottom: calc(0.1 * var(--pico-typography-spacing-vertical)); + margin-top: 0.3em; } -h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { +article header .category a { + font-family: var(--pico-font-family-sans-serif); + font-weight: 700; + line-height: var(--pico-line-height); + color: var(--pico-muted-color); + text-decoration: none; + font-size: 1rem; + margin-bottom: 0.5em; +} + +h2 a, h3 a, h4 a, h5 a, h6 a { color: var(--pico-muted-color); text-decoration: none; margin-left: 0.4rem; diff --git a/themes/bcarlin/assets/static/css/pico.min.css b/assets/static/css/pico.min.css similarity index 100% rename from themes/bcarlin/assets/static/css/pico.min.css rename to assets/static/css/pico.min.css diff --git a/themes/bcarlin/assets/static/css/remixicon.css b/assets/static/css/remixicon.css similarity index 100% rename from themes/bcarlin/assets/static/css/remixicon.css rename to assets/static/css/remixicon.css diff --git a/themes/bcarlin/assets/static/js/bcarlin.js b/assets/static/js/bcarlin.js similarity index 100% rename from themes/bcarlin/assets/static/js/bcarlin.js rename to assets/static/js/bcarlin.js diff --git a/config/_default/hugo.yaml b/config/_default/hugo.yaml new file mode 100644 index 0000000..9806098 --- /dev/null +++ b/config/_default/hugo.yaml @@ -0,0 +1,39 @@ +baseURL: 'https://bcarlin.net/' +languageCode: en +title: 'Bruno Carlin' +#theme: ['bcarlin'] +uglyUrls: true +copyright: '© 2025 Bruno Carlin' +summaryLength: 15 +params: + author: + email: mail@bcarlin.net + name: Bruno Carlin + +languages: + en: + weight: 10 + languageName: English + fr: + weight: 20 + languageName: Français + +outputs: + home: + - html + section: + - html + - rss + taxonomy: + - html + term: + - html + - rss + +outputFormats: + rss: + noUgly: false + +markup: + highlight: + noClasses: false diff --git a/hugo.yaml b/config/_default/menus.en.yaml similarity index 60% rename from hugo.yaml rename to config/_default/menus.en.yaml index 5a57984..627d8d0 100644 --- a/hugo.yaml +++ b/config/_default/menus.en.yaml @@ -1,36 +1,3 @@ -baseURL: 'https://bcarlin.net/' -languageCode: 'en_US' -title: 'Bruno Carlin' -theme: ['bcarlin'] -uglyUrls: true -copyright: '© 2025 Bruno Carlin' -summaryLength: 15 -params: - author: - email: mail@bcarlin.net - name: Bruno Carlin - -outputs: - home: - - html - section: - - html - - rss - taxonomy: - - html - term: - - html - - rss - -outputFormats: - rss: - noUgly: false - -markup: - highlight: - noClasses: false - -menus: main: - name: 'Home' pageRef: '/' @@ -66,3 +33,4 @@ menus: class: "u-key" iconName: "key-fill" iconTitle: "GPG Public Key" + diff --git a/config/_default/menus.fr.yaml b/config/_default/menus.fr.yaml new file mode 100644 index 0000000..69fcffa --- /dev/null +++ b/config/_default/menus.fr.yaml @@ -0,0 +1,36 @@ + main: + - name: 'Accueil' + pageRef: '/' + weight: 10 + params: + class: "u-url" + + - name: 'Blog' + pageRef: '/blog' + weight: 20 + + secondary: + - name: '@bcarlin@hachyderm.io' + url: 'https://hachyderm.io/@bcarlin' + weight: 10 + params: + rel: "me" + iconName: "mastodon-fill" + iconTitle: "Mastodon" + + - name: 'LinkedIn' + url: 'https://www.linkedin.com/in/brunocarlin' + weight: 20 + params: + rel: "me" + iconName: "linkedin-fill" + iconTitle: "LinkedIn" + + - name: 'Clef GPG' + url: '/bcarlin.gpg' + weight: 30 + params: + class: "u-key" + iconName: "key-fill" + iconTitle: "Clef publique GPG" + diff --git a/config/development/hugo.yaml b/config/development/hugo.yaml new file mode 100644 index 0000000..150ea19 --- /dev/null +++ b/config/development/hugo.yaml @@ -0,0 +1,8 @@ +enableMissingTranslationPlaceholders: true +ignoreCache: true +cleanDestinationDir: true +buildDrafts: true +buildFuture: true +printI18nWarnings: true +printPathWarnings: true +printUnusedTemplates: true diff --git a/content/_index.md b/content/_index.en.md similarity index 100% rename from content/_index.md rename to content/_index.en.md diff --git a/content/_index.fr.md b/content/_index.fr.md new file mode 100644 index 0000000..fc1e0c5 --- /dev/null +++ b/content/_index.fr.md @@ -0,0 +1,12 @@ +--- +title: 'Accueil' +date: 2025-06-03T23:00:00Z +draft: false +--- + +Bienvenue ! + +Je suis Bruno Carlin, un technologue fasciné par le développement de logiciels, +les langages de programmation et l'infrastructure qui les soutient. Je suis un +défenseur de longue date de l'open-source et un autohébergeur amateur, toujours +à la recherche de ce qui vient ensuite. diff --git a/content/blog/001-setup-nginx-for-mediawiki.md b/content/blog/001-setup-nginx-for-mediawiki/index.en.md similarity index 96% rename from content/blog/001-setup-nginx-for-mediawiki.md rename to content/blog/001-setup-nginx-for-mediawiki/index.en.md index 46a9dc1..2652967 100644 --- a/content/blog/001-setup-nginx-for-mediawiki.md +++ b/content/blog/001-setup-nginx-for-mediawiki/index.en.md @@ -1,10 +1,11 @@ --- title: Setup Nginx for Mediawiki -date: "2010-09-15T00:00:00+02:00" -tags: -- Nginx -- Mediawiki slug: 1-setup-nginx-for-mediawiki +date: "2010-09-15T00:00:00+02:00" +categories: [DevOps] +tags: +- mediawiki +- nginx summary: > A simple configuration to serve Mediawiki with Nginx and FastCGI --- diff --git a/content/blog/001-setup-nginx-for-mediawiki/index.fr.md b/content/blog/001-setup-nginx-for-mediawiki/index.fr.md new file mode 100644 index 0000000..0fd782a --- /dev/null +++ b/content/blog/001-setup-nginx-for-mediawiki/index.fr.md @@ -0,0 +1,47 @@ +--- +title: Configurer Nginx pour Mediawiki +slug: 1-configurer-nginx-pour-mediawiki +date: "2010-09-15T00:00:00+02:00" +categories: [DevOps] +tags: +- mediawiki +- nginx +summary: > + Une configuration simple de Nginx pour servir Mediawiki en FastCGI +--- + +Il y a deux semaines, j'ai migré un serveur d'Apache/mod_php vers nginx/php-fpm. +Ce n'est qu'aujourd'hui que j'ai réussi à éliminer tous les effets secondaires. +Le dernier en date : + +Les fichiers statiques ne doivent pas passer par php-fpm, mais un simple test +sur les extensions est inefficace, car les URL comme +`http://serveur/File:nom_du_fichier.png` doivent être traitées par PHP. + +Voici ma configuration finale, qui corrige toutes les erreurs que j'ai rencontrées : + +```nginx +server { + listen 80; + server_name server_name; + index index.php; + root /path/to/www/; + + # Serve static files with a far future expiration + # date for browser caches + location ^~ /images/ { + expires 1y; + } + location ^~ /skins/ { + expires 1y; + } + + # Pass the request to php-cgi + location / { + fastcgi_pass 127.0.0.1:9000; + fastcgi_param SCRIPT_FILENAME $document_root/index.php; + fastcgi_index index.php; + include fastcgi_params; + } +} +``` diff --git a/content/blog/002-build-pgpool-on-debian.md b/content/blog/002-build-pgpool-on-debian/index.en.md similarity index 86% rename from content/blog/002-build-pgpool-on-debian.md rename to content/blog/002-build-pgpool-on-debian/index.en.md index 3c9587a..17671d2 100644 --- a/content/blog/002-build-pgpool-on-debian.md +++ b/content/blog/002-build-pgpool-on-debian/index.en.md @@ -1,10 +1,13 @@ --- title: Build the latest PgPool-II on Debian Etch date: "2010-12-14T00:00:00+01:00" -tags: [Debian, PgPool-II] -slug: 2-build-pgpool-on-debian +slug: 2-build-pgpool-on-debian-etch +categories: [DevOps] +tags: +- Debian +- PgPool-II summary: > - Building PgPool-II on RHEL 5.5 to avoid the "libpq is not installed or + Building PgPool-II on Debian Etch and avoid the "libpq is not installed or libpq is old" error --- diff --git a/content/blog/002-build-pgpool-on-debian/index.fr.md b/content/blog/002-build-pgpool-on-debian/index.fr.md new file mode 100644 index 0000000..665d3c0 --- /dev/null +++ b/content/blog/002-build-pgpool-on-debian/index.fr.md @@ -0,0 +1,35 @@ +--- +title: Compiler le dernier PgPool-II sur Debian Etch +slug: 2-compiler-pgpool-sur-debian-etch +date: "2010-12-14T00:00:00+01:00" +categories: [DevOps] +tags: +- Debian +- PgPool-II +summary: > + Compilation de PgPool-II sur RHEL 5.5 pour éviter l'erreur "libpq is not + installed or libpq is old" +--- + +Après avoir compilé PgPool-II sur Red Hat Enterprise Linux 5.5 sans aucun +problème, j'ai essayé de le compiler sur un nouveau Debian Etch. Seulement, je +ne voulais pas installer PostgreSQL 9.0, mais simplement l'extraire des +paquets binaires fournis par Entreprisedb (avec l'option `--extract-only 1`). +Quelles que soient les options que je passais à `./configure`, cela résultait en +la même erreur : + +{{< highlight text >}} +checking for PQexecPrepared in -lpq... no +configure: error: libpq is not installed or libpq is old +{{< /highlight >}} + +Voici la réponse : le paquet binaire contient la libpq avec le nom +`libcrypto.so.0.9.8` (le nom RHEL) lorsque pgpool recherche `libcrypto.so.6` sur +Debian. La même chose s'applique à `libssl`. Donc un simple + +{{< highlight bash >}} +ln -s libcrypto.so.0.9.8 libcrypto.so.0.9.8 +ln -s libssl.so.0.9.8 libssl.so.6 +{{< /highlight >}} + +avant votre `./configure` résoudra le problème ! diff --git a/content/blog/003-aptana-eclipse-and-xulrunner.md b/content/blog/003-aptana-eclipse-and-xulrunner/index.en.md similarity index 97% rename from content/blog/003-aptana-eclipse-and-xulrunner.md rename to content/blog/003-aptana-eclipse-and-xulrunner/index.en.md index bee72d2..a78ee13 100644 --- a/content/blog/003-aptana-eclipse-and-xulrunner.md +++ b/content/blog/003-aptana-eclipse-and-xulrunner/index.en.md @@ -1,7 +1,11 @@ --- slug: 3-aptana-eclipse-and-xulrunner title: Aptana Studio/Eclipse and Xulrunner -tags: [Aptana Studio, Eclipse, Xulrunner, Arch Linux] +categories: [Tooling] +tags: +- Aptana Studio +- Eclipse +- Xulrunner date: "2011-12-16T00:00:00+01:00" summary: > How to solve the "Unhandled event loop exception" error in Aptana Studio and Eclipse 3.7 with Xulrunner diff --git a/content/blog/003-aptana-eclipse-and-xulrunner/index.fr.md b/content/blog/003-aptana-eclipse-and-xulrunner/index.fr.md new file mode 100644 index 0000000..7ecc083 --- /dev/null +++ b/content/blog/003-aptana-eclipse-and-xulrunner/index.fr.md @@ -0,0 +1,93 @@ +--- +slug: 3-aptana-eclipse-et-xulrunner +title: Aptana Studio/Eclipse et Xulrunner +categories: [Outils] +tags: +- Aptana Studio +- Eclipse +- Xulrunner +date: "2011-12-16T00:00:00+01:00" +summary: > + Comment résoudre l'erreur "Unhandled event loop exception" dans Aptana Studio + et Eclipse 3.7 avec Xulrunner +--- + +Depuis quelques mois, j'ai une erreur gênante dans Aptana Studio et Eclipse 3.7 +(les paquets autonomes, pas ceux des dépôts) chaque fois que j'ai essayé de +faire une action git ou hg. + +J'ai pu vivre avec jusqu'à maintenant, mais aujourd'hui, cela me dérangeait +vraiment. + +L'erreur est : + +{{< highlight text >}} +Unhandled event loop exception +No more handles [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)] +{{< /highlight >}} + +Le fichier log montrait la trace suivante : + +{{< highlight text >}} +!ENTRY org.eclipse.ui 4 0 2011-12-16 17:17:30.825 +!MESSAGE Unhandled event loop exception +!STACK 0 +org.eclipse.swt.SWTError: No more handles [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)] + at org.eclipse.swt.SWT.error(SWT.java:4109) + at org.eclipse.swt.browser.Mozilla.initMozilla(Mozilla.java:1739) + at org.eclipse.swt.browser.Mozilla.create(Mozilla.java:656) + at org.eclipse.swt.browser.Browser.(Browser.java:119) + at com.aptana.git.ui.internal.actions.CommitDialog.createDiffArea(CommitDialog.java:237) + at com.aptana.git.ui.internal.actions.CommitDialog.createDialogArea(CommitDialog.java:158) + [...] + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:620) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:575) + at org.eclipse.equinox.launcher.Main.run(Main.java:1408) + at org.eclipse.equinox.launcher.Main.main(Main.java:1384) +{{< /highlight >}} + +Pour faire court, après avoir lu +[beaucoup](https://bugs.archlinux.org/task/5149) +[de](https://bugs.archlinux.org/task/27130) +[posts](https://github.com/eclipse-color-theme/eclipse-color-theme/issues/50) +[à ce sujet](https://bbs.archlinux.org/viewtopic.php?id=129982) +[sur](http://forums.gentoo.org/viewtopic-t-827838-view-previous.html?sid=546c5717e2167c45d9b02f9f20ab36f4) +[ce](http://stackoverflow.com/questions/1017945/problem-with-aptana-studio-xulrunner-8-1) +[problème](http://www.eclipse.org/swt/faq.php#gtk64), il semblait qu'il +suffisait de donner le chemin vers Xulrunner à Aptana. + +Sur mon Arch Linux, c'était : + +{{< highlight bash >}} +export MOZILLA_FIVE_HOME=/usr/lib/xulrunner-8.0 +{{< /highlight >}} + +En essayant de démarrer Aptana Studio, j'ai eu une nouvelle erreur. Elle disait +simplement : + +{{< highlight text >}} +XPCOM error -2147467261 +{{< /highlight >}} + +La solution est qu'Aptana Studio ne peut pas fonctionner avec la version de +Xulrunner dans les dépôts Arch Linux car elle est trop récente. + +Pour résoudre ce problème, j'ai dû installer xulrunner 1.9.2 depuis AUR : + +{{< highlight bash >}} +yaourt -S xulrunner192 +{{< /highlight >}} + +Le PKGBUILD était cassé ce matin et se terminait par une erreur 404 lors de la +récupération des sources. Si vous avez le même problème, voici un PKGBUILD mis à +jour : [PKGBUILD mis à jour](https://gist.github.com/1486851). + +Enfin, j'ai mis : + +{{< highlight bash >}} +-Dorg.eclipse.swt.browser.XULRunnerPath=/usr/lib/xulrunner-1.9.2 +{{< /highlight >}} + +à la fin du fichier `AptanaStudio3.ini` dans le dossier d'Aptana Studio. Pour le +paquet dans les dépôts Arch Linux, ce fichier est +`/usr/share/aptana/AptanaStudio3.ini`. diff --git a/content/blog/004-locking-buzhug.md b/content/blog/004-locking-buzhug/index.en.md similarity index 73% rename from content/blog/004-locking-buzhug.md rename to content/blog/004-locking-buzhug/index.en.md index 13f7cce..af0350c 100644 --- a/content/blog/004-locking-buzhug.md +++ b/content/blog/004-locking-buzhug/index.en.md @@ -1,8 +1,11 @@ --- -tags: [Python, Buzhug, Database, Locks] slug: 4-locking-buzhug title: Locking Buzhug date: "2012-02-07T00:00:00+01:00" +categories: [Dev] +tags: +- Python +- Buzhug summary: > How to implement a cross-process, system-wide lock for Buzhug --- @@ -58,7 +61,7 @@ As it only has to work on Linux, the Vmfarms](http://blog.vmfarms.com/2011/03/cross-process-locking-and.html) fits perfectly. Here is a version slightly modified to make it a context manager : -{{< highlight python >}} +```python import fcntl class PsLock: @@ -87,83 +90,83 @@ class PsLock: def __enter__(self): self.acquire() -{{< /highlight >}} +``` The second step is to define a new class that inheritates from `buzhug.Base` that uses `PsLock` (inspired by `TS_Base`): -{{< highlight python >}} +```python import buzhug _lock = PsLock("/tmp/buzhug.lck") class PS_Base(buzhug.Base): - def create(self,*args,**kw): + def create(self, *args, **kw): with _lock: - res = buzhug.Base.create(self,*args,**kw) + res = buzhug.Base.create(self, *args, **kw) return res - def open(self,*args,**kw): + def open(self, *args, **kw): with _lock: - res = buzhug.Base.open(self,*args,**kw) + res = buzhug.Base.open(self, *args, **kw) return res - def close(self,*args,**kw): + def close(self, *args, **kw): with _lock: - res = buzhug.Base.close(self,*args,**kw) + res = buzhug.Base.close(self, *args, **kw) return res - def destroy(self,*args,**kw): + def destroy(self, *args, **kw): with _lock: - res = buzhug.Base.destroy(self,*args,**kw) + res = buzhug.Base.destroy(self, *args, **kw) return res - def set_default(self,*args,**kw): + def set_default(self, *args, **kw): with _lock: - res = buzhug.Base.set_default(self,*args,**kw) + res = buzhug.Base.set_default(self, *args, **kw) return res - def insert(self,*args,**kw): + def insert(self, *args, **kw): with _lock: - res = buzhug.Base.insert(self,*args,**kw) + res = buzhug.Base.insert(self, *args, **kw) return res - def update(self,*args,**kw): + def update(self, *args, **kw): with _lock: - res = buzhug.Base.update(self,*args,**kw) + res = buzhug.Base.update(self, *args, **kw) return res - def delete(self,*args,**kw): + def delete(self, *args, **kw): with _lock: - res = buzhug.Base.delete(self,*args,**kw) + res = buzhug.Base.delete(self, *args, **kw) return res - def cleanup(self,*args,**kw): + def cleanup(self, *args, **kw): with _lock: - res = buzhug.Base.cleanup(self,*args,**kw) + res = buzhug.Base.cleanup(self, *args, **kw) return res - def commit(self,*args,**kw): + def commit(self, *args, **kw): with _lock: - res = buzhug.Base.commit(self,*args,**kw) + res = buzhug.Base.commit(self, *args, **kw) return res - def add_field(self,*args,**kw): + def add_field(self, *args, **kw): with _lock: - res = buzhug.Base.add_field(self,*args,**kw) + res = buzhug.Base.add_field(self, *args, **kw) return res - def drop_field(self,*args,**kw): + def drop_field(self, *args, **kw): with _lock: - res = buzhug.Base.drop_field(self,*args,**kw) + res = buzhug.Base.drop_field(self, *args, **kw) return res -{{< /highlight >}} +``` Now I just use -{{< highlight python >}} +```python database = PS_Base( ... ) -{{< /highlight >}} +``` And all the errors have vanished. diff --git a/content/blog/004-locking-buzhug/index.fr.md b/content/blog/004-locking-buzhug/index.fr.md new file mode 100644 index 0000000..f3a3639 --- /dev/null +++ b/content/blog/004-locking-buzhug/index.fr.md @@ -0,0 +1,177 @@ +--- +slug: 4-verrouiller-buzhug +title: Verrouiller de Buzhug +date: "2012-02-07T00:00:00+01:00" +categories: [Dev] +tags: +- Python +- Buzhug +summary: > + Comment implémenter un verrou inter-processus et système pour Buzhug +--- + +J'ai récemment décidé d'utiliser [Buzhug] pour un projet. À ce que je puisse en +juger, il s'est avéré efficace, rapide, facile à utiliser et à maintenir. +Cependant, j'ai rencontré quelques problèmes. + +[Buzhug]: http://buzhug.sourceforge.net + +## Les solutions simples sont souvent les meilleures + +J'en suis venu à utiliser Buzhug pour les raisons suivantes : + +- J'avais besoin d'une seule table +- Je ne voulais pas ajouter de dépendances supplémentaires au projet +- La taille de la table serait en moyenne de 5K entrées (sans dépasser + 10k entrées en pic) + +Et une raison supplémentaire (personnelle) : + +- Je ne voulais pas me soucier de SQL. Vraiment pas. Pas question ! + +Cela ne me laissait qu'une option : une base de données embarquée en pur Python. + +Après avoir considéré quelques bibliothèques, j'ai été séduit par la manière +dont l'interface de Buzhug est proche de la manipulation d'objets Python. Et les +benchmarks semblaient montrer qu'il est assez performant pour ce projet. + +Après un rapide prototypage (1 jour), le choix était fait. + +Puis vinrent quelques semaines de développement et les premiers tests de +charge... + +## Et la réalité est revenue rapidement + +Plusieurs fois par jour, l'application soutenue par cette base de données est +intensément utilisée : + +- Elle peut être exécutée jusqu'à 50 fois simultanément dans des processus + Python séparés +- Chaque exécution effectue une opération de lecture et une opération + d'écriture/suppression + +Cela provoque une condition de course sur les fichiers utilisés pour stocker les +données, et les écritures concurrentes corrompent la base de données. + +L'utilisation de `buzhug.TS_Base` au lieu de `buzhug.Base` n'a rien résolu, car +le problème n'est pas lié aux threads, mais aux processus. Ce dont j'ai besoin +est un verrou inter-processus. + +## Voici la solution + +La première étape a été de trouver comment implémenter un verrou inter-processus +et système. + +Comme cela doit seulement fonctionner sur Linux, la +[classe Lock donnée par Chris de +Vmfarms](http://blog.vmfarms.com/2011/03/cross-process-locking-and.html) convient +parfaitement. Voici une version légèrement modifiée pour en faire un gestionnaire de contexte : + +```python +import fcntl + +class PsLock: + """ + Adapté de : + http://blog.vmfarms.com/2011/03/cross-process-locking-and.html + """ + def __init__(self, filename): + self.filename = filename + self.handle = open(filename, 'w') + + # Faites un OR bit à bit avec fcntl.LOCK_NB si vous avez besoin d'un verrou non bloquant + def acquire(self): + fcntl.flock(self.handle, fcntl.LOCK_EX) + + def release(self): + fcntl.flock(self.handle, fcntl.LOCK_UN) + + def __del__(self): + self.handle.close() + + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_type is None: + pass + self.release() + + def __enter__(self): + self.acquire() +``` + +La deuxième étape consiste à définir une nouvelle classe qui hérite de +`buzhug.Base` et qui utilise `PsLock` (inspiré de `TS_Base`) : + +```python +import buzhug + +_lock = PsLock("/tmp/buzhug.lck") + +class PS_Base(buzhug.Base): + def create(self, *args, **kw): + with _lock: + res = buzhug.Base.create(self, *args, **kw) + return res + + def open(self, *args, **kw): + with _lock: + res = buzhug.Base.open(self, *args, **kw) + return res + + def close(self, *args, **kw): + with _lock: + res = buzhug.Base.close(self, *args, **kw) + return res + + def destroy(self, *args, **kw): + with _lock: + res = buzhug.Base.destroy(self, *args, **kw) + return res + + def set_default(self, *args, **kw): + with _lock: + res = buzhug.Base.set_default(self, *args, **kw) + return res + + def insert(self, *args, **kw): + with _lock: + res = buzhug.Base.insert(self, *args, **kw) + return res + + def update(self, *args, **kw): + with _lock: + res = buzhug.Base.update(self, *args, **kw) + return res + + def delete(self, *args, **kw): + with _lock: + res = buzhug.Base.delete(self, *args, **kw) + return res + + def cleanup(self, *args, **kw): + with _lock: + res = buzhug.Base.cleanup(self, *args, **kw) + return res + + def commit(self, *args, **kw): + with _lock: + res = buzhug.Base.commit(self, *args, **kw) + return res + + def add_field(self, *args, **kw): + with _lock: + res = buzhug.Base.add_field(self, *args, **kw) + return res + + def drop_field(self, *args, **kw): + with _lock: + res = buzhug.Base.drop_field(self, *args, **kw) + return res +``` + +Maintenant, j'utilise simplement + +```python + database = PS_Base( ... ) +``` + +Et toutes les erreurs ont disparu. diff --git a/content/blog/005-automatically-open-sublime-text-projects-in-a-directory.md b/content/blog/005-automatically-open-sublime-text-projects-in-a-directory/index.en.md similarity index 71% rename from content/blog/005-automatically-open-sublime-text-projects-in-a-directory.md rename to content/blog/005-automatically-open-sublime-text-projects-in-a-directory/index.en.md index ba4ec63..4ecedf7 100644 --- a/content/blog/005-automatically-open-sublime-text-projects-in-a-directory.md +++ b/content/blog/005-automatically-open-sublime-text-projects-in-a-directory/index.en.md @@ -1,8 +1,12 @@ --- -tags: [Sublime Text 2] -slug: 5-automatically-open-sublime-text-projects-in-a-directory title: Automatically open Sublime Text projects in a directory +slug: 5-automatically-open-sublime-text-projects-in-a-directory date: "2013-05-15T00:00:00+02:00" +categories: +- Tooling +tags: +- Sublime Text +- Bash summary: > How to automatically open Sublimetext with a file, a project or the current directory according to the context. @@ -17,19 +21,19 @@ It ends up with one of the following commands : - `subl .` - `subl my-project.sublime-project` -Here is the snippet I added to my .bashrc file to have the `subl` +Here is the snippet I added to my `.bashrc` file to have the `subl` command automatically "guess" what I want. It does the following: -- If a path is given (subl "my/file.txt"), it opens the file. -- If nothing is given and a .sublime-project file exists in the current +- If a path is given (`subl "my/file.txt"`), it opens the file. +- If nothing is given and a `.sublime-project` file exists in the current directory, it opens it -- If nothing is given and no .sublime-project file has been found, it +- If nothing is given and no `.sublime-project` file has been found, it opens the folder. -{{< highlight bash >}} +```bash function project_aware_subl { project_file=$(ls *.sublime-project 2>/dev/null | head -n 1) subl ${*:-${project_file:-.}} } alias subl="project_aware_subl" -{{< /highlight >}} +``` diff --git a/content/blog/005-automatically-open-sublime-text-projects-in-a-directory/index.fr.md b/content/blog/005-automatically-open-sublime-text-projects-in-a-directory/index.fr.md new file mode 100644 index 0000000..1c2bd6c --- /dev/null +++ b/content/blog/005-automatically-open-sublime-text-projects-in-a-directory/index.fr.md @@ -0,0 +1,39 @@ +--- +title: Ouvrir automatiquement les projets Sublime Text dans un répertoire +slug: 5-ouvrir-automatiquement-les-projets-sublime-text-dans-un-repertoire +date: "2013-05-15T00:00:00+02:00" +categories: +- Outils +tags: +- Sublime Text +- Bash +summary: > + Comment ouvrir automatiquement Sublime Text avec un fichier, un projet ou le + répertoire courant selon le contexte. +--- + +J'ai l'habitude de lancer Sublime Text 2 depuis la ligne de commande pour +travailler, selon le cas, sur le contenu d'un répertoire ou sur un projet +(matérialisé par un fichier `*.sublime-project`). + +J'utilise l'une des commandes suivantes : + +- `subl .` +- `subl mon-projet.sublime-project` + +Voici la fonction que j'ai ajoutée à mon fichier `.bashrc` pour que la commande +`subl` "devine" automatiquement ce que je veux. Il fait ce qui suit : + +- Si un chemin est donné (`subl "mon/fichier.txt"`), il ouvre le fichier. +- Si rien n'est donné et qu'un fichier `.sublime-project` existe dans le + répertoire courant, il l'ouvre. +- Si rien n'est donné et qu'aucun fichier `.sublime-project` n'a été trouvé, il + ouvre le dossier. + +```bash +function project_aware_subl { + project_file=$(ls *.sublime-project 2>/dev/null | head -n 1) + subl ${*:-\${project_file:-.}} +} +alias subl="project_aware_subl" +``` diff --git a/content/blog/006-discourse-without-docker.md b/content/blog/006-discourse-without-docker/index.en.md similarity index 96% rename from content/blog/006-discourse-without-docker.md rename to content/blog/006-discourse-without-docker/index.en.md index 8823541..8f6f595 100644 --- a/content/blog/006-discourse-without-docker.md +++ b/content/blog/006-discourse-without-docker/index.en.md @@ -2,8 +2,13 @@ title: Discourse without Docker slug: 6-discourse-without-docker date: "2016-06-27T00:00:00+02:00" -tags: [discourse, docker] -summary: Detailed instructions on how to install Discourse and plugins without Docker. +categories: +- DevOps +tags: +- discourse +- docker +summary: > + Detailed instructions on how to install Discourse and plugins without Docker. --- {{< warning >}} @@ -277,8 +282,7 @@ WantedBy=multi-user.target ### Discourse For Discourse, just create the service unit for Puma. Create the file -`/etc/systemd/system/discourse.service` with the -following content: +`/etc/systemd/system/discourse.service` with the following content: ```ini [Unit] @@ -325,13 +329,13 @@ Restart Discourse: systemctl restart discourse ``` -What can go wrong? If if I do not give any solution here, it is always +What can go wrong? If I do not give any solution here, it is always recoverable (hence the backups!). - The database migration failed (restore the database with your backup, fix the problem and try again!) - The plugins are not compatible with the latest version (rollback to - the previous working solution and wit for them to be compatible) + the previous working version and wait for them to be compatible) ## Plugins diff --git a/content/blog/006-discourse-without-docker/index.fr.md b/content/blog/006-discourse-without-docker/index.fr.md new file mode 100644 index 0000000..cc5d478 --- /dev/null +++ b/content/blog/006-discourse-without-docker/index.fr.md @@ -0,0 +1,402 @@ +--- +title: Discourse sans Docker +slug: 6-discourse-sans-docker +date: "2016-06-27T00:00:00+02:00" +categories: +- DevOps +tags: +- discourse +- docker +summary: > + Instructions détaillées sur la façon d'installer Discourse et ses plugins sans + Docker. +--- + +{{< warning >}} +La seule méthode officielle est [avec Docker]. Vous pourriez ne pas obtenir de +support de Discourse en suivant cette méthode. +[avec Docker]: http://blog.discourse.org/2014/04/install-discourse-in-under-30-minutes/ +{{< /warning >}} + +L'équipe derrière [Discourse] a choisi de ne publier que des images Docker de +leur logiciel. La raison derrière cela est : il est plus facile de ne supporter +qu'une seule configuration. Je ne discuterai pas de cela. C'est leur choix. + +Cependant, je n'aime pas utiliser Docker pour déployer des applications en +production. Vraiment pas. Si vous êtes comme moi, voici les étapes que +j'ai utilisées pour l'installer et le configurer. + +J'utilise des serveurs Debian en production, donc les étapes ci-dessous sont +toutes orientées Debian. + +{{< note >}} +Ceci n'est pas destiné à être un guide complet. Beaucoup de commandes et +fichiers de configuration pourraient avoir besoin d'être adaptés à votre +environnement. + +Il ne traite même pas de sujets importants en production tels que +la sécurité. Cela est laissé comme exercice au lecteur. +{{< /note >}} + +## Installation + +Discourse est une application Rails. Elle peut être installée comme +n'importe quelle autre application Rails : + +Tout d'abord, Discourse utilise Redis et PostgreSQL (ou du moins, +je préfère utiliser Postgres). J'utilise également Nginx comme proxy pour +l'application. Installez les dépendances externes : + +```sh +# Ajoutez le dépôt pour Redis +echo "deb http://packages.dotdeb.org jessie all" > /etc/apt/sources.list.d/dotdeb.list +wget https://www.dotdeb.org/dotdeb.gpg -O - | apt-key add - + +# Ajoutez le dépôt pour PostgreSQL : +echo "deb http://apt.postgresql.org/pub/repos/apt/ jessie-pgdg main" > /etc/apt/sources.list.d/postgresql.list +wget -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - + +apt-get update +apt-get install postgresql-9.5 redis-server nginx +``` + +Ensuite, créez une base de données pour l'application. Entrez dans l'interface +de commande de postgres : + +```sh +su - postgres -c psql +``` + +et entrez les commandes suivantes : + +```sql +CREATE DATABASE discourse; +CREATE USER discourse; +ALTER USER discourse WITH ENCRYPTED PASSWORD 'password'; +ALTER DATABASE discourse OWNER TO discourse; +\connect discourse +CREATE EXTENSION hstore; +CREATE EXTENSION pg_trgm; +``` + +Ensuite, vous pouvez cloner le code de Discourse : + +```sh +git clone https://github.com/discourse/discourse.git /chemin/vers/discourse +# +# Optionnellement, basculez sur une étiquette spécifique +cd /chemin/vers/discourse +git checkout v1.5.3 +``` + +Ensuite, allez dans le répertoire principal de l'application, et configurez-la +comme n'importe quelle application Rails : + +```bash +# Optionnellement, configurez rvm avec ruby 1.9.3 minimum (j'utilise 2.3.0) +rvm install 2.3.0 +rvm use 2.3.0 + +# installez les dépendances +cd /chemin/vers/discourse +RAILS_ENV=production bundle install +``` + +Il est temps de configurer l'application. + +Ici, Discourse a une petite particularité : la configuration de production se +trouve dans le fichier `./config/discourse.conf`. + +Créez ce fichier : + +```bash +cp config/discourse_defaults.conf config/discourse.conf +``` + +Et modifiez-le avec votre configuration. Les principales zones d'intérêt sont +la configuration pour la base de données et pour le serveur de messagerie : + +```ini +# adresse hôte pour le serveur de base de données +# Ceci est défini à vide pour qu'il essaie d'utiliser les sockets en premier +db_host = localhost + +# port du serveur de base de données, pas besoin de le définir +db_port = 5432 + +# nom de la base de données exécutant discourse +db_name = discourse + +# nom d'utilisateur accédant à la base de données +db_username = discourse + +# mot de passe utilisé pour accéder à la base de données +db_password = password +``` + +et pour le serveur SMTP (dans cet exemple, nous utilisons Gmail) : + +```ini +# adresse du serveur smtp utilisé pour envoyer des emails +smtp_address = smtp.gmail.com + +# port du serveur smtp utilisé pour envoyer des emails +smtp_port = 587 + +# domaine passé au serveur smtp +smtp_domain = gmail.com + +# nom d'utilisateur pour le serveur smtp +smtp_user_name = votre-adresse@gmail.com + +# mot de passe pour le serveur smtp +smtp_password = password + +# mécanisme d'authentification smtp +smtp_authentication = plain + +# activer le chiffrement TLS pour les connexions smtp +smtp_enable_start_tls = true +``` + +Maintenant, nous pouvons préparer Discourse pour la production : + +```bash +RAILS_ENV=production bundle exec rake db:migrate +RAILS_ENV=production bundle exec rake assets:precompile +``` + +Il est temps de démarrer l'application. J'utilise généralement Puma pour +déployer les applications Rails. + +Créez le fichier `config/puma.rb` dans le répertoire de Discourse. Le contenu +suivant devrait suffire (pour plus d'informations, voir +[la documentation de Puma]) : + +```ruby +#!/usr/bin/env puma + +application_path = '/home/discuss.waarp.org/discourse' +directory application_path +environment 'production' +daemonize false +pidfile "#{application_path}/tmp/pids/puma.pid" +state_path "#{application_path}/tmp/pids/puma.state" +bind "unix://#{application_path}/tmp/sockets/puma.socket" +``` + +À partir de là, l'application peut être exécutée avec la commande suivante : + +```bash +bundle exec puma -C config/puma.rb +``` + +Enfin, configurez Nginx pour transférer les requêtes à Discourse. Créez le +fichier `/etc/nginx/conf.d/discourse.conf` avec le contenu suivant : + +```nginx +upstream discourse { + server unix:/chemin/vers/discourse/tmp/sockets/puma.socket; +} + +server { + listen 80; + server_name example.com; + + location / { + try_files $uri @proxy; + } + + location @proxy { + 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 $scheme; + proxy_pass http://discourse; + } +} +``` + +Votre forum avec Discourse est configuré ! + +## Gestion des services + +Selon vos habitudes de travail, vous pouvez ajouter des unités systemd pour +exécuter Discourse. + +Il nécessite au moins deux service : + +1. Sidekiq, qui est utilisé pour traiter les tâches de fond asynchrones +2. Rails, pour Discourse lui-même. + +Avec les services configurés, les services peuvent être démarrés/arrêtés/activés +avec les commandes `systemctl`. + +Mais avant cela, si vous utilisez RVM, vous devez créer un wrapper pour +l'environnement (ruby local, et gemset optionnel) utilisé par Discourse : + +```bash +rvm wrapper 2.3.0 systemd bundle +``` + +Cela crée un exécutable dans `$rvm_bin_path` que vous pouvez appeler +à la place de bundle, et qui chargera automatiquement le bon environnement. + +### Sidekiq + +Tout d'abord, créez une configuration pour Sidekiq. Créez le fichier +`config/sidekiq.yml` dans votre projet Discourse avec le contenu +suivant (pour plus d'informations, voir [la documentation de Sidekiq]) : + +```yaml +--- +:concurrency: 5 +:pidfile: tmp/pids/sidekiq.pid +staging: + :concurrency: 10 +production: + :concurrency: 20 +:queues: + - default + - critical + - low +``` + +Ensuite, créez l'unité de service pour Sidekiq. Créez le fichier +`/etc/systemd/system/discourse-sidekiq.service` avec le +contenu suivant : + +```ini +[Unit] +Description=service sidekiq de discourse +After=multi-user.target + +[Service] +WorkingDirectory=/chemin/vers/discourse +Environment=RAILS_ENV=production +ExecStart=/chemin/vers/rvm/.rvm/bin/systemd_bundle exec sidekiq -C config/sidekiq.yml +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +``` + +### Discourse + +Pour Discourse, créez simplement l'unité de service pour Puma. Créez le fichier +`/etc/systemd/system/discourse.service` avec le contenu suivant : + +```ini +[Unit] +Description=service discourse +After=discourse-sidekiq.service +Requires=discourse-sidekiq.service + +[Service] +WorkingDirectory=/chemin/vers/discourse +Environment=RAILS_ENV=production +ExecStart=/chemin/vers/rvm/.rvm/bin/systemd_bundle exec puma -C config/puma.rb +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +``` + +## Mises à jour + +Les mises à jour sont encore plus faciles : + +Lisez d'abord les notes de version. +Faites ensuite des sauvegardes du code et de la base de données. +Maintenant, vous pouvez basculer vers la nouvelle version : + +```bash +cd /chemin/vers/discourse +git checkout vX.X.X +``` + +Installez les nouvelles dépendances, exécutez les migrations et reconstruisez les +assets : + +```bash +RAILS_ENV=production bundle install +RAILS_ENV=production bundle exec rake db:migrate +RAILS_ENV=production bundle exec rake assets:precompile +``` + +Redémarrez Discourse : + +```bash +systemctl restart discourse +``` + +Que peut-il mal se passer ? Si je ne donne aucune solution ici, c'est toujours +récupérable (d'où les sauvegardes !). + +- La migration de la base de données a échoué (restaurez la base de données avec + votre sauvegarde, corrigez le problème et réessayez !) +- Les plugins ne sont pas compatibles avec la dernière version (revenez à + la solution précédente fonctionnelle et attendez qu'ils soient compatibles) + +## Plugins + +Les plugins Discourse peuvent être gérés de la même manière. + +### Installation de plugins + +Installez le plugin avec l'URL de son dépôt : + +```bash +cd /chemin/vers/discourse +RAILS_ENV=production bundle exec rake plugin:install[URL] +``` + +Installez les nouvelles dépendances, exécutez les migrations et reconstruisez les +assets : + +```bash +RAILS_ENV=production bundle install +RAILS_ENV=production bundle exec rake db:migrate +RAILS_ENV=production bundle exec rake assets:precompile +``` + +Redémarrez Discourse : + +```bash +systemctl restart discourse +``` + +### Mise à jour + +Pour mettre à jour un plugin spécifique, utilisez la commande suivante : + +```bash +RAILS_ENV=production bundle exec rake plugin:update[ID] +``` + +Vous pouvez également mettre à jour tous les plugins en une seule fois avec la commande : + +```bash +RAILS_ENV=production bundle exec rake plugin:update_all +``` + +Ensuite, installez les nouvelles dépendances, exécutez les migrations et reconstruisez les +assets : + +```bash +RAILS_ENV=production bundle install +RAILS_ENV=production bundle exec rake db:migrate +RAILS_ENV=production bundle exec rake assets:precompile +``` + +et redémarrez Discourse : + +```bash +systemctl restart discourse +``` + +[Discourse]: http://www.discourse.org/ +[Documentation de Sidekiq]: https://github.com/mperham/sidekiq/wiki/Advanced-Options +[Documentation de Puma]: https://github.com/puma/puma diff --git a/content/blog/007-prepare-for-the-next-internet-outage.md b/content/blog/007-prepare-for-the-next-internet-outage/index.en.md similarity index 98% rename from content/blog/007-prepare-for-the-next-internet-outage.md rename to content/blog/007-prepare-for-the-next-internet-outage/index.en.md index e2e5df0..01ea218 100644 --- a/content/blog/007-prepare-for-the-next-internet-outage.md +++ b/content/blog/007-prepare-for-the-next-internet-outage/index.en.md @@ -2,7 +2,11 @@ title: 'Prepare for the Next Internet Outage' slug: '7-prepare-for-the-next-internet-outage' date: '2025-06-14T04:05:48+02:00' -tags: [architecture, cloud] +categories: +- DevOps +tags: +- architecture, +- cloud summary: > A reflection on recent internet outages and my takeways to build more resilient web services. @@ -59,7 +63,7 @@ least one of these vendors. The cloud and API world we live in is great. It allows us to build fast, iterate quickly, test things and improve our solutions. You need authentication, use -Subabase or Auth0. Online payment? There is Stripe or Paypal. Transactional +Supabase or Auth0. Online payment? There is Stripe or Paypal. Transactional emails? Sendgrid and MailChimp. Search? Algolia. The list can be long, but now, you can work on creating value. diff --git a/content/blog/007-prepare-for-the-next-internet-outage/index.fr.md b/content/blog/007-prepare-for-the-next-internet-outage/index.fr.md new file mode 100644 index 0000000..3bcfab8 --- /dev/null +++ b/content/blog/007-prepare-for-the-next-internet-outage/index.fr.md @@ -0,0 +1,136 @@ +--- +title: Préparez-vous pour la prochaine panne d'Internet +slug: '7-preparez-vous-pour-la-prochaine-panne-d-internet' +date: '2025-06-14T04:05:48+02:00' +categories: +- DevOps +tags: +- architecture +- cloud +summary: > + Une réflexion sur les récentes pannes d'Internet et les enseignements que j'en + tire pour construire des services web plus résilients. +--- + +Jeudi dernier, [Internet est tombé en panne](https://mashable.com/article/google-down-cloudflare-twitch-character-ai-internet-outage). +Encore une fois. Oui, les médias ont transformé une panne de deux heures en une +crise mondiale pour faire du putaclic. + +Ce qui a rendu cet incident significatif, ce n'est pas seulement la perturbation +de Google Cloud, mais aussi les centaines de sites web et d'applications qui +sont tombés en panne en même temps. Cela incluait certains grands noms comme +Cloudflare, qui utilise GCP pour certains de ses services. Cloudflare étant un +CDN, un cache et un proxy largement répandus, cela a créé un effet domino et a, +à son tour, fait tomber d'innombrables sites web. + +Cela nous rappelle la fragile interconnection de notre monde numérique. Je ne +veux pas pointer du doigt, mais plutôt tirer des leçons de cet incident. Ce +n'était pas juste un incident ; cela a mis en lumière des principes +fondamentaux que, à l'ère du "tout en tant que service", nous avons peut-être +involontairement négligés. + +Voici mes principaux enseignements. + +## Ne mettez pas tous vos œufs dans le même panier de fournisseurs + +Le cloud est devenu synonyme d'une mise à l'échelle infinie, d'un stockage +infini, d'une puissance de calcul infinie, d'une flexibilité infinie. Il est +construit sur la promesse de réduire les coûts (ce qui peut être vrai lorsqu'il +est utilisé correctement). Cependant, cela cache une vérité souvent négligée, +qui est aussi son plus grand risque : la dépendance à un seul fournisseur. La +récente panne a montré comment une panne d'un seul fournisseur, ou même d'un +composant de leur infrastructure, peut avoir un effet en cascade sur la plupart +des services. + +Ajoutons à cela que +[AWS, Azure et Google Cloud Platform ont une part de marché combinée de 63 % en valeur](https://www.crn.com/news/cloud/2025/cloud-market-share-q1-2025-aws-dips-microsoft-and-google-show-growth?page=1&itc=refresh). +Même si votre entreprise n'utilise pas directement ces fournisseurs +d'infrastructure, il est probable que vous utilisiez des fournisseurs qui +dépendent d'eux, ou des fournisseurs qui pourraient dépendre d'eux. Oui, il est +probable que votre application SaaS dépende d'au moins l'un de ces fournisseurs. + +**Ce que vous pouvez faire** : + +* *Cartographiez vos dépendances* : Connaissez-vous vraiment tous les services + sur lesquels repose votre produit, directement et indirectement ? Quels IaaS, + PaaS, API, CDN, etc., utilisez-vous ? Lesquels ces services utilisent-ils à + leur tour ? Dépendez-vous de NpmJS pour construire votre produit ? Votre + application est-elle déployée avec une action Github ? Plus vous en savez, + mieux vous êtes préparé. +* *Faites une évaluation approfondie de vos fournisseurs* : Les garanties de + disponibilité (3 ? 4 ? 5 neufs ?) ne sont que du marketing. Prenez-les comme + tel. Quelle est l'architecture de votre fournisseur ? Son plan de continuité ? + Sa transparence sur les incidents ? Ce sont des critères bien plus importants. +* *Envisagez des stratégies multi-cloud* : Vous ne mettriez pas tous vos + serveurs dans le même centre de données ? Alors ne mettez pas toute votre + infrastructure chez le même fournisseur IaaS ! (Si vous le feriez, vous + devriez faire quelque chose à ce sujet !) + +## Contrôlez vos données, contrôlez votre entreprise + +Le monde du cloud et des API dans lequel nous vivons est formidable. Il nous +permet de construire rapidement, d'itérer rapidement, de tester des choses et +d'améliorer nos solutions. Vous avez besoin d'une authentification, utilisez +Supabase ou Auth0. Paiement en ligne ? Il y a Stripe ou Paypal. E-mails +transactionnels ? Sendgrid et MailChimp. Recherche ? Algolia. La liste peut être +longue, mais maintenant, vous pouvez travailler à créer de la valeur. + +Pourtant, comme l'a montré cette panne, si ces services deviennent +indisponibles, vos utilisateurs pourraient être bloqués, ou votre application +pourrait cesser de fonctionner, indépendamment de la santé de votre propre +infrastructure. Cela peut entraîner une perte significative de contrôle sur les +opérations commerciales de base et l'accès aux données. Les services tiers SONT +des points de défaillance uniques ! + +**Ce que vous pouvez faire** : + +* *Mécanismes de repli pour les services principaux* : Si un service devient + indisponible, comment le remplacez-vous ? Pouvez-vous développer une + alternative de repli ? +* *Mirroring de données robuste* : Assurez-vous d'avoir des sauvegardes + régulières et accessibles de vos données critiques, même si elles résident + principalement chez un tiers. Pouvez-vous les restaurer rapidement dans un + environnement différent si nécessaire ? + +## Construire pour la résilience + +La résilience a toujours été une conséquence de la redondance. Vous devriez +toujours avoir un système de secours qui peut assurer le service pendant que +votre système principal est en panne. + +Mais il ne suffit pas d'avoir de la redondance. Votre application doit également +être conçue pour être tolérante aux pannes et utiliser tout ou partie du système +de secours lorsque nécessaire. Au moins, elle doit garantir que l'impact pour +vos utilisateurs soit le moindre possible : l'impossibilité d'envoyer un e-mail +ne doit jamais bloquer toute votre application. + +**Ce que vous pouvez faire** : + +* *Architectures distribuées* : Concevez vos systèmes avec des architectures de + type microservice. Déployez vos services sur plusieurs fournisseurs IaaS. + Répliquez les données critiques sur plusieurs fournisseurs. L'objectif est de + limiter l'impact de toute défaillance d'un seul composant. +* *Systèmes auto-cicatrisants* : Mettez en place des mécanismes qui peuvent + détecter automatiquement les défaillances, réacheminer le trafic ou redémarrer + les services sans intervention humaine. Plus votre système peut réagir + rapidement, moins une panne aura d'impact. +* *Concevoir pour l'échec* : N'attendez pas qu'un événement externe expose vos + faiblesses. Il sera trop tard. Ajoutez quelques tests de défaillance + automatisés à votre pipeline CI : que se passe-t-il si le client a une latence + de 5 secondes avec votre serveur ? Que se passe-t-il si la base de données est + indisponible ? Que se passe-t-il si un paiement ne peut pas être traité + immédiatement ? Quelle est l'*expérience utilisateur* lorsque quelque chose ne + va pas ? Ces problèmes ARRIVERONT. + +## Conclusion + +La prochaine panne arrivera. C'est certain. Peut-être pas aussi importante, mais +il y en aura qui affecteront votre entreprise. + +Soyez préparé : + +* Connaissez votre infrastructure, vos fournisseurs, leurs fournisseurs, etc. +* Évaluez les risques régulièrement. Votre application évolue, vos fournisseurs + aussi. Ce qui est vrai à un moment ne l'est plus au suivant. +* Prévoyez le pire des cas. Les incidents arriveront. Votre travail est de faire + en sorte que l'expérience utilisateur ne soit pas impactée. diff --git a/content/blog/_index.md b/content/blog/_index.md deleted file mode 100644 index 3ee0432..0000000 --- a/content/blog/_index.md +++ /dev/null @@ -1,6 +0,0 @@ -+++ -title = 'Blog' -date = 2025-06-04T23:00:00Z -draft = false -+++ - diff --git a/content/categories/outils/_index.fr.md b/content/categories/outils/_index.fr.md new file mode 100644 index 0000000..0fb8596 --- /dev/null +++ b/content/categories/outils/_index.fr.md @@ -0,0 +1,5 @@ +--- +title: Outils +translationKey: tooling +--- + diff --git a/content/categories/tooling/_index.en.md b/content/categories/tooling/_index.en.md new file mode 100644 index 0000000..8f09d27 --- /dev/null +++ b/content/categories/tooling/_index.en.md @@ -0,0 +1,4 @@ +--- +title: Tooling +translationKey: tooling +--- diff --git a/i18n/.gitkeep b/i18n/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/i18n/en.yaml b/i18n/en.yaml new file mode 100644 index 0000000..e886410 --- /dev/null +++ b/i18n/en.yaml @@ -0,0 +1,20 @@ +posted: Posted on +license: | + The content of this site is licensed under + {{ .name }} +generated_with_hugo: | + Generated with Hugo using a custom theme. +logo: Logo +mobile_menu: Mobile Menu +toggle_menu: Toggle the Main Menu +open_menu: Open the main menu +close_menu: Close the menu +main_menu: Main Menu +recent_posts: Recent Posts +note: Note +warning: Warning +permalink_section: "Permalink to this section" +tags: Tags +information_icon: Information icon +warning_icon: Warning icon +also_available_in: "Also available in:" diff --git a/i18n/fr.yaml b/i18n/fr.yaml new file mode 100644 index 0000000..acb8a4d --- /dev/null +++ b/i18n/fr.yaml @@ -0,0 +1,20 @@ +posted: Posté le +license: | + Le contenu de ce site est publié selon les termes de la licence + {{ .name }} +generated_with_hugo: | + Généré avec Hugo en utilisant un thème personnalisé. +logo: Logo +mobile_menu: Menu mobile +toggle_menu: Ouvrir le menu +open_menu: Ouvrir le menu +close_menu: Fermer le menu +main_menu: Menu principal +recent_posts: Posts récents +note: Note +warning: Attention +permalink_section: Permalien vers cette partie +tags: Tags +information_icon: Icône information +warning_icon: Icône attention +also_available_in: "Également disponible en :" diff --git a/layouts/.gitkeep b/layouts/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/themes/bcarlin/layouts/_markup/render-heading.html b/layouts/_markup/render-heading.html similarity index 62% rename from themes/bcarlin/layouts/_markup/render-heading.html rename to layouts/_markup/render-heading.html index 566326f..8b6f6b2 100644 --- a/themes/bcarlin/layouts/_markup/render-heading.html +++ b/layouts/_markup/render-heading.html @@ -1,6 +1,6 @@ {{ .Text }} - {{- partial "icon.html" (dict "icon" "links-line" "label" "Permalink to this section") -}} + {{- partial "icon.html" (dict "icon" "links-line" "label" (T "permalink_section")) -}} diff --git a/themes/bcarlin/layouts/_markup/render-link.html b/layouts/_markup/render-link.html similarity index 100% rename from themes/bcarlin/layouts/_markup/render-link.html rename to layouts/_markup/render-link.html diff --git a/themes/bcarlin/layouts/_partials/footer.html b/layouts/_partials/footer.html similarity index 58% rename from themes/bcarlin/layouts/_partials/footer.html rename to layouts/_partials/footer.html index 5582f98..a0f4b73 100644 --- a/themes/bcarlin/layouts/_partials/footer.html +++ b/layouts/_partials/footer.html @@ -2,14 +2,13 @@

© {{ now.Year }} Bruno Carlin
- The content of this site is licensed under - - Creative Commons Attribution-NonCommercial 4.0 - International - + {{ T "license" (dict + "link" "https://creativecommons.org/licenses/by-nc/4.0/" + "name" "Creative Commons Attribution-NonCommercial 4.0 International" + ) | safeHTML }} {{partial "icon.html" (dict "icon" "creative-commons-fill" "label" "Creative Commons Logo")}} {{partial "icon.html" (dict "icon" "creative-commons-by-fill" "label" "Creative Commons Attribution Logo")}} {{partial "icon.html" (dict "icon" "creative-commons-nc-fill" "label" "Creative Commons Non Commercial Logo")}}
- Generated with Hugo using a custom theme. + {{ T "generated_with_hugo" | safeHTML }}

diff --git a/themes/bcarlin/layouts/_partials/head.html b/layouts/_partials/head.html similarity index 82% rename from themes/bcarlin/layouts/_partials/head.html rename to layouts/_partials/head.html index 5626052..15fd811 100644 --- a/themes/bcarlin/layouts/_partials/head.html +++ b/layouts/_partials/head.html @@ -1,5 +1,15 @@ +{{ hugo.Generator }} + +{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }} + + +{{- if .IsTranslated }} + {{- range .Translations }} + + {{- end }} +{{- end }} {{ with .Site.GetPage "/blog" }} {{- with .OutputFormats.Get "rss" }} @@ -19,12 +29,8 @@ -{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }} - - - {{ template "_internal/opengraph.html" . }} -{{ template "_internal/schema.html" . }} +{{- template "_internal/schema.html" . }} {{ partialCached "head/css.html" . }} -{{ partialCached "head/js.html" . }} +{{- partialCached "head/js.html" . }} diff --git a/themes/bcarlin/layouts/_partials/head/css.html b/layouts/_partials/head/css.html similarity index 100% rename from themes/bcarlin/layouts/_partials/head/css.html rename to layouts/_partials/head/css.html diff --git a/themes/bcarlin/layouts/_partials/head/js.html b/layouts/_partials/head/js.html similarity index 93% rename from themes/bcarlin/layouts/_partials/head/js.html rename to layouts/_partials/head/js.html index 3488d68..0ed6b73 100644 --- a/themes/bcarlin/layouts/_partials/head/js.html +++ b/layouts/_partials/head/js.html @@ -14,4 +14,6 @@ {{- end }} {{- end }} {{- end }} +{{- if not hugo.IsDevelopment }} +{{- end }} diff --git a/layouts/_partials/header.html b/layouts/_partials/header.html new file mode 100644 index 0000000..4aee590 --- /dev/null +++ b/layouts/_partials/header.html @@ -0,0 +1,22 @@ + + {{partial "icon.html" (dict "icon" "close-large-fill" "label" (T `close_menu`))}} + + + diff --git a/themes/bcarlin/layouts/_partials/icon.html b/layouts/_partials/icon.html similarity index 100% rename from themes/bcarlin/layouts/_partials/icon.html rename to layouts/_partials/icon.html diff --git a/themes/bcarlin/layouts/_partials/menu.html b/layouts/_partials/menu.html similarity index 100% rename from themes/bcarlin/layouts/_partials/menu.html rename to layouts/_partials/menu.html diff --git a/layouts/_partials/tags.html b/layouts/_partials/tags.html new file mode 100644 index 0000000..d0ac4e6 --- /dev/null +++ b/layouts/_partials/tags.html @@ -0,0 +1,9 @@ +{{- with $terms := .GetTerms "tags" }} +

+ {{ partial "icon.html" (dict "icon" "price-tag-3-line" "label" (T "tags")) }} + {{- range $idx, $it := $terms }} + + {{- if ne $idx (sub $terms.Len 1) }}{{ end }} + {{- end }} +

+{{- end }} diff --git a/layouts/baseof.html b/layouts/baseof.html new file mode 100644 index 0000000..e1c7971 --- /dev/null +++ b/layouts/baseof.html @@ -0,0 +1,54 @@ + + + + {{ partial "head.html" . }} + + + + +
+ {{- if .IsTranslated }} + {{- range .Translations }} + + {{- end }} + {{- end }} + + {{ block "main" . }}{{ end }} +
+
+ {{ partial "footer.html" . }} +
+ + diff --git a/themes/bcarlin/layouts/blog/list-item.html b/layouts/blog/list-item.html similarity index 86% rename from themes/bcarlin/layouts/blog/list-item.html rename to layouts/blog/list-item.html index 347a6e5..d79efbf 100644 --- a/themes/bcarlin/layouts/blog/list-item.html +++ b/layouts/blog/list-item.html @@ -1,5 +1,5 @@ {{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }} -{{ $dateHuman := .Date | time.Format "2006-01-02" }} +{{ $dateHuman := .Date | time.Format ":date_short" }}

{{ .LinkTitle }} diff --git a/themes/bcarlin/layouts/blog/list.html b/layouts/blog/list.html similarity index 100% rename from themes/bcarlin/layouts/blog/list.html rename to layouts/blog/list.html diff --git a/themes/bcarlin/layouts/blog/single.html b/layouts/blog/single.html similarity index 52% rename from themes/bcarlin/layouts/blog/single.html rename to layouts/blog/single.html index f79853a..e7933c4 100644 --- a/themes/bcarlin/layouts/blog/single.html +++ b/layouts/blog/single.html @@ -1,15 +1,24 @@ {{ define "main" }} {{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }} - {{ $dateHuman := .Date | time.Format "2006-01-02" }} + {{ $dateHuman := .Date | time.Format ":date_short" }}

-

{{ .Title }}

+ {{ with .GetTerms "categories" }} +

+ {{ range . }} + + {{ break }} + {{ end }} +

+ {{ end }} +

+ {{ .Title }} +

diff --git a/themes/bcarlin/layouts/home.html b/layouts/home.html similarity index 86% rename from themes/bcarlin/layouts/home.html rename to layouts/home.html index 4438ed1..ecb4dd2 100644 --- a/themes/bcarlin/layouts/home.html +++ b/layouts/home.html @@ -7,7 +7,7 @@ {{ .Content }}
-

Recent posts

+

{{ T "recent_posts" }}

{{- $posts := where .Site.RegularPages "Section" "blog" }} {{- range first 5 $posts }} {{ .Render "list-item" }} diff --git a/themes/bcarlin/layouts/page.html b/layouts/page.html similarity index 73% rename from themes/bcarlin/layouts/page.html rename to layouts/page.html index 47c0ed3..c2c4de4 100644 --- a/themes/bcarlin/layouts/page.html +++ b/layouts/page.html @@ -1,11 +1,11 @@ {{ define "main" }} {{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }} - {{ $dateHuman := .Date | time.Format "2006-01-02" }} + {{ $dateHuman := .Date | time.Format ":date_short" }}

{{ .Title }}

- Posted on + {{ T "posted" }}

@@ -13,6 +13,5 @@
{{ .Content }}
- {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
{{ end }} diff --git a/themes/bcarlin/layouts/section.html b/layouts/section.html similarity index 100% rename from themes/bcarlin/layouts/section.html rename to layouts/section.html diff --git a/themes/bcarlin/layouts/section.rss.xml b/layouts/section.rss.xml similarity index 100% rename from themes/bcarlin/layouts/section.rss.xml rename to layouts/section.rss.xml diff --git a/layouts/shortcodes/note.html b/layouts/shortcodes/note.html new file mode 100644 index 0000000..cfa1180 --- /dev/null +++ b/layouts/shortcodes/note.html @@ -0,0 +1,7 @@ + diff --git a/layouts/shortcodes/warning.html b/layouts/shortcodes/warning.html new file mode 100644 index 0000000..682a5b3 --- /dev/null +++ b/layouts/shortcodes/warning.html @@ -0,0 +1,7 @@ + diff --git a/themes/bcarlin/layouts/taxonomy.html b/layouts/taxonomy.html similarity index 100% rename from themes/bcarlin/layouts/taxonomy.html rename to layouts/taxonomy.html diff --git a/themes/bcarlin/layouts/term.html b/layouts/term.html similarity index 82% rename from themes/bcarlin/layouts/term.html rename to layouts/term.html index e237082..70b4799 100644 --- a/themes/bcarlin/layouts/term.html +++ b/layouts/term.html @@ -6,7 +6,7 @@ {{ .Content }} - {{ range .Pages.ByDate }} + {{ range .Pages.ByPublishDate.Reverse }} {{ .Render "list-item" }} {{ end }}
diff --git a/themes/bcarlin/static/apple-touch-icon.png b/static/apple-touch-icon.png similarity index 100% rename from themes/bcarlin/static/apple-touch-icon.png rename to static/apple-touch-icon.png diff --git a/themes/bcarlin/static/favicon-96x96.png b/static/favicon-96x96.png similarity index 100% rename from themes/bcarlin/static/favicon-96x96.png rename to static/favicon-96x96.png diff --git a/themes/bcarlin/static/favicon.ico b/static/favicon.ico similarity index 100% rename from themes/bcarlin/static/favicon.ico rename to static/favicon.ico diff --git a/themes/bcarlin/static/favicon.svg b/static/favicon.svg similarity index 100% rename from themes/bcarlin/static/favicon.svg rename to static/favicon.svg diff --git a/themes/bcarlin/static/site.webmanifest b/static/site.webmanifest similarity index 100% rename from themes/bcarlin/static/site.webmanifest rename to static/site.webmanifest diff --git a/themes/bcarlin/static/static/css/syntax-dark.css b/static/static/css/syntax-dark.css similarity index 100% rename from themes/bcarlin/static/static/css/syntax-dark.css rename to static/static/css/syntax-dark.css diff --git a/themes/bcarlin/static/static/css/syntax-light.css b/static/static/css/syntax-light.css similarity index 100% rename from themes/bcarlin/static/static/css/syntax-light.css rename to static/static/css/syntax-light.css diff --git a/themes/bcarlin/static/static/favicon.ico b/static/static/favicon.ico similarity index 100% rename from themes/bcarlin/static/static/favicon.ico rename to static/static/favicon.ico diff --git a/themes/bcarlin/static/static/fonts/GentiumPlus-Bold.woff2 b/static/static/fonts/GentiumPlus-Bold.woff2 similarity index 100% rename from themes/bcarlin/static/static/fonts/GentiumPlus-Bold.woff2 rename to static/static/fonts/GentiumPlus-Bold.woff2 diff --git a/themes/bcarlin/static/static/fonts/GentiumPlus-BoldItalic.woff2 b/static/static/fonts/GentiumPlus-BoldItalic.woff2 similarity index 100% rename from themes/bcarlin/static/static/fonts/GentiumPlus-BoldItalic.woff2 rename to static/static/fonts/GentiumPlus-BoldItalic.woff2 diff --git a/themes/bcarlin/static/static/fonts/GentiumPlus-Italic.woff2 b/static/static/fonts/GentiumPlus-Italic.woff2 similarity index 100% rename from themes/bcarlin/static/static/fonts/GentiumPlus-Italic.woff2 rename to static/static/fonts/GentiumPlus-Italic.woff2 diff --git a/themes/bcarlin/static/static/fonts/GentiumPlus-Regular.woff2 b/static/static/fonts/GentiumPlus-Regular.woff2 similarity index 100% rename from themes/bcarlin/static/static/fonts/GentiumPlus-Regular.woff2 rename to static/static/fonts/GentiumPlus-Regular.woff2 diff --git a/themes/bcarlin/static/static/fonts/NunitoSans-Italic.woff2 b/static/static/fonts/NunitoSans-Italic.woff2 similarity index 100% rename from themes/bcarlin/static/static/fonts/NunitoSans-Italic.woff2 rename to static/static/fonts/NunitoSans-Italic.woff2 diff --git a/themes/bcarlin/static/static/fonts/NunitoSans.woff2 b/static/static/fonts/NunitoSans.woff2 similarity index 100% rename from themes/bcarlin/static/static/fonts/NunitoSans.woff2 rename to static/static/fonts/NunitoSans.woff2 diff --git a/themes/bcarlin/static/static/fonts/remixicon.woff2 b/static/static/fonts/remixicon.woff2 similarity index 100% rename from themes/bcarlin/static/static/fonts/remixicon.woff2 rename to static/static/fonts/remixicon.woff2 diff --git a/themes/bcarlin/static/static/logo.png b/static/static/logo.png similarity index 100% rename from themes/bcarlin/static/static/logo.png rename to static/static/logo.png diff --git a/themes/bcarlin/static/static/logo.svg b/static/static/logo.svg similarity index 100% rename from themes/bcarlin/static/static/logo.svg rename to static/static/logo.svg diff --git a/themes/bcarlin/static/web-app-manifest-192x192.png b/static/web-app-manifest-192x192.png similarity index 100% rename from themes/bcarlin/static/web-app-manifest-192x192.png rename to static/web-app-manifest-192x192.png diff --git a/themes/bcarlin/static/web-app-manifest-512x512.png b/static/web-app-manifest-512x512.png similarity index 100% rename from themes/bcarlin/static/web-app-manifest-512x512.png rename to static/web-app-manifest-512x512.png diff --git a/themes/bcarlin/archetypes/default.md b/themes/bcarlin/archetypes/default.md deleted file mode 100644 index 25b6752..0000000 --- a/themes/bcarlin/archetypes/default.md +++ /dev/null @@ -1,5 +0,0 @@ -+++ -date = '{{ .Date }}' -draft = true -title = '{{ replace .File.ContentBaseName "-" " " | title }}' -+++ diff --git a/themes/bcarlin/content/_index.md b/themes/bcarlin/content/_index.md deleted file mode 100644 index 652623b..0000000 --- a/themes/bcarlin/content/_index.md +++ /dev/null @@ -1,9 +0,0 @@ -+++ -title = 'Home' -date = 2023-01-01T08:00:00-07:00 -draft = false -+++ - -Laborum voluptate pariatur ex culpa magna nostrud est incididunt fugiat -pariatur do dolor ipsum enim. Consequat tempor do dolor eu. Non id id anim anim -excepteur excepteur pariatur nostrud qui irure ullamco. diff --git a/themes/bcarlin/hugo.toml b/themes/bcarlin/hugo.toml deleted file mode 100644 index 5c26950..0000000 --- a/themes/bcarlin/hugo.toml +++ /dev/null @@ -1,24 +0,0 @@ -baseURL = 'https://example.org/' -languageCode = 'en-US' -title = 'My New Hugo Site' - -[menus] - [[menus.main]] - name = 'Home' - pageRef = '/' - weight = 10 - - [[menus.main]] - name = 'Posts' - pageRef = '/posts' - weight = 20 - - [[menus.main]] - name = 'Tags' - pageRef = '/tags' - weight = 30 - -[module] - [module.hugoVersion] - extended = false - min = '0.146.0' diff --git a/themes/bcarlin/layouts/_partials/header.html b/themes/bcarlin/layouts/_partials/header.html deleted file mode 100644 index 59cbaca..0000000 --- a/themes/bcarlin/layouts/_partials/header.html +++ /dev/null @@ -1,20 +0,0 @@ - - {{partial "icon.html" (dict "icon" "close-large-fill" "label" "Close the menu")}} - - - diff --git a/themes/bcarlin/layouts/_partials/tags.html b/themes/bcarlin/layouts/_partials/tags.html deleted file mode 100644 index e83f198..0000000 --- a/themes/bcarlin/layouts/_partials/tags.html +++ /dev/null @@ -1,10 +0,0 @@ -{{- with .GetTerms "tags" }} -
- {{ partial "icon.html" (dict "icon" "hashtag" "label" "Tags") }} - -
-{{- end }} diff --git a/themes/bcarlin/layouts/baseof.html b/themes/bcarlin/layouts/baseof.html deleted file mode 100644 index 4133096..0000000 --- a/themes/bcarlin/layouts/baseof.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - {{ partial "head.html" . }} - - - - -
- {{ block "main" . }}{{ end }} -
-
- {{ partial "footer.html" . }} -
- - diff --git a/themes/bcarlin/layouts/shortcodes/note.html b/themes/bcarlin/layouts/shortcodes/note.html deleted file mode 100644 index c6d7f53..0000000 --- a/themes/bcarlin/layouts/shortcodes/note.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/themes/bcarlin/layouts/shortcodes/warning.html b/themes/bcarlin/layouts/shortcodes/warning.html deleted file mode 100644 index c9059c4..0000000 --- a/themes/bcarlin/layouts/shortcodes/warning.html +++ /dev/null @@ -1,4 +0,0 @@ -