はじめに
zenn.devやqiitaでは、X(旧:Twitter)にリンクを貼ると、いい感じのプレビュー画像が出てくる。
あれを自分のブログでも設定したいなと思ったので、設定した際の備忘録である。
環境
Hugo 0.111.3-ext
Jane
Docker 24.0.6
Docker Composer v2.22.0-desktop.2
前知識
どのようにすれば設定できるかについて調べてみたところ、どうやら OGP
を設定する必要があるらしい。
The Open Graph protocol をみてみたところ、 meta
タグに og:title
, og:image
などの要素を入れてあげることで設定できるようだ。
また、X(旧:Twitter)では独自のタグがあるようで、ツイートをカードで最適化する-X開発者プラットフォームを見ると、この辺りの設定も必要なようだ。
検討
実現するにあたっていくつか検討事項があった。
- OGP画像はどのように用意するのか?
- Hugoで
meta
タグの設定ができるのか?
1点目のOGP画像の用意についてだが、シンプルで良いので1記事ごとに自分で作るのは正直かなりしんどい。
なので、自動生成をする方法を考えた。Imagamagick
でテンプレート読み込ませて生成させればいけそうだが、一から自分で作成するのは大変そうだなと思ったので、他の人はどうしているのかをみてみた。
そこで、https://zenn.dev/catnose99/scraps/895b398991918f を見つけ、この中にある事例を参考にしてみたところ、Ladicle/tcardgen | GitHub が良さそうだった。
これであれば、 テンプレート画像を用意するだけであとは自動で生成してくれるのでこれを使用させていただくことにした。
2点目の meta
タグの設定についてだが、これは以前 GoogleAnalytics
を設定した際(GoogleAnalyticsをHugo Blogに導入する)に、layouts/partials/custom_head.html
を作成して、その中にAnalytics
の設定を書いたなというのがあったので問題なさそうだ。
OGP用の画像の自動生成について
Ladicle/tcardgen | GitHubを見ると、テンプレート画像を用意して、そのあとコマンド実行という流れなのでその通りに実行する。
テンプレート画像の作成
Figmaで適当に作成した。
これを、 static/images/card.png
として保存しておいた。
Dockerイメージの作成
Ladicle/tcardgen | GitHubを使用するにあたって、ローカル環境で動作させる必要がある。
そのため、Hugoで使用している docker-compose
の構成に、追加でtcardgen
を使用できるイメージを作成する必要があった。
というわけなので、以下のファイルに修正を適用した。
compose.yml
# https://gohugo.io/installation/linux/#docker
services:
hugo:
image: klakegg/hugo:0.111.3-ext-ubuntu
entrypoint: tail -f /dev/null
volumes:
- .:/src
ports:
- "1313:1313"
+ tcardgen:
+ build:
+ context: ./Docker/tcardgen
+ volumes:
+ - .:/blog
./Docker/tcardgen
FROM golang:1.21.6-bookworm
RUN mkdir -p /blog
# FYI: https://github.com/Ladicle/tcardgen
RUN go install github.com/Ladicle/tcardgen@latest
RUN git clone https://github.com/Ladicle/tcardgen.git /tmp/tcardgen
RUN apt-get install -y git
RUN git clone https://github.com/ookamiinc/kinto.git /tmp/kinto
CMD ["tail", "-f", "/dev/null"]
Makefile
に下記を追加
tcardgen:
make up
docker compose exec tcardgen bash
tcardgen-generate:
make up
docker compose exec tcardgen sh -c "\
find /blog/content -name "*.md" -type f | \
xargs tcardgen -f /tmp/kinto/'Kinto Sans' \
-o /blog/static/tcard \
-t /blog/static/images/card.png"
上記の使い方としては、make tcardgen-generate
をすることで、static/tcard/
いかにイメージが生成される。
内容の解説としては、Makefile
内の tcardgen-generate
のコマンドとして、
find /blog/content -name "*.md" -type f
で記事内のコンテンツ(*.md
)を検索し、
それをパイプで xargs
でtcardgen
コマンドに渡す。tcardgen
のオプションとして、-f
で Kinto Sans
フォントを指定し、 -o
(アウトプット)で、/blog/static/tcard
を指定する。
ちなみに、 /blog/static/tcard
は、ホストとバインドマウントしており、 ホスト側のstatic/tcard/
になる。-t
で テンプレートを指定する (/blog/static/images/card.png
)。これは先ほど作成した画像を指定している。
コマンド実行
make tcardgen-generate
make up
docker compose up -d
[+] Building 0.0s (0/0) docker:desktop-linux
[+] Running 2/0
✔ Container blog-hugo-hugo-1 Running 0.0s
✔ Container blog-hugo-tcardgen-1 Running 0.0s
docker compose exec tcardgen sh -c "\
find /blog/content -name "*.md" -type f | \
xargs tcardgen -f /tmp/kinto/'Kinto Sans' \
-o /blog/static/tcard \
-t /blog/static/images/card.png"
Load fonts from "/tmp/kinto/Kinto Sans"
Load template from "/blog/static/images/card.png" directory
Success to generate twitter card into /blog/static/tcard/senior-thesis.png
Failed to generate twitter card for /blog/static/tcard/recentry-2020-02-3w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-02-2w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-04-4w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-02-1w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-02-5w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-02-4w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-04-1w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-01-2w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-01-3w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-03-1w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-03-3w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-03-2w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/recentry-2020-01-4w.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/introduction.png: "tags" is not defined or empty
Success to generate twitter card into /blog/static/tcard/book-2024-01.png
Failed to generate twitter card for /blog/static/tcard/recentry-2020-03-4w.png: "tags" is not defined or empty
Success to generate twitter card into /blog/static/tcard/pass-fp2.png
Success to generate twitter card into /blog/static/tcard/pass-bookkeeping3.png
Failed to generate twitter card for /blog/static/tcard/resolution-2023y.png: "tags" is not defined or empty
Success to generate twitter card into /blog/static/tcard/buy-benq-mobiuz-ex2510s.png
Success to generate twitter card into /blog/static/tcard/pass-db.png
Success to generate twitter card into /blog/static/tcard/pass-fp3.png
Success to generate twitter card into /blog/static/tcard/review-2023y.png
Success to generate twitter card into /blog/static/tcard/resolution-2024y.png
Success to generate twitter card into /blog/static/tcard/pass-nw.png
Failed to generate twitter card for /blog/static/tcard/recentry-2022-07-4w.png: "tags" is not defined or empty
Success to generate twitter card into /blog/static/tcard/client-character-set.png
Success to generate twitter card into /blog/static/tcard/mysql-replication.png
Success to generate twitter card into /blog/static/tcard/compare-tables-with-checksum-statement.png
Success to generate twitter card into /blog/static/tcard/mysql-case-sensitive.png
Success to generate twitter card into /blog/static/tcard/use-mysql-tuner.png
Success to generate twitter card into /blog/static/tcard/mysql-create-history-table-with-trigger.png
Success to generate twitter card into /blog/static/tcard/use-sql-with.png
Failed to generate twitter card for /blog/static/tcard/login-form-save-password-dialog.png: "categories" is not defined or empty
Success to generate twitter card into /blog/static/tcard/setting-ogp-and-x-card-for-hugo.png
Success to generate twitter card into /blog/static/tcard/web-develop-watch-list.png
Success to generate twitter card into /blog/static/tcard/play-2048.png
Failed to generate twitter card for /blog/static/tcard/valheim-migrate-from-local-to-ec2-server.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/valheim-server-ec2-arm-instance.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/docker-lamp.png: "tags" is not defined or empty
Success to generate twitter card into /blog/static/tcard/ssh-2fa-for-docker.png
Success to generate twitter card into /blog/static/tcard/docker-autoheal.png
Success to generate twitter card into /blog/static/tcard/vue2-to-vue3.png
Success to generate twitter card into /blog/static/tcard/failed-to-find-a-valid-digest.png
Success to generate twitter card into /blog/static/tcard/eol-notify-python.png
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-18.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-28.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/go-competitive-programming-code-01.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-29.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-19.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-1.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-26.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-12.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-5.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-16.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-22.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-4.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-17.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-23.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-27.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-13.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-7.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-14.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-20.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-3.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-24.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-10.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-2.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-25.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-11.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-6.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-15.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-21.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-9.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/build-web-application-with-golang-8.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/go-competitive-programming-code-02.png: "tags" is not defined or empty
Success to generate twitter card into /blog/static/tcard/convert-html-to-pdf-with-headless-chrome.png
Success to generate twitter card into /blog/static/tcard/refactor-vue3-composition-api.png
Success to generate twitter card into /blog/static/tcard/use-chatgpt-api.png
Success to generate twitter card into /blog/static/tcard/renew-article.png
Failed to generate twitter card for /blog/static/tcard/ai-kiritan-play.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/building-gitlab-on-ec2.png: "author" is not defined or empty
Success to generate twitter card into /blog/static/tcard/use-elemental-media-convert.png
Success to generate twitter card into /blog/static/tcard/aws-backup-success-job-notifications.png
Success to generate twitter card into /blog/static/tcard/send-mail-ec2-with-postfix-use-aws-ses.png
Success to generate twitter card into /blog/static/tcard/remove-exif.png
Success to generate twitter card into /blog/static/tcard/use-figure-code-jane.png
Success to generate twitter card into /blog/static/tcard/use-markdown-vscode-extension.png
Success to generate twitter card into /blog/static/tcard/use-exiftool.png
Success to generate twitter card into /blog/static/tcard/use-github-connect-with-ssh-over-https.png
Success to generate twitter card into /blog/static/tcard/diff-two-files-with-vscode.png
Success to generate twitter card into /blog/static/tcard/what-i-think-about-when-migrating-php-versions-to-vanilla-php.png
Success to generate twitter card into /blog/static/tcard/use-sl.png
Success to generate twitter card into /blog/static/tcard/use-countdown.png
Success to generate twitter card into /blog/static/tcard/use-pdfcrack.png
Success to generate twitter card into /blog/static/tcard/reduce-volume-ebs.png
Success to generate twitter card into /blog/static/tcard/git-attention.png
Success to generate twitter card into /blog/static/tcard/use-flock.png
Success to generate twitter card into /blog/static/tcard/apache-tomcat-configuration-to-let-apache-handle-static-resources.png
Success to generate twitter card into /blog/static/tcard/convert-heic-to-png-with-imagemagick.png
Success to generate twitter card into /blog/static/tcard/use-qrencode.png
Success to generate twitter card into /blog/static/tcard/use-ntpdate.png
Success to generate twitter card into /blog/static/tcard/use-apvlv.png
Success to generate twitter card into /blog/static/tcard/mount-wasabi-bucket-with-aws-cli.png
Success to generate twitter card into /blog/static/tcard/use-watch.png
Success to generate twitter card into /blog/static/tcard/ec2-attach-volume.png
Success to generate twitter card into /blog/static/tcard/install-lightbox2-to-hugo-blog.png
Success to generate twitter card into /blog/static/tcard/use-csvtomd.png
Success to generate twitter card into /blog/static/tcard/use-bash-shortcut.png
Success to generate twitter card into /blog/static/tcard/use-slack-notifications-gitlab.png
Success to generate twitter card into /blog/static/tcard/use-yum-whatprovides-option.png
Success to generate twitter card into /blog/static/tcard/convert-html-to-pdf.png
Success to generate twitter card into /blog/static/tcard/nginx-before-tls-1_1-disable.png
Success to generate twitter card into /blog/static/tcard/use-xargs.png
Success to generate twitter card into /blog/static/tcard/use-apg.png
Success to generate twitter card into /blog/static/tcard/git-mv.png
Success to generate twitter card into /blog/static/tcard/rescue-ec2-instance.png
Success to generate twitter card into /blog/static/tcard/use-pdftocairo.png
Success to generate twitter card into /blog/static/tcard/use-webp.png
Success to generate twitter card into /blog/static/tcard/use-zcat.png
Success to generate twitter card into /blog/static/tcard/convert-excel-to-pdf.png
Success to generate twitter card into /blog/static/tcard/ec2-eni-take-over-ip.png
Success to generate twitter card into /blog/static/tcard/create-certificates-with-san.png
Success to generate twitter card into /blog/static/tcard/use-fcrackzip.png
Success to generate twitter card into /blog/static/tcard/use-aamath.png
Success to generate twitter card into /blog/static/tcard/use-rlogin-portforward.png
Success to generate twitter card into /blog/static/tcard/install-gitlab-update-script-amzn-2023-to-2023.png
Success to generate twitter card into /blog/static/tcard/use-codequery.png
Success to generate twitter card into /blog/static/tcard/lost-aws-ec2-keypair.png
Success to generate twitter card into /blog/static/tcard/use-barcode.png
Success to generate twitter card into /blog/static/tcard/use-circumflex.png
Success to generate twitter card into /blog/static/tcard/list-files-modified-after-the-specify-datetime.png
Success to generate twitter card into /blog/static/tcard/use-pdfunite.png
Success to generate twitter card into /blog/static/tcard/use-logrotate.png
Success to generate twitter card into /blog/static/tcard/use-busybox.png
Success to generate twitter card into /blog/static/tcard/use-calc.png
Success to generate twitter card into /blog/static/tcard/fix-code-quote-shaping.png
Success to generate twitter card into /blog/static/tcard/use-cbm.png
Success to generate twitter card into /blog/static/tcard/use-ansiweather.png
Success to generate twitter card into /blog/static/tcard/use-tesseract.png
Success to generate twitter card into /blog/static/tcard/use-pdfseparate.png
Success to generate twitter card into /blog/static/tcard/use-jq.png
Success to generate twitter card into /blog/static/tcard/use-csview.png
Success to generate twitter card into /blog/static/tcard/use-colordiff.png
Success to generate twitter card into /blog/static/tcard/update-hugo-version-from-0.110-to-0.111.png
Success to generate twitter card into /blog/static/tcard/use-md5-sum.png
Success to generate twitter card into /blog/static/tcard/construct-gitlab-runner-docker.png
Success to generate twitter card into /blog/static/tcard/gitlab-mr-review-with-chatgpt-2.png
Success to generate twitter card into /blog/static/tcard/gitlab-mr-review-with-chatgpt.png
Success to generate twitter card into /blog/static/tcard/construct-hugo-docker.png
Failed to generate twitter card for /blog/static/tcard/vscode-php.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/vscode-nuxt.png: "tags" is not defined or empty
Success to generate twitter card into /blog/static/tcard/upgrade-gitlab-15-to-16.png
Success to generate twitter card into /blog/static/tcard/docker-init.png
Success to generate twitter card into /blog/static/tcard/construct-eol-apache-tomcat.png
Success to generate twitter card into /blog/static/tcard/construct-gitlab-ce-docker.png
Failed to generate twitter card for /blog/static/tcard/using-cvs2git.png: "tags" is not defined or empty
Success to generate twitter card into /blog/static/tcard/construct-apache-tomcat-mysql-to-amazonlinux2023-with-ansible.png
Failed to generate twitter card for /blog/static/tcard/migrate-from-s3-to-wasabi.png: "tags" is not defined or empty
Success to generate twitter card into /blog/static/tcard/use-cloudflare-ddns.png
Success to generate twitter card into /blog/static/tcard/construct-gitlab-ce-with-ansible.png
Success to generate twitter card into /blog/static/tcard/construct-ec2-public-private-subnet-alb.png
Success to generate twitter card into /blog/static/tcard/use-ansible-the-first-time.png
Success to generate twitter card into /blog/static/tcard/deploy-portfolio-to-vercel.png
Success to generate twitter card into /blog/static/tcard/construct-ec2-simple-architecture-with-terraform.png
Failed to generate twitter card for /blog/static/tcard/migrate-from-route53-to-cloudflare.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/emulate-raspbian-os-with-qemu.png: "categories" is not defined or empty
Success to generate twitter card into /blog/static/tcard/introduction-google-analytics-for-hugo.png
Failed to generate twitter card for /blog/static/tcard/hugo-theme-update.png: "tags" is not defined or empty
Failed to generate twitter card for /blog/static/tcard/about.png: "author" is not defined or empty
2024/01/23 05:53:55 failed to generate 62 twitter cards
make: *** [tcardgen-generate] Error 123
author
やtags
がないものは自動生成されない。
昔作成した記事はこんな感じなので、面倒なのでそのままにする。
Hugoのcustom_head.htmlの修正
先ほども記載したが、custom_head.html
を修正することで、設定できるので修正を行う。
ただ、実際にやってみると、二重に設定されてしまうようなので、自分の使用しているテーマJane
の実装を確認した。
layouts/partials/head.html
をみてみると、
{{/* NOTE: These Hugo Internal Templates can be found starting at https://github.com/spf13/hugo/blob/master/tpl/tplimpl/template_embedded.go#L158 */}}
{{- template "_internal/opengraph.html" . -}}
{{- template "_internal/schema.html" . -}}
{{- template "_internal/twitter_cards.html" . -}}
となっているので、 _internal
のopengraph.html
, twitter_cards.html
にOGP画像のmeta
タグが入っているようだった。
なので、layouts/_internal
を作成し、opengraph.html
と、twitter_cards.html
を用意し、meta
タグを記載した。
<!-- General -->
<meta property="og:url" content="{{ .Permalink }}" />
<meta property="og:type" content="{{ if .IsHome }}website{{ else }}article{{ end }}" />
<meta property="og:site_name" content="{{ .Site.Title }}" />
<meta property="og:title" content="{{ .Title }}" />
<meta property="og:description" content="{{ with .Description -}}{{ . }}{{ else -}}{{ if .IsPage }}{{ substr .Summary 0 300 }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}" />
<meta property="og:image" content="{{ if .Params.thumbnail -}}{{ .Params.thumbnail|absURL }}{{- else if and .File (hasPrefix .File.Path "post") -}}{{ path.Join "tcard" (print .File.BaseFileName ".png") | absURL }}{{ else -}}{{ "img/default.png" | absURL }}{{ end -}}" />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@{{ .Site.Params.twitterName }}" />
ここまでで設定が完了したので、ソースを表示して確認しておこう。
これら成果物を master
にpush
する。
プレビュー確認
master
にpush
後に netlify
でのデプロイが走るので、下記のサイトで確認をする。
今後の改善
- GitHub Actions を使って成果物を特定のディレクトリにコミットしたい。
参考
HugoでもTwitterCard画像を自動生成したい
https://ladicle.com/post/20200623_164459/いい感じのOGP画像・Twitterカードの事例を集める
https://zenn.dev/catnose99/scraps/895b398991918fHugo × Vercel × tcardgen で動的な OGP 画像を設定
https://zenn.dev/qawatake/articles/220210-152927HUGOをtwitterCardに対応させる
https://gammalab.net/blog/c3r33mlnapd6q/
おわりに
今まで気になっていたOGP画像の設定ができたので満足。tcardgen
には感謝です。