Сидишь бывало, записываешь всякие штукуёвины, которые сам вечно забываешь и вдруг кааак захотелось начать наносить людям добро и причинять радость вопреки их желаниям, а тут ещё и народ периодически спрашивает, стало быть надо завести блох, но не в свитере, а который будет удобный, быстрый, защищённый, красивый и не будет зависеть от модных веяний на очередной платформе-однодневке, не будет обмазан странными всплывашками, баннерами, пропагандой...

Но писать с нуля новый движок или пользоваться вечно дырявыми php-бложиками не барское дело и вообще не хочется разбираться со всей этой кухней из протухших зависимостей.

Markdown

Писать будем простой удобочитаемый текст, из любого удобного места, практически с первого попавшегося холодильника, в любом первом попавшемся редакторе, хоть в vim, как деды верещали, не отвлекаясь в творческом угаре на проблемы софта, доступности облачков, новые свистоперделки облачков, обновления свистоперделок и прочие всплывашки и отвлекаторы. Чем проще - тем лучше. Надёжно как кирпич. Нам поможет в этом тёплый ламповый Markdown, который неплохо так везде поддержан оставаясь достаточно простым и умеет хорошо расширяться. Ну и вообще, все же знают и умеют в Markdown.

Hugo

Markdown будем раскатывать в статичный сайт, который по определению быстр, хорошо кэшируется, индексируется и не хакается, не говоря о том что зависимостей которые будут протухать и отваливаться просто нет из коробки. GitHub Pages проталкивает Jekyll, но ведь есть божественный и быстрый Hugo, с его сотнями шаблонов оптимизированных на любой вкус и цвет, от мобилок и Accessibility (который правильно сделанный помогает не только лишь людям с проблемами, но и для всяких удобных штук типа vimium очень полезен), до СЕО, комментариев и нормальной поддержки опций типа Reader View в Firefox. Hugo написан на Go, поэтому есть под все основные платформы и без приседаний с окружением и зависимостями - просто скачал и работай. Только нам, скорее всего, понадобится SCSS, поэтому из сборок качаем hugo extended. Теперь можно сходить почитать документацию.

Но мы ленивые, а Hugo достаточно взрослый, если его позапускать из консоли, он сам расскажет про hugo help new, ну а дальше по накатанной

> hugo new site my-blog
Congratulations! Your new Hugo site was created in D:\Projects\my-blog.
Just a few more steps...
1. Change the current directory to D:\Projects\my-blog.
2. Create or install a theme:   
  - Create a new theme with the command "hugo new theme <THEMENAME>"   
  - Or, install a theme from https://themes.gohugo.io/
3. Edit hugo.toml, setting the "theme" property to the theme name.
4. Create new content with the command "hugo new content <SECTIONNAME>\<FILENAME>.<FORMAT>".
5. Start the embedded web server with the command "hugo server --buildDrafts".
See documentation at https://gohugo.io/.

И как бы можно с места в карьер, но...

Нужно сначала один раз нормально разобраться и настроить.

Тема по умолчанию - это черновик, который будет добавлять 3 тестовых поста из themes\default\content и в целом просит кирпича напильника.
Поэтому идём в репозиторий тем и выбираем что-нибудь покавайнее под наши нужды. Мне, например, понравилась m10c.

git clone https://github.com/vaga/hugo-theme-m10c.git themes/m10c
echo "theme = 'm10c'" >> hugo.toml

Новые посты по умолчанию генерятся в директории content из шаблона сохранённого в archetypes, но не забывайте прописывать формат файла, иначе получите ошибку

> hugo new content posts\my-first-post
Error: failed to resolve "posts\\my-first-post" to an archetype template

> hugo new content posts\my-first-post.md
Content "my-blog\\content\\posts\\my-first-post.md" created

Пост не будет генериться в блог, пока в шапке с метаданными присутствует draft = true. Удаляем, дописываем что-нибудь весёленькое и дальше по вкусу: можно просто запустить hugo в директории с проектом и он сгенерит public со статичным сайтом, а можно запустить hugo server, кликнуть на предложенный урлик и сразу посмотреть в браузере результат.

Настраиваем параметры генератора и шаблона

Всё основное конфигурируется через config.toml и достаточно очевидно

baseURL = 'https://USERNAME.github.io/'
title = "USERNAME blog"

languageCode = 'ru-ru'
defaultContentLanguage = "ru"
defaultContentLanguageInSubdir = true

theme = 'm10c'

# publishdir = '_site'

enableRobotsTXT = false

[params]
  author = 'USERNAME'
  description = 'Whatever Developer, full-stack. Interested in *nix, maker culture, home assistant, photo, nature, birdwatching'
  avatar = 'https://avatars.githubusercontent.com/u/USERID'

[[params.social]]
  icon = "github"
  name = "My Github"
  url = "https://github.com/USERNAME"
[[params.social]]
  icon = "linkedin"
  name = "linkedin"
  url = "https://www.linkedin.com/in/USERNAME"
[[params.social]]
  icon = "telegram"
  name = "telegram"
  url = "https://t.me/USERNAME"

[menu]
  [[menu.main]]
    identifier = "home"
    name = "Home"
    url = "/"
    weight = 1
  [[menu.main]]
    identifier = "posts"
    name = "Posts"
    url = "/posts/"
    weight = 2
  [[menu.main]]
    identifier = "tags"
    name = "Tags"
    url = "/tags/"
    weight = 3
  [[menu.main]]
    identifier = "about"
    name = "About"
    url = "/about/"
    weight = 4

Меняем под свои аккаунты.

Фото на аватарку можно либо залить сразу в репозиторий, либо сделать ссылку на фото из профиля гитхаба, либо ещё про какой gravatar подумать.

Немножко систематизируем

В директории у нас могут быть

  • _index.md - индексный файл который указывает что нам нужно сгенерировать список статей из того что лежит в текущей директории и поддиректориях. В принципе это поведение по умолчанию для директории, но возможно вы захотите задать заголовок для этой страницы, тогда минимальное содержимое будет примерно таким

    ---
    title: "My custom title for this category"
    ---
  • index.md - главный файл для данной директории (по аналогии с index.html), если он есть то для данного пути Hugo будет использовать именно его и на все остальные статьи вам где-то придётся самостоятельно прописывать ссылки

  • всякие прочие *.md - файлы из которых будут генериться статичные страницы с вашими статьями.

Статья my-blog\content\posts\article.md будет сгенерена в my-blog\public\posts\article\index.html

И раз у нас всё равно под каждую статью генерится директория, чтобы избегать путаницы, я предпочитаю сразу все посты именовать в формате content\posts\YYYYMMDD-article-name\index.md и всякие скрины и аттачи к ним складывать в директорию к статье.
Т.е. новая статья будет генериться как-то так

hugo new content posts\2024-08-24-blog-like-a-freak\index.md

Шапка статьи у меня выглядит примерно так

---
title: 'Бложим как фрики'
description: О мой бложе, что я несу...
date: 2024-08-28T11:46:29+03:00
categories: Blog
tags: ['Blog', 'Hugo', 'Markdown']
layout: post
---

date влияет на порядок сортировки статей

Теги и категории используются для генерации /tags/ и /categories/ соответственно
layout переопределяет какой шаблон из выбранной темы используется

И отдельно стоит упомянуть лэндинг в корне сайта. По умолчанию конкретно эта тема создаёт список постов, а весь контент из content\_index.md игнорируется. Если нас такое поведение страниц со списками сайтов не устраивает, можно в m10c\layouts\_default\list.html добавить секцию {{ .Content }} после заголовка. Если же мы хотим ещё больше кастомизации, то создаём themes\m10c\layouts\_default\home.html с кастомизацией для корневой страницы. Для примера можно взять копию файла из дефолтной темы

{{ define "main" }}
  {{ .Content }}
  {{ range site.RegularPages }}
    <h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
    {{ .Summary }}
  {{ end }}
{{ end }}

Теперь на странице будет присутствовать вводный текст, а уже после него будут перечисляться посты блога с кратким содержимым. Но возможно удобнее будет, например, отображать основные секции

{{ range .Site.Sections }}
  <h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ end }}

Возможности кастомизировать шаблоны Hugo кажутся бескрайними, ярчайший пример тому - документация от Kubernetes

GitHub

Вообще хостить статичный примитив можно тоже на чём угодно, лишь бы места хватило, хоть на ESP32 с microSD или на флешке воткнутой в кинетик. Но уже есть доступный, надёжный, бесплатный и всем знакомый GitHub.

Исключаем в .gitignore всё лишнее

/.hugo_build.lock
/public
/resources/_gen

Инициализируем репозиторий, коммитаем наш нехилый супец

git init
git add .
git commit -m "Initial"

Создаём на GitHub публичный репозиторий и в него заливаемся

git remote add origin git@github.com:USERNAME/my-blog.git
git push -u origin master

GitHub Actions

Ну тут всё просто, мы хотим чтобы на любой коммит в мастер Hugo собирал новую версию статики и публиковал её. Что мы и запишем в .github\workflows\hugo.yml

name: Deploy Hugo site to Pages

on:
  # Runs on pushes targeting the default branch
  push:
    branches: ["master"]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow one concurrent deployment
concurrency:
  group: "pages"
  cancel-in-progress: true

# Default to bash
defaults:
  run:
    shell: bash

jobs:
  # Build job
  build:
    runs-on: ubuntu-latest
    env:
      HUGO_VERSION: 0.133.1
    steps:
      - name: Install Hugo CLI
        run: |
          wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
          && sudo dpkg -i ${{ runner.temp }}/hugo.deb
      - name: Checkout
        uses: actions/checkout@v3
        with:
          submodules: recursive
      - name: Setup Pages
        id: pages
        uses: actions/configure-pages@v2
      - name: Build with Hugo
        env:
          # For maximum backward compatibility with Hugo modules
          HUGO_ENVIRONMENT: production
          HUGO_ENV: production
        run: |
          hugo \
            --minify \
            --baseURL "${{ steps.pages.outputs.base_url }}/"
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v1
        with:
          path: ./public

  # Deployment job
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v1

И раз мы разворачиваем новый контент через GitHub Actions, то и в репозитории, в Settings - Pages - Build and deployment - Source тоже стоит указать GitHub Actions

Всё, уже можно нести в мир улыбку, бешенство и моральную смерть.

Obsidian

Но надо ещё редактор покомфортнее. Если вы себе ещё не нашли подходящий, редактировать сырой Markdown лениво, а редактор самого GitHub вас не устраивает, возьмите Obsidian - замечательный WYSIWYG редактор к Markdown, много кто уже использует для заметок.
Заставим его открыть директорию с нашим репозиторием блога как новый Vault и пройдёмся по настройкам

Editor

Strict line breaks = включаем
Indent using tabs = отключаем, пробелы наше всё

Files and links

Default location for new notes = in the folder specified below
Folder to create new notes in = content/posts

New link format = Relative path to file

Use [[Wikilinks]] - отключаем, нам нужен честный Markdown

Default location for new attachments = Same folder as current file
поскольку для каждой статьи всё равно будет отдельная директория. И всё это можно будет спокойно переносить куда угодно

Шаблоны

Определяемся, будем мы каждый раз ручками вызывать из консоли

hugo new content posts\whatever.md

или всё-таки за шаблон будет отвечать Obsidian и тогда прописываем пути к шаблонам.

Вообще хорошо бы поставить какой-нибудь более продвинутый плагин с шаблонами, чтобы генерить сразу в нужную директорию, но хотя бы минимально из коробки можно настроить

Core plugins - Daily notes

New file location = content/posts
Template file location = archetypes/default

Core Plugins - Templates

Пусть наши шаблоны берутся из директории archetypes

А так же меняем сам шаблон на что-то вида

---
title: PAGE TITLE
description: PAGE DESCRIPTION
date: {{date}} {{time:HH:mm:ss Z}}
categories: blog
tags: []
layout: post
---

Obsidian Git

Идём в настройках в Community Plugins, включаем их, жмём Browse и ищем всякое про Git. Находим, например, Git за авторством Vinzent (Denis Olehov). Ставим, включаем.

Теперь по Ctrl+P мы можем найти нужные нам commit/push/pull прямо в Обсидиане

Коммитаемся... всё наши настройки редактора тоже в репозитории и теперь можно синкать всё на другой ПК или телефон и неспешно, смакуя, под кофирёк, строчить всякую дичь, чтобы полыхало веселее

И простым Ctrl+C, Ctrl+V эти статьи потом при желании можно репостить на хабр и прочие ресурсы с минимальными правками.

При желании всё это можно приспособить для ведения документации, всяких лендингов, сайта-визитки с CV.

Комментарии (8)


  1. ris58h
    02.09.2024 07:18

    При чём здесь хаб Текстовые редакторы и IDE?


  1. PTM
    02.09.2024 07:18

    Ну если как фрики, то почему не lektor или nikola?


    1. Vapaamies
      02.09.2024 07:18

      Спасибо за наводку. Задумываюсь о простом сайте/блоге. В числе требований — чтобы под Windows 7 и без Node.js. Hugo выдал какую-то техническую ошибку при запуске (видимо, хочет Windows 10), Lektor на Node.js, а Nikola — чисто на Python. Выбор очевиден. До этого только про Jekyll слышал, с ним морочиться совсем не хотелось.


      1. ritorichesky_echpochmak Автор
        02.09.2024 07:18

        Говорят, для Win7 последняя была 0.121


      1. PTM
        02.09.2024 07:18

        никола веселенький и простой


  1. fedorovpishet
    02.09.2024 07:18

    Плюсану за название. Крутил-вертел Hugo и подобные инструменты, но потом решил, что я слишком гуманитарий для таких штук и лучше старого доброго Вордпресса мне ещё ничего не попадалось.

    В своё оправдание скажу, что у меня возникали проблемы не с развёртыванием статичных сайтов, а с работой движков. Постоянно случались какие-то косяки. «Не шмогла я, не шмогла...»


    1. ritorichesky_echpochmak Автор
      02.09.2024 07:18

      Пишите что не получилось или ткните в какой репозиторий и можно вместе попробовать разобраться. Скорее всего я тоже в статье упустил какие-то моменты которые были для меня уже очевидны после предыдущего хождения по граблям, но после граблей с раби и зависимостями, да ещё под форточками, хуго работает в духе "раз настроил нужный флоу и погнал"


      1. fedorovpishet
        02.09.2024 07:18

        Спасибо за предложение, но меня уже не спасти. Крепко подсел на Вордпресс и не вижу нужды слезать с этой иглы. Статика была лишь эдаким экспериментом, когда пробовал всё подряд, типа Quartz и других решений, которых сейчас и в Гугле найти не могу)))