HUGO - генератор

21-12-2022

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

Суть всего процесса:

  1. Локально устанавливается Hugo
  2. Создается структура сайта
  3. Добавляется содержимое в виде файлов Markdown
  4. С помощью Hugo генерируется сайт - html страницы
  5. Готовые страницы загружаются на любой хостинг
  6. При обновлении процедуру повторить с третьего шага

Зачем это нужно?

Если страницы на сайт добавляются нечасто, нет нужды использовать CMS. Низкие требования к хостингу. Быстрая скорость загрузки HTML страниц. Подходит для документации, сборников статей, книг, различных энциклопедий. В сети встречал проект магазина на Hugo. Комментарии и обработку форм можно добавить с помощью сторонних сервисов, таких как Disqus. Пример добавления форм. Дополнительно можно добавить и поиск на сайт.

Установка

Установка Hugo через официальные репозитории дистрибутива. Для этого в терминале выполним следующее:

sudo apt-get install hugo

Или установим последнюю расширенную версию Hugo:

wget https://github.com/gohugoio/hugo/releases/download/v0.106.0/hugo_extended_0.106.0_linux-amd64.deb
sudo dpkg -i hugo_extended_0.106.0_linux-amd64.deb

Новый проект

Создаем новый сайт и тему:

hugo new site blog
cd blog
hugo new theme mytheme

Получаем проект с такой структурой:

blog
├── archetypes
│   └── default.md
├── config.toml
├── content
├── data
├── layouts
├── public
├── resources
│   └── _gen
│       ├── assets
│       └── images
├── static
└── themes
    └── mytheme

Archetypes

Здесь хранятся файлы шаблонов контента, его типы или категории, которые содержат предварительно настроенную вступительную часть. Шаблон по умолчанию - default.md содержит заголовок:

---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---
  • title - заголовок страницы, который определяется из имени файла, знаки '-' заменяются пробелами.
  • date - текущая дата создания страницы
  • draft - при сборке сайта черновики не обрабатываются, для обработки нужно изменить значение на - false

Content

Весь контент сайта будет находиться внутри этого каталога. Каждая папка верхнего уровня считается разделом содержимого. Например, раздел news - в каталоге будет content/news. Hugo использует разделы для назначения типов контента по умолчанию.

Data

В этой папке можно хранить дополнительные данные. Файлы данных не используются для создания отдельных страниц. Они предназначены для дополнения файлов содержимого. Эти файлы должны быть в формате YAML, JSON, XML или TOML (с использованием расширения .yml, .yaml, .json, .xml или .toml). Описание в документации.

Layouts

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

Public

Здесь Hugo создает весь статический сайт, в корне проекта. Сюда входят файлы HTML а также изображения, файлы CSS и файлы JavaScript. Можно изменить в настройках сайта переменную publishDir.

Resources

Кэшируются некоторые файлы для ускорения генерации. Может также использоваться для встроенных файлов Sass, поэтому не нужно устанавливать препроцессор.

Static

Хранится весь статический контент: изображения, CSS, JavaScript и т. д. Когда Hugo создает сайт, все ресурсы внутри статического каталога копируются как есть. Хорошим примером использования static папки является подтверждение права собственности на сайт в Google Search Console, чтобы Hugo скопировал полный HTML-файл без изменения его содержимого.

Themes

Хранятся различные темы для сайта.

config.toml

Основной файл конфигурации сайта. Содержимое по умолчанию:

baseURL = 'http://example.org/'
languageCode = 'en-us'
title = 'My New Hugo Site'

Лучшим вариантом конфигурации будет создание каталога config с прочими каталогами и файлами.

mkdir config
mkdir config/_default
mkdir config/production

А также перенесем и создадим следующие файлы, чтобы получить вот такую структуру:

config/
├── _default
│   ├── config.toml
│   ├── menus.yaml
│   └── params.toml
└── production
    ├── config.toml
    └── params.toml

Каждый каталог содержит группу файлов, содержащих настройки, уникальные для среды. Каждый файл представляет собой корневой объект конфигурации, такой как params.toml для [Params], menus.toml для [Menu] и т. д. Содержимое каждого файла должно быть верхнего уровня, например файл params.toml:

# Параметры сайта
keywords = ["блог", "site", "hugo"]
description = "Проект блога на Hugo"

Учитывая приведенную выше структуру, при запуске:

hugo --environment production

Hugo будет использовать все настройки из config/_default и выполнять слияние production поверх них. Ссылка на документацию.

Настройка

Отредактируем файл, config/_default/config.toml.

baseURL = 'http://blog.loc.ru/'
languageCode = 'ru-ru'
title = 'Блог'
theme = 'mytheme'
relativeURLs = true
#uglyurls = true
publishDir = 'web'
disableHugoGeneratorInject = true
enableRobotsTXT = true
summaryLength = 50
paginate = 3

[permalinks]
    articles = '/:year/:month/:slug/'
    article = '/:year/:month/:slug/'
    pages = '/:slug/'
    page = '/:slug/'

[author]
    name = 'DrHellP'
    email = 'drhellp@gmail.com'

baseURL - имя хоста (и путь) к корню сайта.
languageCode - языковой тег, определенный в RFC5646. Это значение используется для заполнения элемента <language> во внутреннем шаблоне RSS.
title - название сайта.
theme - тема используемая сайтом.
relativeURLs - включено, чтобы все относительные URL-адреса относились к корневому каталогу содержимого.
publishDir - меняем каталог, в который Hugo запишет окончательный статический сайт (файлы HTML и т. д.).
disableHugoGeneratorInject - Hugo по умолчанию вставит метатег генератора в заголовок HTML только на главной странице. Разработчики просят так не делать.
enableRobotsTXT - включить генерацию robots.txt файла.
summaryLength - длина текста в словах для отображения в файле .Summary. Своего рода это тизер для главной страницы. По умолчанию 70. Либо можно вставить разделитель  <!--more--> в текст.
paginate - количество элементов по умолчанию на странице в пагинации.

Закомментированный параметр - uglyurls = true, позволяет использовать в ссылке на файл расширение - filename.html. Подробнее.

Более подробно с переменными файла config.toml можно ознакомится в документации.

[permalinks]
    articles = '/:year/:month/:slug/'
    article = '/:year/:month/:slug/'
    pages = '/:slug/'
    page = '/:slug/'

С помощью этих параметров статьи будут размещаться в content/articles, а вот ссылки на них будут преобразованы к виду - https://blog.loc.ru/2022/11/sample-entry/

[author]
    name = 'DrHellP'
    email = 'drhellp@gmail.com'

Доступ к этим параметрам из шаблонов через переменную .Site.Author.name

В файл param.toml можно вносить различные параметры сайта, например ключевые слова и краткое описание сайта.

В файле menus.yaml удобно разместить главное меню сайта. (С форматом конфига .toml возникли проблемы)

# Основное меню сайта
main:
  - identifier: articles
    name: 'Статьи'
    url: '/articles/'
    weight: 10
  - identifier: tags
    name: 'Метки'
    url: '/tags/'
    weight: 20
  - identifier: about
    name: 'О проекте'
    url: '/about/'
    weight: 30

Параметры пунктов меню:

identifier - переменная для использования в шаблонах, значение должно быть уникальным для каждого пункта меню.
name - имя пункта меню.
url - URL-адрес, на который указывает запись в меню.
weight - ключ для сортировки пунктов меню.

Ознакомиться со всеми переменными Меню.

Создание контента

Следующая команда служит для создания страниц:

hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>
  • SECTIONNAME - это необязательный параметр, по нему можно определить тип страницы и путь в каталоге - content. Например - news, post, articles.
  • FILENAME - имя файла, а также ссылка на страницу по умолчанию.
  • FORMAT - расширение файла. Чаще всего md - Markdown. Список поддерживаемых форматов файлов.

Эта команда просто создаст файл по подходящему шаблону из директории archetypes или по шаблону установленной темы. Но при использовании такой команды существуют некоторые неудобства:

  1. Заголовок формируется из имени файла, но в имени файла пробелы должны быть заменены знаком '-'.
  2. Не смотря на то, что существует IDN. Ссылки вида - http://localhost:1313/tags/статья/ при копировании в мессенджер и в RSS становятся - http://localhost:1313/tags/%D1%81%D1%82%D0%B0%D1%82%D1%8C%D1%8F/.
  3. Также отсутствует возможность сразу добавить некоторые данные в статью.

Возможно я плохо читал документацию.

Мы пойдем другим путем.

Напишем скрипт на python hugotrans.py и разместим его в домашней директории /home/drhellp/bin/.

#!/usr/bin/python3

# -----------------------------------------------------------
# Транслитерация заголовков страниц в Hugo
# Интерактивное создание контента для Hugo на русском языке
# Для корректной работы требуется стандартный файл в 
# archetypes - default.md
# во второй строке содержащий название документа:
# ---
# title: "{{ replace .Name "-" " " | title }}"
# date: {{ .Date }}
# draft: true
# ---
#
# Результат работы скрипта:
# ---
# title: "Новая статья"
# slug: "novaja-statja"
# description: "Краткое описание"
# tags: ["статья","python","hugo","run"]
# author: "DrHellP"
# date: 2022-11-24T16:15:49+03:00
# draft: true
# ---
#
# (C) 2022 DrHellP
# license http://drhellp.loc.ru/license/
# email drhellp@gmail.com
# -----------------------------------------------------------

import os, re
from transliterate import translit, get_available_language_codes

# Можно заранее прописать тип контента
pub_type = ''
if pub_type == '':
    print('Введите тип публикации (eng)')
    pub_type = input()
    if pub_type != '':
        pub_type = pub_type + '/'

# Ввод названия материала
print('----------')
print('Введите заголовок публикации')
pub_title = input()
pub_title_tr = (translit(pub_title.title(), 'ru', reversed=True))
file_name = pub_title_tr.replace(' ', '-')
file_name = re.sub('[^A-Za-z0-9-]+', '', file_name)
file_name = file_name.lower()
pub_title = 'title: "' + pub_title + '"\n'
pub_slug = 'slug: "' + file_name + '"\n'

# Проверка существования файла
if os.system('hugo new ' + pub_type + file_name + '.md'):
    print('----------')
    print ("Файл с таким именем существует")
    print('----------')
    exit()

print('----------')
print('Введите описание публикации')
pub_desc = input()
pub_desc = 'description: "' + pub_desc + '"\n'

# Добавляем метки
print('----------')
print('Введите ключевые слова для публикации, через запятую')
pub_tags = input()
list_tags = []
for x in pub_tags.split(","):
    x = x.strip()
    x = '"' + x + '"'
    list_tags.append(x)
pub_tags = ','.join(list_tags)
pub_tags = 'tags: [' + pub_tags + ']\n'

# Можно заранее прописать автора
pub_author = ''
if pub_author == '':
    print('----------')
    print('Введите автора публикации')
    pub_author = input()
    pub_author = 'author: "' + pub_author + '"\n'

# Замена второй строки файла
with open('./content/' + pub_type + file_name + '.md', 'r+') as fd:                                             
    lines = fd.readlines()                                                     
    fd.seek(0)                                                                 
    fd.writelines(lines[:1] + [pub_title] + [pub_slug] + [pub_desc] + [pub_tags] + [pub_author] + lines[2:])

И добавим в файл .bashrc строчку:

alias hugotrans="~/bin/hugotrans.py"

Скрипт в интерактивном режиме создаст новый документ следующего содержания:

---
title: "Новая публикация"
slug: "novaja-publikatsija"
description: "Краткое описание новой публикации"
tags: ["публикация","новая","hugo","site","public"]
author: "DrHellP"
date: 2022-12-02T11:05:05+03:00
draft: true
---

Параметр slug - когда он определен во вступительной части, он заменяет имя файла, от которого образуется ссылка. Остальные параметры в объяснениях не нуждаются. В принципе archetypes/default.md можно слегка отредактировать, для большего удобства:

---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

Небольшое вступление публикуемое на странице со списком статей. Желательно на пару-тройку строк. Своего рода краткое содержание страницы. Заменить это вступление.

<!--more-->

### Заменить заголовок

Заменить это содержание.

Продолжение