Compare commits

...

1 Commits

Author SHA1 Message Date
af52b8581f feat(ci): stamp build metadata and validate footer version hash 2026-02-11 19:06:55 +01:00
5 changed files with 72 additions and 4 deletions

View File

@@ -84,6 +84,12 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
- name: Resolve build metadata
run: |
version=$(bun -e 'const pkg = JSON.parse(await Bun.file("package.json").text()); console.log(pkg.version)')
echo "NEXT_PUBLIC_APP_VERSION=$version" >> "$GITHUB_ENV"
echo "NEXT_PUBLIC_GIT_SHA=${GITHUB_SHA}" >> "$GITHUB_ENV"
- name: Install Playwright browser deps - name: Install Playwright browser deps
run: bunx playwright install --with-deps chromium run: bunx playwright install --with-deps chromium

View File

@@ -101,8 +101,8 @@ 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] 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] 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] [P1] App footer implementation plan for version + commit hash (admin + web)
- [~] [P2] Automated version injection in CI (stamping build from tag + commit hash) - [x] [P2] Automated version injection in CI (stamping build from tag + commit hash)
- [ ] [P2] Validation tests for displayed version/hash consistency per deployment - [x] [P2] Validation tests for displayed version/hash consistency per deployment
- [x] [P1] Release tagging and changelog publication policy in CI - [x] [P1] Release tagging and changelog publication policy in CI
### MVP0 Close-Out Checklist ### MVP0 Close-Out Checklist
@@ -111,8 +111,8 @@ This file is the single source of truth for roadmap and delivery progress.
- [ ] [P1] Run first staging deployment against a real host with deploy workflow and document result - [ ] [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 - [ ] [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 - [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 - [x] [P2] Add CI build stamping for version/hash values consumed by app footers
- [ ] [P2] Add automated tests validating displayed version/hash format and consistency - [x] [P2] Add automated tests validating displayed version/hash format and consistency
## MVP 1: Core CMS Business Features ## MVP 1: Core CMS Business Features

View File

@@ -0,0 +1,29 @@
import { afterEach, describe, expect, it, vi } from "vitest"
import { getBuildInfo } from "./build-info"
afterEach(() => {
vi.unstubAllEnvs()
})
describe("getBuildInfo (admin)", () => {
it("returns fallback values when env is missing", () => {
vi.stubEnv("NEXT_PUBLIC_APP_VERSION", "")
vi.stubEnv("NEXT_PUBLIC_GIT_SHA", "")
expect(getBuildInfo()).toEqual({
version: "0.0.1-dev",
sha: "local",
})
})
it("uses env values and truncates git sha", () => {
vi.stubEnv("NEXT_PUBLIC_APP_VERSION", "0.2.0")
vi.stubEnv("NEXT_PUBLIC_GIT_SHA", "abcdef123456")
expect(getBuildInfo()).toEqual({
version: "0.2.0",
sha: "abcdef1",
})
})
})

View File

@@ -0,0 +1,29 @@
import { afterEach, describe, expect, it, vi } from "vitest"
import { getBuildInfo } from "./build-info"
afterEach(() => {
vi.unstubAllEnvs()
})
describe("getBuildInfo (web)", () => {
it("returns fallback values when env is missing", () => {
vi.stubEnv("NEXT_PUBLIC_APP_VERSION", "")
vi.stubEnv("NEXT_PUBLIC_GIT_SHA", "")
expect(getBuildInfo()).toEqual({
version: "0.0.1-dev",
sha: "local",
})
})
it("uses env values and truncates git sha", () => {
vi.stubEnv("NEXT_PUBLIC_APP_VERSION", "0.2.0")
vi.stubEnv("NEXT_PUBLIC_GIT_SHA", "123456789abc")
expect(getBuildInfo()).toEqual({
version: "0.2.0",
sha: "1234567",
})
})
})

View File

@@ -1,10 +1,13 @@
import { expect, test } from "@playwright/test" import { expect, test } from "@playwright/test"
const BUILD_INFO_PATTERN = /Build v\S+ \+sha\.[a-z0-9]{5,7}/i
test("smoke", async ({ page }, testInfo) => { test("smoke", async ({ page }, testInfo) => {
await page.goto("/") await page.goto("/")
if (testInfo.project.name === "web-chromium") { if (testInfo.project.name === "web-chromium") {
await expect(page.getByRole("heading", { name: /your next\.js cms frontend/i })).toBeVisible() await expect(page.getByRole("heading", { name: /your next\.js cms frontend/i })).toBeVisible()
await expect(page.getByText(BUILD_INFO_PATTERN)).toBeVisible()
return return
} }
@@ -12,6 +15,7 @@ test("smoke", async ({ page }, testInfo) => {
if (await dashboardHeading.isVisible({ timeout: 2000 })) { if (await dashboardHeading.isVisible({ timeout: 2000 })) {
await expect(dashboardHeading).toBeVisible() await expect(dashboardHeading).toBeVisible()
await expect(page.getByText(BUILD_INFO_PATTERN)).toBeVisible()
return return
} }