feat(versioning): show runtime version and git hash in app footers
This commit is contained in:
@@ -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"
|
||||
|
||||
19
TODO.md
19
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.<short>`) 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)
|
||||
|
||||
@@ -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 (
|
||||
<div className="mx-auto flex min-h-screen w-full max-w-7xl gap-8 px-6 py-10">
|
||||
<aside className="sticky top-0 hidden h-fit w-64 shrink-0 space-y-4 lg:block">
|
||||
@@ -111,6 +114,10 @@ export function AdminShell({
|
||||
</header>
|
||||
|
||||
{children}
|
||||
|
||||
<footer className="border-t border-neutral-200 pt-4 text-xs text-neutral-500">
|
||||
Build v{buildInfo.version} +sha.{buildInfo.sha}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
21
apps/admin/src/lib/build-info.ts
Normal file
21
apps/admin/src/lib/build-info.ts
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<footer className="border-t border-neutral-200 bg-neutral-50">
|
||||
@@ -15,6 +18,9 @@ export function PublicSiteFooter() {
|
||||
})}
|
||||
</p>
|
||||
<p>{t("footer.tagline")}</p>
|
||||
<p className="font-mono text-xs text-neutral-500">
|
||||
Build v{buildInfo.version} +sha.{buildInfo.sha}
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
|
||||
21
apps/web/src/lib/build-info.ts
Normal file
21
apps/web/src/lib/build-info.ts
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user