16 KiB
План миграции сайта vakhrushev.me с Sculpin на Astro
Обзор текущего состояния
Стек
- Генератор: Sculpin 3 (PHP 8)
- Шаблоны: Twig (base → internal → article, три уровня наследования)
- Фронтенд: Webpack 4, SCSS, Vue 2 (интерактивная демка гадалки)
- Сборка: Docker (PHP + Node контейнеры), Taskfile
- Деплой: Docker-образ nginx, Ansible на личный сервер
- Аналитика: Яндекс.Метрика
- Шрифты: Google Fonts (PT Serif, Source Code Pro), Font Awesome 5
- Кастомные бандлы Sculpin: HtmlPrettier, SiteMap, TwigExtension (hashed_asset)
Контент
- 8 статей в markdown (2019-2020), русский язык
- 1 статья с интерактивным Vue-компонентом (гадалка Шеннона)
- Страницы: главная, список статей, 404
- Atom-лента (ручной шаблон)
- Sitemap (кастомный бандл + шаблон)
- robots.txt
Структура стилей
- Переменные SCSS: шрифт PT Serif 20px, ширина 740px, цвета GitHub
- Базовые стили, стили главной, стили внутренних страниц, навигация
- BEM-подобная методология
Принятые решения
- URL статей: формат
/articles/2019-05-01-predictor/, slug = имя файла без расширения - Пакет
@anwinged/predictor: чистый TypeScript, без привязки к фреймворку — миграция на Vue 3 затрагивает только обёртку - Tailwind CSS: используем; Tailwind 4 делает tree-shaking из коробки — в сборку попадают только используемые утилиты
- Текст на главной: переносим как есть, автор обновит сам
- Галерея: один альбом с фотографиями, хранение в
src/content/gallery/(оптимизация через Astro Image) - Локальная разработка: всё через Docker (и dev, и build), npm на хосте не используем
Целевое состояние
Стек
- Генератор: Astro
- Стилизация: Tailwind CSS 4
- Интерактивность: Vue 3 (islands, для гадалки и будущих компонентов)
- Сборка: Docker (один Node-контейнер), Taskfile
- Деплой: Docker-образ nginx, Ansible (без изменений)
Разделы сайта
- Главная — краткое описание, ссылки
- Блог — статьи и эссе по программированию
- Галерея — один альбом с избранными фотографиями
Задачи
Этап 1. Инициализация проекта Astro
1.1. Создать проект Astro
- Инициализировать Astro в корне проекта (или в отдельной ветке)
- Настроить
astro.config.mjs: site URLhttps://vakhrushev.me, языкru - Установить интеграции:
@astrojs/sitemap,@astrojs/rss,@astrojs/vue,@astrojs/tailwind - Настроить TypeScript (strict или relaxed — на усмотрение)
1.2. Настроить Tailwind CSS 4
- Установить и настроить Tailwind 4 (tree-shaking из коробки — в сборку попадает только используемый CSS)
- Перенести дизайн-токены из SCSS-переменных в Tailwind-конфигурацию (через
@themeв CSS):- Шрифты: PT Serif (serif), Source Code Pro (mono)
- Цвета:
#24292e(текст),#0366d6(ссылки),#e6e6e6(линии) - Ширина контента: 740px
- Размер шрифта: 20px базовый
1.3. Настроить Docker для сборки
- Заменить два Docker-образа (PHP + Node) одним Node-образом
- Использовать актуальную LTS-версию Node (22)
- Удалить PHP Dockerfile — он больше не нужен
- Сохранить
Dockerfile.nginx.prod, обновить путь:dist/вместоoutput_prod/
1.4. Обновить Taskfile
- Убрать задачи:
composer,sculpin,shell-node,format-php - Обновить задачи сборки:
build-prod:astro build
- Добавить задачу
dev: запускastro devдля локальной разработки (через Docker с проброшенными портами) - Обновить задачу
deploy: путь к собранным файламdist/ - Сохранить структуру Docker-команд через Taskfile
- Все npm/astro-команды выполняются внутри Docker-контейнера
Этап 2. Лейауты и компоненты
2.1. Создать базовый лейаут BaseLayout.astro
Аналог текущего base.html.twig. Должен включать:
<html lang="ru">, мета-теги (charset, viewport, description, keywords)- Open Graph мета-теги (og:site_name, og:title, og:description, og:url, og:locale)
- Подключение шрифтов Google Fonts (PT Serif, Source Code Pro)
- Яндекс.Метрика (вынести в отдельный компонент
YandexMetrika.astro) - Yandex verification мета-тег
- Слот для контента
Параметры (props): title, description, keywords
2.2. Создать лейаут для внутренних страниц InternalLayout.astro
Аналог internal.html.twig:
- Использует
BaseLayout - Навигация (компонент
Navigation.astro) - Контейнер
.pageс ограничением ширины
2.3. Создать лейаут для статей ArticleLayout.astro
Аналог article.html.twig:
- Использует
InternalLayout - Заголовок статьи
<h1> - Дата публикации и email внизу
- Слот для содержимого статьи
2.4. Создать общие компоненты
Navigation.astro— навигация (Главная, Блог, Галерея)YandexMetrika.astro— код счетчика аналитикиArticleList.astro— список статей, сгруппированных по годам
Особенность: Font Awesome больше не нужен (социальные ссылки удалены в коммите 63a324c).
Если понадобятся иконки — использовать astro-icon или inline SVG.
Этап 3. Content Collections и перенос статей
3.1. Настроить Content Collection для статей
- Создать
src/content/articles/для markdown-файлов - Определить схему в
src/content.config.ts:{ title: z.string(), description: z.string().optional(), keywords: z.array(z.string()).optional(), tags: z.array(z.string()).optional(), draft: z.boolean().default(false), // date — парсится из имени файла (YYYY-MM-DD-slug.md), не хранится в frontmatter }
3.2. Перенести статьи
Перенести 8 markdown-файлов из source/_articles/ в src/content/articles/.
Имена файлов сохраняют даты (формат YYYY-MM-DD-slug.md) для удобной навигации в файловой системе. Дата парсится из имени файла при сборке (в content.config.ts или в getStaticPaths), дублировать в frontmatter не нужно.
Необходимые изменения в каждом файле:
- Удалить Sculpin-специфичные поля frontmatter (
layout,styles,scripts,use) - Оставить
title,description,keywords - Опционально: добавить
tagsдля будущей фильтрации
Список статей:
2019-05-01-predictor.md— ОСОБЫЙ СЛУЧАЙ, см. задачу 3.32019-06-01-php-serialization.md2019-06-28-storytelling.md2019-08-08-yandex-disk-image-hosting.md2019-09-26-highload-videos.md2020-06-27-interesting-programming-blogs.md2020-06-27-type-discriminant.md2020-11-08-nullable-fields.md
3.3. Перенести статью с интерактивным компонентом (гадалка)
Статья predictor.md содержит встроенный Vue-компонент <div id="app"></div>.
Шаги:
- Переименовать файл в
.mdx - Обновить Vue-компонент
PredictorDemo.vueдо Vue 3 (Composition API или Options API)@anwinged/predictor— чистый TS, совместимость с Vue 3 не затронута- Перенести scoped-стили компонента на Tailwind или оставить scoped CSS
- В MDX-файле: импортировать компонент и использовать
<PredictorDemo client:visible /> - Удалить ручное подключение JS/CSS через frontmatter (
styles,scripts)
Этап 4. Страницы
4.1. Главная страница src/pages/index.astro
- Использовать
BaseLayout - Имя, краткое описание (обновить текст — актуализировать информацию о себе)
- Ссылки: email, git, другие
- Список последних статей (через
getCollection('articles'))
4.2. Страница списка статей src/pages/articles/index.astro
- Использовать
InternalLayout - Список всех статей, сгруппированных по годам (как сейчас в
article_list.twig) - Сортировка по дате, от новых к старым
4.3. Динамические страницы статей src/pages/articles/[...slug].astro
- Использовать
ArticleLayout - Рендеринг markdown/MDX содержимого через
render() - SEO мета-теги из frontmatter
4.4. Страница 404 src/pages/404.astro
- Использовать
InternalLayout - Текст "Страница не найдена" и ссылка на главную
4.5. Страница галереи src/pages/gallery/index.astro
- Использовать
InternalLayout - Content Collection
gallery— фотографии с метаданными (title, описание, дата, порядок) - Структура хранения:
src/content/gallery/ ├── photo-001.md # frontmatter: title, image, order ├── photo-002.md └── ... public/photos/ ├── photo-001.jpg # оригиналы фотографий ├── photo-002.jpg └── ... - Сетка превью с оптимизацией через Astro Image (WebP/AVIF, responsive sizes)
- Просмотр полноразмерных фотографий: lightbox-библиотека (PhotoSwipe или GLightbox)
- Один альбом, без категорий
Этап 5. RSS, Sitemap, SEO
5.1. Настроить RSS-ленту
- Создать
src/pages/rss.xml.tsс использованием@astrojs/rss - Включить все статьи (title, description, date, link)
- URL:
/rss.xml - Для обратной совместимости: добавить редирект
/atom.xml→/rss.xml(в nginx или через Astro)
5.2. Настроить Sitemap
- Интеграция
@astrojs/sitemapуже генерирует sitemap автоматически - Убедиться, что 404 и служебные страницы исключены
5.3. Настроить robots.txt
- Создать
public/robots.txtсо ссылкой на sitemap
5.4. Open Graph и мета-теги
- Уже реализовано в BaseLayout (задача 2.1)
- Проверить корректность на каждом типе страниц
Этап 6. Сборка и деплой
6.1. Обновить Docker-сборку
- Новый
docker/node/Dockerfileна Node 22 LTS - PHP Dockerfile удалить
- Обновить
Dockerfile.nginx.prod:FROM nginx:stable COPY dist /usr/share/nginx/html - Добавить конфиг nginx для SPA fallback (404.html) и кэширования статики
6.2. Обновить Taskfile
Финальная версия задач:
tasks:
dev: # astro dev (локальная разработка)
build-prod: # astro build
deploy: # build-prod → docker build → ansible
6.3. Проверить деплой
- Собрать Docker-образ
- Проверить, что все страницы отдаются корректно
- Проверить 404
- Проверить RSS и sitemap
Этап 7. Очистка
7.1. Удалить файлы Sculpin
app/— SculpinKernel, конфигиbundle/— кастомные бандлы (HtmlPrettier, SiteMap, TwigExtension)source/— старые шаблоны, ассеты, контент (после проверки, что всё перенесено)composer.json,composer.lockdocker/php/Dockerfilewebpack.config.js.php-cs-fixer.php
7.2. Обновить package.json
- Удалить старые зависимости (webpack, babel, vue 2, node-sass, и т.д.)
- Удалить старые npm-скрипты
- Оставить только Astro и его зависимости
7.3. Обновить .gitignore
- Убрать
output_dev/,output_prod/,vendor/ - Добавить
dist/,.astro/,node_modules/
7.4. Обновить README.md
- Описание нового стека
- Инструкции по локальной разработке и деплою
URL-совместимость
URL статей: /articles/2019-05-01-predictor/.
Slug = имя файла без расширения, Astro использует его как есть.
Редиректы в nginx для старых URL (301):
/articles/2019/05/01/predictor/ → /articles/2019-05-01-predictor/
/articles/2019/06/01/php-serialization/ → /articles/2019-06-01-php-serialization/
/articles/2019/06/28/storytelling/ → /articles/2019-06-28-storytelling/
/articles/2019/08/08/yandex-disk-image-hosting/ → /articles/2019-08-08-yandex-disk-image-hosting/
/articles/2019/09/26/highload-videos/ → /articles/2019-09-26-highload-videos/
/articles/2020/06/27/interesting-programming-blogs/ → /articles/2020-06-27-interesting-programming-blogs/
/articles/2020/06/27/type-discriminant/ → /articles/2020-06-27-type-discriminant/
/articles/2020/11/08/nullable-fields/ → /articles/2020-11-08-nullable-fields/
Редирект /atom.xml → /rss.xml для совместимости RSS-подписок.
Порядок выполнения
Этап 1 (инициализация) → Этап 2 (лейауты) → Этап 3 (контент)
→ Этап 4 (страницы) → Этап 5 (RSS/sitemap) → Этап 6 (деплой) → Этап 7 (очистка)
Этапы 2 и 3 можно частично выполнять параллельно. Этап 7 выполняется только после полной проверки нового сайта.