ごみばこいんブログを支える技術

この記事は公開されてから1年以上経過しており、最新の内容に追従できていない可能性があります。

ごみばこいんは今年に入って裏側をまるっと刷新した。 これまでのごみばこいんの思い出とシーズン3 ので、それをもう少し掘り下げた記事。

全体

Hugoテンプレートは自作

archives/年/月/記事

archives/年/月/記事 という構造にしたかったので結構面倒。Wordpressからの名残でそうしてるけど別にそうしなくてもいいんだよな。。。

具体的にはこのような記述をarchives/list.htmlで書いたりしている。

{{ if findRE "archives/\\d+/\\d+/" .File }}
    {{ range .Pages }}
        {{/* Page: /archives/xxxx/yy/ */}}
        ...
    {{ end }}
{{ else if findRE "archives/\\d+/" .File }}
    {{/* Page: /archives/xxxx/ */}}
    {{ range .Pages }}
        {{ range .Pages }}
            ...
        {{end}}
    {{ end }}
{{ else }}
    {{/* Page: /archives/ */}}
    ...
{{ end }}

テンプレートの分離

以前までのWordpress管理でも、なるべく各パーツを別々のファイルで管理していたので、Hugoテンプレートでもどうようにpartialsを使って部分ごとに分離している。分離している理由は、違う箇所でも同じ見た目を簡単にキープできるように。

記事のヘッダー部分、記事の本文、記事のフッター、タグ、日付。などなど。

外部へのリンクにはnoreferrerをつけたい

つけたいりゆうはこういうところ。

noopener と noreferrer の整理、結局どっちを使えば良いのか

_default/_markup/render-link.htmlをいじっている。

Configure Markup | Hugo

{{ if findRE "^(https?:)?(//)?gomiba.co/" .Destination }}
    <a href="{{ .Destination | safeURL }}" target="_blank">{{ .Text | safeHTML }}</a>
{{ else if findRE "^/[^/]" .Destination }}
    <a href="{{ .Destination | safeURL }}" target="_blank">{{ .Text | safeHTML }}</a>
{{ else }}
    <a href="{{ .Destination | safeURL }}" target="_blank" rel="noreferrer">{{ .Text | safeHTML }}</a>
{{ end }}

JSON-LD

JSON-LD - JSON for Linking Data

記事の情報を教えるのに便利なやつで、ごみばこいんブログでも利用している。 Hugoと合わせて使うと、indexのような連番を降るやりかたがわからなかったけど変数でこねたらできた。

たとえばパンくずリストはこうしている。

{{ $index := 0 }}
<script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "itemListElement": [
        {
            "@type": "ListItem",
            "position": {{ $index = add $index 1 }}{{ $index }},
            "name": {{ .Site.Title }},
            "item": {{ absURL "/" }}
        },
        {
            "@type": "ListItem",
            "position": {{ $index = add $index 1 }}{{ $index }},
            "name": "記事一覧",
            "item": {{ absURL "/archives/" }}
        },
        {
            "@type": "ListItem",
            "position": {{ $index = add $index 1 }}{{ $index }},
            "name": {{ .Page.LinkTitle }},
            "item": {{ .Page.Permalink }}
        }
    ]
}
</script>

デプロイ

以下のようなことをGitHub Actionsでやっている。

  1. masterブランチでトリガ
  2. hugo --minify --gc で静的ファイルにビルド
  3. ビルド結果を別ブランチにコミット&プッシュ
  4. onstaticで該当のブランチを取得

つまりこんな感じのワークフローになっている。

name: Deploy blog content

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-20.04
    steps:
      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: 'latest'

      - uses: actions/checkout@v2

      - name: Build blog content
        run: hugo --minify --gc

      - name: Commit built branch
        run: |
          set +e
          git config user.name github-actions
          git config user.email [email protected]

          cp -R build /tmp/build

          git fetch
          git checkout -f -b built origin/built

          rm -rf ./*
          cp -R /tmp/build/* .

          git add .
          git commit -m "built"
          git push origin built

      - name: deploy
        run: |
          curl -i \
            -X POST \
            -H "X-ONSTATIC-KEY: ${{ secrets.ONSTATIC_KEY }}" \
            -H "X-ONSTATIC-REPONAME: ${{ secrets.ONSTATIC_REPO }}" \
            -H "X-ONSTATIC-BRANCH-NAME: built" \
            ${{ secrets.ONSTATIC_ENDPOINT }}

サーバ

使っているサーバがいわゆるコンテナで動くものとか、コードをアップしたら動くものとかではなくまるっとサーバー、というかVPSなので、Ansibleでいろいろ設定している。 Ansible Galaxyというroleを共通化できるものがあるので、なるべくこれを利用して、ほとんど自分でロールを書くことなく設定を書くだけでいいようにしている。

実際の構成変更の適用はローカルからでもできるし、GitHub Actionsでsecretに鍵を入れているのでCI上からも行えるようになっている。 たとえばちょっとこれを、みたいなものをGitHub上でシュッと変更してコミットすればそのまま反映される。

HTTP/HTTPSを受けるサーバにはnginxを使っている。

NGINX | High Performance Load Balancer, Web Server, & Reverse Proxy

基本的にすべてのリクエストをnginxで受けている。 理由としてはTLSのそれとか、もろもろの制限とか、キャッシュとかを設定している。

ごみばこいんブログのデプロイなどのために、onstaticをデーモンとして動かしていて、一部のリクエストはそこにリバースプロキシをしている。

ドメイン

もともとValueDomainで取得したけれど、管理しやすそうだったのでGoogleDomainに移管して、運用中。

ホーム – Google Domains

フロントエンド

CSSは愛のあふれる手書きでFLOCSSを踏襲している。

hiloki/flocss: CSS organization methodology.

コードブロックのシンタックスハイライトにはprismを使っている。

cdnjsで、言語別の定義ファイルが提供されているので、コードブロックの内容から言語を検出してそれらを非同期に読み込んでいる。 言語の検出はお手製のざっくりしたものだけど、そもそも言語が違ったからといって見た目が大きく崩れるわけでもないし大丈夫かな、と思っている。

sters/prismloader-js: Dynamic prism.js language files loader on web browser.

JavaScriptでやりたいことってそんなに多くない(DOMを生成するとかはいらない)ので、DOMContentLoadedではなくLoadイベントで処理を始めている。 加えてその中でも requestAnimationFrameをつかって非同期にやれるようにしている。

sters/microtaskqueue-js

というかそもそもやることがそんなに多くないので微々すぎるけれど画面を出すまでをなるべく早くして、リッチにするものを順次作業する(ただしレイアウトを崩さない)、みたいな考え方。

アイコンはsimple-iconsを使っている。これもcdnjsで提供されている。

Issueから記事を追加できる

Issueで記事を書けるようにしたのは、エディタがない環境(スマホとか普段と違うPCとか)でも記事を書けるようにしたかったため。と思ったけど Codespaces があるのでいらないかもしれない。

とはいえ、hugoのディレクトリはなかなかいろんなファイルがあって、記事までたどり着くのがややこしい(特に記事ページが階層構造になっているため。。)ので、シンプルに記事だけ書けるインタフェースがどこかにほしい、というのはあった。

で、どういう仕組みになっているかというと:

  1. Issue本文に記事を書く
  2. /publishとコメントするとGitHubActionがトリガ
  3. ISSUE_BODYを受け取ってファイルを設置、コミット&プッシュ

Issueの本文には以下のようなフォーマットで記事を書いている。

---
date: 2021-06-09T12:23:26+09:00
title: 記事タイトル
draft: true
reserved: true
tags:
- タグタグ
---

記事コンテンツ

GitHub Actionsを使うと、Issueへのコメントなどをトリガにワークフローを起動することができる。

具体的には GitHub Actionsでスラッシュコマンド をして /publish というコマンドを見ている。

で、そのときに、ISSUE_BODYでIssue本文が受け取れるので、自作のスクリプトに渡して、適切なディレクトリにファイルを設置している。あとはデプロイのときと同様に、コミットしてプッシュしている。

予約投稿

Hugoで予約投稿を行う をしている。 GitHubActionsではcronが設定できるので、これを使って未公開の予約記事を公開状態に切り替えている。

ただし、そのままプッシュしただけでは他のワークフローがトリガされない。 actions/checkout@v2では任意のトークンを指定できるので、自分のアカウントで発行したものをSecretにいれて利用している。このトークンにはリポジトリへの書き込み権限、GitHubActionsのWorkflowの権限がついている。ので慎重に扱う必要がある。 (これはIssueからコミットしているときも同じ)

name: Check and publish reserved articles

on:
  schedule:
    - cron: '0 1,9,12,18,23 * * *'

jobs:
  sync:
    runs-on: ubuntu-20.04
    steps:
      # https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token
      - uses: actions/checkout@v2
        with:
          token: ${{ secrets.GH_PAT }}

      - run: go get github.com/sters/hugo-publish-reserved-content/cmd/hugo-publish-reserved-content
      - name: Check reserved content
        run: |
          export PATH=${PATH}:`go env GOPATH`/bin
          hugo-publish-reserved-content -basePath content/

      # https://github.com/actions/checkout
      # https://docs.github.com/ja/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions
      - name: Commit and Push
        run: |
          set +e
          git config user.name github-actions
          git config user.email [email protected]
          git add .
          git commit -m "publish reserved article"
          git push

そうすると、この予約投稿の内容がmasterブランチに自動的にコミットされ、masterブランチにコミットされることでトリガされるデプロイのGitHubActionsがトリガされ、デプロイまで行われる。ピタゴラスイッチ感。

サイト案内

運営してるひと: @sters9

最近は Go, Ruby, Rails, Kubernetes, GCP, Datadog あたりをしていますがもっといろいろやりたい!

サイト案内

開発環境の紹介

プライバシーポリシー

tools.gomiba.co

サイト内検索

アーカイブ

2024/09 (3) 2024/07 (1) 2024/06 (3) 2024/05 (1) 2024/04 (7) 2024/03 (4) 2024/01 (3)

2023/12 (1) 2023/11 (3) 2023/10 (1) 2023/09 (1) 2023/08 (2) 2023/05 (4) 2023/04 (4) 2023/03 (4) 2023/02 (2) 2023/01 (1)

2022/12 (2) 2022/11 (4) 2022/10 (3) 2022/09 (2) 2022/08 (4) 2022/07 (5) 2022/06 (4) 2022/05 (9) 2022/04 (8) 2022/03 (10) 2022/02 (21) 2022/01 (8)

2021/12 (11) 2021/11 (1) 2021/10 (4) 2021/09 (2) 2021/08 (1) 2021/07 (2) 2021/06 (5) 2021/05 (10) 2021/04 (1) 2021/03 (8) 2021/02 (12) 2021/01 (8)

2020/05 (2) 2020/04 (2) 2020/02 (2) 2020/01 (1)

2019/12 (3) 2019/11 (2) 2019/10 (5) 2019/09 (3) 2019/07 (6) 2019/06 (4) 2019/04 (3) 2019/01 (2)

2018/12 (6) 2018/10 (4) 2018/09 (6) 2018/08 (7) 2018/07 (16) 2018/06 (7) 2018/05 (7) 2018/04 (5) 2018/03 (3) 2018/02 (10) 2018/01 (6)

2017/12 (8) 2017/11 (6) 2017/10 (10) 2017/09 (12) 2017/08 (12) 2017/07 (3) 2017/06 (1) 2017/01 (4)

2016/12 (5) 2016/10 (3) 2016/09 (1) 2016/07 (2) 2016/06 (1) 2016/04 (1) 2016/02 (1) 2016/01 (2)

2015/12 (1) 2015/10 (1) 2015/09 (3) 2015/06 (1) 2015/01 (1)

2014/08 (2) 2014/07 (3) 2014/05 (1) 2014/01 (7)

2013/12 (2) 2013/11 (4) 2013/10 (1) 2013/09 (1) 2013/08 (3) 2013/07 (4) 2013/06 (5) 2013/05 (2) 2013/04 (7) 2013/03 (1)