Compare commits
6 Commits
todo/mvp0-
...
todo/mvp0-
| Author | SHA1 | Date | |
|---|---|---|---|
|
af52b8581f
|
|||
|
3de4d5732e
|
|||
|
14c3df623a
|
|||
|
a57464d818
|
|||
|
c174f840bc
|
|||
|
334a5e3526
|
@@ -10,5 +10,7 @@ CMS_SUPPORT_EMAIL="support@cms.local"
|
|||||||
CMS_SUPPORT_PASSWORD="change-me-support-password"
|
CMS_SUPPORT_PASSWORD="change-me-support-password"
|
||||||
CMS_SUPPORT_NAME="Technical Support"
|
CMS_SUPPORT_NAME="Technical Support"
|
||||||
CMS_SUPPORT_LOGIN_KEY="support-access-change-me"
|
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.
|
# Optional dev bypass role for admin middleware. Leave empty to require auth login.
|
||||||
# CMS_DEV_ROLE="admin"
|
# CMS_DEV_ROLE="admin"
|
||||||
|
|||||||
4
.env.gitea-runner.example
Normal file
4
.env.gitea-runner.example
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
GITEA_INSTANCE_URL="https://git.example.com"
|
||||||
|
GITEA_RUNNER_REGISTRATION_TOKEN="replace-with-runner-registration-token"
|
||||||
|
GITEA_RUNNER_NAME="cms-runner"
|
||||||
|
GITEA_RUNNER_LABELS="ubuntu-latest:docker://node:20-bookworm"
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
name: CMS CI/CD (Theoretical)
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- dev
|
|
||||||
- main
|
|
||||||
- staging
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
BUN_VERSION: "1.3.5"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
quality:
|
|
||||||
name: Lint Typecheck Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Bun
|
|
||||||
uses: oven-sh/setup-bun@v2
|
|
||||||
with:
|
|
||||||
bun-version: ${{ env.BUN_VERSION }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Generate Prisma client
|
|
||||||
run: bun run db:generate
|
|
||||||
|
|
||||||
- name: Lint
|
|
||||||
run: bun run lint
|
|
||||||
|
|
||||||
- name: Typecheck
|
|
||||||
run: bun run typecheck
|
|
||||||
|
|
||||||
- name: Unit and component tests
|
|
||||||
run: bun run test
|
|
||||||
|
|
||||||
- name: E2E suite discovery check
|
|
||||||
run: bun run test:e2e --list
|
|
||||||
|
|
||||||
- name: Conventional commit check (latest commit)
|
|
||||||
run: bun run commitlint
|
|
||||||
|
|
||||||
build_staging_images:
|
|
||||||
name: Build Staging Images
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: quality
|
|
||||||
if: github.ref == 'refs/heads/staging'
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Build web image (staging)
|
|
||||||
run: docker build -f apps/web/Dockerfile -t cms-web:staging .
|
|
||||||
|
|
||||||
- name: Build admin image (staging)
|
|
||||||
run: docker build -f apps/admin/Dockerfile -t cms-admin:staging .
|
|
||||||
|
|
||||||
build_production_images:
|
|
||||||
name: Build Production Images
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: quality
|
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Build web image (production)
|
|
||||||
run: docker build -f apps/web/Dockerfile -t cms-web:${{ github.ref_name }} .
|
|
||||||
|
|
||||||
- name: Build admin image (production)
|
|
||||||
run: docker build -f apps/admin/Dockerfile -t cms-admin:${{ github.ref_name }} .
|
|
||||||
|
|
||||||
- name: Generate changelog
|
|
||||||
run: |
|
|
||||||
bun install --frozen-lockfile
|
|
||||||
bun run changelog:release
|
|
||||||
|
|
||||||
- name: Push images (placeholder)
|
|
||||||
run: |
|
|
||||||
echo "TODO: docker login to registry"
|
|
||||||
echo "TODO: docker push cms-web:${{ github.ref_name }}"
|
|
||||||
echo "TODO: docker push cms-admin:${{ github.ref_name }}"
|
|
||||||
echo "TODO: publish CHANGELOG.md content as release notes"
|
|
||||||
|
|
||||||
deploy_staging:
|
|
||||||
name: Deploy Staging (Placeholder)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build_staging_images
|
|
||||||
if: github.ref == 'refs/heads/staging'
|
|
||||||
steps:
|
|
||||||
- name: Deploy placeholder
|
|
||||||
run: |
|
|
||||||
echo "TODO: Pull and restart staging compose on target host"
|
|
||||||
echo "docker compose -f docker-compose.staging.yml up -d"
|
|
||||||
|
|
||||||
deploy_production:
|
|
||||||
name: Deploy Production (Placeholder)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build_production_images
|
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
|
||||||
steps:
|
|
||||||
- name: Deploy placeholder
|
|
||||||
run: |
|
|
||||||
echo "TODO: Pull and restart production compose on target host"
|
|
||||||
echo "docker compose -f docker-compose.production.yml up -d"
|
|
||||||
@@ -27,7 +27,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
governance:
|
governance:
|
||||||
name: Governance Checks
|
name: Governance Checks
|
||||||
runs-on: ubuntu-latest
|
runs-on: node22-bun
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
quality:
|
quality:
|
||||||
name: Lint Typecheck Unit E2E
|
name: Lint Typecheck Unit E2E
|
||||||
needs: governance
|
needs: governance
|
||||||
runs-on: ubuntu-latest
|
runs-on: node22-bun
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
@@ -84,12 +84,21 @@ 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
|
||||||
|
|
||||||
- name: Lint and format checks
|
- name: Lint and format checks
|
||||||
run: bun run check
|
run: bun run check
|
||||||
|
|
||||||
|
- name: Generate Prisma client
|
||||||
|
run: bun run db:generate
|
||||||
|
|
||||||
- name: Typecheck
|
- name: Typecheck
|
||||||
run: bun run typecheck
|
run: bun run typecheck
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
name: Deploy Compose Stack
|
name: Deploy Compose Stack
|
||||||
runs-on: ubuntu-latest
|
runs-on: node22-bun
|
||||||
steps:
|
steps:
|
||||||
- name: Resolve deployment target
|
- name: Resolve deployment target
|
||||||
id: target
|
id: target
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Build Push Changelog
|
name: Build Push Changelog
|
||||||
runs-on: ubuntu-latest
|
runs-on: node22-bun
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -24,6 +24,7 @@ test-results
|
|||||||
!.env.example
|
!.env.example
|
||||||
!.env.staging.example
|
!.env.staging.example
|
||||||
!.env.production.example
|
!.env.production.example
|
||||||
|
!.env.gitea-runner.example
|
||||||
|
|
||||||
# prisma
|
# prisma
|
||||||
packages/db/prisma/dev.db*
|
packages/db/prisma/dev.db*
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -97,10 +97,11 @@ bunx playwright install
|
|||||||
|
|
||||||
## Delivery Scaffolding
|
## Delivery Scaffolding
|
||||||
|
|
||||||
The repo includes a theoretical CI/CD and deployment baseline:
|
The repo includes a CI/CD and deployment baseline:
|
||||||
|
|
||||||
- Gitea workflow: `.gitea/workflows/ci-cd-theoretical.yml`
|
- Quality gate workflow: `.gitea/workflows/ci.yml`
|
||||||
- Real quality gate workflow: `.gitea/workflows/ci.yml`
|
- Deployment workflow: `.gitea/workflows/deploy.yml`
|
||||||
|
- Release workflow: `.gitea/workflows/release.yml`
|
||||||
- App images:
|
- App images:
|
||||||
- `apps/web/Dockerfile`
|
- `apps/web/Dockerfile`
|
||||||
- `apps/admin/Dockerfile`
|
- `apps/admin/Dockerfile`
|
||||||
@@ -119,12 +120,20 @@ Environment examples:
|
|||||||
|
|
||||||
- `.env.staging.example`
|
- `.env.staging.example`
|
||||||
- `.env.production.example`
|
- `.env.production.example`
|
||||||
|
- `.env.gitea-runner.example`
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- `dev` remains your local non-docker Bun workflow.
|
- `dev` remains your local non-docker Bun workflow.
|
||||||
- Staging and production compose files are templates and still require real secrets, registry strategy, and deployment host wiring.
|
- Staging and production compose files are templates and still require real secrets, registry strategy, and deployment host wiring.
|
||||||
|
|
||||||
|
Gitea Actions runner compose (self-hosted):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.gitea-runner.example .env.gitea-runner
|
||||||
|
docker compose --env-file .env.gitea-runner -f docker-compose.gitea-runner.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
- Changelog file: `CHANGELOG.md`
|
- Changelog file: `CHANGELOG.md`
|
||||||
|
|||||||
17
TODO.md
17
TODO.md
@@ -81,16 +81,16 @@ This file is the single source of truth for roadmap and delivery progress.
|
|||||||
|
|
||||||
### Delivery Pipeline And Runtime
|
### Delivery Pipeline And Runtime
|
||||||
|
|
||||||
- [x] [P2] Theoretical Gitea Actions workflow scaffold (`.gitea/workflows/ci-cd-theoretical.yml`)
|
- [x] [P2] Gitea workflow baseline (`.gitea/workflows/ci.yml`, `.gitea/workflows/deploy.yml`, `.gitea/workflows/release.yml`)
|
||||||
- [x] [P2] Bun-based Dockerfiles for public and admin apps
|
- [x] [P2] Bun-based Dockerfiles for public and admin apps
|
||||||
- [x] [P2] Staging and production docker-compose templates
|
- [x] [P2] Staging and production docker-compose templates
|
||||||
- [x] [P1] Registry credentials and image push strategy
|
- [x] [P1] Registry credentials and image push strategy
|
||||||
- [x] [P1] Staging deployment automation against real host
|
- [~] [P1] Staging deployment automation against real host
|
||||||
- [x] [P1] Production promotion and rollback procedure
|
- [~] [P1] Production promotion and rollback procedure
|
||||||
|
|
||||||
### Git Flow And Branching
|
### 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] Define PR gates: lint + typecheck + unit + e2e list minimum
|
||||||
- [x] [P1] Enforce one todo item per branch naming convention
|
- [x] [P1] Enforce one todo item per branch naming convention
|
||||||
- [x] [P2] Add PR template requiring linked TODO step
|
- [x] [P2] Add PR template requiring linked TODO step
|
||||||
@@ -105,6 +105,15 @@ This file is the single source of truth for roadmap and delivery progress.
|
|||||||
- [x] [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
|
||||||
|
|
||||||
|
- [ ] [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
|
||||||
|
- [x] [P2] Add CI build stamping for version/hash values consumed by app footers
|
||||||
|
- [x] [P2] Add automated tests validating displayed version/hash format and consistency
|
||||||
|
|
||||||
## MVP 1: Core CMS Business Features
|
## MVP 1: Core CMS Business Features
|
||||||
|
|
||||||
### Admin App (Primary Focus)
|
### Admin App (Primary Focus)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { ReactNode } from "react"
|
|||||||
|
|
||||||
import { LogoutButton } from "@/app/logout-button"
|
import { LogoutButton } from "@/app/logout-button"
|
||||||
import { AdminLocaleSwitcher } from "@/components/admin-locale-switcher"
|
import { AdminLocaleSwitcher } from "@/components/admin-locale-switcher"
|
||||||
|
import { getBuildInfo } from "@/lib/build-info"
|
||||||
|
|
||||||
type AdminShellProps = {
|
type AdminShellProps = {
|
||||||
role: Role
|
role: Role
|
||||||
@@ -57,6 +58,8 @@ export function AdminShell({
|
|||||||
actions,
|
actions,
|
||||||
children,
|
children,
|
||||||
}: AdminShellProps) {
|
}: AdminShellProps) {
|
||||||
|
const buildInfo = getBuildInfo()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex min-h-screen w-full max-w-7xl gap-8 px-6 py-10">
|
<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">
|
<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>
|
</header>
|
||||||
|
|
||||||
{children}
|
{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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
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",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
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 { useTranslations } from "next-intl"
|
||||||
|
|
||||||
|
import { getBuildInfo } from "@/lib/build-info"
|
||||||
|
|
||||||
export function PublicSiteFooter() {
|
export function PublicSiteFooter() {
|
||||||
const t = useTranslations("Layout")
|
const t = useTranslations("Layout")
|
||||||
const year = new Date().getFullYear()
|
const year = new Date().getFullYear()
|
||||||
|
const buildInfo = getBuildInfo()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="border-t border-neutral-200 bg-neutral-50">
|
<footer className="border-t border-neutral-200 bg-neutral-50">
|
||||||
@@ -15,6 +18,9 @@ export function PublicSiteFooter() {
|
|||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
<p>{t("footer.tagline")}</p>
|
<p>{t("footer.tagline")}</p>
|
||||||
|
<p className="font-mono text-xs text-neutral-500">
|
||||||
|
Build v{buildInfo.version} +sha.{buildInfo.sha}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
)
|
||||||
|
|||||||
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",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ module.exports = {
|
|||||||
"always",
|
"always",
|
||||||
["feat", "fix", "refactor", "perf", "test", "docs", "build", "ci", "chore", "revert"],
|
["feat", "fix", "refactor", "perf", "test", "docs", "build", "ci", "chore", "revert"],
|
||||||
],
|
],
|
||||||
"scope-empty": [2, "never"],
|
"scope-empty": [0],
|
||||||
"subject-empty": [2, "never"],
|
"subject-empty": [2, "never"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
13
docker-compose.gitea-runner.yml
Normal file
13
docker-compose.gitea-runner.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
gitea-runner:
|
||||||
|
image: gitea/act_runner:latest
|
||||||
|
container_name: cms-gitea-runner
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
GITEA_INSTANCE_URL: "${GITEA_INSTANCE_URL}"
|
||||||
|
GITEA_RUNNER_REGISTRATION_TOKEN: "${GITEA_RUNNER_REGISTRATION_TOKEN}"
|
||||||
|
GITEA_RUNNER_NAME: "${GITEA_RUNNER_NAME:-cms-runner}"
|
||||||
|
GITEA_RUNNER_LABELS: "${GITEA_RUNNER_LABELS:-ubuntu-latest:docker://node:20-bookworm}"
|
||||||
|
volumes:
|
||||||
|
- ./runner-data:/data
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client"
|
provider = "prisma-client-js"
|
||||||
output = "./generated/client"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PrismaPg } from "@prisma/adapter-pg"
|
import { PrismaPg } from "@prisma/adapter-pg"
|
||||||
|
import { PrismaClient } from "@prisma/client"
|
||||||
import { Pool } from "pg"
|
import { Pool } from "pg"
|
||||||
import { PrismaClient } from "../prisma/generated/client/client"
|
|
||||||
|
|
||||||
const connectionString = process.env.DATABASE_URL
|
const connectionString = process.env.DATABASE_URL
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
updatePostInputSchema,
|
updatePostInputSchema,
|
||||||
} from "@cms/content"
|
} from "@cms/content"
|
||||||
import { type CrudAuditHook, type CrudMutationContext, createCrudService } from "@cms/crud"
|
import { type CrudAuditHook, type CrudMutationContext, createCrudService } from "@cms/crud"
|
||||||
import type { Post } from "../prisma/generated/client/client"
|
import type { Post } from "@prisma/client"
|
||||||
|
|
||||||
import { db } from "./client"
|
import { db } from "./client"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user