feat(ci): stamp build metadata and validate footer version hash
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
8
TODO.md
8
TODO.md
@@ -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
|
||||||
|
|
||||||
|
|||||||
29
apps/admin/src/lib/build-info.test.ts
Normal file
29
apps/admin/src/lib/build-info.test.ts
Normal 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",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
29
apps/web/src/lib/build-info.test.ts
Normal file
29
apps/web/src/lib/build-info.test.ts
Normal 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",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user