このサイトの使い方

このサイトはRubyの自作ミニSSGで生成している。Rubyはビルド側だけで使い、配信されるのは静的ファイルのみ。このページはその運用ガイド。

日常のコマンド

bundle exec rake test   # テスト(test-unit)
bundle exec rake build  # 既定development: content-dev/+templates/→tmp/development/public/に生成(本番は下記参照)
bundle exec rake serve  # ビルドして配信(既定development: tmp/development/public/をhttp://localhost:8000で配信)

環境はBASH0C7_HOMEPAGE_ENVで切り替える(未指定はdevelopment)。development(既定)はcontent-dev/(検証用fixture)を読み、tmp/development/{public,admin}/へ書き出す。本番ビルド・デプロイはBASH0C7_HOMEPAGE_ENV=production bundle exec rake buildでcontent/を読み、public/へ書き出す。new:post/new:episode/transcribe:episode/admin/admin_serveも同じ切り替えに従う。

記事を書く

bundle exec rake new:post

実行時刻でファイル名(<content>/posts/YYYY-MM-DD-HHMMSS.md)が自動採番され、draft: true付きで生成される。<content>は既定developmentではcontent-dev/、BASH0C7_HOMEPAGE_ENV=production指定時はcontent/。タイトルを考える必要はなく、frontmatterのtitleは空のままでもよい。空の場合、一覧やページ見出しには時刻(15:30など)がそのまま表示される。本文を書き、公開するときにdraft行を消す(draftのままだとビルド対象外なので、書きかけをpushしても公開されない)。

記事のfrontmatter:

キー 必須 説明
title - 記事タイトル。空なら一覧・ページ見出しは時刻表示にフォールバックする
date 2026-08-21または時刻つき2026-08-21 14:30:00 +09:00。RSSのpubDateに反映される
description - OGP/meta descriptionに使う要約。無ければサイト説明で代用
image - カバー画像(og:image、ページ上部にも表示)。無ければページ上部のカバーはsite.ymldefault_cover_image、og:imageは/og(Cloudflare Worker)がタイトル入りで動的生成する
draft - trueでビルド対象外

本文はmarkdown(GFM)。コードブロックは```langでシンタックスハイライトされる。画像は<content>/images/に置くと/images/へコピーされるので![alt](/images/foo.png)で参照する。音声/動画は<content>/media/に置くと/media/へコピーされる。

エピソード(podcast)を出す

bundle exec rake new:episode
# 音声/動画ファイルを渡すとlength/durationを自動算出:
bundle exec rake "new:episode[/path/to/audio.mp3]"

<content>/episodes/NNN-YYYY-MM-DD-HHMMSS.mdが採番されて生成される(NNNはitunes:episode番号、ファイル名の残りはタイムスタンプ)。<content>は記事と同じく既定developmentではcontent-dev/、本番はBASH0C7_HOMEPAGE_ENV=production指定時のcontent/。音声ファイルを渡すとaudio_length(バイト数)とduration(afinfoまたはffprobeによる再生時間)が自動で埋まる。audio_urlは音声を実際に配置したURLに書き換えること。

エピソードのfrontmatter(記事のキーに加えて):

キー 必須 説明
audio_url 音声/動画ファイルの配信URL
audio_length ファイルのバイト数(enclosure length)
duration 再生時間"HH:MM:SS"
media_type - 既定audio/mpeg。videocastはvideo/mp4(ページのプレーヤーもvideoに切り替わる)
episode - itunes:episodeのエピソード番号

podcastの録音・ホスティング自体はこのSSGの範囲外。このサイトが保証するのは「audio_urlを書けばプレーヤーが出る」ところまで。ただし音声ファイルを渡した場合、ffmpegによる簡易編集(無音トリム+ラウドネス正規化)と、gems/rb-speech-mac(Apple Speech.frameworkのSpeechAnalyzer/SpeechTranscriberをswift_gem経由でラップしたオンデバイス音声認識のsubgem)による文字起こしの下書きが自動で走る。ネットワークやAPIキーは不要だが、macOS 26(Tahoe)以降・Apple Silicon・Swift 6.3+が必須で、既定ではbundle installに含まれない(bundle install --with transcriptionで有効化)。日本語は既定ロケールja_JPで扱う。ffmpeg/rb-speech-macが無い/失敗した場合はそれぞれ元ファイルのまま・文字起こし無しにフォールバックする。ffmpegはMac側に別途インストールしておくこと。audio_urlは音声を実際に配置したURLに書き換えること。文字起こしは## 文字起こし見出し配下に自動挿入されるので、内容を確認・修正してから公開する。

rb-speech-macが使えない環境(Mac以外・Claude Code webでの作業・Mac不調時など)でも縮退運転できる。 new:episodeはffmpeg/rb-speech-macのどちらが無くても(あるいは両方無くても)止まらずにエピソードを生成し、文字起こしを省略した場合は次のように後から当てるコマンドを表示する。

bundle exec rake "transcribe:episode[NNN-YYYY-MM-DD-HHMMSS,/path/to/audio]"

rb-speech-macが使える環境(実機Mac)でこれを実行すると、指定した音声ファイルを文字起こしして該当エピソードの## 文字起こし見出しに追記する。transcribe:episodeも既定developmentではcontent-dev/を書き換え、本番のエピソードに当てるにはBASH0C7_HOMEPAGE_ENV=productionが必要。元の音声/.processed.mp3さえ残しておけば、生成そのものはどの環境で行っても後から文字起こしだけ差し替えられる。既に## 文字起こしがあるエピソードに対しては何もせず終了する(二重挿入防止)。

録音・編集の具体的な手順

このSSGが自動化するのは音声ファイルを渡した後(簡易編集・文字起こし・スタブ生成)から。録音そのものはMac標準のQuickTime Playerで十分、追加インストールは要らない。

  1. 録音する(QuickTime Player) — 音声だけなら「ファイル→新規オーディオ収録」、画面や顔も残すなら「新規スクリーン収録」/「新規ムービー収録」。終わったら「ファイル→書き出す」でm4a/movとして保存する
  2. 取り込む

    bundle exec rake "new:episode[/path/to/raw.m4a]"
    

    ffmpegでの無音カット・音量そろえと、rb-speech-mac(導入済みの場合)での文字起こしが自動で走り、<content>/episodes/NNN-....mdができる(既定developmentではcontent-dev/へ)

  3. 仕上げる — 元ファイルと同じ場所にできる.processed.mp3を実際のホスティング先にアップロードしてaudio_urlを書き換え、自動挿入された## 文字起こしを読んで誤字や聞き違いを直し、title/descriptionを書いてdraft行を消す

QuickTime Player以外(Audio Hijack、Descript等)を使ってもよい。このSSG側が必要とするのは、最終的に手元に音声/動画ファイルが1つある状態だけ。

Claude Code webなどrb-speech-macが使えない環境で2の取り込みを行った場合、文字起こしは省略されコマンド例が表示される。.processed.mp3(または元音声)を残しておき、実機Macでrake "transcribe:episode[...]"を後から実行すればよい。

管理コンソールで公開準備状況を確認する

bundle exec rake admin        # 走査してadmin/へ生成(既定development: content-dev/→tmp/development/admin/)
bundle exec rake admin_serve  # 生成して配信(既定development: tmp/development/admin/をhttp://localhost:8001で配信)
bundle exec rake status       # 同じ判定をCLIで表示(要対応があれば非0で終了。公開前チェック用)

admin/(本番)またはtmp/development/admin/(既定development)はpublic/とは完全に別物の、非公開の読み取り専用ダッシュボード。rake build/rake testには一切関与せず、.gitignore済みで公開サイトにもリポジトリにも含まれない。配色も公開サイト(暖色×レンガ赤)とは区別できる寒色系のライトモードにしてある。

見られるのは次の2画面。

検出できるのはcontent(既定developmentではcontent-dev/)のファイルから機械的にわかることだけ。audio_urlexample.com等のダミーのまま、audio_length/durationがテスト用の極端に小さい値、本文に## 文字起こし見出しが無い、ダミー値のままdraftが外れている、の4種類。収録・編集そのものの完了状況はファイルから判定できないため、常に「追跡対象外」表示になる。

固定ページを増やす

<content>/pages/slug.mdを置くと/slug.htmlに生成される(このページ自身もcontent/pages/help.md<content>は既定developmentではcontent-dev/)。frontmatterはtitle必須、description/draftは任意。

ホームの構成

ホーム(/)は上から次の3段。

  1. herosite.ymlauthorを見出しに、taglineを一言添える。hero_imageはホームのog:image(SNSでの共有カード用)として使われる
  2. Articlecontent/postscontent/episodesを日時でマージした一覧。ブログ記事とpodcastエピソードが時系列で混在する
  3. Profilecontent/profile.mdの本文

フィードの考え方

どちらもブログ/podcastそれぞれ専用のフィードで、ホームのArticleのように混ざったりはしない。

Article一覧はposts_per_page(既定10)件ずつで、超えた分は/articles/page/2.html以降にページネーションされる。

サイト全体の設定

content/site.ymlで管理する: サイトタイトル・URL・説明、authortaglinehero_imagedefault_cover_imagefeed_limit(既定20)、posts_per_page(既定10)、podcastの番組情報(title/description/image/category/owner_email)。

仕組み

lib/page.rbがfrontmatter+markdownを読み、lib/site_builder.rbがcontentを走査してtemplates/*.erbを適用しpublic/(既定developmentではtmp/development/public/)に書き出す。sitemap.xmlとそれを参照するrobots.txtもビルド時に生成される。テンプレートやCSS(static/style.css)を変えたらrake buildし直すだけ。

rake buildは出力先ディレクトリを毎回消してから作り直す(clean build)。出力先に手で置いたファイルは残らないので、配信したいものはstatic/<content>/images/<content>/media/に置く。

管理コンソールは別経路: lib/site_status.rbがcontentを走査してダミー値・draft漏れなどを検出し、lib/admin_builder.rbtemplates/admin/*.erbを適用してadmin/(既定developmentではtmp/development/admin/)に書き出す(static/style.cssとは別のtemplates/admin/style.cssを使う)。