--- title: DĂ©ploiment du Blog avec Obsidian, Hugo et Gitea Actions date: 2025-05-02 draft: false tags: - obsidian - hugo - gitea categories: - homelab --- ## 💡 Intro J'ai toujours voulu partager mes expĂ©riences pour donner des idĂ©es aux autres ou les aider dans leurs projets. Je suis constamment en train de bidouiller dans mon lab, testant de nouveaux outils et workflows. PlutĂŽt que de conserver toutes ces expĂ©riences dans des notes privĂ©es, j'ai dĂ©cidĂ© de crĂ©er un blog oĂč je peux les documenter et les publier facilement. Je souhaitais que l'ensemble du processus soit automatisĂ©, self-hosted et intĂ©grĂ© aux outils que j'utilise dĂ©jĂ . --- ## 🔧 Outils ### Obsidian J'utilisais auparavant [Notion](https://www.notion.com), mais il y a quelques mois, je suis passĂ© Ă  [Obsidian](https://obsidian.md/). C'est une application de prise de notes basĂ©e sur Markdown qui stocke tout localement, ce qui me donne plus de flexibilitĂ© et de contrĂŽle. Pour synchroniser mes notes entre mes appareils, j'utilise le [plugin Git Obsidian](https://github.com/denolehov/obsidian-git), qui enregistre les modifications dans un dĂ©pĂŽt Git hĂ©bergĂ© sur mon instance Gitea self-hosted. Cette configuration permet non seulement de sauvegarder toutes mes notes avec leurs versions, mais aussi ouvre la porte Ă  l'automatisation. ### Gitea [Gitea](https://gitea.io/) est un service Git self-hosted similaire Ă  GitHub, mais lĂ©ger et facile Ă  maintenir. J'y hĂ©berge mes dĂ©pĂŽts personnels, notamment mon vault Obsidian et mon blog. Gitea prend dĂ©sormais en charge [Gitea Actions](https://docs.gitea.com/usage/actions/overview), un mĂ©canisme de pipeline CI/CD compatible avec la syntaxe GitHub Actions. Pour exĂ©cuter ces workflows, j'ai installĂ© un [Gitea runner](https://gitea.com/gitea/act_runner) sur mon serveur, ce qui me permet de crĂ©er un workflow automatisĂ© dĂ©clenchĂ© lorsque je mets Ă  jour le contenu de mes notes, puis de reconstruire et dĂ©ployer mon blog. ### Hugo [Hugo](https://gohugo.io/) est un gĂ©nĂ©rateur de sites statiques rapide et flexible, Ă©crit en Go. Il est idĂ©al pour gĂ©nĂ©rer du contenu Ă  partir de fichiers Markdown. Hugo est hautement personnalisable, prend en charge les thĂšmes et peut gĂ©nĂ©rer un site web complet en quelques secondes. Il est idĂ©al pour un blog basĂ© sur des notes Obsidian et fonctionne parfaitement dans les pipelines CI/CD grĂące Ă  sa rapiditĂ© et sa simplicitĂ©. --- ## 🔁 Workflow L'idĂ©e est simple : 1. J'Ă©cris le contenu de mon blog dans mon vault Obsidian, sous un dossier `Blog`. 2. Une fois le fichier modifiĂ©, le plugin Git Obsidian effectue automatiquement les commits et les poussent vers le dĂ©pĂŽt Gitea. 3. Lorsque Gitea reçoit ce push, une premiĂšre Gitea Action est dĂ©clenchĂ©e. 4. La premiĂšre action synchronise le contenu du blog mis Ă  jour avec un autre dĂ©pĂŽt [Git distinct](https://git.vezpi.me/Vezpi/blog) qui hĂ©berge le contenu. 5. Dans ce dĂ©pĂŽt, une autre Gitea Action est dĂ©clenchĂ©e. 6. La deuxiĂšme Gitea Action gĂ©nĂšre les pages web statiques tout en mettant Ă  jour Hugo si nĂ©cessaire. 7. Le blog est maintenant mis Ă  jour (celui que vous lisez). De cette façon, je n'ai plus besoin de copier manuellement de fichiers ni de dĂ©clencher de dĂ©ploiements. Tout se dĂ©roule comme prĂ©vu, de l'Ă©criture de Markdown dans Obsidian au dĂ©ploiement complet du site web. --- ## ⚙ ImplĂ©mentation ### Étape 1 : Configuration du vault Obsidian Dans mon vault Obsidian, j'ai créé un dossier `Blog` contenant mes articles de blog en Markdown. Chaque article inclut les pages de garde Hugo (titre, date, brouillon, etc.). Le plugin Git est configurĂ© pour valider et pousser automatiquement les modifications apportĂ©es au dĂ©pĂŽt Gitea. ### Étape 2 : Lancer Gitea Runner Le vault Obsidian est un dĂ©pĂŽt Git privĂ© self-hosted dans Gitea. J'utilise Docker Compose pour gĂ©rer cette instance. Pour activer les Gitea Actions, j'ai ajoutĂ© Gitea Runner Ă  la stack. ```yaml runner: image: gitea/act_runner:latest container_name: gitea_runner restart: on-failure environment: - GITEA_INSTANCE_URL=https://git.vezpi.me - GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_REGISTRATION_TOKEN}$ - GITEA_RUNNER_NAME=self-hosted - GITEA_RUNNER_LABELS=ubuntu:docker://node:lts,alpine:docker://node:lts-alpine - CONFIG_FILE=/data/config.yml volumes: - /var/run/docker.sock:/var/run/docker.sock - /appli/data/gitea/runner:/data - /appli:/appli networks: - backend depends_on: - server ``` Le fichier `config.yml` contient uniquement le volume autorisĂ© Ă  monter dans les conteneurs ```yaml container: valid_volumes: - /appli* ``` Le runner apparaĂźt dans `Administration Area`, sous `Actions`>`Runners`. Pour obtenir le token d'enrĂŽlement , on clique sur le bouton `Create new Runner` ![Pasted_image_20250502230954.png](img/Pasted_image_20250502230954.png) ### Étape 3 : Configurer les Gitea Actions pour le dĂ©pĂŽt Obsidian J'ai d'abord activĂ© les Gitea Actions. Celles-ci sont dĂ©sactivĂ©es par dĂ©faut. Cochez la case `Enable Repository Actions` dans les paramĂštres de ce dĂ©pĂŽt. J'ai créé un nouveau PAT (Personal Access Token) avec autorisation RW sur les dĂ©pĂŽts. ![Pasted_image_20250501235521.png](img/Pasted_image_20250501235521.png) J'ai ajoutĂ© le token comme secret `REPO_TOKEN` dans le dĂ©pĂŽt. ![Pasted_image_20250501235427.png](img/Pasted_image_20250501235427.png) J'ai dĂ» crĂ©er le workflow qui lancera un conteneur et effectuera les opĂ©rations suivantes : 1. Lorsque je crĂ©e/met Ă  jour des fichiers du dossier `Blog` 2. Checkout le dĂ©pĂŽt actuel (vault Obsidian) 3. Clone le dĂ©pĂŽt du blog 4. TransfĂ©rer le contenu du blog depuis Obsidian 5. Commit les modifications dans le dĂ©pĂŽt du blog **sync_blog.yml** ```yaml name: Synchronize content with the blog repo on: push: paths: - 'Blog/**' jobs: Sync: runs-on: ubuntu steps: - name: Install prerequisites run: apt update && apt install -y rsync - name: Check out repository uses: actions/checkout@v4 - name: Clone the blog repository run: git clone https://${{ secrets.REPO_TOKEN }}@git.vezpi.me/Vezpi/blog.git - name: Transfer blog content from Obsidian run: | echo "Copy Markdown files" rsync -av --delete Blog/ blog/content # Gather all used images from markdown files used_images=$(grep -rhoE '^!\[\[.*\]\]' blog/content | sed -E 's/!\[\[(.*)\]\]/\1/' | sort -u) # Create the target image folder mkdir -p blog/static/img # Loop over each used image" while IFS= read -r image; do # Loop through all .md files and replace image links grep -rl "$image" blog/content/* | while IFS= read -r md_file; do sed -i "s|\!\[\[$image\]\]|\!\[${image// /_}\](img/${image// /_})|g" "$md_file" done echo "Copy the image ${image// /_} to the static folder" cp "Images/$image" "blog/static/img/${image// /_}" done <<< "$used_images" - name: Commit the change to the blog repository run: | cd blog git config --global user.name "Gitea Actions" git config --global user.email "actions@local" git config --global --add safe.directory /appli/data/blog git add . git commit -m "Auto-update blog content from Obsidian: $(date '+%F %T')" || echo "Nothing to commit" git push -u origin main ``` Obsidian utilise des liens de type wiki pour les images, comme `![nom_image.png](img/nom_image.png)`, ce qui n'est pas compatible avec Hugo par dĂ©faut. Voici comment j'ai automatisĂ© une solution de contournement dans un workflow Gitea Actions : - Je trouve toutes les rĂ©fĂ©rences d'images utilisĂ©es dans des fichiers `.md`. - Pour chaque image rĂ©fĂ©rencĂ©e, je mets Ă  jour le lien dans les fichiers `.md` correspondants, comme `![nom_image](img/nom_image.png)`. - Je copie ensuite ces images utilisĂ©es dans le rĂ©pertoire statique du blog en remplaçant les espaces par des underscores. ### Étape 4 : Actions Gitea pour le dĂ©pĂŽt du blog Le dĂ©pĂŽt du blog contient l'intĂ©gralitĂ© du site Hugo, y compris le contenu synchronisĂ© et le thĂšme. Son workflow : 1. Checkout du dĂ©pĂŽt du blog 2. VĂ©rification de la mise Ă  jour d'Hugo. Si disponible, la derniĂšre version est tĂ©lĂ©chargĂ©e. 3. GĂ©nĂ©ration du site web statique avec Hugo. **deploy_blog.yml** ```yaml name: Deploy on: [push] jobs: Deploy: runs-on: ubuntu env: BLOG_FOLDER: /blog container: volumes: - /appli/data/blog:/blog steps: - name: Check out repository run: | cd ${BLOG_FOLDER} git config --global user.name "Gitea Actions" git config --global user.email "actions@local" git config --global --add safe.directory ${BLOG_FOLDER} git submodule update --init --recursive git fetch origin git reset --hard origin/main - name: Get current Hugo version run: | current_version=$(${BLOG_FOLDER}/hugo version | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+') echo "current_version=$current_version" | tee -a $GITEA_ENV - name: Verify latest Hugo version run: | latest_version=$(curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest | grep -oP '"tag_name": "\K[^"]+') echo "latest_version=$latest_version" | tee -a $GITEA_ENV - name: Download latest Hugo version if: env.current_version != env.latest_version run: | rm -f ${BLOG_FOLDER}/{LICENSE,README.md,hugo} curl -L https://github.com/gohugoio/hugo/releases/download/$latest_version/hugo_extended_${latest_version#v}_Linux-64bit.tar.gz -o hugo.tar.gz tar -xzvf hugo.tar.gz -C ${BLOG_FOLDER}/ - name: Generate the static files with Hugo run: | rm -f ${BLOG_FOLDER}/content/posts/template.md rm -rf ${BLOG_FOLDER}/private/* ${BLOG_FOLDER}/public/* ${BLOG_FOLDER}/hugo -D -b https://blog-dev.vezpi.me -s ${BLOG_FOLDER} -d ${BLOG_FOLDER}/private ${BLOG_FOLDER}/hugo -s ${BLOG_FOLDER} -d ${BLOG_FOLDER}/public chown 1000:1000 -R ${BLOG_FOLDER} ``` --- ## 🚀 Results This workflow allows me to focus on what matters most: writing and refining my content. By automating the publishing pipeline — from syncing my Obsidian notes to building the blog with Hugo — I no longer need to worry about manually managing content in a CMS. Every note I draft can evolve naturally into a clear, structured article, and the technical workflow fades into the background. It’s a simple yet powerful way to turn personal knowledge into shareable documentation.