GitHub avatar

Fox's Blog

✨ AI Generated Article

How This Blog Works

The internals of the blog: React, Vite, Markdown, the CI/CD

How Does This Blog Work?

Ever wondered how this blog works under the hood? In this article, I'll walk you through the whole app architecture, from the tech stack to the article-writing process. And yes, I'll even show you how I write my articles from VS Code!

The Tech Stack

This blog is built with modern web technologies:

  • React 19: for the user interface
  • TypeScript: for typed, more reliable code
  • Vite: as an ultra-fast build tool
  • React Router v7: for navigation between pages
  • react-markdown: for turning Markdown into HTML
  • rehype-raw + rehype-sanitize: for allowing raw HTML in Markdown safely

Project Structure

Here's what the project tree looks like:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← CI/CD pipeline
├── public/
│   ├── home.md                     ← Homepage content
│   ├── portfolio.md                ← Portfolio content
│   └── articles/
│       ├── index.json              ← List of all articles
│       ├── hello-world.md          ← An article
│       ├── how-this-blog-works.md  ← This article!
│       └── /articles/assets/                 ← Article images
├── src/
│   ├── main.tsx                    ← React entry point
│   ├── App.tsx                     ← Main router
│   ├── components/
│   │   ├── Header.tsx              ← Navigation bar
│   │   └── Footer.tsx              ← Footer
│   └── pages/
│       ├── Home.tsx                ← Homepage
│       ├── BlogList.tsx            ← Article list
│       ├── Article.tsx             ← Article reader
│       ├── Portfolio.tsx           ← Portfolio page
│       └── NotFound.tsx            ← 404 page
└── vite.config.ts                  ← Vite config

The core idea is simple: content is separated from code. Pages are written in Markdown in the public/ folder, and the React code in src/ takes care of displaying them. I care about that separation: it keeps the content easy to reread and the code easy to evolve.

The Routing System

The App.tsx file defines all the app routes with React Router:

Route Page Description
/ Home Homepage, loads home.md
/blog BlogList List of all articles
/blog/:slug Article One article, loads articles/{slug}.md
/portfolio Portfolio Portfolio page, loads portfolio.md
* NotFound 404 page for unknown URLs

Each page has a well-defined role: it fetches a Markdown file, turns it into HTML with react-markdown, and displays it on screen.

How Does an Article Work?

This is my favorite part! Here's an article's lifecycle:

1. The index.json File

All articles are referenced in public/articles/index.json. Each entry holds the article's metadata:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug: the unique identifier, used in the URL (/blog/hello-world)
  • title: the title shown in the list
  • description: a short summary
  • date: the publication date

2. The Markdown File

The article's content is a plain .md file in public/articles/. The filename matches the slug defined in index.json.

You can put anything you want in it: titles, lists, images, tables, and even raw HTML thanks to rehype-raw!

3. React-Side Rendering

When you visit /blog/hello-world, here's what happens:

  1. React Router grabs the slug parameter from the URL
  2. The Article.tsx component loads /articles/hello-world.md
  3. The Markdown is turned into HTML by react-markdown
  4. Links to /articles/assets/ are automatically rewritten to /articles//articles/assets/
  5. In parallel, metadata is loaded from index.json to show the date and description

Simple as that!

The Homepage and Portfolio

The Home and Portfolio pages work exactly the same way: they load a Markdown file (home.md or portfolio.md) and render it as HTML.

The special part is they use a custom sanitization schema that allows class and style attributes on every HTML element. That lets me write styled HTML directly in Markdown, image galleries for example.

The Header is pinned at the top of the page with position: fixed. It contains:

  • My GitHub avatar (loaded directly from github.com/fox3000foxy.png)
  • The blog title
  • The nav links: Home, Blog, Portfolio

The Footer is minimalist: just a copyright with the dynamically computed current year.

The Dark Theme

The site stays in dark mode permanently. I made that choice deliberately to keep reading comfortable in the evening: color-scheme: dark is set in the global styles, with a black #000 background and white #fff text. Links are blue (#64b5f6) and turn green on hover (#81c784).

How I Write an Article

Let's get practical! Here's my workflow for writing a new article:

Step 1: Create the Markdown File

I open VS Code and create a new .md file in public/articles/:

Step 2: Write the Content

I write the article's content directly in Markdown. VS Code has an excellent built-in Markdown preview:

For images, I put them in public/articles//articles/assets/ and reference them with standard Markdown syntax:

![description](/articles/assets/my-image.png)

The Article.tsx component automatically rewrites the /articles/assets/ path to /articles//articles/assets/ so images display correctly.

Step 3: Register the Article in index.json

Once the article is done, I add it in public/articles/index.json so it shows up in the blog list:

Step 4: Test Locally

I start the Vite dev server:

pnpm dev

Vite boots in milliseconds and I can see my article live on localhost:5173. That speed lets me check every change almost in real time:

Step 5: Publish

A simple git push is enough! The CI/CD pipeline takes care of the rest automatically.

The CI/CD Deploy Pipeline

I set up a complete GitHub Actions pipeline that automates lint, build, and site deploy on every push to main. Let's look at it in detail.

The workflow lives in .github/workflows/deploy.yml and is split into two jobs: build and deploy.

Triggers

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

The pipeline runs on every push to main and every pull request targeting main. PRs are checked (lint + build) before merging, but only pushes to main trigger a deploy.

Job 1: Build

The build job runs on ubuntu-latest and follows these steps:

  1. Checkout: Clones the repo with full history (fetch-depth: 0)
  2. Setup pnpm: Installs the latest pnpm with pnpm/action-setup@v4
  3. Setup Node.js 20: Configures Node with pnpm cache enabled for faster installs
  4. Install dependencies: Runs pnpm install --frozen-lockfile to guarantee reproducible builds (no lockfile changes allowed)
  5. Lint: Runs pnpm run lint (ESLint) to check code quality before the build
  6. Build: Runs pnpm run build, which first type-checks TypeScript (tsc -b) then bundles everything with Vite
  7. Upload artifact: Uploads the dist/ folder as a build artifact for the deploy job

If any step fails (lint, type, or build error), the whole pipeline stops and nothing deploys. That guard protects me from broken code in production and I rely on it on every push.

Job 2: Deploy

The deploy job only runs if:

  • The build job succeeded (needs: build)
  • The event is a push (not a PR)
  • The branch is main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

It then proceeds:

  1. Download the build artifact: Fetches the dist/ folder produced by the build job
  2. Configure GitHub Pages: Sets up the Pages environment
  3. Upload to Pages: Prepares the dist/ folder for GitHub Pages
  4. Deploy: Publishes the site with actions/deploy-pages@v4

The Full Picture

Here's what happens from writing to deploying:

Write the article in VS Code
          ↓
     git add & commit
          ↓
        git push
          ↓
    GitHub Actions triggers
          ↓
    ┌─────────────────┐
    │   BUILD JOB     │
    │  1. Checkout    │
    │  2. Setup pnpm  │
    │  3. Setup Node  │
    │  4. Install     │
    │  5. Lint ✓      │
    │  6. Build ✓     │
    │  7. Upload dist │
    └────────┬────────┘
             ↓
    ┌─────────────────┐
    │  DEPLOY JOB     │
    │  1. Download    │
    │  2. Configure   │
    │  3. Upload      │
    │  4. Deploy 🚀   │
    └─────────────────┘
             ↓
      Live on GitHub Pages!

The whole process takes about a minute between push and going live. I publish with a simple git push, everything stays automatic with no FTP or SSH.

The Production Build

Under the hood, the pnpm build command runs:

  1. tsc -b: Checks TypeScript types
  2. vite build: Bundles and optimizes all the code

Vite produces minified, optimized files with automatic code-splitting. The result is an ultra-fast static site.

Why This Architecture?

I could have used a CMS, a static site generator like Hugo or Jekyll, or even Next.js. But here's why I picked this approach:

  • Simplicity: Write in Markdown, push to GitHub, it's live
  • Full control: No dependency on a CMS or database
  • Performance: Vite + React = fast loading
  • Flexibility: I can mix Markdown and HTML however I want
  • Learning: It's a great project for mastering React and TypeScript
  • CI/CD: Quality checks and automated deploys with GitHub Actions

Conclusion

This blog is a simple but well-thought-out project: Markdown for content, React for rendering, Vite for performance, GitHub Actions for CI/CD, and GitHub Pages for hosting. It relies only on static files served efficiently, with an automated pipeline that guarantees quality on every push.

Thanks for reading, and see you in the next article! 🦊

✨ AI Generated Article

Comment fonctionne ce blog ?

Les coulisses du blog : React, Vite, Markdown, la pipeline CI/CD

Comment Fonctionne Ce Blog ?

Tu t'es déjà demandé comment ce blog fonctionne sous le capot ? Dans cet article, je vais te détailler toute l'architecture de l'application, de la stack technique jusqu'au processus de rédaction d'un article. Et oui, je vais même te montrer comment j'écris mes articles depuis VS Code !

La Stack Technique

Ce blog est construit avec des technologies web modernes :

  • React 19 : pour l'interface utilisateur
  • TypeScript : pour un code typé et plus fiable
  • Vite : comme outil de build ultra-rapide
  • React Router v7 : pour la navigation entre les pages
  • react-markdown : pour transformer le Markdown en HTML
  • rehype-raw + rehype-sanitize : pour autoriser du HTML brut dans le Markdown en toute sécurité

Structure du Projet

Voici à quoi ressemble l'arborescence du projet :

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Contenu de la page d'accueil
│   ├── portfolio.md                ← Contenu du portfolio
│   └── articles/
│       ├── index.json              ← Liste de tous les articles
│       ├── hello-world.md          ← Un article
│       ├── how-this-blog-works.md  ← Cet article !
│       └── /articles/assets/                 ← Images des articles
├── src/
│   ├── main.tsx                    ← Point d'entrée React
│   ├── App.tsx                     ← Routeur principal
│   ├── components/
│   │   ├── Header.tsx              ← Barre de navigation
│   │   └── Footer.tsx              ← Pied de page
│   └── pages/
│       ├── Home.tsx                ← Page d'accueil
│       ├── BlogList.tsx            ← Liste des articles
│       ├── Article.tsx             ← Lecteur d'article
│       ├── Portfolio.tsx           ← Page portfolio
│       └── NotFound.tsx            ← Page 404
└── vite.config.ts                  ← Configuration Vite

L'idée centrale est simple : le contenu est séparé du code. Les pages sont écrites en Markdown dans le dossier public/, et le code React dans src/ s'occupe de les afficher. Je tiens à cette séparation : elle rend le contenu facile à relire et le code facile à faire évoluer.

Le Système de Routage

Le fichier App.tsx définit toutes les routes de l'application avec React Router :

Route Page Description
/ Home Page d'accueil, charge home.md
/blog BlogList Liste de tous les articles
/blog/:slug Article Un article, charge articles/{slug}.md
/portfolio Portfolio Page portfolio, charge portfolio.md
* NotFound Page 404 pour les URLs inconnues

Chaque page a un rôle bien défini : elle récupère un fichier Markdown, le transforme en HTML avec react-markdown, et l'affiche à l'écran.

Comment Fonctionne un Article ?

C'est la partie que je préfère ! Voici le cycle de vie d'un article :

1. Le Fichier index.json

Tous les articles sont référencés dans public/articles/index.json. Chaque entrée contient les métadonnées de l'article :

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug : l'identifiant unique, utilisé dans l'URL (/blog/hello-world)
  • title : le titre affiché dans la liste
  • description : un court résumé
  • date : la date de publication

2. Le Fichier Markdown

Le contenu de l'article est un simple fichier .md dans public/articles/. Le nom du fichier correspond au slug défini dans index.json.

Tu peux y mettre ce que tu veux : titres, listes, images, tableaux, et même du HTML brut grâce à rehype-raw !

3. Le Rendu Côté React

Quand tu visites /blog/hello-world, voici ce qui se passe :

  1. React Router récupère le paramètre slug depuis l'URL
  2. Le composant Article.tsx charge /articles/hello-world.md
  3. Le Markdown est transformé en HTML par react-markdown
  4. Les liens vers /articles/assets/ sont automatiquement réécrits vers /articles//articles/assets/
  5. En parallèle, les métadonnées sont chargées depuis index.json pour afficher la date et la description

C'est aussi simple que ça !

La Page d'Accueil et le Portfolio

Les pages Accueil et Portfolio fonctionnent exactement de la même manière : elles chargent un fichier Markdown (home.md ou portfolio.md) et le rendent en HTML.

La particularité, c'est qu'elles utilisent un schéma de sanitization personnalisé qui autorise les attributs class et style sur tous les éléments HTML. Ça me permet d'écrire du HTML stylisé directement dans le Markdown, comme des galeries d'images par exemple.

Le Header est épinglé en haut de la page avec position: fixed. Il contient :

  • Mon avatar GitHub (chargé directement depuis github.com/fox3000foxy.png)
  • Le titre du blog
  • Les liens de navigation : Accueil, Blog, Portfolio

Le Footer est minimaliste : juste un copyright avec l'année courante calculée dynamiquement.

Le Thème Sombre

Le site reste en mode sombre en permanence. J'ai fait ce choix délibérément pour garder une lecture confortable le soir : color-scheme: dark est défini dans les styles globaux, avec un fond noir #000 et du texte blanc #fff. Les liens sont bleus (#64b5f6) et deviennent verts au survol (#81c784).

Comment J'Écris un Article

Passons à la pratique ! Voici mon workflow pour écrire un nouvel article :

Étape 1 : Créer le Fichier Markdown

J'ouvre VS Code et je crée un nouveau fichier .md dans public/articles/ :

Étape 2 : Écrire le Contenu

J'écris le contenu de l'article directement en Markdown. VS Code a un excellent aperçu Markdown intégré :

Pour les images, je les place dans public/articles//articles/assets/ et je les référence avec la syntaxe Markdown standard :

![description](/articles/assets/my-image.png)

Le composant Article.tsx réécrit automatiquement le chemin /articles/assets/ vers /articles//articles/assets/ pour que les images s'affichent correctement.

Étape 3 : Enregistrer l'Article dans index.json

Une fois l'article terminé, je l'ajoute dans public/articles/index.json pour qu'il apparaisse dans la liste du blog :

Étape 4 : Tester en Local

Je lance le serveur de développement Vite :

pnpm dev

Vite démarre en quelques millisecondes et je peux voir mon article en temps réel sur localhost:5173. Cette rapidité me permet de vérifier chaque modification presque instantanément :

Étape 5 : Publier

Un simple git push suffit ! Le pipeline CI/CD s'occupe du reste automatiquement.

Le Pipeline de Déploiement CI/CD

J'ai mis en place un pipeline GitHub Actions complet qui automatise le lint, le build et le déploiement du site à chaque push sur main. Voyons ça en détail.

Le workflow se trouve dans .github/workflows/deploy.yml et est divisé en deux jobs : build et deploy.

Déclencheurs

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Le pipeline s'exécute à chaque push sur main et à chaque pull request visant main. Les PRs sont donc vérifiées (lint + build) avant d'être mergées, mais seuls les pushes sur main déclenchent un déploiement.

Job 1 : Build

Le job de build tourne sur ubuntu-latest et suit ces étapes :

  1. Checkout : Clone le dépôt avec tout l'historique (fetch-depth: 0)
  2. Setup pnpm : Installe la dernière version de pnpm avec pnpm/action-setup@v4
  3. Setup Node.js 20 : Configure Node avec le cache pnpm activé pour des installations plus rapides
  4. Install dependencies : Exécute pnpm install --frozen-lockfile pour garantir des builds reproductibles (aucune modification du lockfile autorisée)
  5. Lint : Exécute pnpm run lint (ESLint) pour vérifier la qualité du code avant le build
  6. Build : Exécute pnpm run build, qui vérifie d'abord les types TypeScript (tsc -b) puis bundle le tout avec Vite
  7. Upload artifact : Téléverse le dossier dist/ comme artefact de build pour le job de déploiement

Si une étape échoue (erreur de lint, de type ou de build), tout le pipeline s'arrête et rien n'est déployé. Cette garde me protège du code cassé en production et je m'y fie à chaque push.

Job 2 : Deploy

Le job de déploiement ne s'exécute que si :

  • Le job de build a réussi (needs: build)
  • L'événement est un push (pas une PR)
  • La branche est main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Il procède ensuite :

  1. Télécharge l'artefact de build : Récupère le dossier dist/ produit par le job de build
  2. Configure GitHub Pages : Met en place l'environnement Pages
  3. Téléverse vers Pages : Prépare le dossier dist/ pour GitHub Pages
  4. Déploie : Publie le site avec actions/deploy-pages@v4

Le Tableau Complet

Voici ce qui se passe de l'écriture au déploiement :

Écrire l'article dans VS Code
          ↓
    git add & commit
          ↓
       git push
          ↓
   GitHub Actions se déclenche
          ↓
   ┌─────────────────┐
   │   BUILD JOB     │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ✓      │
   │  6. Build ✓     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy 🚀   │
   └─────────────────┘
            ↓
     En ligne sur GitHub Pages !

Le processus entier prend environ une minute entre le push et la mise en ligne. Je publie avec un simple git push, le tout reste automatique sans passer par FTP ni SSH.

Le Build de Production

Sous le capot, la commande pnpm build exécute :

  1. tsc -b : Vérifie les types TypeScript
  2. vite build : Bundle et optimise tout le code

Vite produit des fichiers minifiés et optimisés avec du code-splitting automatique. Le résultat est un site statique ultra-rapide.

Pourquoi Cette Architecture ?

J'aurais pu utiliser un CMS, un générateur de site statique comme Hugo ou Jekyll, ou même Next.js. Mais voici pourquoi j'ai choisi cette approche :

  • Simplicité : Écris en Markdown, push sur GitHub, c'est en ligne
  • Contrôle total : Aucune dépendance à un CMS ou une base de données
  • Performance : Vite + React = chargement rapide
  • Flexibilité : Je peux mélanger Markdown et HTML comme je veux
  • Apprentissage : C'est un super projet pour maîtriser React et TypeScript
  • CI/CD : Vérifications de qualité et déploiement automatisés avec GitHub Actions

Conclusion

Ce blog est un projet simple mais bien pensé : Markdown pour le contenu, React pour le rendu, Vite pour la performance, GitHub Actions pour le CI/CD, et GitHub Pages pour l'hébergement. Il repose uniquement sur des fichiers statiques servis efficacement, avec un pipeline automatisé qui garantit la qualité à chaque push.

Merci d'avoir lu, et à bientôt dans le prochain article ! 🦊

✨ AI Generated Article

这个博客是怎么运转的?

博客的幕后:React、Vite、Markdown、CI/CD 流水线和写作流程。

这个博客是怎么运转的?

你有没有好奇过这个博客在幕后是怎么工作的?在这篇文章里,我会把整个应用的架构拆开给你看,从技术栈到写一篇文章的完整流程。是的,我还会告诉你我是如何在 VS Code 里写文章的!

技术栈

这个博客用现代网页技术搭建:

  • React 19:用户界面
  • TypeScript:类型更可靠的代码
  • Vite:超快的构建工具
  • React Router v7:页面导航
  • react-markdown:把 Markdown 转成 HTML
  • rehype-raw + rehype-sanitize:在 Markdown 里安全地使用原生 HTML

项目结构

项目目录长这样:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← CI/CD 流水线
├── public/
│   ├── home.md                     ← 首页内容
│   ├── portfolio.md                ← 作品集内容
│   └── articles/
│       ├── index.json              ← 全部文章列表
│       ├── hello-world.md          ← 一篇文章
│       ├── how-this-blog-works.md  ← 就是这篇!
│       └── /articles/assets/                 ← 文章图片
├── src/
│   ├── main.tsx                    ← React 入口
│   ├── App.tsx                     ← 主路由
│   ├── components/
│   │   ├── Header.tsx              ← 导航栏
│   │   └── Footer.tsx              ← 页脚
│   └── pages/
│       ├── Home.tsx                ← 首页
│       ├── BlogList.tsx            ← 文章列表
│       ├── Article.tsx             ← 文章阅读器
│       ├── Portfolio.tsx           ← 作品集页
│       └── NotFound.tsx            ← 404 页
└── vite.config.ts                  ← Vite 配置

核心思路很简单:内容和代码分离。页面用 public/ 里的 Markdown 写,src/ 里的 React 代码负责展示。我坚持这种分离:内容容易重读,代码容易演进。

路由系统

App.tsx 用 React Router 定义了全部路由:

路由 页面 说明
/ Home 首页,加载 home.md
/blog BlogList 全部文章列表
/blog/:slug Article 单篇文章,加载 articles/{slug}.md
/portfolio Portfolio 作品集页,加载 portfolio.md
* NotFound 未知 URL 的 404 页

每个页面职责明确:取一个 Markdown 文件,用 react-markdown 转成 HTML,再显示出来。

一篇文章是怎么工作的?

这是我最喜欢的部分!下面是一篇文章的生命周期:

1. index.json 文件

所有文章都在 public/articles/index.json 里登记。每条记录是文章的元数据:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug:唯一标识,用在 URL 里(/blog/hello-world)
  • title:列表里显示的标题
  • description:一句话简介
  • date:发布日期

2. Markdown 文件

文章正文就是 public/articles/ 里的一个 .md 文件。文件名对应 index.json 里的 slug。

里面想写什么都行:标题、列表、图片、表格,甚至靠 rehype-raw 写原生 HTML!

3. React 端渲染

当你访问 /blog/hello-world 时,会发生这些事:

  1. React Router 从 URL 里取出 slug 参数
  2. Article.tsx 组件加载 /articles/hello-world.md
  3. Markdown 被 react-markdown 转成 HTML
  4. 指向 /articles/assets/ 的链接被自动重写为 /articles//articles/assets/,图片才能正常显示
  5. 同时从 index.json 加载元数据,显示日期和简介

就这么简单!

首页和作品集页

首页和作品集页的原理完全一样:加载一个 Markdown 文件(home.md 或 portfolio.md)再渲染成 HTML。

特别之处在于,它们用的消毒 schema 是定制的,允许所有 HTML 元素带 class 和 style 属性。这样我可以直接在 Markdown 里写带样式的 HTML,比如图片画廊。

页眉和页脚

页眉用 position: fixed 固定在顶部,包含:

  • 我的 GitHub 头像(直接从 github.com/fox3000foxy.png 加载)
  • 博客标题
  • 导航链接:首页、博客、作品集

页脚极简:只有一个用当前年份动态计算的版权行。

深色主题

全站永远是深色模式。我是故意这样选的,为了晚上阅读舒服:全局样式里写了 color-scheme: dark,背景纯黑 #000,文字纯白 #fff。链接是蓝色(#64b5f6),悬停变绿(#81c784)。

我怎么写文章

进入实战!下面是我写一篇新文章的工作流:

第 1 步:创建 Markdown 文件

打开 VS Code,在 public/articles/ 里新建一个 .md 文件:

第 2 步:写正文

直接用 Markdown 写正文。VS Code 自带的 Markdown 预览很好用:

图片放在 public/articles//articles/assets/ 里,用标准 Markdown 语法引用:

![description](/articles/assets/my-image.png)

Article.tsx 组件会自动把 /articles/assets/ 路径重写为 /articles//articles/assets/,图片就能正常显示。

第 3 步:在 index.json 里登记文章

文章写完后,把它加进 public/articles/index.json,它才会出现在博客列表里:

第 4 步:本地测试

启动 Vite 开发服务器:

pnpm dev

Vite 几毫秒就起来了,我在 localhost:5173 上实时看文章。改完立刻能看到,验证速度极快:

第 5 步:发布

一个 git push 就够了!剩下的全交给 CI/CD 流水线自动处理。

CI/CD 部署流水线

我搭了一条完整的 GitHub Actions 流水线,每次 push 到 main 就自动做 lint、构建和部署。来细看一下。

工作流在 .github/workflows/deploy.yml 里,分成两个 job:build 和 deploy。

触发条件

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

每次 push 到 main、每次指向 main 的** pull request** 都会触发流水线。PR 先被验证(lint + build)再合并,只有 push 到 main 才会真正部署。

Job 1:Build

构建 job 跑在 ubuntu-latest 上,步骤如下:

  1. Checkout:克隆仓库,带完整历史(fetch-depth: 0)
  2. Setup pnpm:用 pnpm/action-setup@v4 装最新版 pnpm
  3. Setup Node.js 20:配置 Node,并打开 pnpm 缓存让安装更快
  4. Install dependencies:跑 pnpm install --frozen-lockfile,保证构建可复现(不允许改 lockfile)
  5. Lint:跑 pnpm run lint(ESLint),构建前先查代码质量
  6. Build:跑 pnpm run build,先做 TypeScript 类型检查(tsc -b),再用 Vite 打包
  7. Upload artifact:把 dist/ 目录作为构建产物上传,供部署 job 用

任何一步失败(lint、类型或构建报错),整条流水线停下,什么都不部署。这道防线让我每次 push 都很安心。

Job 2:Deploy

部署 job 只在满足以下条件时跑:

  • 构建 job 成功(needs: build)
  • 事件是 push(不是 PR)
  • 分支是 main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

它接着做:

  1. 下载构建产物:取回构建 job 产出的 dist/ 目录
  2. 配置 GitHub Pages:准备 Pages 环境
  3. 上传到 Pages:把 dist/ 目录准备好给 GitHub Pages
  4. 部署:用 actions/deploy-pages@v4 发布站点

完整全景

从写作到上线是这样的:

在 VS Code 里写文章
          ↓
     git add & commit
          ↓
        git push
          ↓
    GitHub Actions 触发
          ↓
    ┌─────────────────┐
    │   BUILD JOB     │
    │  1. Checkout    │
    │  2. Setup pnpm  │
    │  3. Setup Node  │
    │  4. Install     │
    │  5. Lint ✓      │
    │  6. Build ✓     │
    │  7. Upload dist │
    └────────┬────────┘
             ↓
    ┌─────────────────┐
    │  DEPLOY JOB     │
    │  1. Download    │
    │  2. Configure   │
    │  3. Upload      │
    │  4. Deploy 🚀   │
    └─────────────────┘
             ↓
      在 GitHub Pages 上线!

从 push 到上线,整个过程大约一分钟。我只管 git push,剩下全自动,不用 FTP 也不用 SSH。

生产构建

底层 pnpm build 其实执行了:

  1. tsc -b:检查 TypeScript 类型
  2. vite build:打包并优化全部代码

Vite 产出压缩优化过的文件,还带自动代码分割。最终是一个超快的纯静态站。

为什么选这个架构?

我本可以用 CMS、Hugo/Jekyll 这类静态站生成器,甚至 Next.js。但我选这个方案的原因是:

  • 简单:用 Markdown 写,push 到 GitHub,就上线了
  • 完全可控:不依赖任何 CMS 或数据库
  • 性能:Vite + React = 加载飞快
  • 灵活:Markdown 和 HTML 想怎么混就怎么混
  • 学习:这是个掌握 React 和 TypeScript 的好项目
  • CI/CD:GitHub Actions 自动做质量检查和部署

结语

这个博客是个简单但用心的项目:Markdown 管内容,React 管渲染,Vite 管性能,GitHub Actions 管 CI/CD,GitHub Pages 管托管。全靠高效 served 的静态文件,加一条每次 push 自动保驾的流水线。

感谢阅读,下篇文章见!🦊

✨ AI Generated Article

このブログの仕組み

ブログの舞台裏:React、Vite、Markdown、CI/CDパイプライン、執筆フロー。

このブログの仕組み

このブログが内部でどう動いてるか気になったことはない?この記事では、技術スタックから記事の執筆プロセスまで、アプリケーションのアーキテクチャ全体を解説する。そう、VS Codeからどうやって記事を書いてるかまで見せるよ!

技術スタック

このブログはモダンなウェブ技術で作られてる。

  • React 19:ユーザーインターフェース用
  • TypeScript:型付きで信頼できるコード用
  • Vite:超高速ビルドツール
  • React Router v7:ページ間ナビゲーション用
  • react-markdown:MarkdownをHTMLに変換
  • rehype-raw + rehype-sanitize:安全に素のHTMLをMarkdown内で許可

プロジェクト構造

プロジェクトのツリーはこんな感じだ。

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Contenu de la page d'accueil
│   ├── portfolio.md                ← Contenu du portfolio
│   └── articles/
│       ├── index.json              ← Liste de tous les articles
│       ├── hello-world.md          ← Un article
│       ├── how-this-blog-works.md  ← Cet article !
│       └── /articles/assets/                 ← Images des articles
├── src/
│   ├── main.tsx                    ← Point d'entrée React
│   ├── App.tsx                     ← Routeur principal
│   ├── components/
│   │   ├── Header.tsx              ← Barre de navigation
│   │   └── Footer.tsx              ← Pied de page
│   └── pages/
│       ├── Home.tsx                ← Page d'accueil
│       ├── BlogList.tsx            ← Liste des articles
│       ├── Article.tsx             ← Lecteur d'article
│       ├── Portfolio.tsx           ← Page portfolio
│       └── NotFound.tsx            ← Page 404
└── vite.config.ts                  ← Configuration Vite

中心的な考えは単純だ。コンテンツはコードから分離されてる。ページはpublic/内のMarkdownで書かれて、src/内のReactコードが表示を担う。この分離にこだわってる。コンテンツは読み返しやすく、コードは進化させやすいからね。

ルーティングシステム

App.tsxファイルがReact Routerでアプリの全ルートを定義してる。

ルート ページ 説明
/ Home トップページ、home.mdを読む
/blog BlogList 全記事の一覧
/blog/:slug Article 1つの記事、articles/{slug}.mdを読む
/portfolio Portfolio ポートフォリオページ、portfolio.mdを読む
* NotFound 不明URL用404ページ

各ページの役割は明確だ。Markdownファイルを取り、react-markdownでHTMLに変換して、画面に表示する。

記事はどう動くの

いちばん好きな部分だよ!記事のライフサイクルはこうだ。

1. index.jsonファイル

全記事はpublic/articles/index.jsonに参照されてる。各エントリが記事のメタデータを持つ。

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug:一意ID、URLに使う(/blog/hello-world)
  • title:一覧に表示されるタイトル
  • description:短い要約
  • date:公開日

2. Markdownファイル

記事内容はpublic/articles/内の単なる.mdファイルだ。ファイル名はindex.jsonで定義したslugに対応する。

何でも入れられる。タイトル、リスト、画像、テーブル、果てはrehype-rawのおかげで素のHTMLまで!

3. React側のレンダー

/blog/hello-worldを開くとこうなる。

  1. React RouterがURLからslugパラメータを取る
  2. Article.tsxコンポーネントが/articles/hello-world.mdを読む
  3. Markdownはreact-markdownでHTMLに変換される
  4. /articles/assets/へのリンクは自動で/articles//articles/assets/に書き換えられる
  5. 並行して日付と説明表示用メタデータがindex.jsonから読まれる

ほんと単純だよ!

トップページとポートフォリオ

トップとポートフォリオのページはまったく同じ仕組みだ。Markdownファイル(home.mdかportfolio.md)を読んでHTMLにレンダーする。

特徴は、カスタム消毒スキーマを使って全HTML要素にclassとstyle属性を許可してることだ。画像ギャラリーみたいにスタイル付きHTMLをMarkdownに直接書ける。

ヘッダーとフッター

ヘッダーはposition: fixedでページ上部に固定されてる。内容はこうだ。

  • 私のGitHubアバター(github.com/fox3000foxy.pngから直接読み込み)
  • ブログタイトル
  • ナビリンク:トップ、ブログ、ポートフォリオ

フッターは最小限だ。動的計算の当年入りコピーライトだけだ。

ダークテーマ

サイトは常にダークモードのままだ。夜も快適に読めるよう意識的にこう選んだ。グローバルスタイルでcolor-scheme: darkを定義して、背景黒#000に文字白#fffだ。リンクは青(#64b5f6)でホバーで緑(#81c784)になる。

記事の書き方

実践に入ろう!新規記事を書く私のワークフローはこうだ。

ステップ1:Markdownファイルを作る

VS Codeを開いてpublic/articles/に新しい.mdファイルを作る。

ステップ2:内容を書く

記事内容を直接Markdownで書く。VS Codeには優秀な内蔵Markdownプレビューがある。

画像はpublic/articles//articles/assets/に置いて、標準Markdown記法で参照する。

![description](/articles/assets/my-image.png)

Article.tsxコンポーネントが画像表示用に/articles/assets/パスを自動で/articles//articles/assets/に書き換える。

ステップ3:index.jsonに記事を登録する

記事が終わったら、ブログ一覧に出るようpublic/articles/index.jsonに足す。

ステップ4:ローカルで試す

Vite開発サーバーを起動する。

pnpm dev

Viteは数ミリ秒で起動して、localhost:5173で記事をリアルタイムに見られる。この速さで各変更をほぼ即確認できる。

ステップ5:公開する

単なるgit pushで十分だ!CI/CDパイプラインが残り全部を自動でやる。

CI/CDデプロイパイプライン

GitHub Actionsの完全パイプラインを入れてて、mainへの各pushでサイトのlintとbuildとデプロイを自動化してる。詳しく見よう。

ワークフローは.github/workflows/deploy.ymlにあって、buildとdeployの2ジョブに分かれてる。

トリガー

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

パイプラインはpushでmainへ、そしてmain宛て各pull requestで動く。PRはマージ前に検証(lintとbuild)される。デプロイを起こすのはmainへのpushだけだ。

ジョブ1:Build

buildジョブはubuntu-latestで回って、こんな手順だ。

  1. Checkout:全履歴付きでリポジトリをクローン(fetch-depth: 0)
  2. Setup pnpm:pnpm/action-setup@v4で最新pnpmを入れる
  3. Setup Node.js 20:pnpmキャッシュ有効でNodeを設定して高速インストール
  4. Install dependencies:再現build保証でpnpm install --frozen-lockfileを実行(lockfile変更不可)
  5. Lint:build前にコード品質確認でpnpm run lint(ESLint)を実行
  6. Build:pnpm run buildを実行。TypeScript型確認(tsc -b)してからViteでまとめる
  7. Upload artifact:デプロイジョブ用にdist/フォルダをbuild成果物として上げる

1段階でも失敗(lint、型、buildのエラー)したらパイプライン全体が止まって何もデプロイされない。この guard で壊れたコードの本番混入を防いでて、各pushで頼ってる。

ジョブ2:Deploy

デプロイジョブは以下全部のときだけ動く。

  • buildジョブが成功した(needs: build)
  • イベントがpushだ(PRじゃない)
  • ブランチがmainだ
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

次にこう進む。

  1. build成果物を落とす:buildジョブが作ったdist/フォルダを取る
  2. GitHub Pagesを設定する:Pages環境を整える
  3. Pagesへ上げる:GitHub Pages用にdist/フォルダを準備する
  4. デプロイする:actions/deploy-pages@v4でサイト公開する

全体図

執筆からデプロイまではこうなる。

VS Codeで記事を書く
          ↓
    git addとcommit
          ↓
       git push
          ↓
   GitHub Actionsが起動
          ↓
   ┌─────────────────┐
   │   BUILD JOB     │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ✓      │
   │  6. Build ✓     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy 🚀   │
   └─────────────────┘
            ↓
     GitHub Pagesで公開!

pushから公開まで全体で約1分だ。単なるgit pushで公開する。FTPもSSHも通さず全部自動のままだ。

本番Build

内部ではpnpm buildコマンドがこう動く。

  1. tsc -b:TypeScript型を確認する
  2. vite build:全コードをまとめて最適化する

Viteは自動コード分割付きの最小最適化ファイルを作る。結果は超高速な静的サイトだ。

なぜこのアーキテクチャ

CMSやHugoやJekyllみたいな静的サイトジェネレータやNext.jsだって使えた。でもこの手法を選んだ理由はこうだ。

  • 単純さ:Markdownで書いて、GitHubにpushしたら公開だ
  • 完全管理:CMSやデータベースへの依存なし
  • 性能:ViteとReactは読み込み高速だ
  • 柔軟さ:MarkdownとHTMLを好きに混ぜられる
  • 学習:ReactとTypeScript習得に最高のプロジェクトだ
  • CI/CD:GitHub Actionsで品質確認と自動デプロイだ

結論

このブログは単純だが考え抜かれたプロジェクトだ。内容にMarkdown、レンダーにReact、性能にVite、CI/CDにGitHub Actions、ホスティングにGitHub Pagesだ。効率よく出される静的ファイルだけに頼ってて、各pushで品質を保証する自動パイプライン付きだ。

読んでくれてありがとう、次の記事でね!🦊

✨ AI Generated Article

이 블로그는 어떻게 돌아가나요?

블로그의 뒷이야기: React, Vite, Markdown, CI/CD 파이프라인과 집필 흐름.

이 블로그는 어떻게 돌아가나요?

이 블로그가 후드 아래에서 어떻게 돌아가는지 궁금하신 적 있나요? 이 글에서는 기술 스택부터 글 쓰기 과정까지, 애플리케이션 아키텍처 전체를 자세히 보여드릴게요. 맞아요, VS Code에서 글을 쓰는 방법까지 보여드릴게요!

기술 스택

이 블로그는 현대 웹 기술로 지어졌어요.

  • React 19: 사용자 인터페이스용
  • TypeScript: 타입이 있고 더 믿을 만한 코드용
  • Vite: 초고속 빌드 도구
  • React Router v7: 페이지 사이 탐색용
  • react-markdown: Markdown을 HTML로 바꾸기용
  • rehype-raw + rehype-sanitize: 안전하게 Markdown에 날것 HTML 허용용

프로젝트 구조

프로젝트 트리가 이렇게 생겼어요.

├── .github/
│   └── workflows/
│       └── deploy.yml              ← CI/CD 파이프라인
├── public/
│   ├── home.md                     ← 홈페이지 내용
│   ├── portfolio.md                ← 포트폴리오 내용
│   └── articles/
│       ├── index.json              ← 모든 글 목록
│       ├── hello-world.md          ← 글 하나
│       ├── how-this-blog-works.md  ← 이 글이에요!
│       └── /articles/assets/                 ← 글 이미지
├── src/
│   ├── main.tsx                    ← React 진입점
│   ├── App.tsx                     ← 주 라우터
│   ├── components/
│   │   ├── Header.tsx              ← 탐색 바
│   │   └── Footer.tsx              ← 바닥글
│   └── pages/
│       ├── Home.tsx                ← 홈페이지
│       ├── BlogList.tsx            ← 글 목록
│       ├── Article.tsx             ← 글 읽기
│       ├── Portfolio.tsx           ← 포트폴리오 페이지
│       └── NotFound.tsx            ← 404 페이지
└── vite.config.ts                  ← Vite 설정

핵심 생각은 단순해요. 콘텐츠는 코드와 분리돼요. 페이지는 public/에 Markdown으로 쓰고, src/의 React 코드가 표시를 맡아요. 저는 이 분리를 고수해요. 콘텐츠를 다시 읽기 쉽게 하고 코드를 발전시키기 쉽게 하니까요.

라우팅 시스템

App.tsx 파일이 React Router로 애플리케이션의 모든 라우트를 정해요.

Route Page 설명
/ Home 홈페이지, home.md 불러와요
/blog BlogList 모든 글 목록
/blog/:slug Article 글 하나, articles/{slug}.md 불러와요
/portfolio Portfolio 포트폴리오 페이지, portfolio.md 불러와요
* NotFound 알 수 없는 URL용 404 페이지

각 페이지의 역할이 분명해요. Markdown 파일을 가져와서 react-markdown으로 HTML로 바꾸고 화면에 보여줘요.

글은 어떻게 동작하나요?

제가 제일 좋아하는 부분이에요! 글의 생애주기는 이렇습니다.

1. index.json 파일

모든 글은 public/articles/index.json에 참조되어 있어요. 각 항목에 글 메타데이터가 들어 있어요.

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug: 고유 식별자, URL에 쓰여요 (/blog/hello-world)
  • title: 목록에 표시되는 제목
  • description: 짧은 요약
  • date: 발행일

2. Markdown 파일

글 내용은 public/articles/의 단순 .md 파일이에요. 파일 이름이 index.json에 정한 slug와 일치해요.

원하는 것을 넣을 수 있어요. 제목, 목록, 이미지, 표, 심지어 rehype-raw 덕분에 날것 HTML까지!

3. React 쪽 렌더링

/blog/hello-world에 들어가면 이런 일이 일어나요.

  1. React Router가 URL에서 slug 매개변수를 가져와요
  2. Article.tsx 컴포넌트가 /articles/hello-world.md를 불러와요
  3. Markdown이 react-markdown으로 HTML로 바뀌어요
  4. /articles/assets/ 링크는 자동으로 /articles//articles/assets/로 다시 써져요
  5. 나란히 메타데이터가 index.json에서 불러와져서 날짜와 설명을 보여줘요

이렇게 단순해요!

홈 페이지와 포트폴리오

홈과 포트폴리오 페이지는 정확히 같은 방식으로 동작해요. Markdown 파일(home.md 또는 portfolio.md)을 불러와서 HTML로 렌더링해요.

특이한 점은 모든 HTML 요소에 class와 style 속성을 허용하는 맞춤형 살균 스키마를 쓴다는 거예요. 그래서 이미지 갤러리처럼 스타일된 HTML을 Markdown에 바로 쓸 수 있어요.

헤더와 푸터

헤더는 position: fixed로 페이지 위에 고정돼 있어요. 들어있는 것은요.

  • 제 GitHub 아바타 (github.com/fox3000foxy.png에서 바로 불러와요)
  • 블로그 제목
  • 탐색 링크: 홈, 블로그, 포트폴리오

푸터는 미니멀해요. 동적으로 계산된 올해 연도가 있는 copyright뿐이에요.

다크 테마

사이트는 항상 다크 모드로 있어요. 저녁에 편하게 읽으려고 일부러 고른 거예요. 전역 스타일에 color-scheme: dark가 정해져 있고, 검은 배경 #000에 흰 글자 #fff예요. 링크는 파랑(#64b5f6)이고 호버하면 초록(#81c784)이 돼요.

글을 쓰는 방법

실전으로 넘어가요! 새 글을 쓰는 제 워크플로는 이렇습니다.

1단계: Markdown 파일 만들기

VS Code를 열고 public/articles/에 새 .md 파일을 만들어요.

2단계: 내용 쓰기

글 내용을 바로 Markdown으로 써요. VS Code에는 훌륭한 내장 Markdown 미리보기가 있어요.

이미지는 public/articles//articles/assets/에 두고 표준 Markdown 문법으로 참조해요.

![description](/articles/assets/my-image.png)

Article.tsx 컴포넌트가 이미지가 제대로 보이도록 /articles/assets/ 경로를 자동으로 /articles//articles/assets/로 다시 써줘요.

3단계: index.json에 글 등록하기

글을 마치면 블로그 목록에 나타나도록 public/articles/index.json에 추가해요.

4단계: 로컬에서 테스트하기

Vite 개발 서버를 실행해요.

pnpm dev

Vite는 몇 밀리초 만에 시작되고 localhost:5173에서 실시간으로 제 글을 볼 수 있어요. 이 속도 덕분에 매 수정을 거의 바로 확인할 수 있어요.

5단계: 발행하기

단순한 git push면 충분해요! CI/CD 파이프라인이 나머지를 자동으로 맡아요.

CI/CD 배포 파이프라인

매 main 푸시마다 사이트의 린트와 빌드, 배포를 자동화하는 완전한 GitHub Actions 파이프라인을 두었어요. 자세히 봐요.

워크플로는 .github/workflows/deploy.yml에 있고, build와 deploy 두 작업으로 나뉘어요.

트리거

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

파이프라인은 main으로의 매 push와 main을 향한 매 pull request에 실행돼요. PR은 합치기 전에 검증(린트 + 빌드)받고, main으로의 푸시만이 배포를 일으켜요.

작업 1: 빌드

빌드 작업은 ubuntu-latest에서 돌고 다음 단계를 따라요.

  1. Checkout: 전체 히스토리와 함께 저장소를 복제해요 (fetch-depth: 0)
  2. Setup pnpm: pnpm/action-setup@v4로 최신 pnpm을 설치해요
  3. Setup Node.js 20: 더 빠른 설치를 위해 pnpm 캐시를 켜고 Node를 설정해요
  4. Install dependencies: 재현 가능한 빌드를 보장하려고 pnpm install --frozen-lockfile을 실행해요 (lockfile 수정 허용 안 해요)
  5. Lint: 빌드 전에 코드 품질을 확인하려고 pnpm run lint(ESLint)를 실행해요
  6. Build: 먼저 TypeScript 타입을 확인(tsc -b)한 뒤 Vite로 전부 묶는 pnpm run build를 실행해요
  7. Upload artifact: 배포 작업을 위해 dist/ 폴더를 빌드 산출물로 올려요

한 단계라도 실패하면(린트, 타입, 빌드 오류) 파이프라인 전체가 멈추고 아무것도 배포되지 않아요. 깨진 코드가 프로덕션에 가는 것을 막아주는 장치이고, 매 푸시마다 의지해요.

작업 2: 배포

배포 작업은 다음일 때만 실행돼요.

  • 빌드 작업이 성공했어요 (needs: build)
  • 이벤트가 push예요 (PR이 아니에요)
  • 브랜치가 main이에요
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

그리고 이렇게 진행해요.

  1. 빌드 산출물 내려받기: 빌드 작업이 만든 dist/ 폴더를 가져와요
  2. GitHub Pages 설정: Pages 환경을 세팅해요
  3. Pages로 올리기: dist/ 폴더를 GitHub Pages용으로 준비해요
  4. 배포: actions/deploy-pages@v4로 사이트를 올려요

전체 그림

쓰기에서 배포까지 일어나는 일은 이렇습니다.

VS Code에서 글 쓰기
          ↓
    git add와 commit
          ↓
       git push
          ↓
   GitHub Actions 발동
          ↓
   ┌─────────────────┐
   │   BUILD JOB     │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ✓      │
   │  6. Build ✓     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy 🚀   │
   └─────────────────┘
            ↓
     GitHub Pages에 온라인!

푸시부터 온라인까지 전 과정이 1분쯤 걸려요. 단순한 git push로 발행하고, 전부 FTP나 SSH 없이 자동으로 남아요.

프로덕션 빌드

후드 아래에서 pnpm build 명령은 이렇게 실행돼요.

  1. tsc -b: TypeScript 타입을 확인해요
  2. vite build: 모든 코드를 묶고 최적화해요

Vite는 자동 코드 스플리팅이 있는 minify되고 최적화된 파일을 만들어요. 결과는 초고속 정적 사이트예요.

왜 이 아키텍처인가요?

CMS나 Hugo, Jekyll 같은 정적 사이트 생성기, 심지어 Next.js를 쓸 수도 있었어요. 하지만 이 접근을 고른 이유는 이렇습니다.

  • 단순함: Markdown으로 쓰고 GitHub에 푸시하면 온라인이에요
  • 완전한 통제: CMS나 데이터베이스 의존성 없어요
  • 성능: Vite + React = 빠른 불러오기
  • 유연함: Markdown과 HTML을 원하는 대로 섞을 수 있어요
  • 학습: React와 TypeScript를 익히는 훌륭한 프로젝트예요
  • CI/CD: GitHub Actions로 품질 검사와 배포 자동화예요

결론

이 블로그는 단순하지만 잘 생각된 프로젝트예요. 콘텐츠는 Markdown, 렌더링은 React, 성능은 Vite, CI와 CD는 GitHub Actions, 호스팅은 GitHub Pages. 효율적으로 서빙되는 정적 파일에만 기대고, 매 푸시마다 품질을 보장하는 자동 파이프라인이 있어요.

읽어 주셔서 감사하고, 다음 글에서 만나요! 🦊

✨ AI Generated Article

Bu blog nasıl çalışıyor?

Blogun perde arkası: React, Vite, Markdown, CI/CD pipelineı ve

Bu Blog Nasıl Çalışıyor?

Bu blogun kaputun altında nasıl çalıştığını hiç merak ettin mi? Bu makalede uygulamanın tüm mimarisini, teknik yığından bir makale yazma sürecine kadar detaylandıracağım. Ve evet, makalelerimi VS Code üzerinden nasıl yazdığımı bile göstereceğim!

Teknik Yığın

Bu blog modern web teknolojileriyle inşa edildi:

  • React 19: kullanıcı arayüzü için
  • TypeScript: tipli ve daha güvenilir kod için
  • Vite: ultra hızlı derleme aracı olarak
  • React Router v7: sayfalar arası gezinme için
  • react-markdown: Markdownı HTMLye dönüştürmek için
  • rehype-raw + rehype-sanitize: Markdown içinde ham HTMLye güvenli şekilde izin vermek için

Proje Yapısı

Proje ağacı şöyle görünür:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← CI/CD Pipelineı
├── public/
│   ├── home.md                     ← Ana sayfa içeriği
│   ├── portfolio.md                ← Portföy içeriği
│   └── articles/
│       ├── index.json              ← Tüm makalelerin listesi
│       ├── hello-world.md          ← Bir makale
│       ├── how-this-blog-works.md  ← Bu makale!
│       └── /articles/assets/                 ← Makalelerin görselleri
├── src/
│   ├── main.tsx                    ← React giriş noktası
│   ├── App.tsx                     ← Ana yönlendirici
│   ├── components/
│   │   ├── Header.tsx              ← Gezinme çubuğu
│   │   └── Footer.tsx              ← Alt bilgi
│   └── pages/
│       ├── Home.tsx                ← Ana sayfa
│       ├── BlogList.tsx            ← Makale listesi
│       ├── Article.tsx             ← Makale okuyucu
│       ├── Portfolio.tsx           ← Portföy sayfası
│       └── NotFound.tsx            ← 404 sayfası
└── vite.config.ts                  ← Vite yapılandırması

Merkez fikir basittir: içerik koddan ayrıdır. Sayfalar public/ klasöründe Markdown yazılır ve src/ içindeki React kodu onları göstermekle ilgilenir. Bu ayrıma önem veriyorum: içeriği yeniden okumayı kolaylaştırır ve kodu geliştirmeyi kolaylaştırır.

Yönlendirme Sistemi

App.tsx dosyası React Router ile uygulamanın tüm rotalarını tanımlar:

Rota Sayfa Açıklama
/ Home Ana sayfa, home.md yükler
/blog BlogList Tüm makalelerin listesi
/blog/:slug Article Bir makale, articles/{slug}.md yükler
/portfolio Portfolio Portföy sayfası, portfolio.md yükler
* NotFound Bilinmeyen URLler için 404 sayfası

Her sayfanın net bir rolü vardır: bir Markdown dosyası alır, react-markdown ile HTMLye dönüştürür ve ekranda gösterir.

Bir Makale Nasıl Çalışır?

En sevdiğim kısım bu! İşte bir makalenin yaşam döngüsü:

1. index.json Dosyası

Tüm makaleler public/articles/index.json içinde referanslanır. Her girdi makalenin meta verilerini içerir:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug: URLde kullanılan benzersiz tanımlayıcı (/blog/hello-world)
  • title: listede gösterilen başlık
  • description: kısa bir özet
  • date: yayın tarihi

2. Markdown Dosyası

Makalenin içeriği public/articles/ içinde basit bir .md dosyasıdır. Dosya adı index.json içinde tanımlı slug ile eşleşir.

İstediğini koyabilirsin: başlıklar, listeler, görseller, tablolar ve rehype-raw sayesinde ham HTML bile!

3. React Tarafında Render

/blog/hello-world adresini ziyaret ettiğinde şunlar olur:

  1. React Router URLden slug parametresini alır
  2. Article.tsx bileşeni /articles/hello-world.md dosyasını yükler
  3. Markdown react-markdown tarafından HTMLye dönüştürülür
  4. /articles/assets/ bağlantıları otomatik olarak /articles//articles/assets/ yoluna yeniden yazılır
  5. Paralel olarak meta veriler tarihi ve açıklamayı göstermek için index.json dosyasından yüklenir

Bu kadar basit!

Ana Sayfa ve Portföy

Ana Sayfa ve Portföy sayfaları tamamen aynı şekilde çalışır: bir Markdown dosyası (home.md ya da portfolio.md) yükleyip HTML olarak render ederler.

Özellikleri, tüm HTML öğelerinde class ve style özniteliklerine izin veren özel bir sanitizasyon şeması kullanmalarıdır. Bu, bana doğrudan Markdown içinde stilize HTML yazma izni verir, örneğin görsel galerileri gibi.

Header position: fixed ile sayfanın üstüne sabitlenmiştir. Şunları içerir:

  • GitHub avatarım (doğrudan github.com/fox3000foxy.png adresinden yüklenir)
  • Blog başlığı
  • Gezinme bağlantıları: Ana Sayfa, Blog, Portföy

Footer minimalisttir: dinamik hesaplanan güncel yılla sadece bir telif hakkı.

Koyu Tema

Site kalıcı olarak koyu modda kalır. Akşam rahat okuma için bu seçimi bilinçli yaptım: global stillerde color-scheme: dark tanımlıdır, siyah arka plan #000 ve beyaz metin #fff ile. Bağlantılar mavidir (#64b5f6) ve üzerine gelince yeşil olur (#81c784).

Bir Makaleyi Nasıl Yazıyorum

Pratiğe geçelim! İşte yeni bir makale yazma workflowum:

Adım 1: Markdown Dosyasını Oluşturmak

VS Code açar ve public/articles/ içinde yeni bir .md dosyası oluştururum:

Adım 2: İçeriği Yazmak

Makalenin içeriğini doğrudan Markdown yazarım. VS Codeun mükemmel bir yerleşik Markdown önizlemesi vardır:

Görseller için onları public/articles//articles/assets/ içine koyar ve standart Markdown sözdizimiyle referanslarım:

![description](/articles/assets/my-image.png)

Article.tsx bileşeni görsellerin doğru görüntülenmesi için /articles/assets/ yolunu otomatik olarak /articles//articles/assets/ yoluna yeniden yazar.

Adım 3: Makaleyi index.json içine Kaydetmek

Makale bitince blog listesinde görünmesi için onu public/articles/index.json içine eklerim:

Adım 4: Yerelde Test Etmek

Vite geliştirme sunucusunu çalıştırırım:

pnpm dev

Vite milisaniyeler içinde başlar ve makalemi localhost:5173 üzerinde gerçek zamanlı görebilirim. Bu hız her değişikliği neredeyse anında kontrol etmeme izin verir:

Adım 5: Yayınlamak

Basit bir git push yeter! CI/CD pipelineı gerisini otomatik halleder.

CI/CD Dağıtım Pipelineı

Her main pushunda sitenin lint, derleme ve dağıtımını otomatikleştiren eksiksiz bir GitHub Actions pipelineı kurdum. Ayrıntılı bakalım.

Workflow .github/workflows/deploy.yml içindedir ve iki işe bölünmüştür: build ve deploy.

Tetikleyiciler

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Pipeline her push üzerinde main üzerine ve main hedefli her pull request üzerinde çalışır. PRler birleştirilmeden önce doğrulanır (lint + build) ama yalnızca main üzerindeki pushlar dağıtımı tetikler.

İş 1: Build

Derleme işi ubuntu-latest üzerinde döner ve şu adımları izler:

  1. Checkout: Depoyu tüm tarihçeyle klonlar (fetch-depth: 0)
  2. Setup pnpm: pnpm/action-setup@v4 ile en güncel pnpm sürümünü kurar
  3. Setup Node.js 20: Daha hızlı kurulumlar için pnpm önbelleği etkin Node yapılandırır
  4. Install dependencies: Yeniden üretilebilir derlemeleri garantilemek için pnpm install --frozen-lockfile çalıştırır (lockfile değişikliğine izin yok)
  5. Lint: Derlemeden önce kod kalitesini kontrol etmek için pnpm run lint (ESLint) çalıştırır
  6. Build: Önce TypeScript türlerini kontrol eden (tsc -b) sonra her şeyi Vite ile paketleyen pnpm run build çalıştırır
  7. Upload artifact: Dağıtım işi için dist/ klasörünü derleme artefaktı olarak yükler

Bir adım başarısız olursa (lint, tür ya da derleme hatası) tüm pipeline durur ve hiçbir şey dağıtılmaz. Bu koruma beni üretimde kırık koddan korur ve her pushta ona güvenirim.

İş 2: Deploy

Dağıtım işi yalnızca şunlarsa çalışır:

  • Derleme işi başarılıysa (needs: build)
  • Olay bir push ise (PR değil)
  • Dal main ise
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Sonra şöyle ilerler:

  1. Derleme artefaktını indir: Derleme işinin ürettiği dist/ klasörünü alır
  2. GitHub Pagesi yapılandır: Pages ortamını kurar
  3. Pagese yükle: dist/ klasörünü GitHub Pages için hazırlar
  4. Dağıt: Siteyi actions/deploy-pages@v4 ile yayınlar

Tam Tablo

Yazımdan dağıtıma neler olduğu:

Écrire l'article dans VS Code
          ↓
    git add & commit
          ↓
        git push
          ↓
    GitHub Actions se déclenche
          ↓
    ┌─────────────────┐
    │   BUILD JOB     │
    │  1. Checkout    │
    │  2. Setup pnpm  │
    │  3. Setup Node  │
    │  4. Install     │
    │  5. Lint ✓      │
    │  6. Build ✓     │
    │  7. Upload dist │
    └────────┬────────┘
             ↓
    ┌─────────────────┐
    │  DEPLOY JOB     │
    │  1. Download    │
    │  2. Configure   │
    │  3. Upload      │
    │  4. Deploy 🚀   │
    └─────────────────┘
             ↓
      En ligne sur GitHub Pages !

Tüm süreç push ile çevrimiçi olma arasında yaklaşık bir dakika sürer. Basit bir git push ile yayınlarım, her şey FTP ya da SSH olmadan otomatik kalır.

Üretim Derlemesi

Kaputun altında pnpm build komutu şunu çalıştırır:

  1. tsc -b: TypeScript türlerini kontrol eder
  2. vite build: Tüm kodu paketler ve optimize eder

Vite otomatik kod bölmeli minifiye ve optimize dosyalar üretir. Sonuç ultra hızlı statik bir sitedir.

Neden Bu Mimari?

Bir CMS, Hugo ya da Jekyll gibi bir statik site üretici, hatta Next.js kullanabilirdim. Ama bu yaklaşımı seçmemin nedenleri:

  • Sadelik: Markdown yaz, GitHuba pushla, çevrimiçi
  • Tam kontrol: CMS ya da veritabanı bağımlılığı yok
  • Performans: Vite + React = hızlı yükleme
  • Esneklik: Markdown ve HTMLyi istediğim gibi karıştırabilirim
  • Öğrenme: React ve TypeScriptte ustalaşmak için süper bir proje
  • CI/CD: GitHub Actions ile otomatik kalite kontrolleri ve dağıtım

Sonuç

Bu blog basit ama iyi düşünülmüş bir projedir: içerik için Markdown, render için React, performans için Vite, CI/CD için GitHub Actions ve barındırma için GitHub Pages. Verimli sunulan statik dosyalara dayanır, her pushta kaliteyi garantileyen otomatik bir pipeline ile.

Okuduğun için teşekkürler, sonraki makalede görüşmek üzere! 🦊

✨ AI Generated Article

Come funziona questo blog?

Il dietro le quinte del blog: React, Vite, Markdown, la pipeline CI/CD e il flusso di redazione.

Come funziona questo blog?

Ti sei mai chiesto come funziona questo blog sotto il cofano? In questo articolo, ti dettaglio tutta l'architettura dell'applicazione, dallo stack tecnico fino al processo di redazione di un articolo. E sì, ti mostro pure come scrivo i miei articoli da VS Code!

Lo stack tecnico

Questo blog è costruito con tecnologie web moderne:

  • React 19: per l'interfaccia utente
  • TypeScript: per codice tipizzato e più affidabile
  • Vite: come strumento di build ultra-rapido
  • React Router v7: per la navigazione tra le pagine
  • react-markdown: per trasformare il Markdown in HTML
  • rehype-raw + rehype-sanitize: per autorizzare HTML grezzo nel Markdown in tutta sicurezza

Struttura del progetto

Ecco com'è l'albero del progetto:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Contenuto della home page
│   ├── portfolio.md                ← Contenuto del portfolio
│   └── articles/
│       ├── index.json              ← Lista di tutti gli articoli
│       ├── hello-world.md          ← Un articolo
│       ├── how-this-blog-works.md  ← Questo articolo!
│       └── /articles/assets/                 ← Immagini degli articoli
├── src/
│   ├── main.tsx                    ← Punto di ingresso React
│   ├── App.tsx                     ← Router principale
│   ├── components/
│   │   ├── Header.tsx              ← Barra di navigazione
│   │   └── Footer.tsx              ← Piè di pagina
│   └── pages/
│       ├── Home.tsx                ← Home page
│       ├── BlogList.tsx            ← Lista degli articoli
│       ├── Article.tsx             ← Lettore di articolo
│       ├── Portfolio.tsx           ← Pagina portfolio
│       └── NotFound.tsx            ← Pagina 404
└── vite.config.ts                  ← Configurazione Vite

L'idea centrale è semplice: il contenuto è separato dal codice. Le pagine sono scritte in Markdown nella cartella public/, e il codice React in src/ si occupa di mostrarle. Tengo a questa separazione: rende il contenuto facile da rileggere e il codice facile da far evolvere.

Il sistema di routing

Il file App.tsx definisce tutte le route dell'applicazione con React Router:

Route Pagina Descrizione
/ Home Home page, carica home.md
/blog BlogList Lista di tutti gli articoli
/blog/:slug Article Un articolo, carica articles/{slug}.md
/portfolio Portfolio Pagina portfolio, carica portfolio.md
* NotFound Pagina 404 per gli URL sconosciuti

Ogni pagina ha un ruolo ben definito: recupera un file Markdown, lo trasforma in HTML con react-markdown, e lo mostra a schermo.

Come funziona un articolo?

È la parte che preferisco! Ecco il ciclo di vita di un articolo:

1. Il file index.json

Tutti gli articoli sono referenziati in public/articles/index.json. Ogni voce contiene i metadati dell'articolo:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug: l'identificatore unico, usato nell'URL (/blog/hello-world)
  • title: il titolo mostrato nella lista
  • description: un corto riassunto
  • date: la data di pubblicazione

2. Il file Markdown

Il contenuto dell'articolo è un semplice file .md in public/articles/. Il nome del file corrisponde allo slug definito in index.json.

Puoi metterci ciò che vuoi: titoli, liste, immagini, tabelle, e persino HTML grezzo grazie a rehype-raw!

3. Il rendering lato React

Quando visiti /blog/hello-world, ecco cosa succede:

  1. React Router recupera il parametro slug dall'URL
  2. Il componente Article.tsx carica /articles/hello-world.md
  3. Il Markdown è trasformato in HTML da react-markdown
  4. I link verso /articles/assets/ sono automaticamente riscritti verso /articles//articles/assets/
  5. In parallelo, i metadati sono caricati da index.json per mostrare data e descrizione

È semplice così!

La home page e il portfolio

Le pagine Home e Portfolio funzionano esattamente allo stesso modo: caricano un file Markdown (home.md o portfolio.md) e lo rendono in HTML.

La particolarità, è che usano uno schema di sanitization personalizzato che autorizza gli attributi class e style su tutti gli elementi HTML. Mi permette di scrivere HTML stilizzato direttamente nel Markdown, come gallerie di immagini per esempio.

L'header è fissato in cima alla pagina con position: fixed. Contiene:

  • Il mio avatar GitHub (caricato direttamente da github.com/fox3000foxy.png)
  • Il titolo del blog
  • I link di navigazione: Home, Blog, Portfolio

Il footer è minimalista: solo un copyright con l'anno corrente calcolato dinamicamente.

Il tema scuro

Il sito resta in modalità scura in permanenza. Ho fatto questa scelta deliberatamente per tenere una lettura confortevole la sera: color-scheme: dark è definito negli stili globali, con uno sfondo nero #000 e testo bianco #fff. I link sono blu (#64b5f6) e diventano verdi al passaggio (#81c784).

Come scrivo un articolo

Passiamo alla pratica! Ecco il mio workflow per scrivere un nuovo articolo:

Passo 1: crea il file Markdown

Apro VS Code e creo un nuovo file .md in public/articles/:

Passo 2: scrivi il contenuto

Scrivo il contenuto dell'articolo direttamente in Markdown. VS Code ha un'ottima anteprima Markdown integrata:

Per le immagini, le metto in public/articles//articles/assets/ e le referenzio con la sintassi Markdown standard:

![descrizione](/articles/assets/my-image.png)

Il componente Article.tsx riscrive automaticamente il percorso /articles/assets/ verso /articles//articles/assets/ perché le immagini si mostrino correttamente.

Passo 3: registra l'articolo in index.json

Una volta finito l'articolo, lo aggiungo in public/articles/index.json perché appaia nella lista del blog:

Passo 4: testa in locale

Lancio il server di sviluppo Vite:

pnpm dev

Vite parte in qualche millisecondo e posso vedere il mio articolo in tempo reale su localhost:5173. Questa rapidità mi permette di verificare ogni modifica quasi istantaneamente:

Passo 5: pubblica

Un semplice git push basta! La pipeline CI/CD si occupa del resto automaticamente.

La pipeline di deploy CI/CD

Ho messo in piedi una pipeline GitHub Actions completa che automatizza lint, build e deploy del sito a ogni push su main. Vediamola in dettaglio.

Il workflow sta in .github/workflows/deploy.yml ed è diviso in due job: build e deploy.

Trigger

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

La pipeline gira a ogni push su main e a ogni pull request verso main. Le PR sono quindi verificate (lint + build) prima di essere fuse, ma solo i push su main innescano un deploy.

Job 1: Build

Il job di build gira su ubuntu-latest e segue questi passi:

  1. Checkout: clona il deposito con tutto lo storico (fetch-depth: 0)
  2. Setup pnpm: installa l'ultima versione di pnpm con pnpm/action-setup@v4
  3. Setup Node.js 20: configura Node col cache pnpm attivato per installazioni più rapide
  4. Install dependencies: esegue pnpm install --frozen-lockfile per garantire build riproducibili (nessuna modifica del lockfile autorizzata)
  5. Lint: esegue pnpm run lint (ESLint) per verificare la qualità del codice prima della build
  6. Build: esegue pnpm run build, che verifica prima i tipi TypeScript (tsc -b) poi fa bundle del tutto con Vite
  7. Upload artifact: carica la cartella dist/ come artefatto di build per il job di deploy

Se un passo fallisce (errore di lint, di tipo o di build), tutta la pipeline si ferma e niente è deployato. Questa guardia mi protegge dal codice rotto in produzione e mi ci affido a ogni push.

Job 2: Deploy

Il job di deploy gira solo se:

  • Il job di build è riuscito (needs: build)
  • L'evento è un push (non una PR)
  • Il branch è main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Procede poi:

  1. Scarica l'artefatto di build: recupera la cartella dist/ prodotta dal job di build
  2. Configura GitHub Pages: mette in piedi l'ambiente Pages
  3. Carica verso Pages: prepara la cartella dist/ per GitHub Pages
  4. Deploya: pubblica il sito con actions/deploy-pages@v4

Il quadro completo

Ecco cosa succede dalla scrittura al deploy:

Scrivi l'articolo in VS Code
          ↓
    git add & commit
          ↓
       git push
          ↓
   GitHub Actions si attiva
          ↓
   ┌─────────────────┐
   │   BUILD JOB     │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ok      │
   │  6. Build ok     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy avvio   │
   └─────────────────┘
            ↓
     Online su GitHub Pages!

Il processo intero prende circa un minuto tra il push e la messa online. Pubblico con un semplice git push, il tutto resta automatico senza passare da FTP né SSH.

La build di produzione

Sotto il cofano, il comando pnpm build esegue:

  1. tsc -b: verifica i tipi TypeScript
  2. vite build: fa bundle e ottimizza tutto il codice

Vite produce file minificati e ottimizzati con code-splitting automatico. Il risultato è un sito statico ultra-rapido.

Perché questa architettura?

Avrei potuto usare un CMS, un generatore di sito statico come Hugo o Jekyll, o persino Next.js. Ma ecco perché ho scelto questo approccio:

  • Semplicità: scrivi in Markdown, fai push su GitHub, è online
  • Controllo totale: nessuna dipendenza da un CMS o un database
  • Performance: Vite + React = caricamento rapido
  • Flessibilità: posso mischiare Markdown e HTML come voglio
  • Apprendimento: è un super progetto per padroneggiare React e TypeScript
  • CI/CD: verifiche di qualità e deploy automatizzati con GitHub Actions

Conclusione

Questo blog è un progetto semplice ma ben pensato: Markdown per il contenuto, React per il rendering, Vite per la performance, GitHub Actions per il CI/CD, e GitHub Pages per l'hosting. Poggia solo su file statici serviti efficacemente, con una pipeline automatizzata che garantisce la qualità a ogni push.

Grazie di aver letto, e a presto nel prossimo articolo!

✨ AI Generated Article

Wie funktioniert dieser Blog?

Die Kulissen des Blogs: React, Vite, Markdown, die CI/CD-Pipeline

Wie funktioniert dieser Blog?

Hast du dich schon gefragt, wie dieser Blog unter der Haube funktioniert? In diesem Artikel lege ich dir die gesamte Architektur der App dar, vom Tech-Stack bis zum Prozess, einen Artikel zu schreiben. Und ja, ich zeige dir sogar, wie ich meine Artikel aus VS Code schreibe!

Der Tech-Stack

Dieser Blog ist mit modernen Webtechnologien gebaut:

  • React 19: für die Benutzeroberfläche
  • TypeScript: für typisierten und verlässlicheren Code
  • Vite: als ultra-schnelles Build-Tool
  • React Router v7: für die Navigation zwischen den Seiten
  • react-markdown: um Markdown in HTML zu verwandeln
  • rehype-raw + rehype-sanitize: um rohes HTML im Markdown sicher zu erlauben

Projektstruktur

So sieht der Projektbaum aus:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← CI/CD-Pipeline
├── public/
│   ├── home.md                     ← Inhalt der Startseite
│   ├── portfolio.md                ← Inhalt des Portfolios
│   └── articles/
│       ├── index.json              ← Liste aller Artikel
│       ├── hello-world.md          ← Ein Artikel
│       ├── how-this-blog-works.md  ← Dieser Artikel !
│       └── /articles/assets/                 ← Bilder der Artikel
├── src/
│   ├── main.tsx                    ← React-Einstiegspunkt
│   ├── App.tsx                     ← Hauptrouter
│   ├── components/
│   │   ├── Header.tsx              ← Navigationsleiste
│   │   └── Footer.tsx              ← Fußzeile
│   └── pages/
│       ├── Home.tsx                ← Startseite
│       ├── BlogList.tsx            ← Artikelliste
│       ├── Article.tsx             ← Artikelreader
│       ├── Portfolio.tsx           ← Portfolioseite
│       └── NotFound.tsx            ← 404-Seite
└── vite.config.ts                  ← Vite-Konfiguration

Die zentrale Idee ist simpel: Der Inhalt ist vom Code getrennt. Die Seiten sind in Markdown im public/-Ordner geschrieben, und der React-Code in src/ kümmert sich darum, sie anzuzeigen. Mir liegt an dieser Trennung: Sie macht den Inhalt leicht nachlesbar und den Code leicht weiterentwickelbar.

Das Routing-System

Die Datei App.tsx definiert alle Routen der App mit React Router:

Route Page Description
/ Home Startseite, lädt home.md
/blog BlogList Liste aller Artikel
/blog/:slug Article Ein Artikel, lädt articles/{slug}.md
/portfolio Portfolio Portfolioseite, lädt portfolio.md
* NotFound 404-Seite für unbekannte URLs

Jede Seite hat eine klar definierte Rolle: Sie holt eine Markdown-Datei, verwandelt sie mit react-markdown in HTML und zeigt sie auf dem Bildschirm an.

Wie funktioniert ein Artikel?

Das ist der Teil, den ich am liebsten mag! Hier der Lebenszyklus eines Artikels:

1. Die Datei index.json

Alle Artikel sind in public/articles/index.json referenziert. Jeder Eintrag enthält die Metadaten des Artikels:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug: die eindeutige Kennung, genutzt in der URL (/blog/hello-world)
  • title: der in der Liste angezeigte Titel
  • description: eine kurze Zusammenfassung
  • date: das Veröffentlichungsdatum

2. Die Markdown-Datei

Der Inhalt des Artikels ist eine simple .md-Datei in public/articles/. Der Dateiname entspricht dem in index.json definierten slug.

Du kannst hineintun, was du willst: Titel, Listen, Bilder, Tabellen und sogar rohes HTML dank rehype-raw!

3. Das Rendering auf React-Seite

Wenn du /blog/hello-world besuchst, passiert Folgendes:

  1. React Router holt den slug-Parameter aus der URL
  2. Die Komponente Article.tsx lädt /articles/hello-world.md
  3. Das Markdown wird von react-markdown in HTML verwandelt
  4. Die Links nach /articles/assets/ werden automatisch nach /articles//articles/assets/ umgeschrieben
  5. Parallel werden die Metadaten aus index.json geladen, um Datum und Beschreibung anzuzeigen

So einfach ist das!

Die Startseite und das Portfolio

Die Start- und Portfolioseiten funktionieren exakt genauso: Sie laden eine Markdown-Datei (home.md oder portfolio.md) und rendern sie in HTML.

Die Besonderheit ist, dass sie ein angepasstes Sanitization-Schema nutzen, das die Attribute class und style auf allen HTML-Elementen erlaubt. Das erlaubt mir, gestyltes HTML direkt ins Markdown zu schreiben, wie zum Beispiel Bildergalerien.

Der Header ist oben auf der Seite mit position: fixed angepinnt. Er enthält:

  • Meinen GitHub-Avatar (direkt von github.com/fox3000foxy.png geladen)
  • Den Blogtitel
  • Die Navigationslinks: Start, Blog, Portfolio

Der Footer ist minimalistisch: Nur ein Copyright mit dem dynamisch berechneten aktuellen Jahr.

Das dunkle Theme

Die Seite bleibt permanent im Dark Mode. Ich habe diese Wahl bewusst getroffen, um abends komfortabel lesen zu können: color-scheme: dark ist in den globalen Styles definiert, mit schwarzem Hintergrund #000 und weißem Text #fff. Die Links sind blau (#64b5f6) und werden beim Hover grün (#81c784).

Wie ich einen Artikel schreibe

Zur Praxis! Hier mein Workflow, um einen neuen Artikel zu schreiben:

Schritt 1: Die Markdown-Datei erstellen

Ich öffne VS Code und erstelle eine neue .md-Datei in public/articles/:

Schritt 2: Den Inhalt schreiben

Ich schreibe den Artikelinhalt direkt in Markdown. VS Code hat eine exzellente eingebaute Markdown-Vorschau:

Für Bilder lege ich sie in public/articles//articles/assets/ und referenziere sie mit der Standard-Markdown-Syntax:

![description](/articles/assets/my-image.png)

Die Komponente Article.tsx schreibt den Pfad /articles/assets/ automatisch nach /articles//articles/assets/ um, damit die Bilder korrekt angezeigt werden.

Schritt 3: Den Artikel in index.json registrieren

Sobald der Artikel fertig ist, füge ich ihn in public/articles/index.json hinzu, damit er in der Blogliste erscheint:

Schritt 4: Lokal testen

Ich starte den Vite-Dev-Server:

pnpm dev

Vite startet in wenigen Millisekunden und ich kann meinen Artikel live auf localhost:5173 sehen. Diese Geschwindigkeit erlaubt mir, jede Änderung fast augenblicklich zu prüfen:

Schritt 5: Veröffentlichen

Ein simples git push reicht! Die CI/CD-Pipeline kümmert sich automatisch um den Rest.

Die CI/CD-Deployment-Pipeline

Ich habe eine komplette GitHub-Actions-Pipeline aufgesetzt, die Lint, Build und Deployment der Seite bei jedem Push auf main automatisiert. Schauen wir uns das im Detail an.

Der Workflow liegt in .github/workflows/deploy.yml und ist in zwei Jobs geteilt: build und deploy.

Trigger

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Die Pipeline läuft bei jedem Push auf main und bei jedem Pull Request auf main. PRs werden also vor dem Merge geprüft (Lint + Build), aber nur Pushes auf main lösen ein Deployment aus.

Job 1: Build

Der Build-Job läuft auf ubuntu-latest und folgt diesen Schritten:

  1. Checkout: Klont das Repo mit der gesamten Historie (fetch-depth: 0)
  2. Setup pnpm: Installiert die neueste pnpm-Version mit pnpm/action-setup@v4
  3. Setup Node.js 20: Konfiguriert Node mit aktiviertem pnpm-Cache für schnellere Installationen
  4. Install dependencies: Führt pnpm install --frozen-lockfile aus, um reproduzierbare Builds zu garantieren (keine Lockfile-Änderung erlaubt)
  5. Lint: Führt pnpm run lint (ESLint) aus, um die Codequalität vor dem Build zu prüfen
  6. Build: Führt pnpm run build aus, das zuerst die TypeScript-Typen prüft (tsc -b) und dann alles mit Vite bundelt
  7. Upload artifact: Lädt den dist/-Ordner als Build-Artefakt für den Deployment-Job hoch

Wenn ein Schritt fehlschlägt (Lint-, Typ- oder Buildfehler), stoppt die ganze Pipeline und nichts wird deployed. Diese Wache schützt mich vor kaputtem Code in Produktion und ich verlasse mich bei jedem Push darauf.

Job 2: Deploy

Der Deployment-Job läuft nur, wenn:

  • Der Build-Job erfolgreich war (needs: build)
  • Das Event ein Push ist (kein PR)
  • Der Branch main ist
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Er geht dann so vor:

  1. Lädt das Build-Artefakt herunter: Holt den vom Build-Job produzierten dist/-Ordner
  2. Konfiguriert GitHub Pages: Richtet die Pages-Umgebung ein
  3. Lädt zu Pages hoch: Bereitet den dist/-Ordner für GitHub Pages vor
  4. Deployed: Veröffentlicht die Seite mit actions/deploy-pages@v4

Die komplette Tabelle

Hier passiert vom Schreiben bis zum Deployment:

Artikel in VS Code schreiben
          ↓
    git add & commit
          ↓
       git push
          ↓
   GitHub Actions löst aus
          ↓
   ┌─────────────────┐
   │   BUILD JOB     │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ✓      │
   │  6. Build ✓     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy 🚀   │
   └─────────────────┘
            ↓
     Online auf GitHub Pages !

Der ganze Prozess dauert etwa eine Minute zwischen Push und Onlinestellung. Ich veröffentliche mit einem simplen git push, alles bleibt automatisch ohne FTP oder SSH.

Der Produktionsbuild

Unter der Haube führt der Befehl pnpm build aus:

  1. tsc -b: Prüft die TypeScript-Typen
  2. vite build: Bundelt und optimiert den ganzen Code

Vite produziert minifizierte und optimierte Dateien mit automatischem Code-Splitting. Das Ergebnis ist eine ultra-schnelle statische Seite.

Warum diese Architektur?

Ich hätte ein CMS, einen statischen Seitengenerator wie Hugo oder Jekyll oder sogar Next.js nutzen können. Aber hier, warum ich diesen Ansatz gewählt habe:

  • Simplizität: In Markdown schreiben, auf GitHub pushen, es ist online
  • Totale Kontrolle: Keine Abhängigkeit von CMS oder Datenbank
  • Performance: Vite + React = schnelles Laden
  • Flexibilität: Ich kann Markdown und HTML mischen, wie ich will
  • Lernen: Es ist ein super Projekt, um React und TypeScript zu beherrschen
  • CI/CD: Qualitätsprüfungen und automatisiertes Deployment mit GitHub Actions

Fazit

Dieser Blog ist ein simples, aber gut durchdachtes Projekt: Markdown für den Inhalt, React fürs Rendering, Vite für die Performance, GitHub Actions für CI/CD und GitHub Pages fürs Hosting. Er beruht nur auf effizient servierten statischen Dateien, mit einer automatisierten Pipeline, die bei jedem Push die Qualität garantiert.

Danke fürs Lesen, und bis bald im nächsten Artikel! 🦊

✨ AI Generated Article

Как работает этот блог?

Закулисье блога : React, Vite, Markdown, CI/CD-пайплайн

Как работает этот блог?

Ты уже спрашивал себя, как этот блог устроен под капотом? В этой статье я детально разберу всю архитектуру приложения, от технического стека до процесса написания статьи. И да, даже покажу, как я пишу статьи из VS Code!

Технический стек

Этот блог построен на современных веб-технологиях :

  • React 19 : для пользовательского интерфейса
  • TypeScript : для типизированного и надёжного кода
  • Vite : как ультрабыстрый инструмент сборки
  • React Router v7 : для навигации между страницами
  • react-markdown : чтобы превращать Markdown в HTML
  • rehype-raw + rehype-sanitize : чтобы разрешать сырой HTML в Markdown безопасно

Структура проекта

Вот как выглядит дерево проекта :

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Contenu de la page d'accueil
│   ├── portfolio.md                ← Contenu du portfolio
│   └── articles/
│       ├── index.json              ← Liste de tous les articles
│       ├── hello-world.md          ← Un article
│       ├── how-this-blog-works.md  ← Cet article !
│       └── /articles/assets/                 ← Images des articles
├── src/
│   ├── main.tsx                    ← Point d'entrée React
│   ├── App.tsx                     ← Routeur principal
│   ├── components/
│   │   ├── Header.tsx              ← Barre de navigation
│   │   └── Footer.tsx              ← Pied de page
│   └── pages/
│       ├── Home.tsx                ← Page d'accueil
│       ├── BlogList.tsx            ← Liste des articles
│       ├── Article.tsx             ← Lecteur d'article
│       ├── Portfolio.tsx           ← Page portfolio
│       └── NotFound.tsx            ← Page 404
└── vite.config.ts                  ← Configuration Vite

Центральная идея проста : контент отделён от кода. Страницы пишутся в Markdown в папке public/, а код React в src/ занимается их показом. Держусь за это разделение : оно делает контент лёгким для перечитывания, а код лёгким для развития.

Система роутинга

Файл App.tsx определяет все роуты приложения через React Router :

Route Page Description
/ Home Page d'accueil, charge home.md
/blog BlogList Liste de tous les articles
/blog/:slug Article Un article, charge articles/{slug}.md
/portfolio Portfolio Page portfolio, charge portfolio.md
* NotFound Page 404 pour les URLs inconnues

У каждой страницы чёткая роль : она забирает файл Markdown, превращает его в HTML через react-markdown и показывает на экране.

Как работает статья?

Это часть, которую я люблю больше всего! Вот жизненный цикл статьи :

1. Файл index.json

Все статьи перечислены в public/articles/index.json. Каждая запись содержит метаданные статьи :

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug : уникальный идентификатор, используется в URL (/blog/hello-world)
  • title : заголовок, показываемый в списке
  • description : короткое резюме
  • date : дата публикации

2. Файл Markdown

Содержимое статьи это простой файл .md в public/articles/. Имя файла соответствует slug, определённому в index.json.

Ты можешь класть туда что хочешь : заголовки, списки, картинки, таблицы и даже сырой HTML благодаря rehype-raw!

3. Рендер на стороне React

Когда заходишь на /blog/hello-world, происходит вот что :

  1. React Router забирает параметр slug из URL
  2. Компонент Article.tsx грузит /articles/hello-world.md
  3. Markdown превращается в HTML через react-markdown
  4. Ссылки на /articles/assets/ автоматически переписываются в /articles//articles/assets/
  5. Параллельно метаданные грузятся из index.json, чтобы показать дату и описание

Это так же просто!

Главная страница и портфолио

Страницы Главная и Портфолио работают ровно так же : они грузят файл Markdown (home.md или portfolio.md) и рендерят его в HTML.

Особенность в том, что они используют кастомную схему санитизации, разрешающую атрибуты class и style на всех HTML-элементах. Это позволяет мне писать стилизованный HTML прямо в Markdown, например галереи картинок.

Header прибит к верху страницы через position: fixed. Он содержит :

  • Мой аватар GitHub (грузится прямо с github.com/fox3000foxy.png)
  • Название блога
  • Навигационные ссылки : Главная, Блог, Портфолио

Footer минималистичный : просто копирайт с динамически вычисляемым текущим годом.

Тёмная тема

Сайт постоянно в тёмном режиме. Я сделал этот выбор осознанно, чтобы комфортно читать вечером : color-scheme: dark задан в глобальных стилях, с чёрным фоном #000 и белым текстом #fff. Ссылки синие (#64b5f6) и становятся зелёными при наведении (#81c784).

Как я пишу статью

К практике! Вот мой воркфлоу для написания новой статьи :

Шаг 1 : Создать файл Markdown

Открываю VS Code и создаю новый файл .md в public/articles/ :

Шаг 2 : Написать содержимое

Пишу содержимое статьи прямо в Markdown. В VS Code отличный встроенный предпросмотр Markdown :

Для картинок кладу их в public/articles//articles/assets/ и ссылаюсь стандартным синтаксисом Markdown :

![description](/articles/assets/my-image.png)

Компонент Article.tsx автоматически переписывает путь /articles/assets/ в /articles//articles/assets/, чтобы картинки корректно показывались.

Шаг 3 : Зарегистрировать статью в index.json

Когда статья готова, добавляю её в public/articles/index.json, чтобы она появилась в списке блога :

Шаг 4 : Протестировать локально

Запускаю dev-сервер Vite :

pnpm dev

Vite стартует за миллисекунды, и я вижу статью в реальном времени на localhost:5173. Эта скорость позволяет проверять каждое изменение почти мгновенно :

Шаг 5 : Опубликовать

Достаточно простого git push! CI/CD-пайплайн занимается остальным автоматически.

CI/CD-пайплайн деплоя

Я настроил полный пайплайн GitHub Actions, автоматизирующий линт, сборку и деплой сайта при каждом push в main. Смотрим в деталях.

Воркфлоу лежит в .github/workflows/deploy.yml и разбит на две джобы : build и deploy.

Триггеры

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Пайплайн выполняется при каждом push в main и при каждом pull request в main. PR, таким образом, проверяются (линт + сборка) до мержа, но деплой триггерят только пуши в main.

Джоба 1 : Build

Сборочная джоба крутится на ubuntu-latest и идёт по шагам :

  1. Checkout : Клонирует репозиторий со всей историей (fetch-depth: 0)
  2. Setup pnpm : Ставит последнюю версию pnpm через pnpm/action-setup@v4
  3. Setup Node.js 20 : Настраивает Node с включённым кэшем pnpm для более быстрых установок
  4. Install dependencies : Выполняет pnpm install --frozen-lockfile, чтобы гарантировать воспроизводимые сборки (никаких модификаций lockfile)
  5. Lint : Выполняет pnpm run lint (ESLint), чтобы проверить качество кода до сборки
  6. Build : Выполняет pnpm run build, сначала проверяющий типы TypeScript (tsc -b), затем бандлящий всё через Vite
  7. Upload artifact : Заливает папку dist/ как артефакт сборки для деплой-джобы

Если шаг падает (ошибка линта, типов или сборки), весь пайплайн останавливается и ничего не деплоится. Эта стража защищает меня от сломанного кода в проде, и я на неё полагаюсь при каждом push.

Джоба 2 : Deploy

Деплой-джоба выполняется, только если :

  • Сборочная джоба успешна (needs: build)
  • Событие это push (не PR)
  • Ветка main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Дальше она идёт так :

  1. Скачивает артефакт сборки : Забирает папку dist/, произведённую сборочной джобой
  2. Настраивает GitHub Pages : Готовит окружение Pages
  3. Заливает в Pages : Готовит папку dist/ для GitHub Pages
  4. Деплоит : Публикует сайт через actions/deploy-pages@v4

Полная картина

Вот что происходит от написания до деплоя :

Пишу статью в VS Code
          ↓
    git add & commit
          ↓
       git push
          ↓
   GitHub Actions триггерится
          ↓
   ┌─────────────────┐
   │   BUILD JOB     │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ✓      │
   │  6. Build ✓     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy 🚀   │
   └─────────────────┘
            ↓
     Онлайн на GitHub Pages !

Весь процесс занимает около минуты между push и выходом онлайн. Публикую простым git push, всё остаётся автоматическим без FTP и SSH.

Продакшн-сборка

Под капотом команда pnpm build выполняет :

  1. tsc -b : Проверяет типы TypeScript
  2. vite build : Бандлит и оптимизирует весь код

Vite даёт минифицированные и оптимизированные файлы с автоматическим code-splitting. Итог это ультрабыстрый статичный сайт.

Почему такая архитектура?

Я мог бы использовать CMS, генератор статичных сайтов вроде Hugo или Jekyll, или даже Next.js. Но вот почему я выбрал этот подход :

  • Простота : Пиши в Markdown, пушь на GitHub, оно онлайн
  • Полный контроль : Никакой зависимости от CMS или базы данных
  • Производительность : Vite + React = быстрая загрузка
  • Гибкость : Могу мешать Markdown и HTML как хочу
  • Учёба : Отличный проект, чтобы освоить React и TypeScript
  • CI/CD : Проверки качества и автодеплой через GitHub Actions

Заключение

Этот блог простой, но продуманный проект : Markdown для контента, React для рендера, Vite для производительности, GitHub Actions для CI/CD и GitHub Pages для хостинга. Он опирается только на статичные файлы, отдаваемые эффективно, с автоматизированным пайплайном, гарантирующим качество при каждом push.

Спасибо, что прочитал, и до встречи в следующей статье! 🦊

✨ AI Generated Article

Cómo funciona este blog

Entre bambalinas del blog: React, Vite, Markdown, la pipeline CI/CD

Cómo funciona este blog

¿Alguna vez te preguntaste cómo funciona este blog bajo el capó? En este artículo, te detallo toda la arquitectura de la aplicación, desde el stack técnico hasta el proceso de redacción de un artículo. Y sí, hasta te muestro cómo escribo mis artículos desde VS Code.

El stack técnico

Este blog está construido con tecnologías web modernas:

  • React 19: para la interfaz de usuario
  • TypeScript: para un código tipado y más fiable
  • Vite: como herramienta de build ultrarrápida
  • React Router v7: para la navegación entre las páginas
  • react-markdown: para transformar el Markdown en HTML
  • rehype-raw + rehype-sanitize: para autorizar HTML en bruto en el Markdown con seguridad

Estructura del proyecto

Así se ve el árbol del proyecto:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Contenido de la página inicio
│   ├── portfolio.md                ← Contenido del portfolio
│   └── articles/
│       ├── index.json              ← Lista de todos los artículos
│       ├── hello-world.md          ← Un artículo
│       ├── how-this-blog-works.md  ← ¡Este artículo!
│       └── /articles/assets/                 ← Imágenes de los artículos
├── src/
│   ├── main.tsx                    ← Punto de entrada React
│   ├── App.tsx                     ← Router principal
│   ├── components/
│   │   ├── Header.tsx              ← Barra de navegación
│   │   └── Footer.tsx              ← Pie de página
│   └── pages/
│       ├── Home.tsx                ← Página inicio
│       ├── BlogList.tsx            ← Lista de los artículos
│       ├── Article.tsx             ← Lector de artículo
│       ├── Portfolio.tsx           ← Página portfolio
│       └── NotFound.tsx            ← Página 404
└── vite.config.ts                  ← Configuración Vite

La idea central es simple: el contenido está separado del código. Las páginas se escriben en Markdown en la carpeta public/, y el código React en src/ se encarga de mostrarlas. Mantengo esa separación: hace el contenido fácil de releer y el código fácil de hacer evolucionar.

El sistema de ruteo

El archivo App.tsx define todas las rutas de la aplicación con React Router:

Ruta Página Descripción
/ Home Página inicio, carga home.md
/blog BlogList Lista de todos los artículos
/blog/:slug Article Un artículo, carga articles/{slug}.md
/portfolio Portfolio Página portfolio, carga portfolio.md
* NotFound Página 404 para las URLs desconocidas

Cada página tiene un rol bien definido: recupera un archivo Markdown, lo transforma en HTML con react-markdown, y lo muestra en pantalla.

Cómo funciona un artículo

Esta es la parte que más me gusta. Aquí el ciclo de vida de un artículo:

1. El archivo index.json

Todos los artículos están referenciados en public/articles/index.json. Cada entrada contiene los metadatos del artículo:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug: el identificador único, usado en la URL (/blog/hello-world)
  • title: el título mostrado en la lista
  • description: un breve resumen
  • date: la fecha de publicación

2. El archivo Markdown

El contenido del artículo es un simple archivo .md en public/articles/. El nombre del archivo corresponde al slug definido en index.json.

Puedes meter lo que quieras: títulos, listas, imágenes, tablas, ¡e incluso HTML en bruto gracias a rehype-raw!

3. El render en React

Cuando visitas /blog/hello-world, esto es lo que pasa:

  1. React Router recupera el parámetro slug desde la URL
  2. El componente Article.tsx carga /articles/hello-world.md
  3. El Markdown se transforma en HTML por react-markdown
  4. Los enlaces hacia /articles/assets/ se reescriben automáticamente hacia /articles//articles/assets/
  5. En paralelo, los metadatos se cargan desde index.json para mostrar la fecha y la descripción

Así de simple.

La página inicio y el portfolio

Las páginas Inicio y Portfolio funcionan exactamente igual: cargan un archivo Markdown (home.md o portfolio.md) y lo renderizan en HTML.

La particularidad es que usan un esquema de sanitización personalizado que autoriza los atributos class y style en todos los elementos HTML. Eso me permite escribir HTML estilizado directamente en el Markdown, como galerías de imágenes por ejemplo.

El Header está fijado arriba de la página con position: fixed. Contiene:

  • Mi avatar GitHub (cargado directamente desde github.com/fox3000foxy.png)
  • El título del blog
  • Los enlaces de navegación: Inicio, Blog, Portfolio

El Footer es minimalista: solo un copyright con el año actual calculado dinámicamente.

El tema oscuro

El sitio queda en modo oscuro permanentemente. Elegí eso deliberadamente para mantener una lectura cómoda por la noche: color-scheme: dark está definido en los estilos globales, con un fondo negro #000 y texto blanco #fff. Los enlaces son azules (#64b5f6) y se vuelven verdes al pasar (#81c784).

Cómo escribo un artículo

Pasemos a la práctica. Aquí mi flujo de trabajo para escribir un artículo nuevo:

Paso 1: Crear el archivo Markdown

Abro VS Code y creo un nuevo archivo .md en public/articles/:

Paso 2: Escribir el contenido

Escribo el contenido del artículo directamente en Markdown. VS Code tiene una excelente vista previa Markdown integrada:

Para las imágenes, las pongo en public/articles//articles/assets/ y las referencio con la sintaxis Markdown estándar:

![description](/articles/assets/my-image.png)

El componente Article.tsx reescribe automáticamente la ruta /articles/assets/ hacia /articles//articles/assets/ para que las imágenes se muestren correctamente.

Paso 3: Registrar el artículo en index.json

Una vez terminado el artículo, lo añado en public/articles/index.json para que aparezca en la lista del blog:

Paso 4: Probar en local

Lanzo el servidor de desarrollo Vite:

pnpm dev

Vite arranca en unos milisegundos y puedo ver mi artículo en tiempo real en localhost:5173. Esa rapidez me permite comprobar cada modificación casi al instante:

Paso 5: Publicar

¡Un simple git push basta! El pipeline CI/CD se encarga del resto automáticamente.

El pipeline de despliegue CI/CD

Puse en marcha un pipeline GitHub Actions completo que automatiza el lint, el build y el despliegue del sitio en cada push a main. Veámoslo en detalle.

El workflow está en .github/workflows/deploy.yml y se divide en dos jobs: build y deploy.

Disparadores

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

El pipeline se ejecuta en cada push a main y en cada pull request hacia main. Las PRs se verifican (lint + build) antes de fusionarse, pero solo los pushes a main disparan un despliegue.

Job 1: Build

El job de build corre en ubuntu-latest y sigue estos pasos:

  1. Checkout: Clona el repo con todo el historial (fetch-depth: 0)
  2. Setup pnpm: Instala la última versión de pnpm con pnpm/action-setup@v4
  3. Setup Node.js 20: Configura Node con la caché pnpm activada para instalaciones más rápidas
  4. Install dependencies: Ejecuta pnpm install --frozen-lockfile para garantizar builds reproducibles (ninguna modificación del lockfile autorizada)
  5. Lint: Ejecuta pnpm run lint (ESLint) para comprobar la calidad del código antes del build
  6. Build: Ejecuta pnpm run build, que primero verifica los tipos TypeScript (tsc -b) y luego empaqueta todo con Vite
  7. Upload artifact: Sube la carpeta dist/ como artefacto de build para el job de despliegue

Si una etapa falla (error de lint, de tipo o de build), todo el pipeline se detiene y nada se despliega. Esa guardia me protege del código roto en producción y confío en ella en cada push.

Job 2: Deploy

El job de despliegue solo se ejecuta si:

  • El job de build tuvo éxito (needs: build)
  • El evento es un push (no una PR)
  • La rama es main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Luego procede:

  1. Descarga el artefacto de build: Recupera la carpeta dist/ producida por el job de build
  2. Configura GitHub Pages: Pone en marcha el entorno Pages
  3. Sube hacia Pages: Prepara la carpeta dist/ para GitHub Pages
  4. Despliega: Publica el sitio con actions/deploy-pages@v4

El cuadro completo

Esto es lo que pasa desde la escritura hasta el despliegue:

Escribir el artículo en VS Code
          ↓
     git add & commit
          ↓
        git push
          ↓
    GitHub Actions se dispara
          ↓
    ┌─────────────────┐
    │   BUILD JOB     │
    │  1. Checkout    │
    │  2. Setup pnpm  │
    │  3. Setup Node  │
    │  4. Install     │
    │  5. Lint ✓      │
    │  6. Build ✓     │
    │  7. Upload dist │
    └────────┬────────┘
             ↓
    ┌─────────────────┐
    │  DEPLOY JOB     │
    │  1. Download    │
    │  2. Configure   │
    │  3. Upload      │
    │  4. Deploy 🚀   │
    └─────────────────┘
             ↓
      En línea en GitHub Pages

El proceso entero tarda alrededor de un minuto entre el push y la puesta en línea. Publico con un simple git push, todo queda automático sin pasar por FTP ni SSH.

El build de producción

Bajo el capó, el comando pnpm build ejecuta:

  1. tsc -b: Verifica los tipos TypeScript
  2. vite build: Empaqueta y optimiza todo el código

Vite produce archivos minificados y optimizados con code-splitting automático. El resultado es un sitio estático ultrarrápido.

Por qué esta arquitectura

Podría haber usado un CMS, un generador de sitio estático como Hugo o Jekyll, o incluso Next.js. Pero aquí por qué elegí este enfoque:

  • Simplicidad: Escribe en Markdown, push a GitHub, está en línea
  • Control total: Ninguna dependencia de un CMS o una base de datos
  • Rendimiento: Vite + React = carga rápida
  • Flexibilidad: Puedo mezclar Markdown y HTML como quiera
  • Aprendizaje: Es un superproyecto para dominar React y TypeScript
  • CI/CD: Comprobaciones de calidad y despliegue automatizados con GitHub Actions

Conclusión

Este blog es un proyecto simple pero bien pensado: Markdown para el contenido, React para el render, Vite para el rendimiento, GitHub Actions para el CI/CD, y GitHub Pages para el alojamiento. Solo se apoya en archivos estáticos servidos eficazmente, con un pipeline automatizado que garantiza la calidad en cada push.

Gracias por leer, ¡y hasta pronto en el próximo artículo! 🦊

✨ AI Generated Article

Como este blog funciona?

Os bastidores do blog: React, Vite, Markdown, o pipeline CI/CD

Como Este Blog Funciona?

Você já se perguntou como este blog funciona por baixo dos panos? Neste artigo, vou detalhar toda a arquitetura da aplicação, desde a stack técnica até o processo de redação de um artigo. E sim, vou até te mostrar como escrevo meus artigos diretamente do VS Code!

A Stack Técnica

Este blog é construído com tecnologias web modernas:

  • React 19 -- para a interface do usuário
  • TypeScript -- para um código tipado e mais confiável
  • Vite -- como ferramenta de build ultra-rápida
  • React Router v7 -- para a navegação entre as páginas
  • react-markdown -- para transformar Markdown em HTML
  • rehype-raw + rehype-sanitize -- para permitir HTML bruto no Markdown com segurança

Estrutura do Projeto

Aqui está como é a árvore do projeto:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Conteúdo da página inicial
│   ├── portfolio.md                ← Conteúdo do portfólio
│   └── articles/
│       ├── index.json              ← Lista de todos os artigos
│       ├── hello-world.md          ← Um artigo
│       ├── how-this-blog-works.md  ← Este artigo!
│       └── /articles/assets/                 ← Imagens dos artigos
├── src/
│   ├── main.tsx                    ← Ponto de entrada React
│   ├── App.tsx                     ← Roteador principal
│   ├── components/
│   │   ├── Header.tsx              ← Barra de navegação
│   │   └── Footer.tsx              ← Rodapé
│   └── pages/
│       ├── Home.tsx                ← Página inicial
│       ├── BlogList.tsx            ← Lista de artigos
│       ├── Article.tsx             ← Leitor de artigos
│       ├── Portfolio.tsx           ← Página de portfólio
│       └── NotFound.tsx            ← Página 404
└── vite.config.ts                  ← Configuração Vite

A ideia central é simples: o conteúdo é separado do código. As páginas são escritas em Markdown na pasta public/, e o código React em src/ cuida de exibi-las.

O Sistema de Roteamento

O arquivo App.tsx define todas as rotas da aplicação com React Router:

Rota Página Descrição
/ Home Página inicial, carrega home.md
/blog BlogList Lista de todos os artigos
/blog/:slug Article Um artigo, carrega articles/{slug}.md
/portfolio Portfolio Página de portfólio, carrega portfolio.md
* NotFound Página 404 para URLs desconhecidas

Cada página tem um papel bem definido: ela busca um arquivo Markdown, transforma em HTML com react-markdown, e o exibe na tela.

Como Funciona um Artigo?

Esta é a parte mais interessante! Aqui está o ciclo de vida de um artigo:

1. O Arquivo index.json

Todos os artigos são referenciados em public/articles/index.json. Cada entrada contém os metadados do artigo:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- o identificador único, usado na URL (/blog/hello-world)
  • title -- o título exibido na lista
  • description -- um breve resumo
  • date -- a data de publicação

2. O Arquivo Markdown

O conteúdo do artigo é um simples arquivo .md em public/articles/. O nome do arquivo corresponde ao slug definido em index.json.

Você pode colocar o que quiser: títulos, listas, imagens, tabelas, e até HTML bruto graças ao rehype-raw!

3. A Renderização no React

Quando você visita /blog/hello-world, aqui está o que acontece:

  1. React Router captura o parâmetro slug da URL
  2. O componente Article.tsx carrega /articles/hello-world.md
  3. O Markdown é transformado em HTML pelo react-markdown
  4. Os links para /articles/assets/ são automaticamente reescritos para /articles//articles/assets/
  5. Em paralelo, os metadados são carregados do index.json para exibir a data e a descrição

É simples assim!

A Página Inicial e o Portfólio

As páginas Inicial e Portfólio funcionam exatamente da mesma forma: elas carregam um arquivo Markdown (home.md ou portfolio.md) e o renderizam em HTML.

A particularidade é que elas usam um esquema de sanitização personalizado que permite os atributos class e style em todos os elementos HTML. Isso me permite escrever HTML estilizado diretamente no Markdown, como galerias de imagens, por exemplo.

O Header está fixo no topo da página com position: fixed. Ele contém:

  • Meu avatar do GitHub (carregado diretamente de github.com/fox3000foxy.png)
  • O título do blog
  • Os links de navegação: Início, Blog, Portfólio

O Footer é minimalista: apenas um copyright com o ano atual calculado dinamicamente.

O Tema Escuro

O site está sempre no modo escuro -- sem alternância dia/noite. É uma escolha deliberada: color-scheme: dark está definido nos estilos globais, com fundo preto #000 e texto branco #fff. Os links são azuis (#64b5f6) e ficam verdes ao passar o mouse (#81c784).

Como Eu Escrevo um Artigo

Vamos à prática! Aqui está meu fluxo de trabalho para escrever um novo artigo:

Etapa 1: Criar o Arquivo Markdown

Abro o VS Code e crio um novo arquivo .md em public/articles/:

Etapa 2: Escrever o Conteúdo

Escrevo o conteúdo do artigo diretamente em Markdown. O VS Code tem uma excelente pré-visualização de Markdown integrada:

Para as imagens, coloco-as em public/articles//articles/assets/ e as referencio com a sintaxe Markdown padrão:

![descrição](/articles/assets/my-image.png)

O componente Article.tsx reescreve automaticamente o caminho /articles/assets/ para /articles//articles/assets/ para que as imagens sejam exibidas corretamente.

Etapa 3: Registrar o Artigo no index.json

Assim que o artigo estiver pronto, eu o adiciono em public/articles/index.json para que apareça na lista do blog:

Etapa 4: Testar Localmente

Inicio o servidor de desenvolvimento Vite:

pnpm dev

O Vite inicia em alguns milissegundos e eu posso ver meu artigo em tempo real em localhost:5173:

Etapa 5: Publicar

Um simples git push é suficiente! O pipeline CI/CD cuida do resto automaticamente.

O Pipeline de Deploy CI/CD

Montei um pipeline GitHub Actions completo que automatiza o lint, o build e o deploy do site a cada push na main. Vamos ver isso em detalhes.

O workflow está em .github/workflows/deploy.yml e é dividido em dois jobs: build e deploy.

Gatilhos

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

O pipeline é executado a cada push na main e a cada pull request visando main. As PRs são verificadas (lint + build) antes de serem mescladas, mas apenas pushes na main disparam um deploy.

Job 1: Build

O job de build roda em ubuntu-latest e segue estas etapas:

  1. Checkout -- Clona o repositório com todo o histórico (fetch-depth: 0)
  2. Setup pnpm -- Instala a versão mais recente do pnpm com pnpm/action-setup@v4
  3. Setup Node.js 20 -- Configura o Node com cache pnpm ativado para instalações mais rápidas
  4. Install dependencies -- Executa pnpm install --frozen-lockfile para garantir builds reproduzíveis (nenhuma modificação do lockfile é permitida)
  5. Lint -- Executa pnpm run lint (ESLint) para verificar a qualidade do código antes do build
  6. Build -- Executa pnpm run build, que primeiro verifica os tipos TypeScript (tsc -b) e então empacota tudo com Vite
  7. Upload artifact -- Envia a pasta dist/ como artefato de build para o job de deploy

Se alguma etapa falhar -- um erro de lint, tipo ou build -- todo o pipeline para e nada é implantado. Isso protege o site em produção contra código quebrado.

Job 2: Deploy

O job de deploy só é executado se:

  • O job de build tiver sucesso (needs: build)
  • O evento for um push (não uma PR)
  • A branch for main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Ele então prossegue:

  1. Baixa o artefato de build -- Recupera a pasta dist/ produzida pelo job de build
  2. Configura GitHub Pages -- Prepara o ambiente Pages
  3. Envia para Pages -- Prepara a pasta dist/ para o GitHub Pages
  4. Deploy -- Publica o site com actions/deploy-pages@v4

O Fluxo Completo

Aqui está o que acontece desde a escrita até o deploy:

Escrever o artigo no VS Code
         ↓
   git add & commit
         ↓
      git push
         ↓
  GitHub Actions é acionado
         ↓
  ┌─────────────────┐
  │   BUILD JOB     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY JOB     │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    Online no GitHub Pages!

O processo inteiro leva cerca de um minuto entre o push e a publicação. Sem deploy manual, sem FTP, sem SSH -- apenas git push e pronto.

O Build de Produção

Por baixo dos panos, o comando pnpm build executa:

  1. tsc -b -- Verifica os tipos TypeScript
  2. vite build -- Empacota e otimiza todo o código

Vite produz arquivos minificados e otimizados com code-splitting automático. O resultado é um site estático ultra-rápido.

Por Que Esta Arquitetura?

Eu poderia ter usado um CMS, um gerador de site estático como Hugo ou Jekyll, ou até Next.js. Mas aqui está por que escolhi esta abordagem:

  • Simplicidade -- Escreva em Markdown, faça push no GitHub, está online
  • Controle total -- Sem dependência de CMS ou banco de dados
  • Performance -- Vite + React = carregamento rápido
  • Flexibilidade -- Posso misturar Markdown e HTML como quiser
  • Aprendizado -- É um ótimo projeto para dominar React e TypeScript
  • CI/CD -- Verificações de qualidade e deploy automatizados com GitHub Actions

Conclusão

Este blog é um projeto simples, mas bem pensado: Markdown para o conteúdo, React para a renderização, Vite para performance, GitHub Actions para CI/CD, e GitHub Pages para hospedagem. Sem banco de dados, sem servidor backend, apenas arquivos estáticos servidos eficientemente com um pipeline automatizado que garante a qualidade a cada push.

Obrigado por ler, e até o próximo artigo! 🦊

✨ AI Generated Article

Bagaimana cara kerja blog ini?

Di balik layar blog : React, Vite, Markdown, pipeline CI/CD

Bagaimana Cara Kerja Blog Ini?

Pernah bertanya-tanya bagaimana blog ini bekerja di balik kapnya? Dalam artikel ini, saya akan merinci seluruh arsitektur aplikasinya, dari stack teknis sampai proses menulis artikel. Dan ya, saya bahkan akan menunjukkan cara saya menulis artikel dari VS Code!

Stack Teknis

Blog ini dibangun dengan teknologi web modern :

  • React 19 : untuk antarmuka pengguna
  • TypeScript : untuk kode bertipe dan lebih andal
  • Vite : sebagai alat build super cepat
  • React Router v7 : untuk navigasi antar halaman
  • react-markdown : untuk mengubah Markdown menjadi HTML
  • rehype-raw + rehype-sanitize : untuk mengizinkan HTML mentah di Markdown dengan aman

Struktur Proyek

Beginilah tampilan pohon proyeknya :

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Isi halaman beranda
│   ├── portfolio.md                ← Isi portofolio
│   └── articles/
│       ├── index.json              ← Daftar semua artikel
│       ├── hello-world.md          ← Un article
│       ├── how-this-blog-works.md  ← Cet article !
│       └── /articles/assets/                 ← Gambar artikel
├── src/
│   ├── main.tsx                    ← Titik masuk React
│   ├── App.tsx                     ← Router utama
│   ├── components/
│   │   ├── Header.tsx              ← Bilah navigasi
│   │   └── Footer.tsx              ← Kaki halaman
│   └── pages/
│       ├── Home.tsx                ← Halaman beranda
│       ├── BlogList.tsx            ← Daftar artikel
│       ├── Article.tsx             ← Pembaca artikel
│       ├── Portfolio.tsx           ← Halaman portofolio
│       └── NotFound.tsx            ← Halaman 404
└── vite.config.ts                  ← Konfigurasi Vite

Ide sentralnya sederhana : konten dipisahkan dari kode. Halaman ditulis dalam Markdown di folder public/, dan kode React di src/ mengurus menampilkannya. Saya memegang pemisahan ini : ia membuat konten mudah dibaca ulang dan kode mudah dikembangkan.

Sistem Routing

Berkas App.tsx mendefinisikan semua rute aplikasi dengan React Router :

Route Page Description
/ Home Halaman beranda, memuat home.md
/blog BlogList Daftar semua artikel
/blog/:slug Article Satu artikel, memuat articles/{slug}.md
/portfolio Portfolio Halaman portofolio, memuat portfolio.md
* NotFound Halaman 404 untuk URL tak dikenal

Setiap halaman punya peran yang jelas : ia mengambil berkas Markdown, mengubahnya menjadi HTML dengan react-markdown, dan menampilkannya di layar.

Bagaimana Cara Kerja Sebuah Artikel?

Ini bagian favorit saya! Berikut siklus hidup sebuah artikel :

1. Berkas index.json

Semua artikel direferensikan di public/articles/index.json. Setiap entri berisi metadata artikel :

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug : pengenal unik, dipakai di URL (/blog/hello-world)
  • title : judul yang ditampilkan di daftar
  • description : ringkasan pendek
  • date : tanggal publikasi

2. Berkas Markdown

Isi artikel adalah berkas .md sederhana di public/articles/. Nama berkas cocok dengan slug yang didefinisikan di index.json.

Kamu bisa menaruh apa pun di sana : judul, daftar, gambar, tabel, bahkan HTML mentah berkat rehype-raw !

3. Render Sisi React

Ketika kamu mengunjungi /blog/hello-world, inilah yang terjadi :

  1. React Router mengambil parameter slug dari URL
  2. Komponen Article.tsx memuat /articles/hello-world.md
  3. Markdown diubah menjadi HTML oleh react-markdown
  4. Tautan ke /articles/assets/ otomatis ditulis ulang ke /articles//articles/assets/
  5. Secara paralel, metadata dimuat dari index.json untuk menampilkan tanggal dan deskripsi

Sesederhana itu!

Halaman Beranda dan Portofolio

Halaman Beranda dan Portofolio bekerja persis dengan cara yang sama : mereka memuat berkas Markdown (home.md atau portfolio.md) dan me rendernya menjadi HTML.

Kekhasannya adalah mereka memakai skema sanitization kustom yang mengizinkan atribut class dan style pada semua elemen HTML. Itu memungkinkan saya menulis HTML bergaya langsung di Markdown, seperti galeri gambar misalnya.

Header disematkan di atas halaman dengan position: fixed. Ia berisi :

  • Avatar GitHub saya (dimuat langsung dari github.com/fox3000foxy.png)
  • Judul blog
  • Tautan navigasi : Beranda, Blog, Portofolio

Footer minimalis : hanya copyright dengan tahun berjalan yang dihitung dinamis.

Tema Gelap

Situs tetap dalam mode gelap permanen. Saya membuat pilihan ini dengan sengaja agar bacaan tetap nyaman di malam hari : color-scheme: dark didefinisikan dalam gaya global, dengan latar hitam #000 dan teks putih #fff. Tautan berwarna biru (#64b5f6) dan menjadi hijau saat dihover (#81c784).

Cara Saya Menulis Artikel

Masuk ke praktik! Berikut workflow saya untuk menulis artikel baru :

Langkah 1 : Membuat Berkas Markdown

Saya membuka VS Code dan membuat berkas .md baru di public/articles/ :

Langkah 2 : Menulis Isi

Saya menulis isi artikel langsung dalam Markdown. VS Code punya pratinjau Markdown bawaan yang bagus :

Untuk gambar, saya menaruhnya di public/articles//articles/assets/ dan mereferensikannya dengan sintaks Markdown standar :

![description](/articles/assets/my-image.png)

Komponen Article.tsx otomatis menulis ulang path /articles/assets/ ke /articles//articles/assets/ supaya gambar tampil dengan benar.

Langkah 3 : Mencatat Artikel di index.json

Setelah artikel selesai, saya menambahkannya di public/articles/index.json supaya muncul di daftar blog :

Langkah 4 : Menguji Secara Lokal

Saya menjalankan server pengembangan Vite :

pnpm dev

Vite menyala dalam beberapa milidetik dan saya bisa melihat artikel saya waktu nyata di localhost:5173. Kecepatan ini memungkinkan saya memeriksa setiap perubahan hampir seketika :

Langkah 5 : Menerbitkan

Satu git push saja cukup! Pipeline CI/CD mengurus sisanya otomatis.

Pipeline Deployment CI/CD

Saya menyiapkan pipeline GitHub Actions lengkap yang mengotomatisasi lint, build, dan deployment situs di setiap push ke main. Mari lihat detailnya.

Workflow ada di .github/workflows/deploy.yml dan dibagi dua job : build dan deploy.

Pemicu

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Pipeline berjalan di setiap push ke main dan setiap pull request yang menarget main. Jadi PR diverifikasi (lint + build) sebelum di merge, tapi hanya push ke main yang memicu deployment.

Job 1 : Build

Job build berjalan di ubuntu-latest dan mengikuti langkah ini :

  1. Checkout : Clone repositori dengan seluruh riwayat (fetch-depth: 0)
  2. Setup pnpm : Memasang versi terbaru pnpm dengan pnpm/action-setup@v4
  3. Setup Node.js 20 : Mengonfigurasi Node dengan cache pnpm aktif untuk instalasi lebih cepat
  4. Install dependencies : Menjalankan pnpm install --frozen-lockfile untuk menjamin build yang bisa direproduksi (tidak ada modifikasi lockfile yang diizinkan)
  5. Lint : Menjalankan pnpm run lint (ESLint) untuk memeriksa kualitas kode sebelum build
  6. Build : Menjalankan pnpm run build, yang pertama memeriksa tipe TypeScript (tsc -b) lalu membundel semuanya dengan Vite
  7. Upload artifact : Mengunggah folder dist/ sebagai artefak build untuk job deployment

Kalau satu langkah gagal (error lint, tipe, atau build), seluruh pipeline berhenti dan tidak ada yang di deploy. Penjagaan ini melindungi saya dari kode rusak di produksi dan saya mengandalkannya di setiap push.

Job 2 : Deploy

Job deployment hanya berjalan kalau :

  • Job build berhasil (needs: build)
  • Eventnya push (bukan PR)
  • Branchnya main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Lalu ia melanjutkan :

  1. Mengunduh artefak build : Mengambil folder dist/ yang dihasilkan job build
  2. Mengonfigurasi GitHub Pages : Menyiapkan lingkungan Pages
  3. Mengunggah ke Pages : Menyiapkan folder dist/ untuk GitHub Pages
  4. Mendeploy : Menerbitkan situs dengan actions/deploy-pages@v4

Tabel Lengkap

Inilah yang terjadi dari penulisan hingga deployment :

Menulis artikel di VS Code
          ↓
    git add & commit
          ↓
        git push
          ↓
    GitHub Actions terpicu
          ↓
    ┌─────────────────┐
    │   BUILD JOB     │
    │  1. Checkout    │
    │  2. Setup pnpm  │
    │  3. Setup Node  │
    │  4. Install     │
    │  5. Lint ✓      │
    │  6. Build ✓     │
    │  7. Upload dist │
    └────────┬────────┘
             ↓
    ┌─────────────────┐
    │  DEPLOY JOB     │
    │  1. Download    │
    │  2. Configure   │
    │  3. Upload      │
    │  4. Deploy 🚀   │
    └─────────────────┘
             ↓
      Online di GitHub Pages !

Seluruh proses memakan waktu sekitar satu menit antara push dan online. Saya menerbitkan dengan satu git push, semuanya tetap otomatis tanpa lewat FTP maupun SSH.

Build Produksi

Di balik kapnya, perintah pnpm build menjalankan :

  1. tsc -b : Memeriksa tipe TypeScript
  2. vite build : Membundel dan mengoptimalkan seluruh kode

Vite menghasilkan berkas yang diminifikasi dan dioptimalkan dengan code-splitting otomatis. Hasilnya adalah situs statis super cepat.

Kenapa Arsitektur Ini?

Saya bisa saja memakai CMS, pembangkit situs statis seperti Hugo atau Jekyll, atau bahkan Next.js. Tapi inilah kenapa saya memilih pendekatan ini :

  • Kesederhanaan : Tulis dalam Markdown, push ke GitHub, online
  • Kontrol total : Tidak ada ketergantungan pada CMS atau basis data
  • Performa : Vite + React = pemuatan cepat
  • Fleksibilitas : Saya bisa mencampur Markdown dan HTML sesuka saya
  • Pembelajaran : Ini proyek bagus untuk menguasai React dan TypeScript
  • CI/CD : Pemeriksaan kualitas dan deployment otomatis dengan GitHub Actions

Kesimpulan

Blog ini proyek sederhana tapi dipikirkan matang : Markdown untuk konten, React untuk render, Vite untuk performa, GitHub Actions untuk CI/CD, dan GitHub Pages untuk hosting. Ia hanya bersandar pada berkas statis yang disajikan efisien, dengan pipeline otomatis yang menjamin kualitas di setiap push.

Terima kasih sudah membaca, dan sampai jumpa di artikel berikutnya! 🦊

✨ AI Generated Article

यह ब्लॉग कैसे काम करता है ?

ब्लॉग के परदे के पीछे : React, Vite, Markdown, CI/CD पाइपलाइन और लेखन प्रवाह।

यह ब्लॉग कैसे काम करता है ?

तुमने कभी सोचा यह ब्लॉग हुड के नीचे कैसे काम करता है ? इस लेख में, मैं application की पूरी आर्किटेक्चर विस्तार से बताऊँगा, तकनीकी स्टैक से लेकर लेख लिखने की प्रक्रिया तक। और हाँ, मैं यह भी दिखाऊँगा कि मैं VS Code से अपने लेख कैसे लिखता हूँ !

तकनीकी स्टैक

यह ब्लॉग आधुनिक वेब तकनीकों से बना है :

  • React 19 : उपयोगकर्ता इंटरफेस के लिए
  • TypeScript : typed और ज्यादा विश्वसनीय कोड के लिए
  • Vite : अति तेज build उपकरण के रूप में
  • React Router v7 : पेजों के बीच नेविगेशन के लिए
  • react-markdown : Markdown को HTML में बदलने के लिए
  • rehype-raw + rehype-sanitize : Markdown में कच्चा HTML सुरक्षित अनुमति देने के लिए

प्रोजेक्ट संरचना

प्रोजेक्ट वृक्ष ऐसा दिखता है :

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Contenu de la page d'accueil
│   ├── portfolio.md                ← Contenu du portfolio
│   └── articles/
│       ├── index.json              ← Liste de tous les articles
│       ├── hello-world.md          ← Un article
│       ├── how-this-blog-works.md  ← Cet article !
│       └── /articles/assets/                 ← Images des articles
├── src/
│   ├── main.tsx                    ← Point d'entrée React
│   ├── App.tsx                     ← Routeur principal
│   ├── components/
│   │   ├── Header.tsx              ← Barre de navigation
│   │   └── Footer.tsx              ← Pied de page
│   └── pages/
│       ├── Home.tsx                ← Page d'accueil
│       ├── BlogList.tsx            ← Liste des articles
│       ├── Article.tsx             ← Lecteur d'article
│       ├── Portfolio.tsx           ← Page portfolio
│       └── NotFound.tsx            ← Page 404
└── vite.config.ts                  ← Configuration Vite

केंद्रीय विचार सरल है : सामग्री कोड से अलग है। पेज public/ फोल्डर में Markdown में लिखे हैं, और src/ में React कोड उन्हें दिखाने का काम करता है। मैं इस अलगाव से जुड़ा हूँ : यह सामग्री को फिर से पढ़ना आसान और कोड को विकसित करना आसान बनाता है।

routing तंत्र

App.tsx फाइल React Router से application के सभी route परिभाषित करती है :

Route Page Description
/ Home Page d'accueil, charge home.md
/blog BlogList Liste de tous les articles
/blog/:slug Article Un article, charge articles/{slug}.md
/portfolio Portfolio Page portfolio, charge portfolio.md
* NotFound Page 404 pour les URLs inconnues

हर पेज की अच्छी तरह परिभाषित भूमिका है : वह Markdown फाइल लाता है, उसे react-markdown से HTML में बदलता है, और स्क्रीन पर दिखाता है।

लेख कैसे काम करता है ?

यह वह हिस्सा है जो मुझे सबसे ज्यादा पसंद है ! यहाँ लेख का जीवन चक्र है :

1. index.json फाइल

सभी लेख public/articles/index.json में संदर्भित हैं। हर प्रविष्टि में लेख के मेटाडेटा हैं :

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug : अद्वितीय पहचान, URL में इस्तेमाल (/blog/hello-world)
  • title : सूची में दिखाया शीर्षक
  • description : संक्षिप्त सारांश
  • date : प्रकाशन तिथि

2. Markdown फाइल

लेख की सामग्री public/articles/ में साधारण .md फाइल है। फाइल नाम index.json में परिभाषित slug से मेल खाता है।

तुम उसमें जो चाहो रख सकते हो : शीर्षक, सूचियाँ, चित्र, तालिकाएँ, और rehype-raw से कच्चा HTML भी !

3. React तरफ रेंडर

जब तुम /blog/hello-world देखते हो, तो यह होता है :

  1. React Router URL से slug पैरामीटर लेता है
  2. Article.tsx घटक /articles/hello-world.md लोड करता है
  3. Markdown react-markdown से HTML में बदला जाता है
  4. /articles/assets/ की ओर लिंक अपने आप /articles//articles/assets/ की ओर फिर से लिखे जाते हैं
  5. समानांतर में, तिथि और विवरण दिखाने के लिए मेटाडेटा index.json से लोड किए जाते हैं

इतना ही सरल है !

होम पेज और Portfolio

होम और Portfolio पेज बिल्कुल वैसे ही काम करते हैं : वे Markdown फाइल (home.md या portfolio.md) लोड करते हैं और उसे HTML में रेंडर करते हैं।

विशेषता यह है कि वे कस्टम sanitization schema इस्तेमाल करते हैं जो सभी HTML तत्वों पर class और style विशेषताएँ अनुमति देता है। यह मुझे सीधे Markdown में स्टाइल HTML लिखने देता है, जैसे चित्र गैलरी उदाहरण के लिए।

Header position: fixed से पेज के ऊपर टिका है। इसमें है :

  • मेरा GitHub अवतार (सीधे github.com/fox3000foxy.png से लोड)
  • ब्लॉग शीर्षक
  • नेविगेशन लिंक : होम, ब्लॉग, Portfolio

Footer न्यूनतम है : सिर्फ गतिशील गणना किए वर्तमान वर्ष के साथ copyright।

गहरा थीम

साइट स्थायी रूप से गहरे मोड में रहती है। मैंने शाम को आरामदायक पढ़ने के लिए जानबूझकर यह चुनाव किया : वैश्विक शैलियों में color-scheme: dark परिभाषित है, काली पृष्ठभूमि #000 और सफेद पाठ #fff के साथ। लिंक नीले हैं (#64b5f6) और होवर पर हरे हो जाते हैं (#81c784)।

मैं लेख कैसे लिखता हूँ

व्यवहार पर आते हैं ! नया लेख लिखने का मेरा workflow यहाँ है :

चरण 1 : Markdown फाइल बनाना

मैं VS Code खोलता हूँ और public/articles/ में नई .md फाइल बनाता हूँ :

चरण 2 : सामग्री लिखना

मैं लेख की सामग्री सीधे Markdown में लिखता हूँ। VS Code में उत्कृष्ट अंतर्निहित Markdown पूर्वावलोकन है :

चित्रों के लिए, मैं उन्हें public/articles//articles/assets/ में रखता हूँ और मानक Markdown syntax से संदर्भित करता हूँ :

![description](/articles/assets/my-image.png)

Article.tsx घटक /articles/assets/ पथ को अपने आप /articles//articles/assets/ की ओर फिर से लिखता है ताकि चित्र सही दिखें।

चरण 3 : index.json में लेख दर्ज करना

लेख खत्म होने पर, मैं उसे public/articles/index.json में जोड़ता हूँ ताकि वह ब्लॉग सूची में दिखे :

चरण 4 : स्थानीय परीक्षण

मैं Vite विकास सर्वर चलाता हूँ :

pnpm dev

Vite कुछ मिलीसेकंड में शुरू होता है और मैं अपना लेख localhost:5173 पर वास्तविक समय देख सकता हूँ। यह गति मुझे लगभग तुरंत हर बदलाव जाँचने देती है :

चरण 5 : प्रकाशित करना

साधारण git push काफी है ! CI/CD पाइपलाइन बाकी अपने आप संभालती है।

CI/CD परिनियोजन पाइपलाइन

मैंने पूरा GitHub Actions पाइपलाइन लगाया है जो main पर हर push पर साइट का lint, build और परिनियोजन स्वचालित करता है। विस्तार से देखते हैं।

workflow .github/workflows/deploy.yml में है और दो job में बँटा है : build और deploy।

trigger

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

पाइपलाइन main पर हर push और main को लक्षित हर pull request पर चलता है। इसलिए PR merge होने से पहले जाँचे जाते हैं (lint + build), लेकिन सिर्फ main पर push परिनियोजन ट्रिगर करते हैं।

Job 1 : Build

build job ubuntu-latest पर चलता है और इन चरणों का पालन करता है :

  1. Checkout : पूरे इतिहास के साथ रिपॉजिटरी clone करता है (fetch-depth: 0)
  2. Setup pnpm : pnpm/action-setup@v4 से pnpm का नवीनतम संस्करण स्थापित करता है
  3. Setup Node.js 20 : तेज स्थापना के लिए सक्षम pnpm cache के साथ Node कॉन्फ़िगर करता है
  4. Install dependencies : पुनरुत्पादनीय build गारंटी के लिए pnpm install --frozen-lockfile निष्पादित करता है (lockfile का कोई बदलाव अनुमत नहीं)
  5. Lint : build से पहले कोड गुणवत्ता जाँचने के लिए pnpm run lint (ESLint) निष्पादित करता है
  6. Build : pnpm run build निष्पादित करता है, जो पहले TypeScript प्रकार जाँचता है (tsc -b) फिर Vite से सब bundle करता है
  7. Upload artifact : परिनियोजन job के लिए dist/ फोल्डर build कलाकृति के रूप में अपलोड करता है

अगर कोई चरण विफल होता है (lint, प्रकार या build त्रुटि), तो पूरा पाइपलाइन रुकता है और कुछ परिनियोजित नहीं होता। यह सुरक्षा मुझे production में टूटे कोड से बचाती है और मैं हर push पर इस पर भरोसा करता हूँ।

Job 2 : Deploy

परिनियोजन job तभी चलता है अगर :

  • build job सफल हुआ (needs: build)
  • घटना push है (PR नहीं)
  • शाखा main है
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

फिर वह आगे बढ़ता है :

  1. build कलाकृति डाउनलोड : build job द्वारा बनाई dist/ फोल्डर लेता है
  2. GitHub Pages कॉन्फ़िगर : Pages वातावरण स्थापित करता है
  3. Pages की ओर अपलोड : GitHub Pages के लिए dist/ फोल्डर तैयार करता है
  4. परिनियोजन : actions/deploy-pages@v4 से साइट प्रकाशित करता है

पूरा चित्र

लेखन से परिनियोजन तक यह होता है :

VS Code में लेख लिखना
          ↓
    git add & commit
          ↓
       git push
          ↓
   GitHub Actions ट्रिगर होता है
          ↓
   ┌─────────────────┐
   │   BUILD JOB     │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ✓      │
   │  6. Build ✓     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy 🚀   │
   └─────────────────┘
            ↓
     GitHub Pages पर ऑनलाइन !

पूरी प्रक्रिया push और ऑनलाइन होने के बीच लगभग एक मिनट लेती है। मैं साधारण git push से प्रकाशित करता हूँ, सब FTP या SSH के बिना स्वचालित रहता है।

Production Build

हुड के नीचे, pnpm build कमांड निष्पादित करता है :

  1. tsc -b : TypeScript प्रकार जाँचता है
  2. vite build : सारा कोड bundle और अनुकूलित करता है

Vite स्वचालित code-splitting के साथ minified और अनुकूलित फाइलें बनाता है। परिणाम अति तेज स्थिर साइट है।

यह आर्किटेक्चर क्यों ?

मैं CMS, Hugo या Jekyll जैसा स्थिर साइट जनरेटर, या Next.js भी इस्तेमाल कर सकता था। लेकिन मैंने यह दृष्टिकोण क्यों चुना :

  • सरलता : Markdown में लिखो, GitHub पर push करो, ऑनलाइन है
  • पूर्ण नियंत्रण : CMS या डेटाबेस पर कोई निर्भरता नहीं
  • प्रदर्शन : Vite + React = तेज लोडिंग
  • लचीलापन : मैं Markdown और HTML जैसे चाहूँ मिला सकता हूँ
  • सीखना : React और TypeScript में महारत का बढ़िया प्रोजेक्ट है
  • CI/CD : GitHub Actions से स्वचालित गुणवत्ता जाँच और परिनियोजन

निष्कर्ष

यह ब्लॉग सरल लेकिन सोच समझा प्रोजेक्ट है : सामग्री के लिए Markdown, रेंडर के लिए React, प्रदर्शन के लिए Vite, CI/CD के लिए GitHub Actions, और होस्टिंग के लिए GitHub Pages। यह सिर्फ कुशलता से परोसी स्थिर फाइलों पर टिका है, स्वचालित पाइपलाइन के साथ जो हर push पर गुणवत्ता गारंटी करती है।

पढ़ने के लिए धन्यवाद, और अगले लेख में मिलते हैं ! 🦊

✨ AI Generated Article

كيف تعمل هذه المدونة؟

وراء كواليس المدونة : React وVite وMarkdown وخط CI/CD

كيف تعمل هذه المدونة ؟

هل تساءلت يوما كيف تعمل هذه المدونة تحت الغطاء ؟ في هذا المقال، سأفصّل لك بنية التطبيق كاملة، من الحزمة التقنية حتى عملية كتابة مقال. ونعم، سأريك حتى كيف أكتب مقالاتي من VS Code !

الحزمة التقنية

هذه المدونة مبنية بتقنيات ويب حديثة :

  • React 19 : لواجهة المستخدم
  • TypeScript : لكود مُنمَّط وأكثر موثوقية
  • Vite : كأداة بناء فائقة السرعة
  • React Router v7 : للتنقل بين الصفحات
  • react-markdown : لتحويل Markdown إلى HTML
  • rehype-raw + rehype-sanitize : للسماح بـHTML خام في Markdown بأمان

بنية المشروع

إليكم كيف تبدو شجرة المشروع :

├── .github/
│   └── workflows/
│       └── deploy.yml              ← خط CI/CD
├── public/
│   ├── home.md                     ← محتوى الصفحة الرئيسية
│   ├── portfolio.md                ← محتوى المحفظة
│   └── articles/
│       ├── index.json              ← قائمة كل المقالات
│       ├── hello-world.md          ← مقال
│       ├── how-this-blog-works.md  ← هذا المقال !
│       └── /articles/assets/                 ← صور المقالات
├── src/
│   ├── main.tsx                    ← مدخل React
│   ├── App.tsx                     ← الموجه الرئيسي
│   ├── components/
│   │   ├── Header.tsx              ← شريط التنقل
│   │   └── Footer.tsx              ← تذييل الصفحة
│   └── pages/
│       ├── Home.tsx                ← الصفحة الرئيسية
│       ├── BlogList.tsx            ← قائمة المقالات
│       ├── Article.tsx             ← قارئ المقال
│       ├── Portfolio.tsx           ← صفحة المحفظة
│       └── NotFound.tsx            ← صفحة 404
└── vite.config.ts                  ← إعداد Vite

الفكرة المركزية بسيطة : المحتوى منفصل عن الكود. تُكتَب الصفحات بـMarkdown في مجلد public/، وكود React في src/ يتكفل بعرضها. أتمسك بهذا الفصل : يجعل المحتوى سهل المراجعة والكود سهل التطوير.

نظام التوجيه

يعرّف ملف App.tsx كل طرق التطبيق مع React Router :

الطريق الصفحة الوصف
/ Home الصفحة الرئيسية، تحمّل home.md
/blog BlogList قائمة كل المقالات
/blog/:slug Article مقال، يحمّل articles/{slug}.md
/portfolio Portfolio صفحة المحفظة، تحمّل portfolio.md
* NotFound صفحة 404 للروابط المجهولة

لكل صفحة دور محدد : تسترجع ملف Markdown، وتحوّله إلى HTML مع react-markdown، وتعرضه على الشاشة.

كيف يعمل مقال ؟

هذا هو الجزء الذي أفضله ! إليكم دورة حياة مقال :

1. ملف index.json

كل المقالات مُشار إليها في public/articles/index.json. يحوي كل مدخل بيانات المقال الوصفية :

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug : المعرف الوحيد، المستخدم في الرابط (/blog/hello-world)
  • title : العنوان المعروض في القائمة
  • description : ملخص قصير
  • date : تاريخ النشر

2. ملف Markdown

محتوى المقال هو ملف .md بسيط في public/articles/. يطابق اسم الملف slug المعرّف في index.json.

تستطيع وضع ما تريد فيه : عناوين وقوائم وصور وجداول، وحتى HTML خام بفضل rehype-raw !

3. العرض جهة React

عندما تزور /blog/hello-world، يحدث هذا :

  1. يستخرج React Router معامل slug من الرابط
  2. يحمّل المكوّن Article.tsx ملف /articles/hello-world.md
  3. يتحوّل Markdown إلى HTML بواسطة react-markdown
  4. تُعاد كتابة روابط /articles/assets/ تلقائيا نحو /articles//articles/assets/
  5. بالتوازي، تُحمَّل البيانات الوصفية من index.json لعرض التاريخ والوصف

بهذه البساطة !

الصفحة الرئيسية والمحفظة

تعمل صفحتا الرئيسية والمحفظة بنفس الطريقة تماما : تحمّلان ملف Markdown (home.md أو portfolio.md) وتعرضانه كـHTML.

خصوصيتهما أنهما تستخدمان مخطط تعقيم مخصصا يسمح بخصائص class وstyle على كل عناصر HTML. وهذا يسمح لي بكتابة HTML منسق مباشرة في Markdown، كمعارض صور مثلا.

الترويسة والتذييل

الترويسة مثبتة أعلى الصفحة مع position: fixed. تحوي :

  • صورتي الرمزية في GitHub (مُحمَّلة مباشرة من github.com/fox3000foxy.png)
  • عنوان المدونة
  • روابط التنقل : الرئيسية والمدونة والمحفظة

التذييل بسيط : فقط حقوق نشر مع السنة الحالية محسوبة ديناميكيا.

السمة الداكنة

يبقى الموقع في الوضع الداكن دائما. اخترت ذلك عمدا لقراءة مريحة مساء : color-scheme: dark معرّف في الأنماط العامة، مع خلفية سوداء #000 ونص أبيض #fff. الروابط زرقاء (#64b5f6) وتصبح خضراء عند التحويم (#81c784).

كيف أكتب مقالا

لننتقل للعملي ! إليكم سير عملي لكتابة مقال جديد :

الخطوة 1 : إنشاء ملف Markdown

أفتح VS Code وأنشئ ملف .md جديدا في public/articles/ :

الخطوة 2 : كتابة المحتوى

أكتب محتوى المقال مباشرة بـMarkdown. لدى VS Code معاينة Markdown مدمجة ممتازة :

للصور، أضعها في public/articles//articles/assets/ وأشير إليها بصياغة Markdown القياسية :

![description](/articles/assets/my-image.png)

يعيد المكوّن Article.tsx كتابة مسار /articles/assets/ تلقائيا نحو /articles//articles/assets/ لكي تظهر الصور صحيحا.

الخطوة 3 : تسجيل المقال في index.json

بمجرد انتهاء المقال، أضيفه في public/articles/index.json ليظهر في قائمة المدونة :

الخطوة 4 : الاختبار محليا

أطلق خادم تطوير Vite :

pnpm dev

يقلع Vite في مللي ثوانٍ وأستطيع رؤية مقالي في الوقت الحقيقي على localhost:5173. تسمح لي هذه السرعة بالتحقق من كل تعديل فورا تقريبا :

الخطوة 5 : النشر

تكفي git push بسيطة ! يتكفل خط CI/CD بالباقي تلقائيا.

خط النشر CI/CD

وضعت خط GitHub Actions كاملا يؤتمت الفحص والبناء ونشر الموقع مع كل دفع على main. لنرَ ذلك بالتفصيل.

يوجد workflow في .github/workflows/deploy.yml ومنقسم إلى وظيفتين : build وdeploy.

المحفزات

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

يعمل الخط مع كل دفع على main ومع كل pull request تستهدف main. تُفحَص إذن PRs (فحص + بناء) قبل دمجها، لكن الدفعات وحدها على main تحفّز نشرا.

الوظيفة 1 : Build

تعمل وظيفة البناء على ubuntu-latest وتتبع هذه الخطوات :

  1. Checkout : استنساخ المستودع مع السجل كله (fetch-depth: 0)
  2. Setup pnpm : تثبيت أحدث إصدار من pnpm مع pnpm/action-setup@v4
  3. Setup Node.js 20 : إعداد Node مع تفعيل كاش pnpm لتثبيتات أسرع
  4. Install dependencies : تنفيذ pnpm install --frozen-lockfile لضمان بناء قابلة لإعادة الإنتاج (لا تعديل لملف القفل مسموح)
  5. Lint : تنفيذ pnpm run lint (ESLint) لفحص جودة الكود قبل البناء
  6. Build : تنفيذ pnpm run build، الذي يتحقق أولا من أنواع TypeScript (tsc -b) ثم يحزم الكل مع Vite
  7. Upload artifact : رفع مجلد dist/ كمنتج بناء لوظيفة النشر

إذا فشلت خطوة (خطأ فحص أو نوع أو بناء)، يتوقف الخط كله ولا يُنشَر شيء. يحميني هذا الحارس من الكود المكسور في الإنتاج وأعتمد عليه مع كل دفع.

الوظيفة 2 : Deploy

لا تعمل وظيفة النشر إلا إذا :

  • نجحت وظيفة البناء (needs: build)
  • الحدث دفع (لا PR)
  • الفرع main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

ثم تتابع :

  1. تنزيل منتج البناء : استرجاع مجلد dist/ الذي أنتجته وظيفة البناء
  2. إعداد GitHub Pages : تجهيز بيئة Pages
  3. الرفع نحو Pages : تجهيز مجلد dist/ لـGitHub Pages
  4. النشر : نشر الموقع مع actions/deploy-pages@v4

الجدول الكامل

إليكم ما يحدث من الكتابة للنشر :

اكتب المقال في VS Code
          ↓
    git add وcommit
          ↓
       git push
          ↓
   يُحفَّز GitHub Actions
          ↓
   ┌─────────────────┐
   │   BUILD JOB     │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ✓      │
   │  6. Build ✓     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy 🚀   │
   └─────────────────┘
            ↓
     متاح على GitHub Pages !

تستغرق العملية كلها حوالي دقيقة بين الدفع والظهور. أنشر مع git push بسيطة، ويبقى الكل آليا دون المرور عبر FTP ولا SSH.

بناء الإنتاج

تحت الغطاء، ينفذ أمر pnpm build :

  1. tsc -b : يتحقق من أنواع TypeScript
  2. vite build : يحزم ويحسّن كل الكود

ينتج Vite ملفات مصغرة ومحسنة مع تقسيم كود آلي. النتيجة موقع ثابت فائق السرعة.

لماذا هذه البنية ؟

كان بإمكاني استخدام CMS أو مولد موقع ثابت مثل Hugo أو Jekyll، أو حتى Next.js. لكن إليكم لماذا اخترت هذه المقاربة :

  • بساطة : اكتب بـMarkdown، وادفع على GitHub، وهو على الإنترنت
  • تحكم كامل : لا تبعية لـCMS ولا قاعدة بيانات
  • أداء : Vite + React = تحميل سريع
  • مرونة : أستطيع مزج Markdown وHTML كما أريد
  • تعلم : إنه مشروع رائع لإتقان React وTypeScript
  • CI/CD : فحوص جودة ونشر مؤتمت مع GitHub Actions

الخلاصة

هذه المدونة مشروع بسيط لكن مدروس : Markdown للمحتوى وReact للعرض وVite للأداء وGitHub Actions للـCI/CD وGitHub Pages للاستضافة. يرتكز فقط على ملفات ثابتة تُقدَّم بكفاءة، مع خط مؤتمت يضمن الجودة مع كل دفع.

شكرا على القراءة، وإلى اللقاء في المقال التالي ! 🦊

✨ AI Generated Article

Blog này chạy thế nào?

Hậu trường của blog : React, Vite, Markdown, pipeline CI/CD

Blog Này Chạy Thế Nào?

Bạn từng thắc mắc blog này chạy thế nào dưới nắp capo? Trong bài này, tôi sẽ trình bày chi tiết toàn bộ kiến trúc ứng dụng, từ stack kỹ thuật tới quy trình viết bài. Và đúng, tôi thậm chí sẽ cho bạn thấy tôi viết bài từ VS Code thế nào!

Stack Kỹ Thuật

Blog này xây bằng công nghệ web hiện đại :

  • React 19 : cho giao diện người dùng
  • TypeScript : cho mã có kiểu và đáng tin hơn
  • Vite : làm công cụ build siêu nhanh
  • React Router v7 : cho điều hướng giữa các trang
  • react-markdown : để biến Markdown thành HTML
  • rehype-raw + rehype-sanitize : cho phép HTML thô trong Markdown một cách an toàn

Cấu Trúc Dự Án

Đây là dáng cây thư mục của dự án :

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Nội dung trang chủ
│   ├── portfolio.md                ← Nội dung portfolio
│   └── articles/
│       ├── index.json              ← Danh sách mọi bài viết
│       ├── hello-world.md          ← Một bài viết
│       ├── how-this-blog-works.md  ← Chính bài này!
│       └── /articles/assets/                 ← Ảnh của bài viết
├── src/
│   ├── main.tsx                    ← Điểm vào React
│   ├── App.tsx                     ← Router chính
│   ├── components/
│   │   ├── Header.tsx              ← Thanh điều hướng
│   │   └── Footer.tsx              ← Chân trang
│   └── pages/
│       ├── Home.tsx                ← Trang chủ
│       ├── BlogList.tsx            ← Danh sách bài viết
│       ├── Article.tsx             ← Trình đọc bài viết
│       ├── Portfolio.tsx           ← Trang portfolio
│       └── NotFound.tsx            ← Trang 404
└── vite.config.ts                  ← Cấu hình Vite

Ý tưởng trung tâm đơn giản : nội dung tách khỏi mã. Các trang viết bằng Markdown trong thư mục public/, và mã React trong src/ lo hiển thị chúng. Tôi coi trọng tách biệt này : nó khiến nội dung dễ đọc lại và mã dễ tiến hóa.

Hệ Thống Định Tuyến

Tệp App.tsx định nghĩa mọi route của ứng dụng bằng React Router :

Route Trang Mô tả
/ Home Trang chủ, tải home.md
/blog BlogList Danh sách mọi bài viết
/blog/:slug Article Một bài viết, tải articles/{slug}.md
/portfolio Portfolio Trang portfolio, tải portfolio.md
* NotFound Trang 404 cho URL không biết

Mỗi trang có vai trò rõ : nó lấy một tệp Markdown, biến thành HTML bằng react-markdown, và hiện lên màn hình.

Bài Viết Chạy Thế Nào?

Đây là phần tôi thích nhất! Đây là vòng đời của một bài viết :

1. Tệp index.json

Mọi bài viết được tham chiếu trong public/articles/index.json. Mỗi mục chứa metadata của bài viết :

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug : định danh duy nhất, dùng trong URL (/blog/hello-world)
  • title : tiêu đề hiện trong danh sách
  • description : tóm tắt ngắn
  • date : ngày đăng

2. Tệp Markdown

Nội dung bài viết là một tệp .md đơn giản trong public/articles/. Tên tệp khớp slug định nghĩa trong index.json.

Bạn có thể đặt gì tùy : tiêu đề, danh sách, ảnh, bảng, và cả HTML thô nhờ rehype-raw!

3. Render Phía React

Khi bạn thăm /blog/hello-world, đây là những gì xảy ra :

  1. React Router lấy tham số slug từ URL
  2. Thành phần Article.tsx tải /articles/hello-world.md
  3. Markdown được biến thành HTML bởi react-markdown
  4. Các link tới /articles/assets/ được tự viết lại thành /articles//articles/assets/
  5. Song song, metadata được tải từ index.json để hiện ngày và mô tả

Đơn giản vậy thôi!

Trang Chủ và Portfolio

Các trang Chủ và Portfolio chạy đúng cùng cách : chúng tải một tệp Markdown (home.md hay portfolio.md) và render thành HTML.

Điểm đặc biệt là chúng dùng schema sanitize tùy chỉnh cho phép thuộc tính class và style trên mọi phần tử HTML. Việc này cho phép tôi viết HTML tạo kiểu trực tiếp trong Markdown, như gallery ảnh chẳng hạn.

Header ghim trên đầu trang với position: fixed. Nó chứa :

  • Avatar GitHub của tôi (tải trực tiếp từ github.com/fox3000foxy.png)
  • Tiêu đề blog
  • Các link điều hướng : Trang chủ, Blog, Portfolio

Footer tối giản : chỉ copyright với năm hiện tại tính động.

Chủ Đề Tối

Trang luôn ở chế độ tối. Tôi chọn vậy có chủ ý để giữ đọc thoải mái buổi tối : color-scheme: dark được đặt trong style toàn cục, với nền đen #000 và chữ trắng #fff. Các link màu xanh (#64b5f6) và thành xanh lá khi di chuột (#81c784).

Tôi Viết Bài Thế Nào

Vào thực hành! Đây là workflow của tôi để viết bài mới :

Bước 1 : Tạo Tệp Markdown

Tôi mở VS Code và tạo tệp .md mới trong public/articles/ :

Bước 2 : Viết Nội Dung

Tôi viết nội dung bài trực tiếp bằng Markdown. VS Code có xem trước Markdown tích hợp xuất sắc :

Với ảnh, tôi đặt chúng trong public/articles//articles/assets/ và tham chiếu bằng cú pháp Markdown chuẩn :

![description](/articles/assets/my-image.png)

Thành phần Article.tsx tự viết lại đường dẫn /articles/assets/ thành /articles//articles/assets/ để ảnh hiện đúng.

Bước 3 : Đăng Ký Bài Viết vào index.json

Khi bài xong, tôi thêm nó vào public/articles/index.json để nó hiện trong danh sách blog :

Bước 4 : Test Cục Bộ

Tôi chạy máy chủ phát triển Vite :

pnpm dev

Vite khởi động trong vài mili giây và tôi có thể thấy bài thời gian thực trên localhost:5173. Tốc độ này cho phép tôi kiểm tra mỗi sửa đổi gần như tức thì :

Bước 5 : Đăng

Một git push đơn giản là đủ! Pipeline CI/CD lo phần còn lại tự động.

Pipeline Triển Khai CI/CD

Tôi đã dựng pipeline GitHub Actions đầy đủ tự động hóa lint, build và triển khai trang mỗi lần đẩy lên main. Xem chi tiết.

Workflow nằm trong .github/workflows/deploy.yml và chia thành hai job : build và deploy.

Kích hoạt

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Pipeline chạy mỗi lần đẩy lên main và mỗi pull request nhắm main. Các PR vì vậy được kiểm tra (lint + build) trước khi merge, nhưng chỉ các lần đẩy lên main mới kích hoạt triển khai.

Job 1 : Build

Job build chạy trên ubuntu-latest và theo các bước :

  1. Checkout : Clone repo với toàn bộ lịch sử (fetch-depth: 0)
  2. Setup pnpm : Cài bản pnpm mới nhất bằng pnpm/action-setup@v4
  3. Setup Node.js 20 : Cấu hình Node với cache pnpm bật để cài nhanh hơn
  4. Install dependencies : Chạy pnpm install --frozen-lockfile để đảm bảo build tái tạo được (không cho sửa lockfile)
  5. Lint : Chạy pnpm run lint (ESLint) để kiểm tra chất lượng mã trước build
  6. Build : Chạy pnpm run build, kiểm tra kiểu TypeScript trước (tsc -b) rồi bundle tất cả bằng Vite
  7. Upload artifact : Tải thư mục dist/ lên làm artefact build cho job triển khai

Nếu một bước hỏng (lỗi lint, kiểu hay build), toàn bộ pipeline dừng và không gì được triển khai. Chốt này bảo vệ tôi khỏi mã hỏng trong production và tôi tin nó mỗi lần đẩy.

Job 2 : Deploy

Job triển khai chỉ chạy nếu :

  • Job build thành công (needs: build)
  • Sự kiện là đẩy (không phải PR)
  • Nhánh là main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Sau đó nó tiến hành :

  1. Tải artefact build : Lấy thư mục dist/ do job build tạo
  2. Cấu hình GitHub Pages : Dựng môi trường Pages
  3. Tải lên Pages : Chuẩn bị thư mục dist/ cho GitHub Pages
  4. Triển khai : Đăng trang bằng actions/deploy-pages@v4

Bảng Đầy Đủ

Đây là những gì xảy ra từ viết tới triển khai :

Viết bài trong VS Code
          ↓
    git add & commit
          ↓
       git push
          ↓
   GitHub Actions kích hoạt
          ↓
   ┌─────────────────┐
   │   BUILD JOB     │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ✓      │
   │  6. Build ✓     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy 🚀   │
   └─────────────────┘
            ↓
     Trực tuyến trên GitHub Pages !

Toàn bộ quá trình mất khoảng một phút giữa đẩy và lên sóng. Tôi đăng bằng git push đơn giản, mọi thứ vẫn tự động mà không qua FTP hay SSH.

Build Production

Dưới nắp capo, lệnh pnpm build chạy :

  1. tsc -b : Kiểm tra kiểu TypeScript
  2. vite build : Bundle và tối ưu toàn bộ mã

Vite cho ra tệp thu gọn và tối ưu với code-splitting tự động. Kết quả là trang tĩnh siêu nhanh.

Vì Sao Kiến Trúc Này?

Tôi có thể dùng CMS, trình tạo trang tĩnh như Hugo hay Jekyll, hay thậm chí Next.js. Nhưng đây là lý do tôi chọn cách này :

  • Đơn giản : Viết bằng Markdown, đẩy lên GitHub, lên sóng
  • Kiểm soát toàn bộ : Không phụ thuộc CMS hay cơ sở dữ liệu nào
  • Hiệu năng : Vite + React = tải nhanh
  • Linh hoạt : Tôi có thể trộn Markdown và HTML tùy ý
  • Học hỏi : Đó là dự án siêu hay để thành thạo React và TypeScript
  • CI/CD : Kiểm tra chất lượng và triển khai tự động bằng GitHub Actions

Kết luận

Blog này là dự án đơn giản mà nghĩ kỹ : Markdown cho nội dung, React cho render, Vite cho hiệu năng, GitHub Actions cho CI/CD, và GitHub Pages cho host. Nó chỉ dựa trên tệp tĩnh phục vụ hiệu quả, với pipeline tự động đảm bảo chất lượng mỗi lần đẩy.

Cảm ơn đã đọc, và hẹn gặp trong bài tiếp theo! 🦊

✨ AI Generated Article

บล็อกนี้ทำงานอย่างไร ?

เบื้องหลังของบล็อก: React, Vite, Markdown, CI/CD Pipeline

บล็อกนี้ทำงานอย่างไร ?

คุณเคยสงสัยไหมว่าบล็อกนี้ทำงานภายใต้ฝาครอบอย่างไร ? ในบทความนี้ ผมจะอธิบายรายละเอียดทั้งหมดเกี่ยวกับสถาปัตยกรรมของแอปพลิเคชัน ตั้งแต่เทคนิคสแตกไปจนถึงกระบวนการเขียนบทความ ใช่แล้ว ผมจะโชว์ให้คุณเห็นด้วยว่าผมเขียนบทความของผมจาก VS Code อย่างไร !

เทคนิคสแตก (Tech Stack)

บล็อกนี้สร้างขึ้นด้วยเทคโนโลยีเว็บสมัยใหม่:

  • React 19 -- สำหรับอินเทอร์เฟซผู้ใช้
  • TypeScript -- สำหรับโค้ดที่กำหนดชนิดและเชื่อถือได้มากขึ้น
  • Vite -- เป็นเครื่องมือ build ที่เร็วเป็นพิเศษ
  • React Router v7 -- สำหรับการนำทางระหว่างหน้า
  • react-markdown -- สำหรับแปลง Markdown เป็น HTML
  • rehype-raw + rehype-sanitize -- สำหรับอนุญาต HTML ดิบใน Markdown อย่างปลอดภัย

โครงสร้างโปรเจกต์

นี่คือลักษณะโครงสร้างของโปรเจกต์:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← CI/CD Pipeline
├── public/
│   ├── home.md                     ← เนื้อหาหน้าแรก
│   ├── portfolio.md                ← เนื้อหาพอร์ตโฟลิโอ
│   └── articles/
│       ├── index.json              ← รายการบทความทั้งหมด
│       ├── hello-world.md          ← บทความหนึ่ง
│       ├── how-this-blog-works.md  ← บทความนี้ !
│       └── /articles/assets/                 ← รูปภาพของบทความ
├── src/
│   ├── main.tsx                    ← จุดเริ่มต้น React
│   ├── App.tsx                     ← เราเตอร์หลัก
│   ├── components/
│   │   ├── Header.tsx              ← แถบนำทาง
│   │   └── Footer.tsx              ← ส่วนท้าย
│   └── pages/
│       ├── Home.tsx                ← หน้าแรก
│       ├── BlogList.tsx            ← รายการบทความ
│       ├── Article.tsx             ← โปรแกรมอ่านบทความ
│       ├── Portfolio.tsx           ← หน้าพอร์ตโฟลิโอ
│       └── NotFound.tsx            ← หน้า 404
└── vite.config.ts                  ← การกำหนดค่า Vite

แนวคิดหลักนั้นเรียบง่าย: เนื้อหาถูกแยกออกจากโค้ด หน้าต่าง ๆ เขียนด้วย Markdown ในโฟลเดอร์ public/ และโค้ด React ใน src/ ทำหน้าที่แสดงผล

ระบบการกำหนดเส้นทาง (Routing System)

ไฟล์ App.tsx กำหนดเส้นทางทั้งหมดของแอปพลิเคชันด้วย React Router:

Route Page คำอธิบาย
/ Home หน้าแรก, โหลด home.md
/blog BlogList รายการบทความทั้งหมด
/blog/:slug Article บทความ, โหลด articles/{slug}.md
/portfolio Portfolio หน้าพอร์ตโฟลิโอ, โหลด portfolio.md
* NotFound หน้า 404 สำหรับ URL ที่ไม่รู้จัก

แต่ละหน้ามีบทบาทที่ชัดเจน: ดึงไฟล์ Markdown, แปลงเป็น HTML ด้วย react-markdown, และแสดงผลบนหน้าจอ

บทความทำงานอย่างไร ?

นี่คือส่วนที่น่าสนใจที่สุด ! นี่คือวงจรชีวิตของบทความ:

1. ไฟล์ index.json

บทความทั้งหมดถูกอ้างอิงใน public/articles/index.json แต่ละรายการประกอบด้วยข้อมูลเมตาของบทความ:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- ตัวระบุเฉพาะ, ใช้ใน URL (/blog/hello-world)
  • title -- ชื่อเรื่องที่แสดงในรายการ
  • description -- สรุปสั้น ๆ
  • date -- วันที่เผยแพร่

2. ไฟล์ Markdown

เนื้อหาของบทความเป็นไฟล์ .md ธรรมดาใน public/articles/ ชื่อไฟล์ตรงกับ slug ที่กำหนดใน index.json

คุณสามารถใส่สิ่งที่ต้องการ: หัวข้อ, รายการ, รูปภาพ, ตาราง, และแม้แต่ HTML ดิบได้ด้วย rehype-raw !

3. การเรนเดอร์ฝั่ง React

เมื่อคุณเยี่ยมชม /blog/hello-world สิ่งนี้จะเกิดขึ้น:

  1. React Router ดึงพารามิเตอร์ slug จาก URL
  2. คอมโพเนนต์ Article.tsx โหลด /articles/hello-world.md
  3. Markdown ถูกแปลงเป็น HTML โดย react-markdown
  4. ลิงก์ไปยัง /articles/assets/ ถูกเขียนใหม่เป็น /articles//articles/assets/ โดยอัตโนมัติ
  5. ในเวลาเดียวกัน, ข้อมูลเมตาจะถูกโหลดจาก index.json เพื่อแสดงวันที่และคำอธิบาย

ง่ายแบบนั้นเลย !

หน้าแรกและพอร์ตโฟลิโอ

หน้าหลักและหน้าพอร์ตโฟลิโอทำงานในลักษณะเดียวกันทุกประการ: โหลดไฟล์ Markdown (home.md หรือ portfolio.md) และเรนเดอร์เป็น HTML

จุดพิเศษคือ พวกมันใช้ schema sanitization ที่กำหนดเองซึ่งอนุญาตให้ใช้แอตทริบิวต์ class และ style บนองค์ประกอบ HTML ทั้งหมด ซึ่งทำให้ผมสามารถเขียน HTML ที่มีสไตล์ได้โดยตรงใน Markdown เช่น แกลเลอรีรูปภาพ

Header ถูกปักหมุดไว้ด้านบนของหน้าด้วย position: fixed ประกอบด้วย:

  • Avatar GitHub ของผม (โหลดโดยตรงจาก github.com/fox3000foxy.png)
  • ชื่อบล็อก
  • ลิงก์นำทาง: หน้าแรก, บล็อก, พอร์ตโฟลิโอ

Footer เป็นแบบมินิมอล: แค่ลิขสิทธิ์พร้อมปีปัจจุบันที่คำนวณแบบไดนามิก

โหมดมืด (Dark Theme)

เว็บไซต์เป็น โหมดมืดเสมอ -- ไม่มีการสลับกลางวัน/กลางคืน นี่คือการตัดสินใจโดยเจตนา: color-scheme: dark ถูกกำหนดในสไตล์ส่วนกลาง, พร้อมพื้นหลังสีดำ #000 และข้อความสีขาว #fff ลิงก์เป็นสีน้ำเงิน (#64b5f6) และเปลี่ยนเป็นสีเขียวเมื่อชี้ (#81c784)

ผมเขียนบทความอย่างไร

มาถึงภาคปฏิบัติ ! นี่คือขั้นตอนการทำงานของผมในการเขียนบทความใหม่:

ขั้นตอนที่ 1: สร้างไฟล์ Markdown

ผมเปิด VS Code และสร้างไฟล์ .md ใหม่ใน public/articles/:

ขั้นตอนที่ 2: เขียนเนื้อหา

ผมเขียนเนื้อหาของบทความโดยตรงใน Markdown VS Code มีตัวอย่าง Markdown ในตัวที่ยอดเยี่ยม:

สำหรับรูปภาพ ผมวางไว้ใน public/articles//articles/assets/ และอ้างอิงด้วยไวยากรณ์ Markdown มาตรฐาน:

![description](/articles/assets/my-image.png)

คอมโพเนนต์ Article.tsx จะเขียนเส้นทาง /articles/assets/ เป็น /articles//articles/assets/ โดยอัตโนมัติเพื่อให้รูปภาพแสดงผลอย่างถูกต้อง

ขั้นตอนที่ 3: ลงทะเบียนบทความใน index.json

เมื่อบทความเสร็จสมบูรณ์ ผมเพิ่มมันใน public/articles/index.json เพื่อให้ปรากฏในรายการบล็อก:

ขั้นตอนที่ 4: ทดสอบในเครื่อง

ผมรันเซิร์ฟเวอร์พัฒนา Vite:

pnpm dev

Vite เริ่มทำงานในไม่กี่มิลลิวินาที และผมสามารถดูบทความแบบเรียลไทม์ที่ localhost:5173:

ขั้นตอนที่ 5: เผยแพร่

แค่ git push ก็พอ ! CI/CD Pipeline จัดการส่วนที่เหลือโดยอัตโนมัติ

Deployment Pipeline CI/CD

ผมตั้งค่า GitHub Actions pipeline ที่สมบูรณ์ซึ่งจะทำ lint, build และ deploy เว็บไซต์โดยอัตโนมัติทุกครั้งที่มีการ push ไปยัง main มาดูรายละเอียดกัน

Workflow อยู่ใน .github/workflows/deploy.yml และแบ่งออกเป็นสอง jobs: build และ deploy

ตัวกระตุ้น (Triggers)

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Pipeline จะทำงานทุกครั้งที่มี push ไปยัง main และทุก pull request ที่มีเป้าหมายเป็น main ดังนั้น PRs จะถูกตรวจสอบ (lint + build) ก่อนที่จะถูก merge แต่เฉพาะ push ที่ main เท่านั้นที่จะกระตุ้นให้มีการ deploy

Job 1: Build

Job build ทำงานบน ubuntu-latest และทำตามขั้นตอนเหล่านี้:

  1. Checkout -- โคลน repository พร้อมประวัติทั้งหมด (fetch-depth: 0)
  2. Setup pnpm -- ติดตั้ง pnpm เวอร์ชันล่าสุดด้วย pnpm/action-setup@v4
  3. Setup Node.js 20 -- กำหนดค่า Node พร้อมเปิดใช้งานแคช pnpm เพื่อการติดตั้งที่เร็วขึ้น
  4. Install dependencies -- รัน pnpm install --frozen-lockfile เพื่อรับประกัน build ที่ reproducible (ไม่อนุญาตให้แก้ไข lockfile)
  5. Lint -- รัน pnpm run lint (ESLint) เพื่อตรวจสอบคุณภาพโค้ดก่อน build
  6. Build -- รัน pnpm run build ซึ่งตรวจสอบชนิด TypeScript ก่อน (tsc -b) จากนั้น bundle ทุกอย่างด้วย Vite
  7. Upload artifact -- อัปโหลดโฟลเดอร์ dist/ เป็น build artifact สำหรับ job deploy

หากขั้นตอนใดล้มเหลว -- ไม่ว่าจะเป็น lint error, type error หรือ build error -- ทั้ง pipeline จะหยุดและไม่มีอะไรถูก deploy ซึ่งช่วยปกป้องเว็บไซต์ใน production จากโค้ดที่เสียหาย

Job 2: Deploy

Job deploy จะทำงานก็ต่อเมื่อ:

  • Job build สำเร็จ (needs: build)
  • Event เป็น push (ไม่ใช่ PR)
  • Branch เป็น main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

จากนั้นดำเนินการ:

  1. ดาวน์โหลด build artifact -- ดึงโฟลเดอร์ dist/ ที่ผลิตโดย job build
  2. กำหนดค่า GitHub Pages -- ตั้งค่าสภาพแวดล้อม Pages
  3. อัปโหลดไปยัง Pages -- เตรียมโฟลเดอร์ dist/ สำหรับ GitHub Pages
  4. Deploy -- เผยแพร่เว็บไซต์ด้วย actions/deploy-pages@v4

ตารางแบบครบถ้วน

นี่คือสิ่งที่เกิดขึ้นตั้งแต่การเขียนจนถึงการ deploy:

เขียนบทความใน VS Code
         ↓
   git add & commit
         ↓
      git push
         ↓
 GitHub Actions เริ่มทำงาน
         ↓
 ┌─────────────────┐
 │   BUILD JOB     │
 │  1. Checkout    │
 │  2. Setup pnpm  │
 │  3. Setup Node  │
 │  4. Install     │
 │  5. Lint ✓      │
 │  6. Build ✓     │
 │  7. Upload dist │
 └────────┬────────┘
          ↓
 ┌─────────────────┐
 │  DEPLOY JOB     │
 │  1. Download    │
 │  2. Configure   │
 │  3. Upload      │
 │  4. Deploy 🚀   │
 └─────────────────┘
          ↓
   ออนไลน์บน GitHub Pages !

กระบวนการทั้งหมดใช้เวลาประมาณหนึ่งนาทีตั้งแต่ push จนถึงออนไลน์ ไม่มีการ deploy ด้วยตนเอง, ไม่มี FTP, ไม่มี SSH -- แค่ git push เท่านั้น

Production Build

ภายใต้ฝาครอบ, คำสั่ง pnpm build จะทำงาน:

  1. tsc -b -- ตรวจสอบชนิด TypeScript
  2. vite build -- Bundle และปรับแต่งโค้ดทั้งหมด

Vite สร้างไฟล์ที่ minified และปรับแต่งแล้วพร้อม code-splitting อัตโนมัติ ผลลัพธ์คือเว็บไซต์แบบ static ที่เร็วเป็นพิเศษ

ทำไมถึงเลือกสถาปัตยกรรมนี้ ?

ผมอาจใช้ CMS, เครื่องมือสร้างเว็บไซต์ static อย่าง Hugo หรือ Jekyll, หรือแม้แต่ Next.js แต่ทำไมผมถึงเลือกแนวทางนี้:

  • ความเรียบง่าย -- เขียนใน Markdown, push ไปยัง GitHub, ก็ออนไลน์
  • ควบคุมได้ทั้งหมด -- ไม่พึ่งพา CMS หรือฐานข้อมูล
  • ประสิทธิภาพ -- Vite + React = โหลดเร็ว
  • ความยืดหยุ่น -- ผมสามารถผสม Markdown และ HTML ได้ตามต้องการ
  • การเรียนรู้ -- มันเป็นโปรเจกต์ที่ยอดเยี่ยมสำหรับการเรียนรู้ React และ TypeScript
  • CI/CD -- การตรวจสอบคุณภาพและการ deploy อัตโนมัติด้วย GitHub Actions

บทสรุป

บล็อกนี้เป็นโปรเจกต์ที่เรียบง่ายแต่ออกแบบมาอย่างดี: Markdown สำหรับเนื้อหา, React สำหรับการเรนเดอร์, Vite เพื่อประสิทธิภาพ, GitHub Actions สำหรับ CI/CD, และ GitHub Pages สำหรับโฮสต์ ไม่มีฐานข้อมูล, ไม่มีเซิร์ฟเวอร์ backend, แค่ไฟล์ static ที่ให้บริการอย่างมีประสิทธิภาพด้วย pipeline อัตโนมัติที่รับประกันคุณภาพทุกครั้งที่มีการ push

ขอบคุณที่อ่าน แล้วพบกันในบทความหน้าครับ ! 🦊

Related Articles