Skip to content

Commit a67a87b

Browse files
authored
Merge pull request #14 from rtcode337/claude/build-number
ビルド番号を生成してイメージに埋め込み管理画面に表示する
2 parents 545f9ec + 679b86f commit a67a87b

6 files changed

Lines changed: 38 additions & 3 deletions

File tree

.github/workflows/docker-publish.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ jobs:
1818
steps:
1919
- uses: actions/checkout@v4
2020

21+
# 管理画面に表示するビルド番号(JSTの日時 + 短縮コミットハッシュ)。
22+
# 本番で動いているイメージがいつのどのコミットのものかを画面から確認できるようにする
23+
- id: build_number
24+
run: |
25+
echo "value=$(TZ=Asia/Tokyo date +'%Y%m%d-%H%M')-$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
26+
2127
# arm64イメージをamd64ランナー上でビルドするためのQEMUエミュレーション
2228
- uses: docker/setup-qemu-action@v3
2329

@@ -47,6 +53,8 @@ jobs:
4753
# 本番ホスト(arm64)とローカル検証用(amd64)のマルチアーキビルド。
4854
# arm64側はQEMUエミュレーションのためビルド時間が長い
4955
platforms: linux/amd64,linux/arm64
56+
build-args: |
57+
BUILD_NUMBER=${{ steps.build_number.outputs.value }}
5058
tags: ${{ steps.meta.outputs.tags }}
5159
labels: ${{ steps.meta.outputs.labels }}
5260
cache-from: type=gha

CLAUDE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ NextAuthではなく自前実装。`lib/auth/session.ts`がHMAC-SHA256で署名
4444

4545
manifest(`app/manifest.ts`、Next.jsのMetadata Files規約で`/manifest.webmanifest`として配信)+アイコン(`public/icons/`のmanifest用3枚と、`app/icon.png``app/apple-icon.png`のファビコン/apple-touch-icon)による最小構成のPWA対応で、Service Worker・オフライン対応は意図的に持たない(「デプロイしたのに古い画面が出る」系の問題を避けるため、必要になるまで導入しない方針)。インストール後も中身は同じWebアプリで、認証Cookieもそのまま使われる。iOSはmanifestの`display`/`icons`を見ないため、`app/layout.tsx``metadata.appleWebApp``app/apple-icon.png`で別途同等の設定をしている。`/manifest.webmanifest``middleware.ts`のガード対象から除外が必要(上記「認証」参照)。アイコンPNGは`scripts/generate-icons.mjs`(sharp使用、依存には含めない)で生成したものをコミットしてあり、デザイン変更時のみ再生成する。
4646

47+
### ビルド番号
48+
49+
GitHub Actions(`.github/workflows/docker-publish.yml`)がビルド時に`<JST日時>-<短縮コミットハッシュ>`形式のビルド番号を生成し、`--build-arg BUILD_NUMBER`でDockerfileのprodステージに渡して`ENV BUILD_NUMBER`として焼き込む。`app/[type]/admin/page.tsx`(サーバーコンポーネント)が`process.env.BUILD_NUMBER`を読んで`AdminView``buildNumber` propに渡し、管理画面の見出し横に表示する(未設定時は「開発ビルド」)。`NEXT_PUBLIC_``next build`時に埋め込むのではなくリクエスト時に環境変数を読む方式にしてあるため、ビルド番号が毎回変わってもNext.jsのビルドキャッシュには影響しない(prodステージは`next build`の後段のため、Dockerレイヤキャッシュも実質壊さない)。
50+
4751
### スポット種別(`spot_types`)の設計
4852

4953
単一の`spots`テーブルを`spot_types`により複数の「種別」で使い回す設計。`tourist`(観光地)がアプリ初期化時(`db/init/01_schema.sql`)に必ず作成される唯一の既定種別で、それ以外の種別は管理者が`/[type]/admin`から追加する(空の種別ではデータが入らないだけで、削除しない限り存在し続ける)。既定種別といっても自動で作られるのは`spot_types`の行だけで、スポットデータ自体は他の種別と同様シードデータを`db/init/`に直接コミットせず外部リポジトリ[travel-log-data](../travel-log-data)にCSVとして置き、`/[type]/admin`のCSVインポートから取り込む(下記「外部データソース」の段落参照)。観光地データの`description`はWikipedia記事冒頭文の引用でCC BY-SA 4.0、`name`/`lat`/`lng`の一部はOpenStreetMap(ODbL)由来のため、travel-logリポジトリ本体には同梱せずtravel-log-data側でのみ管理・出典表示する。

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ RUN npm run build
1919
FROM node:22-alpine AS prod
2020
WORKDIR /app
2121
ENV NODE_ENV=production
22+
# GitHub Actionsが --build-arg BUILD_NUMBER=<JST日時>-<短縮コミットハッシュ> で渡すビルド番号。
23+
# 管理画面(app/[type]/admin)がリクエスト時にこの環境変数を読んで表示する。
24+
# ビルド時に埋め込まず実行時に読むためnext buildのキャッシュには影響しない(未指定なら空=開発ビルド扱い)
25+
ARG BUILD_NUMBER=""
26+
ENV BUILD_NUMBER=$BUILD_NUMBER
2227
COPY --from=builder /app/.next/standalone ./
2328
COPY --from=builder /app/.next/static ./.next/static
2429
EXPOSE 3000

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ docker compose pull app && docker compose up -d
109109
ホストに合う方が自動選択される
110110
- 特定時点に戻したいときは`docker-compose.yml`のイメージタグを`latest`から
111111
`sha-xxxxxxx`(Actionsが付けるコミットSHAタグ)に一時的に変えてpullし直す
112+
- Actionsはビルド番号(`20260722-1035-9162ba9`のようなJST日時+短縮コミットハッシュ)を
113+
イメージに埋め込み、管理画面`/[種別キー]/admin`の見出し横に表示する。今動いている
114+
イメージがいつのどのコミットのものか、pull後の反映確認に使える(ローカル開発時など
115+
埋め込みが無い場合は「開発ビルド」と表示される)
112116

113117
## 画面
114118

app/[type]/admin/page.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ export default async function TypedAdminPage({
1313
const { rows } = await query("select 1 from spot_types where key = $1", [type]);
1414
if (!rows[0]) notFound();
1515

16-
return <AdminView typeKey={type} />;
16+
// ビルド番号(Dockerfileのprodステージが --build-arg BUILD_NUMBER で受け取る)。
17+
// 開発時など未設定の場合はnullを渡し、管理画面側で「開発ビルド」と表示する
18+
return <AdminView typeKey={type} buildNumber={process.env.BUILD_NUMBER || null} />;
1719
}

components/AdminView.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,13 @@ const CSV_COLUMNS = [
4848
"description",
4949
] as const;
5050

51-
export default function AdminView({ typeKey }: { typeKey: string }) {
51+
export default function AdminView({
52+
typeKey,
53+
buildNumber,
54+
}: {
55+
typeKey: string;
56+
buildNumber?: string | null;
57+
}) {
5258
const router = useRouter();
5359
const [checkingRole, setCheckingRole] = useState(true);
5460
const [hasPageAccess, setHasPageAccess] = useState(false);
@@ -662,7 +668,13 @@ export default function AdminView({ typeKey }: { typeKey: string }) {
662668

663669
return (
664670
<main className="mx-auto max-w-6xl p-4">
665-
<h1 className="mb-4 text-lg font-bold">管理画面</h1>
671+
<div className="mb-4 flex flex-wrap items-baseline gap-x-3 gap-y-1">
672+
<h1 className="text-lg font-bold">管理画面</h1>
673+
{/* 本番で動いているイメージの識別用。GitHub Actionsのビルド時に埋め込まれる */}
674+
<span className="font-mono text-xs text-gray-500">
675+
ビルド: {buildNumber ?? "開発ビルド"}
676+
</span>
677+
</div>
666678

667679
{/* スポット種別(ログイン後の既定・種別マスタ) */}
668680
<div className="grid grid-cols-1 gap-6 lg:grid-cols-[360px_1fr]">

0 commit comments

Comments
 (0)