Development and more...

Простой Блог на Swift c помощью Publish и размещение его на GitHub Pages

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

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

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, да чтоб товарищу показать, что бы не ... <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: Добавлено видео