Простой блог на Swift c помощью Publish
This site is under construction.

version: 2.1.0

C Новым Годом!

mode: 

Простой блог на Swift c помощью Publish

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

logo

Prehistory instead of introduction | Предыстория вместо вступления

Написать эту статью, собственно как и на саму идею создания собственного блога - меня подтолкнул разговор с одним из моих знакомых.

Однажды, в дни осенней Covid'ной изоляции, когда заспорили мы в Telegram с одним моим знакомцем, синьором Front-End Developer'ом, о том, что Swift - так себе язык, и на нём "на Swift'е сайты не пишутъ".

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

И вот, слоняясь по бескрайним просторам бесконечного Web'а, наткнулся я, на очень замечательную и интересную вещь под названием Publish, написанную одним польским программистом, по имени John Sundell (Джон Санделл). О ней и пойдет мой дальнейший повествовательный рассказ. Кстати, как рассказывает сам Сандел (Publish is used to build all of swiftbysundell.com). Его блог полностью создан при помощи Publish. И тут меня осенило, а не написать ли мне свой сайт, да еще и на Swift, да чтоб товарищу показать, что-бы не 3.141592653... <censored>.

И так, в данной статье мы рассмотрим:

  • Что такое Publish и как с ним работать?
  • Создание репозитория на GitHub для нашего сайта.
  • Как размещать сайты на GitHub Pages при помощи Publish

Так что же такое Publish ?

Publish - это генератор статических сайтов написанный на Swift, который позволяет строить всевозможную логику при генерации сайта. Вы пишите странички при помощи облегченного языка разметки под названием Markdown и Swift строит вам сайт с ... (with blackjack and ... <censored>) тегами, картой сайта, RSS и много чего еще на что нам только хватит фантазии. А это как раз то, что нам нужно.

Установка Publish.

Для успешного использования Publish, убедитесь что в вашей системе установлен Swift версии 5.2 (или выше). Если вы используете Mac, так-же убедитесь что xcode-select указывает на Xcode который включает в себя требуемую версию Swift, и что вы используете macOS Catalina (10.15) или новее. Обратите внимание, что Publish официально не поддерживает бета-версии программного обеспечения, включая бета-версии Xcode и macOS, или невыпущенные версии Swift.

Для начала в своей домашней директории, создадим временный каталог под названием ~/tmp, например, вы можете создать любой. Это каталог, куда мы будем скачивать исходники Publish. После установки утилиты его можно удалить, он нам больше не понадобится. Далее клонируем исходники себе на диск и устанавливаем. Для этого в терминале – выполним следующие команды.

$ mkdir tmp
$ cd tmp
$ git clone https://github.com/JohnSundell/Publish.git
$ cd Publish
$ make

После того, как все установится - проверяем, выполнив комманду:

$ which publish
/usr/local/bin/publish

Все, Publish установлен и директория ~/tmp нам больше не нужна, можно смело ее удалять, для этого выполним команду: rm -rf ~/tmp и снова перейдем в домашний каталог командой cd ~ или просто ~.

Создание и генерация сайта.

Создадим еще одну директорию, где мы будем размещать наш сайт, например: ~/Projects/myBlog и перейдем в нее:

$ mkdir -p ~/Projects/myBlog && cd "$_"

Теперь, если выполнить команду publish --help или просто publish без параметров, мы увидим список доступных нам параметров:

$ publish --help
Publish Command Line Interface
------------------------------
Interact with the Publish static site generator from
the command line, to create new websites, or to generate
and deploy existing ones.

Available commands:

- new: Set up a new website in the current folder.
- generate: Generate the website in the current folder.
- run: Generate and run a localhost server on default port 8000
       for the website in the current folder. Use the "-p"
       or "--port" option for customizing the default port.
- deploy: Generate and deploy the website in the current
       folder, according to its deployment method.

Находясь в директории ~/Projects/myBlog выполним команду publish new, она создаст структуру нашего сайта. (собственно исходники, из которых мы и будем в дальнейшем генерировать наш сайт) .

$ publish new
✅ Generated website project for 'MyBlog'
Run 'open Package.swift' to open it and start building

Нам предлагается открыть наш проект и начать строить сайт, команда open, в macOS откроет нам файл Package.swift в Xcode (если вы конечно не настроили другой редактор по умолчанию для файлов с расширением .swift). Собственно Xcode и не обязателен. Вы можете например установить это все в Linux и открыть этот проект в любом своем любимом текстовом редакторе или IDE (с блэкджеком и ... <censored>), с поддержкой синтаксиса, пред просмотра Markdown и тд, и просто, после внесения изменений, каждый раз запускать команду publish generate. Это почти то же самое, если в Xcode нажать кнопку Выполнить (Run) на панели инструментов, или сочетание клавиш + R (Command+R) toolbar

Давайте наконец сгенерируем наш сайт и запустим веб-сервер, выполнив для этого команду publish run, по умолчанию она использует 8000 порт - это можно исправить, если запустить ее с параметром -p <номер_порта> или --port <номер порта>, например: publish run -p 80.

publish run

      ...

Publishing MyBlog (6 steps)
[1/6] Copy 'Resources' files
[2/6] Add Markdown files from 'Content' folder
[3/6] Sort items
[4/6] Generate HTML
[5/6] Generate RSS feed
[6/6] Generate site map
✅ Successfully published MyBlog
Starting web server at http://localhost:8000

Press ENTER to stop the server and exit

И наконец, после некоторого ожидания, пока Publish подтянет все зависимости и соберет наш сайт, переходим по ссылке http://localhost:8000 и Look at that! Look at that!:

browser

НИЧОСИ! Hooray! У нас теперь есть свой сайт. Просто, не правда ли?

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

Создание репозитария для GitHub Pages

Если вы не знакомы с git, и не знаете как его настраивать, перед выполнением следующего шага рекомендуется почитать: Git Book

Внимание! Название репозитория должно быть в формате {login}.github.io.

Перейдем по ссылке https://github.com/new

github-new
github-set

Готово! Tеперь настроим локальный репозиторий и свяжем его c GitHub. Для этого, в терминале, находясь в папке нашего сайта ~/Projects/myBlog выполним команды как на картинке выше:

$ git init

Так, мы создали локальный репозиторий, теперь давайте исключим папку Output, нет смысла ее индексировать т.к. ее содержимое, мы будем 'деплоить' на {login}.github.io, Добавим /Output в .gitignore

$ git add .
$ git commit -m "First commit of our Blog"
$ git branch -M main
$ git remote add origin https://github.com/{login}/{login}.github.io.git
$ git push -u origin main

С git разобрались, переходим к настройкам.

Настройки

Откроем еще одну вкладку в терминале нажав сочетание клавиш + T и введем команду:

$ open Package.swift

Откроем проект и рассмотрим структуру каталогов которую нам создал Publish

filetree

Структура нашего проекта состоит из :

  • Content (исходники страниц сайта в формате markdown)
  • Output (сгенерированные ресурсы - HTML, стили и тд.)
  • Resources (стили, картинки, медиа)
  • Sources (исходники шаблоны на Swift)

Если потом еще посмотреть в терминале командой ls -a , то будет видно, что там есть и скрытые папки со всякими зависимостями, кешами и тд, в дальнейшем, нам еще пригодится каталог .build, а пока ...

Файл Package.swift - это менеджер пакетов Swift, сюда мы будем добавлять всякие пакеты, прописывать зависимости и Xcode будет автоматически их подгружать, или вручную в терминале командой publish generate

// swift-tools-version:5.2

import PackageDescription

let package = Package(
    name: "MyBlog",
    products: [
        .executable(
            name: "MyBlog",
            targets: ["MyBlog"]
        )
    ],
    dependencies: [
        .package(name: "Publish", url: "https://github.com/johnsundell/publish.git", from: "0.6.0")
    ],
    targets: [
        .target(
            name: "MyBlog",
            dependencies: ["Publish"]
        )
    ]
)

Файл main.swift - это настройки нашего сайта, там настраиваются всякие, описания, метаданные, подключаются всевозможные плагины, темы.

import Foundation
import Publish
import Plot

// This type acts as the configuration for your website.
struct MyBlog: Website {
    enum SectionID: String, WebsiteSectionID {
        // Add the sections that you want your website to contain here:
        case posts
    }

    struct ItemMetadata: WebsiteItemMetadata {
        // Add any site-specific metadata that you want to use here.
    }

    // Update these properties to configure your website:
    var url = URL(string: "https://your-website-url.com")!
    var name = "MyBlog"
    var description = "A description of MyBlog"
    var language: Language { .english }
    var imagePath: Path? { nil }
}

// This will generate your website using the built-in Foundation theme:
try MyBlog().publish(withTheme: .foundation)

В общем сайт мы создали, хоть и очень простой . Но ведь мы собственно еще пока ни единой строчки кода не написали, занимались установкой тысызыть.

Deploy*

Deploy (от англ. deployment) - разворачивание, в данном случае – установка нашего локального сайта на удаленный сервер GitHub.

Пока что Publish ничего не знает о нашем репозитории, давайте его Publish загружать сгенерированные странички в созданный на GitHub репозиторий, для этого в нашем генераторе Publish существует DeploymentMethod.

В файле Sources/main.swift заменим строку:

// This will generate your website using the built-in Foundation theme:
try MyBlog().publish(withTheme: .foundation)

на

// This will generate your website using the built-in Foundation theme:
try MyBlog().publish(
                        withTheme: .foundation,
                        deployedUsing: .gitHub("{login}/{login}.github.io", useSSH: false)
                    )

и в терминале выполним команду:

$ publish deploy

После этого возвращаемся на github.com и в настройках нашего репозитория находим пункт GitHub Pages, где в качестве ресурса выбираем ветку master и нажимаем кнопку сохранить.

git-pages

Через некоторое время наш сайт будет доступен по адресу https://{login}.github.io

Кастомизация*

Кастомизация (от англ. to customize — настраивать, изменять)

Давайте немного изменим наш сайт, добавим разделы, страницы, настроим тему, сделаем подсветку синтаксиса, мы же программисты, нам без подсветки никуда.

Итак, добавим раздел о себе, создав файл about.md в папке Content и добавим туда немного информации о себе, так же в директорию Resources/myBlog добавим файл с картинкой (с нашей аватаркой).

xcode

Но, Publish опять-таки (по умолчанию), ничего не знает об этом, он, конечно сгенерирует файл Output/about/index.html по имени markdown файла и страница будет доступна по адресу http://localhost:8000/about. Но это – не совсем то, что нам нужно, нам же еще нужна ссылка на нее (пункт меню). Давайте расскажем Publish об этом, добавив case about в файл main.swift

enum SectionID: String, WebsiteSectionID {
    // Add the sections that you want your website to contain here:
    case posts
    case about
    }

Снова генерируем сайт и...

about-us

Oh my God! We have done it! Божечки, мы сделали это!

Теперь создадим Theme (тему) для нашего блога на основе стандартной темы Foundation

Создание темы

Помните я говорил про скрытый каталог .build в корне каталога нашего сайта? Ну что, настало его время. В терминале находясь в каталоге ~/Projects/myBlog выполним команды:

$ cp .build/checkouts/publish/Sources/Publish/API/Theme+Foundation.swift Sources/MyBlog/Theme+myBlog.swift
$ cp .build/checkouts/publish/Resources/FoundationTheme/styles.css Resources/MyBlog/styles.css

Тем самым скопировав тему и стили поmумолчанию для нашего сайта, которые в дальнейшем мы и будем изменять:

Заставим Publish использовать их вместо стандартных, изменив в файле Sources/MyBlog/Theme+myBlog.swift строки:

import Plot

public extension Theme {
    /// The default "Foundation" theme that Publish ships with, a very
    /// basic theme mostly implemented for demonstration purposes.
    static var foundation: Self {
        Theme(
            htmlFactory: FoundationHTMLFactory(),
            resourcePaths: ["Resources/FoundationTheme/styles.css"]
        )
    }
}

на

import Plot
import Publish

public extension Theme {
    /// The default "Foundation" theme that Publish ships with, a very
    /// basic theme mostly implemented for demonstration purposes.
    static var myblog: Self {
        Theme(
            htmlFactory: MyBlogHTMLFactory(),
            resourcePaths: ["Resources/MyBlog/styles.css"]
        )
    }
}

и еще строку:

private struct FoundationHTMLFactory<Site: Website>: HTMLFactory {

заменим на:

private struct MyBlogHTMLFactory<Site: Website>: HTMLFactory

А так же в файле main.swift изменим строку:

try MyBlog().publish(withTheme: .foundation,

на строку:

try MyBlog().publish(withTheme: .myblog,

Добавление плагина подсветки синтаксиса:

Давайте теперь добавим подсветку синтаксиса для нашего блога, я выбрал Pygments, этот плагин поддерживает подсветку, около 500 языков. Я думаю для большинства задач – этого будет более чем достаточно.

Теперь в файл Package.swift добавим наш пакет

 dependencies: [
     .package(name: "Publish", url: "https://github.com/johnsundell/publish.git", from: "0.6.0"),
     .package(url: "https://github.com/Ze0nC/SwiftPygmentsPublishPlugin", .branch("master"))

а так же зависимость SwiftPygmentsPublishPlugin

 .target(
     name: "MyBlog",
     dependencies: ["Publish",  "SwiftPygmentsPublishPlugin"]

в файле main.swift пропишем import SwiftPygmentsPublishPlugin и plugins: [.pygments()]

import SwiftPygmentsPublishPlugin

     try Blog().publish(
         withTheme: .blog,
         deployedUsing: .gitHub("nazares/nazares.github.io", useSSH: false),
         plugins: [.pygments()]
     )

Так же нужно в файле Theme+Blog.swift в head каждой страницы после свойства on:context.site дописать путь к нашим стилям stylesheetPaths:["/style.css"], вот так:

.head(for: index, on: context.site, stylesheetPaths: ["/styles.css"])
.head(for: section, on: context.site, stylesheetPaths: ["/styles.css"])
.head(for: item, on: context.site, stylesheetPaths: ["/styles.css"])
.head(for: page, on: context.site, stylesheetPaths: ["/styles.css"])

После этого, нужно в файл style.css добавить стили для подсветки, например: pygments-native.css

Еще больше тем для Pygments можно найти здесь.

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

$ git commit -m "Blog release v0.1"
$ git push -u origin main
$ publish deploy

И насладимся трудами нашего творчества!

release

Cпасибо за внимание, позитива и добра!

UPD: Добавлено видео

Youtube

Rutube

Platform