22
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

静的サイトジェネレータ「Publish」のセットアップ&使い方(Swift)

Last updated at Posted at 2022-03-29

「PublishでWebサイトを構築する」は6部構成です。
記事を順番に読み進めると、PublishでWebサイトが構築できるようになります。

第1部: セットアップ&使い方 ←イマココ
第2部: カスタムテーマの作成
第3部: プラグインのインストール
第4部: プラグインの作成(未投稿)
第5部: ツイートボタンの表示
第6部: はてなブックマークボタンの表示

「Publish」とは?

Swift製の静的サイトジェネレータです。
SwiftとMarkdownファイルからHTMLやCSSを生成し、GitHub Pagesなどへデプロイできます。

環境

  • OS:macOS Monterey 12.2.1
  • Xcode:13.3 (13E113)
  • Swift:5.6
  • Publish:0.8.0

セットアップ

CLIツールのインストール

Publishで静的サイトを生成するには、CLIツールとライブラリの両方を使います。
どちらも1つのパッケージで管理されています。

PublishのPackage.swift
let package = Package(
    // ...
    products: [
        .library(name: "Publish", targets: ["Publish"]),
        .executable(name: "publish-cli", targets: ["PublishCLI"])
    ],
    // ...
)

引用:https://github.com/JohnSundell/Publish/blob/9e1b20a23c73ba078966a4fcce23146088d0de63/Package.swift#L14-L15

私はCLIツールとライブラリでバージョンを揃えたい、かつどちらもSwiftPMで管理したいため、まず以下のような Package.swift を用意しました。

後述しますが、あとで捨てるので丁寧に作る必要はありません。

Package.swift
// swift-tools-version:5.6

import PackageDescription

let package = Package(
    name: "Tools",
    platforms: [
        .macOS(.v12),
    ],
    dependencies: [
        .package(url: "https://github.com/johnsundell/publish.git", from: "0.8.0"),
    ],
    targets: [.target(name: "Tools", path: "")]
)

Package.swift を作成したら、PublishのCLIツールをリリースビルドします。

$ swift build -c release --product publish-cli

グローバル環境を汚したくないため、パスを通さずに使うことにします。
リリースビルドしたCLIツールは .build/release/ フォルダに生成されます。

$ .build/release/publish-cli 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.

publish-cli help を実行して、new generate run deploy と、コマンドが4つあることがわかります。

これでインストールは完了です。

Webサイトの基盤生成

publish-cli new を実行し、Webサイトの基盤を生成します。

Package.swift などが残っていると生成できないので、削除します。

❌ファイルが残っていると生成できない
$ .build/release/publish-cli new
❌ New projects can only be generated in empty folders.
✅フォルダが空だと生成できる
$ rm -f Package.swift Package.resolved
$ .build/release/publish-cli new
✅ Generated website project for 'WebsiteSample'
Run 'open Package.swift' to open it and start building

成功すると Package.swift やソースなどが生成されます。(すごい!)
tree コマンドだと見えないですが .gitignore も生成されています。

$ tree
.
├── Content
│   ├── index.md
│   └── posts
│       ├── first-post.md
│       └── index.md
├── Package.resolved
├── Package.swift
├── Resources
└── Sources
    └── WebsiteSample
        └── main.swift

5 directories, 6 files

プロジェクト名は現在のフォルダ名をアッパーキャメルケースに変換した文字列が使われます。
私は website-sample フォルダで実行したため、プロジェクト名が WebsiteSample になりました。
必要に応じてパッケージ名などを変更してください。

指示通り open Package.swift を実行し、生成されたパッケージをXcodeで開いて開発します。

プロジェクトの更新

Publish 0.8.0現在、生成されるパッケージのswift-tools-versionが古かったりするので、更新します。

Package.swift
- // swift-tools-version:5.2
+ // swift-tools-version:5.6

import PackageDescription

let package = Package(
    name: "WebsiteSample",
    products: [
        .executable(
            name: "WebsiteSample",
            targets: ["WebsiteSample"]
        )
    ],
    dependencies: [
-        .package(name: "Publish", url: "https://github.com/johnsundell/publish.git", from: "0.7.0")
+        .package(url: "https://github.com/johnsundell/publish.git", from: "0.8.0")
    ],
    targets: [
-        .target(
+        .executableTarget(
            name: "WebsiteSample",
-            dependencies: ["Publish"]
+            dependencies: [
+                .product(name: "Publish", package: "publish")
+            ]
        )
    ]
)

成果物(HTMLなど)が生成される Output フォルダが .gitignore に記述されていないため、成果物をGit管理しない場合は追記します。

.gitignore
+ Output

実装によりますが、成果物の生成時に Resources フォルダが存在しないとエラーが発生するため、フォルダ内に .gitkeep を追加しておくといいです。

❌Resourcesフォルダがないとエラーになることがある
$ .build/release/publish-cli generate
[1/1] Planning build
Building for debugging...
[1/3] Emitting module WebsiteSample
[2/3] Compiling WebsiteSample main.swift
[3/3] Linking WebsiteSample
Build complete! (0.51s)
Swift/ErrorType.swift:200: Fatal error: Error raised at top level: Publish encountered an error:
[step] Copy 'Resources' files
[path] Resources
[info] Folder not found
/bin/bash: line 1: 77958 Trace/BPT trap: 5       swift run
❌ ShellOut encountered an error
Status code: 133
Message: "[1/1] Planning build
Building for debugging...
[1/3] Emitting module WebsiteSample
[2/3] Compiling WebsiteSample main.swift
[3/3] Linking WebsiteSample
Build complete! (0.51s)
Swift/ErrorType.swift:200: Fatal error: Error raised at top level: Publish encountered an error:
[step] Copy 'Resources' files
[path] Resources
[info] Folder not found
/bin/bash: line 1: 77958 Trace/BPT trap: 5       swift run"
Output: ""
make: *** [generate] Error 1

.copyResources() を実行しなければエラーは発生しないので、実装でも防げます。

✅.copyResources()を実行しなければResourcesフォルダがなくてもエラーにならない
try WebsiteSample().publish(using: [
    .addMarkdownFiles(),
-   .copyResources(),
    .generateHTML(withTheme: .foundation),
    .generateRSSFeed(including: [.posts]),
    .generateSiteMap(),
    .deploy(using: .gitHub("uhooi/uhooi.github.io", useSSH: false))
])

これらの更新はすべてPublishにPRを作成したので、マージされれば不要になります。

publish-cliコマンドのラップ

Webサイトの動作確認のため、 publish-cli run を頻繁に使います。
毎回 .build/release/publish-cli と入力するのは手間なので、 Makefile に隠蔽すると便利です。

Makefile
PUBLISH_CLI_PATH := ./.build/release/publish-cli

.PHONY: setup
setup:
	$(MAKE) build-cli-tool

.PHONY: build-cli-tool
build-cli-tool:
	swift build -c release --product publish-cli

.PHONY: help
help:
	${PUBLISH_CLI_PATH} help

.PHONY: run
run:
	${PUBLISH_CLI_PATH} run

.PHONY: generate
generate:
	${PUBLISH_CLI_PATH} generate

これで make runmake generate と実行するだけで publish-cli を使えます。
私は new は最初の1回のみ、 deploy は使わないため、 Makefile に定義していません。

ついでに make setup でCLIツールをビルドできるようにしています。
ローカルやCIで開発環境を構築する際に便利です。

使い方

Webサイトのプレビュー

publish-cli run を実行すると、Webサイトのプレビューを見れます。

$ make run
./.build/release/publish-cli run
Building for debugging...
Build complete! (0.10s)
Publishing MyWebsite (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 WebsiteSample
🌍 Starting web server at http://localhost:8000

Press ENTER to stop the server and exit

Enterキーを押下するまでローカルでWebサイトを参照できます。
http://localhost:8000

スクリーンショット 2022-03-28 13.39.35.png

Webサイトが表示されました。
デフォルトのテーマ( foundation )がシンプルで、なんとなくAppleみを感じて好きです。

成果物の生成

publish-cli generate を実行すると、 Output フォルダに成果物が生成されます。

$ make generate 
./.build/release/publish-cli generate
Building for debugging...
Build complete! (0.09s)
Publishing WebsiteSample (5 steps)
[1/5] Add Markdown files from 'Content' folder
[2/5] Copy 'Resources' files
[3/5] Generate HTML
[4/5] Generate RSS feed
[5/5] Generate site map
✅ Successfully published WebsiteSample

HTMLやCSS、RSSなどが生成されていることがわかります。

$ tree Output/
Output/
├── feed.rss
├── index.html
├── posts
│   ├── first-post
│   │   └── index.html
│   └── index.html
├── sitemap.xml
├── styles.css
└── tags
    ├── article
    │   └── index.html
    ├── first
    │   └── index.html
    └── index.html

5 directories, 9 files

publish-cli run でも生成されますが、ターミナルを止めてしまうため、CIで使えません。
成果物を生成するのみなら publish-cli generate を使うべきです。

Webサイトのデプロイ

publish-cli deploy を実行すると、Webサイトをデプロイします。

ただGitHub Actionsでデプロイを自動化したらエラーが発生しました。
おそらく認証周りが原因ですが、解決できませんでした。

そのため私は publish-cli deploy を使わず publish-cli generate で成果物を生成し、それをサードパーティ製のアクションでGitHub Pagesへデプロイしています。

以下がワークフローの例です。

deploy.yml
name: Deploy

on:
  push:
    branches: [ main ]

jobs:
  deploy-github-pages:
    runs-on: ubuntu-latest
    container: "swift:5.6"
    steps:
    - uses: actions/checkout@v3

    - name: Setup
      run: make setup

    - name: Generate the website
      run: make generate

    - name: Deploy to GitHub Pages
      uses: peaceiris/actions-gh-pages@v3
      with:
        personal_token: ${{ secrets.PERSONAL_TOKEN }}
        external_repository: uhooi/uhooi.github.io
        publish_branch: master
        publish_dir: ./Output

peaceiris/actions-gh-pages アクションの詳細は、GitHubのREADMEをご参照ください。
https://github.com/peaceiris/actions-gh-pages

実装

メインファイルの更新

基本的には Sources/{パッケージ名}/main.swift の1ファイルのみ更新します。

GitHub Pagesへデプロイする場合は、以下のように変更します。
GitHub PagesのURLは http だとエラーになるので https で指定します。

main.swift
struct WebsiteSample: Website {
    enum SectionID: String, WebsiteSectionID {
        case posts
    }

    struct ItemMetadata: WebsiteItemMetadata {
    }

-   var url = URL(string: "https://your-website-url.com")!
+   var url = URL(string: "https://uhooi.github.io")!
    var name = "WebsiteSample"
    var description = "A description of WebsiteSample"
    var language: Language { .english }
    var imagePath: Path? { nil }
}

- try WebsiteSample().publish(withTheme: .foundation)
+ try WebsiteSample().publish(using: [
+   .addMarkdownFiles(),
+   .copyResources(),
+   .generateHTML(withTheme: .foundation),
+   .generateRSSFeed(including: [.posts]),
+   .generateSiteMap(),
+   .deploy(using: .gitHub("uhooi/uhooi.github.io", useSSH: false))
+ ])

.deploy(using:useSSH:)branch: も指定できるのですが、エラーになる不具合があるため、デフォルト( master )のブランチにしかプッシュできません。
参考:https://github.com/JohnSundell/Publish/pull/116

useSSH:true が推奨ですが、CIでの設定がわからないため false にしてしまっています。
私は publish-cli deploy を使っていないので、おそらく false でも問題ありません。

必要に応じて名前や概要、言語なども変更します。

main.swift
struct WebsiteSample: Website {
    enum SectionID: String, WebsiteSectionID {
        case posts
    }

    struct ItemMetadata: WebsiteItemMetadata {
    }

    var url = URL(string: "https://uhooi.github.io")!
-   var name = "WebsiteSample"
-   var description = "A description of WebsiteSample"
-   var language: Language { .english }
+   var name = "THE Uhooi"
+   var description = "Uhooi's Website"
+   var language: Language { .japanese }
    var imagePath: Path? { nil }
}

foundation テーマでは、名前や概要は以下に反映されます。

スクリーンショット_2022-03-28_16_34_57.png

記事の追加や更新

記事は .md ファイルにMarkdown記法で書きます。
デフォルトでは Content/posts の中に .md ファイルを格納して rungenerate するだけで記事がHTMLで生成されます。(簡単!)

記事の形式は以下の通りです。

first-post.md
---
date: 2022-03-28 12:40
description: A description of my first post.
tags: first, article
---
# My first post

My first post's text.

...

ヘッダーに date description tags を付けます。
dateYYYY-MM-DD hh:mm フォーマット、 description は文字列、 tags はカンマ区切りの文字列で記述します。

ヘッダー以下が本文になります。
Markdown記法を使って記述します。

例えば以下のMarkdownをもとにHTMLを生成します。

first-post.md
---
date: 2022-03-28 12:40
description: A description of my first post.
tags: first, article
---
# My first post

My first post's text.

## foo

foo.

### Swift

```swift
let foo = Foo()
```

```diff
- foo
+ bar
```

## bar

`bar` is code span.
_bar_ is italic.
__bar__ is bold.

- foo
- bar
  - test
  - test2

https://uhooi.github.io/

[https://uhooi.github.io/](https://uhooi.github.io/)

foundation テーマだとこのように変換されます。

スクリーンショット 2022-03-29 10.59.33.png

いい感じですが、デフォルトのテーマなので、いくつか気になる箇所もあります。

  • コードがシンタックスハイライトされない
  • コードスパンとコードブロックの文字サイズが小さい
  • 箇条書きがインデントされない
  • コードブロックと箇条書きの前後にマージンがない
  • URLがリンクにならない

これらはCSS修正のPRを作成するのもありだと思いますが、「プラグイン」という仕組みで解決できる可能性があります。
プラグインについてはまだ勉強不足であり、本記事とは別のタイミングで紹介したいです。

おわりに

これでSwiftとMarkdownを使ってWebサイトを構築できました!
ぜひみなさんも試してみてください :relaxed:

参考リンク

22
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?