Added soft page
This commit is contained in:
parent
111af09ec7
commit
2c702f1f92
11 changed files with 412 additions and 4 deletions
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
1. go Справочник по языку Go
|
1. go Справочник по языку Go
|
||||||
Установка, синтаксис, типы, функции, горутины...
|
Установка, синтаксис, типы, функции, горутины...
|
||||||
|
5. soft Справочник по софту
|
||||||
|
Обзоры используемого софта: Zola, Forgejo...
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ var All = []Book{
|
||||||
LinuxBook,
|
LinuxBook,
|
||||||
DevOpsBook,
|
DevOpsBook,
|
||||||
InfoSecBook,
|
InfoSecBook,
|
||||||
|
SoftBook,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find ищет книгу по ключу или алиасу
|
// Find ищет книгу по ключу или алиасу
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"rubric/modules/gomodules"
|
"rubric/modules/gomodules"
|
||||||
"rubric/modules/infosecmodules"
|
"rubric/modules/infosecmodules"
|
||||||
"rubric/modules/linuxmodules"
|
"rubric/modules/linuxmodules"
|
||||||
|
"rubric/modules/softmodules"
|
||||||
"rubric/utils"
|
"rubric/utils"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -79,6 +80,16 @@ func buildSearchIndex() []searchEntry {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Soft
|
||||||
|
for _, m := range softmodules.All {
|
||||||
|
entries = append(entries, searchEntry{
|
||||||
|
bookKey: "soft", bookTitle: "Soft",
|
||||||
|
modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
|
||||||
|
keywords: softmodules.Keywords(m.Key),
|
||||||
|
run: m.Run,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,6 +207,8 @@ func bookColorFor(key string) string {
|
||||||
return utils.ColorMagenta
|
return utils.ColorMagenta
|
||||||
case "infosec":
|
case "infosec":
|
||||||
return utils.ColorRed
|
return utils.ColorRed
|
||||||
|
case "soft":
|
||||||
|
return utils.ColorBlue
|
||||||
}
|
}
|
||||||
return utils.ColorWhite
|
return utils.ColorWhite
|
||||||
}
|
}
|
||||||
|
|
|
||||||
99
catalog/softbook.go
Normal file
99
catalog/softbook.go
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
package catalog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"rubric/modules/softmodules"
|
||||||
|
"rubric/utils"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SoftBook = Book{
|
||||||
|
Key: "soft",
|
||||||
|
Aliases: []string{"sw", "software"},
|
||||||
|
Title: "Справочник по софту",
|
||||||
|
Description: "Обзоры используемого софта: Zola, Forgejo и другое",
|
||||||
|
Run: runSoftBook,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSoftBook(scanner *bufio.Scanner) {
|
||||||
|
for {
|
||||||
|
showSoftMenu()
|
||||||
|
|
||||||
|
input, ok := ReadLine(scanner)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if input == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if IsQuit(input) {
|
||||||
|
fmt.Printf("\n %s%sДо свидания!%s\n\n",
|
||||||
|
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if IsBack(input) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if n, err := strconv.Atoi(input); err == nil {
|
||||||
|
if n >= 1 && n <= len(softmodules.All) {
|
||||||
|
softmodules.All[n-1].Run()
|
||||||
|
if softWaitBack(scanner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
||||||
|
utils.ColorBold, utils.ColorRed, n, len(softmodules.All), utils.ColorReset)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, ok := softmodules.Find(input); ok {
|
||||||
|
m.Run()
|
||||||
|
if softWaitBack(scanner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
||||||
|
utils.ColorBold, utils.ColorRed, input, utils.ColorReset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// softWaitBack возвращает true если пользователь нажал b (выйти в главное меню)
|
||||||
|
func softWaitBack(scanner *bufio.Scanner) bool {
|
||||||
|
fmt.Printf("\n %s%s[Enter — назад в меню Soft, b — главное меню]%s ",
|
||||||
|
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||||
|
if scanner.Scan() {
|
||||||
|
return IsBack(strings.TrimSpace(scanner.Text()))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func showSoftMenu() {
|
||||||
|
utils.Header("Справочник Soft")
|
||||||
|
|
||||||
|
fmt.Printf(" %s%sРазделы:%s\n\n", utils.ColorBold, utils.ColorWhite, utils.ColorReset)
|
||||||
|
|
||||||
|
for i, m := range softmodules.All {
|
||||||
|
aliases := ""
|
||||||
|
if len(m.Aliases) > 0 {
|
||||||
|
aliases = fmt.Sprintf("%s [%s]%s",
|
||||||
|
utils.ColorDim, strings.Join(m.Aliases, ", "), utils.ColorReset)
|
||||||
|
}
|
||||||
|
fmt.Printf(" %s%s %2d.%s %s%-10s%s %s%-32s%s%s\n",
|
||||||
|
utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
|
||||||
|
utils.ColorYellow+utils.ColorBold, m.Key, utils.ColorReset,
|
||||||
|
utils.ColorWhite, m.Title, utils.ColorReset,
|
||||||
|
aliases,
|
||||||
|
)
|
||||||
|
fmt.Printf(" %s%s%s\n\n",
|
||||||
|
utils.ColorDim, m.Description, utils.ColorReset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" %s%sРаздел (номер / ключ / b — главное меню / q — выход):%s ",
|
||||||
|
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||||
|
}
|
||||||
12
main.go
12
main.go
|
|
@ -10,12 +10,13 @@ import (
|
||||||
"rubric/modules/gomodules"
|
"rubric/modules/gomodules"
|
||||||
"rubric/modules/infosecmodules"
|
"rubric/modules/infosecmodules"
|
||||||
"rubric/modules/linuxmodules"
|
"rubric/modules/linuxmodules"
|
||||||
|
"rubric/modules/softmodules"
|
||||||
"rubric/utils"
|
"rubric/utils"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
book := flag.String("book", "", "открыть справочник (go, linux, devops, infosec)")
|
book := flag.String("book", "", "открыть справочник (go, linux, devops, infosec, soft)")
|
||||||
topic := flag.String("topic", "", "открыть раздел (используется вместе с -book)")
|
topic := flag.String("topic", "", "открыть раздел (используется вместе с -book)")
|
||||||
search := flag.String("search", "", "поиск по всем справочникам")
|
search := flag.String("search", "", "поиск по всем справочникам")
|
||||||
list := flag.Bool("list", false, "список всех справочников")
|
list := flag.Bool("list", false, "список всех справочников")
|
||||||
|
|
@ -102,6 +103,15 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
m.Run()
|
m.Run()
|
||||||
|
case "soft", "sw", "software":
|
||||||
|
m, ok := softmodules.Find(*topic)
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("\n %s%sРаздел '%s' не найден в Soft.%s\n",
|
||||||
|
utils.ColorBold, utils.ColorRed, *topic, utils.ColorReset)
|
||||||
|
softmodules.ListModules()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
m.Run()
|
||||||
default:
|
default:
|
||||||
fmt.Printf("\n %s%sСправочник '%s' не найден.%s\n",
|
fmt.Printf("\n %s%sСправочник '%s' не найден.%s\n",
|
||||||
utils.ColorBold, utils.ColorRed, *book, utils.ColorReset)
|
utils.ColorBold, utils.ColorRed, *book, utils.ColorReset)
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,8 @@ func Keywords(key string) []string {
|
||||||
"provider", "ресурс", "resource", "state", "план", "plan", "apply",
|
"provider", "ресурс", "resource", "state", "план", "plan", "apply",
|
||||||
"destroy", "модуль", "module", "workspace", "backend", "S3",
|
"destroy", "модуль", "module", "workspace", "backend", "S3",
|
||||||
"AWS", "GCP", "Azure", "переменные", "output",
|
"AWS", "GCP", "Azure", "переменные", "output",
|
||||||
|
"opentofu", "tofu", "форк", "fork", "MPL", "BSL", "лицензия",
|
||||||
|
"license", "Linux Foundation", "state encryption", "шифрование state",
|
||||||
},
|
},
|
||||||
"monitoring": {
|
"monitoring": {
|
||||||
"мониторинг", "Prometheus", "Grafana", "AlertManager", "Loki",
|
"мониторинг", "Prometheus", "Grafana", "AlertManager", "Loki",
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ import (
|
||||||
|
|
||||||
var TerraformModule = Module{
|
var TerraformModule = Module{
|
||||||
Key: "terraform",
|
Key: "terraform",
|
||||||
Aliases: []string{"tf", "iac", "infra"},
|
Aliases: []string{"tf", "iac", "infra", "opentofu", "tofu"},
|
||||||
Title: "Terraform — инфраструктура как код",
|
Title: "Terraform / OpenTofu — инфраструктура как код",
|
||||||
Description: "HCL, провайдеры, ресурсы, state, модули, workspace",
|
Description: "HCL, провайдеры, ресурсы, state, модули, workspace, форк OpenTofu",
|
||||||
Run: showTerraform,
|
Run: showTerraform,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,6 +255,61 @@ terraform workspace new staging
|
||||||
terraform workspace select production
|
terraform workspace select production
|
||||||
# В коде: terraform.workspace → "staging" / "production"`)
|
# В коде: terraform.workspace → "staging" / "production"`)
|
||||||
|
|
||||||
|
utils.Section("OpenTofu — форк Terraform")
|
||||||
|
utils.Code(`OpenTofu — открытый форк Terraform, созданный в 2023 году после того,
|
||||||
|
как HashiCorp сменил лицензию Terraform с MPL 2.0 на BSL 1.1 (Business Source License).
|
||||||
|
Форк собран из последней MPL-версии Terraform (1.5.x) и живёт под управлением
|
||||||
|
Linux Foundation — то есть под вендор-нейтральным, многосторонним управлением,
|
||||||
|
а не под контролем одной компании.
|
||||||
|
|
||||||
|
# Установка:
|
||||||
|
curl --proto '=https' --tlsv1.2 -fsSL \
|
||||||
|
https://get.opentofu.org/install-opentofu.sh -o install-opentofu.sh
|
||||||
|
chmod +x install-opentofu.sh
|
||||||
|
./install-opentofu.sh --install-method standalone
|
||||||
|
|
||||||
|
tofu version
|
||||||
|
|
||||||
|
# Совместимость: тот же HCL, тот же формат state, те же провайдеры —
|
||||||
|
# миграция практически всегда сводится к замене бинаря:
|
||||||
|
alias terraform=tofu`)
|
||||||
|
|
||||||
|
utils.Section("Почему в 2026 лучше выбрать OpenTofu")
|
||||||
|
utils.Code(`1. Лицензия
|
||||||
|
OpenTofu — MPL 2.0, одобрена OSI, без коммерческих ограничений.
|
||||||
|
Terraform (с версии 1.6) — BSL 1.1, не одобрена OSI. BSL не мешает
|
||||||
|
обычному использованию "terraform apply" для своей инфраструктуры,
|
||||||
|
но запрещает предлагать Terraform как конкурирующий управляемый сервис —
|
||||||
|
это касается вендоров SaaS/managed-платформ, не рядовых инженеров.
|
||||||
|
|
||||||
|
2. Управление (governance)
|
||||||
|
OpenTofu развивается комитетом под эгидой Linux Foundation —
|
||||||
|
роадмап не завязан на коммерческие приоритеты одной компании.
|
||||||
|
Terraform принадлежит HashiCorp (с 2025 года — в составе IBM).
|
||||||
|
|
||||||
|
3. Функции, которых нет в открытом Terraform CLI:
|
||||||
|
- нативное шифрование state и plan-файлов (KMS/PBKDF2) —
|
||||||
|
важно, если бэкенду state нельзя полностью доверять
|
||||||
|
- provider for_each — итерация по нескольким конфигурациям одного провайдера
|
||||||
|
- раннее вычисление переменных, в т.ч. в конфигурации backend
|
||||||
|
- флаг -exclude (точечное исключение ресурсов из plan/apply)
|
||||||
|
- собственный открытый реестр провайдеров/модулей registry.opentofu.org
|
||||||
|
|
||||||
|
4. Открытость без риска vendor lock-in
|
||||||
|
Если однажды понадобится вернуться на Terraform — это тоже
|
||||||
|
практически замена бинаря, конфигурация и state совместимы.
|
||||||
|
|
||||||
|
Что осталось только в Terraform:
|
||||||
|
Terraform Stacks и часть функций тесно интегрированы с HCP Terraform
|
||||||
|
(управляемой платформой HashiCorp) — в открытом бинаре их нет.
|
||||||
|
|
||||||
|
Итог: для новых проектов в 2026 OpenTofu — выбор по умолчанию с меньшим риском:
|
||||||
|
идентичный синтаксис, та же экосистема провайдеров, плюс нативное шифрование
|
||||||
|
state и вендор-нейтральное управление. Если уже есть большая инвестиция в
|
||||||
|
Terraform/HCP и используются Stacks — есть смысл остаться или мигрировать не спеша.`)
|
||||||
|
|
||||||
|
utils.Note("tofu — почти всегда совместимая замена terraform: 'alias terraform=tofu', большинство конфигураций и .tfstate работают без изменений")
|
||||||
|
|
||||||
utils.Section("Лучшие практики")
|
utils.Section("Лучшие практики")
|
||||||
utils.Code(`Структура проекта:
|
utils.Code(`Структура проекта:
|
||||||
infra/
|
infra/
|
||||||
|
|
|
||||||
82
modules/softmodules/soft_forgejo.go
Normal file
82
modules/softmodules/soft_forgejo.go
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
package softmodules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"rubric/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ForgejoModule = Module{
|
||||||
|
Key: "forgejo",
|
||||||
|
Aliases: []string{"fj", "git-forge"},
|
||||||
|
Title: "Forgejo — самохостинг git-форжа",
|
||||||
|
Description: "Форк Gitea: Docker-развёртывание, Angie, SSH на нестандартном порту",
|
||||||
|
Run: showForgejo,
|
||||||
|
}
|
||||||
|
|
||||||
|
func showForgejo() {
|
||||||
|
utils.Header("Soft — Forgejo")
|
||||||
|
|
||||||
|
utils.Section("Что это")
|
||||||
|
utils.Code(`Forgejo — самохостимый git-форж, форк Gitea. Свой репозиторий кода
|
||||||
|
с веб-интерфейсом, issues, pull request'ами, вики и (опционально)
|
||||||
|
Forgejo Actions для CI/CD — как собственный GitHub/GitLab на своём VDS.
|
||||||
|
|
||||||
|
Развёрнут на git.thebookofmagnus.ru (пользователь mroot),
|
||||||
|
за Angie как реверс-прокси, SSH-доступ на порту 222.`)
|
||||||
|
|
||||||
|
utils.Section("Развёртывание через Docker")
|
||||||
|
utils.Code(`# docker-compose.yml (упрощённо, с SQLite — не нужен отдельный БД-сервер)
|
||||||
|
services:
|
||||||
|
forgejo:
|
||||||
|
image: codeberg.org/forgejo/forgejo:latest
|
||||||
|
environment:
|
||||||
|
- FORGEJO__database__DB_TYPE=sqlite3
|
||||||
|
- FORGEJO__server__DOMAIN=git.thebookofmagnus.ru
|
||||||
|
- FORGEJO__server__SSH_PORT=222
|
||||||
|
- FORGEJO__service__DISABLE_REGISTRATION=true
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3000:3000"
|
||||||
|
- "222:22"
|
||||||
|
restart: unless-stopped`)
|
||||||
|
|
||||||
|
utils.Section("Angie как реверс-прокси")
|
||||||
|
utils.Code(`server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name git.thebookofmagnus.ru;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/git.thebookofmagnus.ru/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/git.thebookofmagnus.ru/privkey.pem;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTP-блок для certbot (webroot-метод, --nginx с Angie не работает):
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name git.thebookofmagnus.ru;
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
utils.Section("Частые проблемы при развёртывании")
|
||||||
|
utils.Println("• certbot: NXDOMAIN — DNS A-запись ещё не разошлась, подождать пропагации")
|
||||||
|
utils.Println("• certbot: 404 на /.well-known/acme-challenge/ — не хватает HTTP(80) server-блока в Angie для сабдомена")
|
||||||
|
utils.Println("• До готовности DNS/TLS — временный доступ через SSH-туннель: ssh -L 3000:127.0.0.1:3000 user@host")
|
||||||
|
utils.Println("• Предупреждение о смене SSH host key при первом git push — решается добавлением ключа в ssh-agent")
|
||||||
|
utils.Println("• Management → Users в новых версиях Forgejo переехал в раздел \"Identity & access\", а не отдельная вкладка")
|
||||||
|
|
||||||
|
utils.Section("Настройка")
|
||||||
|
utils.Command("FORGEJO__service__DISABLE_REGISTRATION=true", "отключить самостоятельную регистрацию")
|
||||||
|
utils.Command("FORGEJO__ui__APP_NAME", "название, отображаемое в заголовке вкладки браузера")
|
||||||
|
utils.Command("/data/gitea/public/assets/img/", "путь для замены логотипа и favicon (в примонтированном volume)")
|
||||||
|
|
||||||
|
utils.Note("CI/CD (Forgejo Actions) можно не настраивать сразу — вполне рабочий вариант отложить его на потом")
|
||||||
|
|
||||||
|
utils.Footer()
|
||||||
|
}
|
||||||
75
modules/softmodules/soft_registry.go
Normal file
75
modules/softmodules/soft_registry.go
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
package softmodules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"rubric/utils"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Module описывает один раздел справочника Soft
|
||||||
|
type Module struct {
|
||||||
|
Key string
|
||||||
|
Aliases []string
|
||||||
|
Title string
|
||||||
|
Description string
|
||||||
|
Run func()
|
||||||
|
}
|
||||||
|
|
||||||
|
// All — список всех модулей в порядке меню
|
||||||
|
var All = []Module{
|
||||||
|
ZolaModule,
|
||||||
|
ForgejoModule,
|
||||||
|
// сюда будут добавляться новые обзоры софта
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find ищет модуль по ключу или алиасу
|
||||||
|
func Find(key string) (Module, bool) {
|
||||||
|
key = strings.ToLower(strings.TrimSpace(key))
|
||||||
|
for _, m := range All {
|
||||||
|
if m.Key == key {
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
for _, a := range m.Aliases {
|
||||||
|
if a == key {
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Module{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListModules выводит краткий список модулей
|
||||||
|
func ListModules() {
|
||||||
|
fmt.Printf("\n%s%sРазделы справочника Soft:%s\n\n",
|
||||||
|
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||||
|
for i, m := range All {
|
||||||
|
fmt.Printf(" %s%d.%s %-14s — %s\n",
|
||||||
|
utils.ColorDim, i+1, utils.ColorReset,
|
||||||
|
utils.ColorYellow+utils.ColorBold+m.Key+utils.ColorReset,
|
||||||
|
m.Title,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keywords возвращает ключевые слова для поиска по модулю
|
||||||
|
func Keywords(key string) []string {
|
||||||
|
kw := map[string][]string{
|
||||||
|
"zola": {
|
||||||
|
"zola", "статический сайт", "static site", "генератор сайта",
|
||||||
|
"SSG", "Rust", "шаблоны", "templates", "Tera", "markdown",
|
||||||
|
"сайт", "rsync", "деплой", "deploy", "front matter",
|
||||||
|
"content", "themes", "темы",
|
||||||
|
},
|
||||||
|
"forgejo": {
|
||||||
|
"forgejo", "git", "форж", "git forge", "gitea", "self-hosted",
|
||||||
|
"самохостинг", "репозиторий", "repository", "docker", "sqlite",
|
||||||
|
"SSH", "issues", "pull request", "CI", "actions", "webhook",
|
||||||
|
"git-сервер",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if v, ok := kw[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
69
modules/softmodules/soft_zola.go
Normal file
69
modules/softmodules/soft_zola.go
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
package softmodules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"rubric/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ZolaModule = Module{
|
||||||
|
Key: "zola",
|
||||||
|
Aliases: []string{"z", "static"},
|
||||||
|
Title: "Zola — генератор статических сайтов",
|
||||||
|
Description: "Однобинарный SSG на Rust: сборка, деплой через rsync, Angie",
|
||||||
|
Run: showZola,
|
||||||
|
}
|
||||||
|
|
||||||
|
func showZola() {
|
||||||
|
utils.Header("Soft — Zola")
|
||||||
|
|
||||||
|
utils.Section("Что это")
|
||||||
|
utils.Code(`Zola — генератор статических сайтов (SSG), написанный на Rust.
|
||||||
|
Один бинарь без зависимостей, встроенный шаблонизатор Tera,
|
||||||
|
встроенный Sass-компилятор и Markdown-парсер — ничего ставить
|
||||||
|
отдельно не нужно (в отличие от Jekyll/Hugo-плагинов и npm-тулчейнов).
|
||||||
|
|
||||||
|
Подходит для блогов, документации, персональных сайтов —
|
||||||
|
как раз для thebookofmagnus.ru.`)
|
||||||
|
|
||||||
|
utils.Section("Основные команды")
|
||||||
|
utils.Command("zola init mysite", "создать новый проект сайта")
|
||||||
|
utils.Command("zola build", "собрать сайт в папку public/")
|
||||||
|
utils.Command("zola serve", "локальный dev-сервер с live reload")
|
||||||
|
utils.Command("zola check", "проверить ссылки и конфигурацию")
|
||||||
|
|
||||||
|
utils.Section("Модель развёртывания: локальная сборка + rsync")
|
||||||
|
utils.Code(`Zola не обязательно ставить на сервер — она нужна только там,
|
||||||
|
где собирается сайт. Рабочая схема:
|
||||||
|
|
||||||
|
1. Пишешь контент локально
|
||||||
|
2. zola build → получаешь папку public/
|
||||||
|
3. rsync -avz --delete public/ user@server:/var/www/mysite/public/
|
||||||
|
4. Angie отдаёт статику из /var/www/mysite/public
|
||||||
|
|
||||||
|
Разбор флагов rsync:
|
||||||
|
-a archive-режим: права, симлинки, время модификации
|
||||||
|
-v verbose — видно что копируется
|
||||||
|
-z сжатие при передаче
|
||||||
|
--delete удаляет на сервере файлы, которых больше нет локально
|
||||||
|
(важно при переименовании/удалении страниц)
|
||||||
|
|
||||||
|
Альтернатива — автосборка на сервере: bare git-репозиторий +
|
||||||
|
post-receive хук, который сам делает checkout + zola build +
|
||||||
|
копирование в /var/www/mysite/public. Проще для старта — вариант
|
||||||
|
с локальной сборкой и rsync, без Zola на самом VDS.`)
|
||||||
|
|
||||||
|
utils.Section("Раздача через Angie")
|
||||||
|
utils.Code(`server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name thebookofmagnus.ru www.thebookofmagnus.ru;
|
||||||
|
|
||||||
|
root /var/www/mysite/public;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/thebookofmagnus.ru/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/thebookofmagnus.ru/privkey.pem;
|
||||||
|
}`)
|
||||||
|
|
||||||
|
utils.Note("certbot с Angie: плагин --nginx конфликтует с Angie. Всегда используй --webroot (webroot-папка вроде /var/www/certbot + location /.well-known/acme-challenge/ в HTTP-server-блоке)")
|
||||||
|
|
||||||
|
utils.Footer()
|
||||||
|
}
|
||||||
BIN
rubric
BIN
rubric
Binary file not shown.
Loading…
Add table
Reference in a new issue