diff --git a/.env.example b/.env.example index 4674258..8f4e725 100644 --- a/.env.example +++ b/.env.example @@ -10,5 +10,7 @@ CMS_SUPPORT_EMAIL="support@cms.local" CMS_SUPPORT_PASSWORD="change-me-support-password" CMS_SUPPORT_NAME="Technical Support" CMS_SUPPORT_LOGIN_KEY="support-access-change-me" +NEXT_PUBLIC_APP_VERSION="0.1.0-dev" +NEXT_PUBLIC_GIT_SHA="local" # Optional dev bypass role for admin middleware. Leave empty to require auth login. # CMS_DEV_ROLE="admin" diff --git a/TODO.md b/TODO.md index 65be19c..a999fba 100644 --- a/TODO.md +++ b/TODO.md @@ -85,12 +85,12 @@ This file is the single source of truth for roadmap and delivery progress. - [x] [P2] Bun-based Dockerfiles for public and admin apps - [x] [P2] Staging and production docker-compose templates - [x] [P1] Registry credentials and image push strategy -- [x] [P1] Staging deployment automation against real host -- [x] [P1] Production promotion and rollback procedure +- [~] [P1] Staging deployment automation against real host +- [~] [P1] Production promotion and rollback procedure ### Git Flow And Branching -- [x] [P1] Protect `main` and `staging` branches in Gitea +- [~] [P1] Protect `main` and `staging` branches in Gitea - [x] [P1] Define PR gates: lint + typecheck + unit + e2e list minimum - [x] [P1] Enforce one todo item per branch naming convention - [x] [P2] Add PR template requiring linked TODO step @@ -101,10 +101,19 @@ This file is the single source of truth for roadmap and delivery progress. - [x] [P1] Source of truth for version (`package.json` root) and release tagging rules (`vX.Y.Z`) - [x] [P1] Build metadata policy for git hash (`+sha.`) in app runtime footer - [x] [P1] App footer implementation plan for version + commit hash (admin + web) -- [x] [P2] Automated version injection in CI (stamping build from tag + commit hash) -- [x] [P2] Validation tests for displayed version/hash consistency per deployment +- [~] [P2] Automated version injection in CI (stamping build from tag + commit hash) +- [ ] [P2] Validation tests for displayed version/hash consistency per deployment - [x] [P1] Release tagging and changelog publication policy in CI +### MVP0 Close-Out Checklist + +- [ ] [P1] Verify and document protected branch rules in Gitea (`main`, `staging`) +- [ ] [P1] Run first staging deployment against a real host with deploy workflow and document result +- [ ] [P1] Replace release workflow placeholders with real release-notes and rollback execution steps +- [x] [P1] Expose runtime version + short git hash in admin and public app footer +- [ ] [P2] Add CI build stamping for version/hash values consumed by app footers +- [ ] [P2] Add automated tests validating displayed version/hash format and consistency + ## MVP 1: Core CMS Business Features ### Admin App (Primary Focus) diff --git a/apps/admin/src/components/admin-shell.tsx b/apps/admin/src/components/admin-shell.tsx index 0457d7d..73711d9 100644 --- a/apps/admin/src/components/admin-shell.tsx +++ b/apps/admin/src/components/admin-shell.tsx @@ -4,6 +4,7 @@ import type { ReactNode } from "react" import { LogoutButton } from "@/app/logout-button" import { AdminLocaleSwitcher } from "@/components/admin-locale-switcher" +import { getBuildInfo } from "@/lib/build-info" type AdminShellProps = { role: Role @@ -57,6 +58,8 @@ export function AdminShell({ actions, children, }: AdminShellProps) { + const buildInfo = getBuildInfo() + return (
) diff --git a/apps/admin/src/lib/build-info.ts b/apps/admin/src/lib/build-info.ts new file mode 100644 index 0000000..3c1f1d8 --- /dev/null +++ b/apps/admin/src/lib/build-info.ts @@ -0,0 +1,21 @@ +const FALLBACK_VERSION = "0.0.1-dev" +const FALLBACK_SHA = "local" + +function shortenSha(input: string): string { + const value = input.trim() + if (!value) { + return FALLBACK_SHA + } + + return value.slice(0, 7) +} + +export function getBuildInfo() { + const version = process.env.NEXT_PUBLIC_APP_VERSION?.trim() || FALLBACK_VERSION + const sha = shortenSha(process.env.NEXT_PUBLIC_GIT_SHA ?? "") + + return { + version, + sha, + } +} diff --git a/apps/web/src/components/public-site-footer.tsx b/apps/web/src/components/public-site-footer.tsx index aa3185d..66432bb 100644 --- a/apps/web/src/components/public-site-footer.tsx +++ b/apps/web/src/components/public-site-footer.tsx @@ -2,9 +2,12 @@ import { useTranslations } from "next-intl" +import { getBuildInfo } from "@/lib/build-info" + export function PublicSiteFooter() { const t = useTranslations("Layout") const year = new Date().getFullYear() + const buildInfo = getBuildInfo() return ( ) diff --git a/apps/web/src/lib/build-info.ts b/apps/web/src/lib/build-info.ts new file mode 100644 index 0000000..3c1f1d8 --- /dev/null +++ b/apps/web/src/lib/build-info.ts @@ -0,0 +1,21 @@ +const FALLBACK_VERSION = "0.0.1-dev" +const FALLBACK_SHA = "local" + +function shortenSha(input: string): string { + const value = input.trim() + if (!value) { + return FALLBACK_SHA + } + + return value.slice(0, 7) +} + +export function getBuildInfo() { + const version = process.env.NEXT_PUBLIC_APP_VERSION?.trim() || FALLBACK_VERSION + const sha = shortenSha(process.env.NEXT_PUBLIC_GIT_SHA ?? "") + + return { + version, + sha, + } +}