Hugo - это фреймворк для веб-сайтов общего назначения. С технической точки зрения Hugo - это генератор статических сайтов . В отличие от систем, которые динамически создают страницу с каждым запросом посетителя, Hugo создает страницы, когда вы создаете или обновляете свой контент. Поскольку веб-сайты просматриваются гораздо чаще, чем редактируются, Hugo предназначен для обеспечения оптимального просмотра для конечных пользователей вашего веб-сайта и идеального опыта написания для авторов веб-сайтов.
Суть всего процесса:
- Локально устанавливается Hugo
- Создается структура сайта
- Добавляется содержимое в виде файлов Markdown
- С помощью Hugo генерируется сайт - html страницы
- Готовые страницы загружаются на любой хостинг
- При обновлении процедуру повторить с третьего шага
Зачем это нужно?
Если страницы на сайт добавляются нечасто, нет нужды использовать 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 или по шаблону установленной темы. Но при использовании такой команды существуют некоторые неудобства:
- Заголовок формируется из имени файла, но в имени файла пробелы должны быть заменены знаком '-'.
- Не смотря на то, что существует IDN. Ссылки вида - http://localhost:1313/tags/статья/ при копировании в мессенджер и в RSS становятся - http://localhost:1313/tags/%D1%81%D1%82%D0%B0%D1%82%D1%8C%D1%8F/.
- Также отсутствует возможность сразу добавить некоторые данные в статью.
Возможно я плохо читал документацию.
Мы пойдем другим путем.
Напишем скрипт на 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-->
### Заменить заголовок
Заменить это содержание.