docs(pp-58): define deployable runtime image contract
This commit is contained in:
@@ -1,141 +0,0 @@
|
||||
name: CICD
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
GITEA_REGISTRY_HOST: kankali.darkhelm.lan
|
||||
GITEA_REGISTRY_IP: 10.18.75.2
|
||||
|
||||
concurrency:
|
||||
group: cicd-launch-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
launch:
|
||||
name: Launch CICD Start
|
||||
# Use the same stable runner pool as the rest of CICD.
|
||||
runs-on: ubuntu-act
|
||||
timeout-minutes: 8
|
||||
steps:
|
||||
- name: Configure registry host resolution
|
||||
run: |
|
||||
if ! grep -q "${GITEA_REGISTRY_HOST}" /etc/hosts; then
|
||||
echo "${GITEA_REGISTRY_IP} ${GITEA_REGISTRY_HOST}" >> /etc/hosts
|
||||
fi
|
||||
|
||||
- name: Dispatch CICD Start workflow
|
||||
env:
|
||||
ACTIONS_TRIGGER_TOKEN: ${{ secrets.ACTIONS_TRIGGER_TOKEN }}
|
||||
PACKAGE_ACCESS_TOKEN: ${{ secrets.PACKAGE_ACCESS_TOKEN }}
|
||||
REPO_FULL: ${{ github.repository }}
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
HEAD_SHA: ${{ github.sha }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
DISPATCH_TOKEN="${ACTIONS_TRIGGER_TOKEN:-${PACKAGE_ACCESS_TOKEN:-}}"
|
||||
|
||||
if [ -z "${DISPATCH_TOKEN}" ]; then
|
||||
echo "❌ Missing dispatch token. Set ACTIONS_TRIGGER_TOKEN (repo write scope) or ensure PACKAGE_ACCESS_TOKEN has Actions workflow-dispatch permissions."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q "${GITEA_REGISTRY_HOST}" /etc/hosts; then
|
||||
echo "${GITEA_REGISTRY_IP} ${GITEA_REGISTRY_HOST}" >> /etc/hosts
|
||||
fi
|
||||
|
||||
REPO_OWNER="${REPO_FULL%/*}"
|
||||
REPO_NAME="${REPO_FULL#*/}"
|
||||
TARGET_REF="${HEAD_REF:-${REF_NAME}}"
|
||||
TRACE_ID="cicd-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${HEAD_SHA:0:8}"
|
||||
|
||||
echo "trace_id=${TRACE_ID}"
|
||||
echo "target_ref=${TARGET_REF}"
|
||||
CANDIDATE_API_BASES=()
|
||||
if [ -n "${GITHUB_SERVER_URL:-}" ]; then
|
||||
CANDIDATE_API_BASES+=("${GITHUB_SERVER_URL%/}/api/v1")
|
||||
fi
|
||||
CANDIDATE_API_BASES+=("http://${GITEA_REGISTRY_IP}:3001/api/v1")
|
||||
CANDIDATE_API_BASES+=("http://${GITEA_REGISTRY_HOST}:3001/api/v1")
|
||||
|
||||
ensure_curl() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq curl ca-certificates
|
||||
fi
|
||||
command -v curl >/dev/null 2>&1
|
||||
}
|
||||
|
||||
ensure_curl || { echo "❌ curl unavailable for dispatch"; exit 1; }
|
||||
|
||||
HELPER_PATH="/tmp/dispatch-workflow.sh"
|
||||
|
||||
fetch_dispatch_helper() {
|
||||
local helper_ref="$1"
|
||||
local api_base
|
||||
for api_base in "${CANDIDATE_API_BASES[@]}"; do
|
||||
helper_url="${api_base}/repos/${REPO_OWNER}/${REPO_NAME}/raw/scripts/dispatch-workflow.sh?ref=${helper_ref}"
|
||||
if curl -fsS --connect-timeout 5 --max-time 20 \
|
||||
-H "Authorization: token ${DISPATCH_TOKEN}" \
|
||||
-H "User-Agent: plex-playlist-cicd" \
|
||||
-o "${HELPER_PATH}" \
|
||||
"${helper_url}"; then
|
||||
chmod +x "${HELPER_PATH}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
if ! fetch_dispatch_helper "${TARGET_REF}" && ! fetch_dispatch_helper "${HEAD_SHA}"; then
|
||||
echo "❌ Failed to fetch scripts/dispatch-workflow.sh from repository"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DISPATCH_ARGS=(
|
||||
--token "${DISPATCH_TOKEN}"
|
||||
--repo "${REPO_FULL}"
|
||||
--workflow "cicd-start.yaml"
|
||||
--ref "${TARGET_REF}"
|
||||
--head-sha "${HEAD_SHA}"
|
||||
--source-workflow "CICD"
|
||||
--trace-id "${TRACE_ID}"
|
||||
)
|
||||
|
||||
for API_BASE in "${CANDIDATE_API_BASES[@]}"; do
|
||||
DISPATCH_ARGS+=(--api-base "${API_BASE}")
|
||||
done
|
||||
|
||||
"${HELPER_PATH}" "${DISPATCH_ARGS[@]}"
|
||||
|
||||
- name: Failure diagnostics
|
||||
if: failure()
|
||||
run: |
|
||||
echo "=== Failure Diagnostics ==="
|
||||
date -u '+timestamp_utc=%Y-%m-%dT%H:%M:%SZ'
|
||||
echo "runner_name=${RUNNER_NAME:-unknown}"
|
||||
echo "runner_hostname=${HOSTNAME:-unknown}"
|
||||
uname -a || true
|
||||
cat /etc/os-release 2>/dev/null || true
|
||||
df -h || true
|
||||
free -h || true
|
||||
ps aux --sort=-%mem | head -n 30 || true
|
||||
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
echo "=== Docker Diagnostics ==="
|
||||
docker version || true
|
||||
docker info || true
|
||||
docker ps -a || true
|
||||
docker images --digests | head -n 50 || true
|
||||
else
|
||||
echo "docker not available on this runner"
|
||||
fi
|
||||
|
||||
echo "=== Kernel Tail ==="
|
||||
dmesg | tail -n 120 || true
|
||||
104
README.md
104
README.md
@@ -9,6 +9,17 @@ A full-stack application for managing Plex playlists with a FastAPI backend and
|
||||
- **Database**: PostgreSQL 16
|
||||
- **Containerization**: Docker + Docker Compose
|
||||
|
||||
## Deployable Runtime Contract
|
||||
|
||||
Deployable image requirements are defined in:
|
||||
|
||||
- [docs/DEPLOYABLE_RUNTIME_CONTRACT.md](docs/DEPLOYABLE_RUNTIME_CONTRACT.md)
|
||||
- [docs/adr/ADR003-deployable_runtime_image_contract.md](docs/adr/ADR003-deployable_runtime_image_contract.md)
|
||||
|
||||
These documents define backend/frontend runtime boundaries, startup and health
|
||||
behavior expectations, environment contracts, and disallowed non-runtime
|
||||
tooling classes in deployable artifacts.
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
@@ -22,20 +33,24 @@ A full-stack application for managing Plex playlists with a FastAPI backend and
|
||||
This project uses comprehensive linting and formatting:
|
||||
|
||||
**Backend (Python):**
|
||||
|
||||
- `ruff` - Fast Python linter and formatter
|
||||
- `pyright` - Type checking
|
||||
- `pydoclint` - Docstring linting (Google style)
|
||||
|
||||
**Frontend (TypeScript/Vue):**
|
||||
|
||||
- `eslint` - Linting with Vue and TypeScript support
|
||||
- `prettier` - Code formatting
|
||||
- `vue-tsc` - Vue TypeScript checking
|
||||
- `eslint-plugin-tsdoc` - TSDoc documentation linting
|
||||
|
||||
**Task Runner:**
|
||||
|
||||
- `poethepoet` - Unified task runner for development workflows
|
||||
|
||||
**General:**
|
||||
|
||||
- `pre-commit` - Git hooks for automated quality checks
|
||||
- TOML formatting and validation
|
||||
|
||||
@@ -66,45 +81,49 @@ pre-commit install
|
||||
|
||||
### Quick Start
|
||||
|
||||
1. **Clone the repository**
|
||||
Clone the repository, then choose one of these startup paths.
|
||||
|
||||
2. **Unified Development (Recommended):**
|
||||
```bash
|
||||
# Complete setup and start development environment
|
||||
poe setup
|
||||
#### Unified Development
|
||||
|
||||
# Or manually:
|
||||
cd backend
|
||||
pip install -e .
|
||||
poe dev-env-start
|
||||
```
|
||||
```bash
|
||||
# Complete setup and start development environment
|
||||
poe setup
|
||||
|
||||
3. **Traditional Setup:**
|
||||
```bash
|
||||
# Backend development
|
||||
cd backend
|
||||
pip install -e .
|
||||
# Or manually:
|
||||
cd backend
|
||||
pip install -e .
|
||||
poe dev-env-start
|
||||
```
|
||||
|
||||
# Frontend development
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
#### Traditional Setup
|
||||
|
||||
4. **Docker Development:**
|
||||
```bash
|
||||
# Using Poe (recommended)
|
||||
poe docker-dev-up
|
||||
```bash
|
||||
# Backend development
|
||||
cd backend
|
||||
pip install -e .
|
||||
|
||||
# Or directly
|
||||
docker compose -f compose.dev.yml up --build
|
||||
```
|
||||
# Frontend development
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
5. **Production Build:**
|
||||
```bash
|
||||
poe docker-prod-up
|
||||
# Or: docker compose up --build
|
||||
```
|
||||
#### Docker Development
|
||||
|
||||
```bash
|
||||
# Using Poe (recommended)
|
||||
poe docker-dev-up
|
||||
|
||||
# Or directly
|
||||
docker compose -f compose.dev.yml up --build
|
||||
```
|
||||
|
||||
#### Production Build
|
||||
|
||||
```bash
|
||||
poe docker-prod-up
|
||||
# Or: docker compose up --build
|
||||
```
|
||||
|
||||
### Running in Production Mode
|
||||
|
||||
@@ -113,13 +132,14 @@ docker compose up --build -d
|
||||
```
|
||||
|
||||
This will start:
|
||||
|
||||
- PostgreSQL database on port 5432
|
||||
- FastAPI backend on port 8000
|
||||
- Vue.js frontend on port 80
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
```text
|
||||
plex-playlist/
|
||||
├── backend/ # FastAPI backend
|
||||
├── frontend/ # Vue.js frontend
|
||||
@@ -144,16 +164,20 @@ For full troubleshooting context, see `docs/GITEA_ACTIONS_TROUBLESHOOTING.md`.
|
||||
## Environment Variables
|
||||
|
||||
### Backend
|
||||
|
||||
- `DATABASE_URL`: PostgreSQL connection string
|
||||
- `BACKEND_REQUIRED_PYTHON`: Runtime policy baseline (`3.14` default)
|
||||
- `ENVIRONMENT`: `development` or `production`
|
||||
- `RELOAD`: Enable uvicorn auto-reload (development only)
|
||||
|
||||
### Frontend
|
||||
- `NODE_ENV`: `development` or `production`
|
||||
|
||||
- No required runtime environment variables for production nginx serving
|
||||
|
||||
## Database
|
||||
|
||||
The PostgreSQL database is configured with:
|
||||
|
||||
- Database: `plex_playlist`
|
||||
- User: `plex_user`
|
||||
- Password: `plex_password`
|
||||
@@ -168,12 +192,13 @@ The PostgreSQL database is configured with:
|
||||
## API Documentation
|
||||
|
||||
When running, the FastAPI automatic documentation is available at:
|
||||
- Development: http://localhost:8001/docs
|
||||
- Production: http://localhost:8000/docs
|
||||
|
||||
- Development: <http://localhost:8001/docs>
|
||||
- Production: <http://localhost:8000/docs>
|
||||
|
||||
---
|
||||
|
||||
# Manual Setup (if not using Docker)
|
||||
## Manual Setup (if not using Docker)
|
||||
|
||||
## Backend Setup (FastAPI, Python 3.14, uv, ruff, pyright)
|
||||
|
||||
@@ -227,6 +252,7 @@ npm install
|
||||
### 2. Recommended: Enable strictest TypeScript settings
|
||||
|
||||
Edit `tsconfig.json` and set:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
@@ -255,8 +281,11 @@ npm run dev
|
||||
## Documentation
|
||||
|
||||
### Development & Workflow
|
||||
|
||||
- **[Development Environment Setup](docs/DEVELOPMENT.md)** - Comprehensive guide for setting up your development environment, git workflow, pre-commit hooks, manual tool usage, and CI/CD pipeline understanding
|
||||
- **[Poe Task Reference](docs/POE_TASK_REFERENCE.md)** - Complete guide to unified development tasks and workflows using Poe the Poet
|
||||
- **[Deployable Runtime Contract](docs/DEPLOYABLE_RUNTIME_CONTRACT.md)** - Canonical backend/frontend deployable runtime image requirements and exclusions
|
||||
- **[ADR003: Deployable Runtime Image Contract Boundaries](docs/adr/ADR003-deployable_runtime_image_contract.md)** - Architectural decision that locks deployable image boundary policy
|
||||
|
||||
### Architecture & CI/CD
|
||||
|
||||
@@ -267,7 +296,6 @@ npm run dev
|
||||
### Dependency Management & Automation
|
||||
|
||||
- **[Renovate Bot Setup](docs/RENOVATE_SETUP_GUIDE.md)** - Automated dependency updates with Renovate for Python, Node.js, and Docker dependencies
|
||||
- **[Gitea API Token Setup](docs/GITEA_TOKEN_SETUP.md)** - Step-by-step guide for creating organization API tokens with proper permissions
|
||||
|
||||
### Operations & Troubleshooting
|
||||
|
||||
|
||||
@@ -129,8 +129,10 @@ jobs:
|
||||
```
|
||||
|
||||
### Responsibility Split
|
||||
- `.gitea/workflows/cicd-base.yml` owns base publication and verification.
|
||||
- `.gitea/workflows/cicd.yml` owns complete-image build, tests, and deployment checks.
|
||||
- `.gitea/workflows/docker-build-base.yaml` owns base publication and verification.
|
||||
- `.gitea/workflows/docker-build-main.yaml` owns complete-image publication.
|
||||
- `.gitea/workflows/cicd-start.yaml`, `.gitea/workflows/cicd-checks.yaml`, and
|
||||
`.gitea/workflows/cicd-tests.yaml` own CI validation and checks.
|
||||
- Main CI never rebuilds the base image locally.
|
||||
|
||||
## Local Development
|
||||
|
||||
@@ -114,7 +114,7 @@ Firefox: browserType.launch: Executable doesn't exist
|
||||
|
||||
#### Docker Registry Operations
|
||||
```yaml
|
||||
# .gitea/workflows/cicd.yml
|
||||
# .gitea/workflows/cicd-checks.yaml
|
||||
- name: Login to Container Registry (with retry)
|
||||
run: |
|
||||
for attempt in {1..5}; do
|
||||
|
||||
156
docs/DEPLOYABLE_RUNTIME_CONTRACT.md
Normal file
156
docs/DEPLOYABLE_RUNTIME_CONTRACT.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Deployable Runtime Image Contract
|
||||
|
||||
## Purpose
|
||||
|
||||
Define the minimum deployable runtime contract for backend and frontend images.
|
||||
This document is the canonical source for what must be present, what must not
|
||||
be present, and what behavior deployment environments can rely on.
|
||||
|
||||
This contract supports issue PP-58 and establishes a baseline for future
|
||||
automation work under epic #66.
|
||||
|
||||
## Scope
|
||||
|
||||
Included:
|
||||
|
||||
- Backend deployable runtime image requirements.
|
||||
- Frontend deployable runtime image requirements.
|
||||
- Runtime entrypoint, ports, health behavior, startup behavior, and environment
|
||||
contracts.
|
||||
- Explicitly disallowed non-runtime tooling classes in deployable images.
|
||||
|
||||
Excluded:
|
||||
|
||||
- CI workflow rewiring.
|
||||
- Test execution redesign.
|
||||
- New runtime hardening implementations not required to define contract.
|
||||
|
||||
## Backend Runtime Contract
|
||||
|
||||
### Runtime Artifact Definition
|
||||
|
||||
- Container build source: `Dockerfile.backend`.
|
||||
- Runtime base image: `python:3.14-slim`.
|
||||
- Runtime process: `uvicorn main:app --host 0.0.0.0 --port 8000`.
|
||||
- Exposed runtime port: `8000`.
|
||||
|
||||
### Required Runtime Dependencies
|
||||
|
||||
The runtime artifact must include versions compatible with:
|
||||
|
||||
- `fastapi==0.120.2`
|
||||
- `psycopg==3.2.12`
|
||||
- `sqlalchemy==2.0.44`
|
||||
- `uvicorn==0.38.0`
|
||||
|
||||
The lockfile in `backend/uv.lock` is the dependency source of truth.
|
||||
|
||||
### Required Runtime Environment Contract
|
||||
|
||||
- `DATABASE_URL` is required.
|
||||
- Accepted form: `postgresql://...` or SQLAlchemy async form.
|
||||
- Runtime normalization to async psycopg dialect is performed in
|
||||
`backend/src/backend/database.py`.
|
||||
- `BACKEND_REQUIRED_PYTHON` is optional and defaults to `3.14`.
|
||||
|
||||
### Backend Health and Startup Behavior
|
||||
|
||||
- Startup must fail fast if runtime policy checks fail.
|
||||
- Source: `backend/src/backend/main.py` lifecycle (`lifespan`) validation.
|
||||
- Health endpoint contract:
|
||||
- `GET /health` returns `200` with `{"status":"healthy","database":"connected"}`
|
||||
when database probe succeeds.
|
||||
- `GET /health` returns `503` with
|
||||
`{"status":"unhealthy","database":"disconnected"}` when probe fails.
|
||||
- Compatibility diagnostics endpoint:
|
||||
- `GET /compatibility` reports policy and package compatibility status.
|
||||
|
||||
### Backend Runtime Checklist
|
||||
|
||||
- [ ] Runtime image built from `Dockerfile.backend`.
|
||||
- [ ] Runtime process is uvicorn serving `main:app` on `0.0.0.0:8000`.
|
||||
- [ ] `DATABASE_URL` is set in deployment runtime.
|
||||
- [ ] `GET /health` behavior matches contract.
|
||||
- [ ] Startup fails on runtime policy mismatch.
|
||||
- [ ] Deployable artifact excludes CI-only and test-only tooling classes.
|
||||
|
||||
## Frontend Runtime Contract
|
||||
|
||||
### Frontend Runtime Artifact Definition
|
||||
|
||||
- Container build source: `Dockerfile.frontend` (target `production`).
|
||||
- Runtime base image: `nginx:alpine`.
|
||||
- Runtime process: `nginx -g "daemon off;"`.
|
||||
- Exposed runtime port: `80`.
|
||||
- Runtime artifact payload: static assets from `/app/dist` copied to
|
||||
`/usr/share/nginx/html`.
|
||||
|
||||
### Frontend Runtime Entry and Routing Contract
|
||||
|
||||
- Nginx configuration source: `frontend/nginx.conf`.
|
||||
- SPA routing behavior must use fallback to `index.html` for unknown routes.
|
||||
- API requests under `/api/` are proxied to backend service endpoint
|
||||
`http://backend:8000/` in compose deployments.
|
||||
|
||||
### Frontend Health and Startup Behavior
|
||||
|
||||
- Startup expectation: nginx process starts and serves static assets on port 80.
|
||||
- Runtime health expectation for deployment checks:
|
||||
- `GET /` should return `200` and serve frontend entry document.
|
||||
- API proxy readiness is dependent on backend runtime availability.
|
||||
|
||||
### Frontend Runtime Environment Contract
|
||||
|
||||
- No required runtime environment variables are defined for nginx static serving.
|
||||
- Build-time frontend mode is production-oriented and not part of runtime env
|
||||
contract.
|
||||
|
||||
### Frontend Runtime Checklist
|
||||
|
||||
- [ ] Runtime image built from `Dockerfile.frontend` production target.
|
||||
- [ ] Runtime process is nginx serving on port 80.
|
||||
- [ ] SPA route fallback behavior is present.
|
||||
- [ ] `/api/` proxy behavior aligns with backend service wiring.
|
||||
- [ ] Deployable artifact excludes CI-only and test-only tooling classes.
|
||||
|
||||
## Disallowed Tooling Classes in Deployable Runtime Images
|
||||
|
||||
Deployable runtime artifacts must not include tooling classes that are only
|
||||
needed for CI, validation, or local development workflows.
|
||||
|
||||
Disallowed classes:
|
||||
|
||||
- Linters and formatters (example: ruff, eslint, prettier).
|
||||
- Type checking and static analysis tooling (example: pyright, vue-tsc).
|
||||
- Test frameworks and test drivers (example: pytest, vitest, playwright).
|
||||
- Browser test binaries and CI runner helper tools.
|
||||
- Build-only package managers and build toolchains not needed at runtime.
|
||||
|
||||
Note:
|
||||
|
||||
- Multi-stage builds may use these tools in build stages.
|
||||
- These tools must not be required by or present in final deployable runtime
|
||||
image layers.
|
||||
|
||||
## Acceptance Criteria Traceability (PP-58)
|
||||
|
||||
1. Backend runtime requirements are documented and approved.
|
||||
- Covered by: "Backend Runtime Contract" and backend checklist.
|
||||
2. Frontend runtime requirements are documented and approved.
|
||||
- Covered by: "Frontend Runtime Contract" and frontend checklist.
|
||||
3. Runtime contracts include health endpoint expectations and startup behavior.
|
||||
- Covered by: backend health/startup section and frontend health/startup
|
||||
section.
|
||||
4. Non-runtime tool classes are explicitly excluded from deployable image
|
||||
definition.
|
||||
- Covered by: "Disallowed Tooling Classes in Deployable Runtime Images".
|
||||
|
||||
## Future Enforcement Hooks (Out of Scope for PP-58)
|
||||
|
||||
Potential follow-up automation under epic #66:
|
||||
|
||||
- Policy checks validating final image layers do not include disallowed tooling
|
||||
classes.
|
||||
- Contract tests that assert documented health/startup behavior.
|
||||
- CI checks that verify Dockerfile target boundaries remain aligned with this
|
||||
contract.
|
||||
@@ -19,6 +19,24 @@ This document outlines how to set up your development environment and work with
|
||||
- **[CI/CD Multi-Stage Build Architecture](CICD_MULTI_STAGE_BUILD.md)** - Technical details of the optimized build system
|
||||
- **[CI/CD Troubleshooting](GITEA_ACTIONS_TROUBLESHOOTING.md)** - Common issues and solutions
|
||||
- **[Secure Docker CI/CD](SECURE_DOCKER_CICD.md)** - Security considerations and practices
|
||||
- **[Deployable Runtime Contract](DEPLOYABLE_RUNTIME_CONTRACT.md)** - Canonical backend/frontend runtime artifact contract and exclusion rules
|
||||
- **[ADR003: Deployable Runtime Image Contract Boundaries](adr/ADR003-deployable_runtime_image_contract.md)** - Decision record for deployable runtime boundaries
|
||||
|
||||
## Deployable Runtime Artifacts
|
||||
|
||||
When changing deployment behavior or image composition, treat
|
||||
`DEPLOYABLE_RUNTIME_CONTRACT.md` as the source of truth for:
|
||||
|
||||
- Runtime entrypoint and exposed ports.
|
||||
- Health and startup expectations.
|
||||
- Runtime environment contract.
|
||||
- Disallowed non-runtime tooling classes in final deployable images.
|
||||
|
||||
Scope boundary:
|
||||
|
||||
- This repository separates contract definition from enforcement mechanics.
|
||||
- CI workflow rewiring and test execution redesign are out of scope for PP-58
|
||||
and belong to follow-up work under epic #66.
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -69,10 +87,10 @@ docker compose -f compose.dev.yml up -d --build
|
||||
|
||||
### Service Access
|
||||
|
||||
- **Frontend**: http://localhost:3000
|
||||
- **Backend API**: http://localhost:8000
|
||||
- **API Documentation**: http://localhost:8000/docs
|
||||
- **Database**: localhost:5432 (user: `plex`, password: see `secrets/postgres_password`)
|
||||
- **Frontend**: <http://localhost:3000>
|
||||
- **Backend API**: <http://localhost:8000>
|
||||
- **API Documentation**: <http://localhost:8000/docs>
|
||||
- **Database**: `localhost:5432` (user: `plex`, password: see `secrets/postgres_password`)
|
||||
|
||||
## Poe the Poet Task Runner
|
||||
|
||||
@@ -197,6 +215,7 @@ pre-commit run --all-files
|
||||
### How It Works
|
||||
|
||||
Pre-commit automatically runs on every `git commit` and will:
|
||||
|
||||
- Format code (Prettier, Ruff)
|
||||
- Check syntax (ESLint, Pyright)
|
||||
- Validate files (YAML, TOML, trailing whitespace)
|
||||
@@ -207,6 +226,7 @@ If any hook fails, the commit is blocked until issues are fixed.
|
||||
### Pre-commit is Optional
|
||||
|
||||
While strongly recommended, pre-commit is not required because:
|
||||
|
||||
- **CI Validation**: All the same checks run in CI
|
||||
- **Developer Choice**: Some prefer manual tool usage
|
||||
- **Learning**: Developers can run tools individually to understand them
|
||||
@@ -221,7 +241,8 @@ If you prefer not to use pre-commit, here's how to run each tool manually:
|
||||
|
||||
Navigate to the `backend/` directory for all backend commands.
|
||||
|
||||
#### Code Formatting
|
||||
#### Backend Code Formatting
|
||||
|
||||
```bash
|
||||
# Format code with Ruff
|
||||
uv run ruff format .
|
||||
@@ -230,13 +251,15 @@ uv run ruff format .
|
||||
uv run ruff check . --fix
|
||||
```
|
||||
|
||||
#### Type Checking
|
||||
#### Backend Type Checking
|
||||
|
||||
```bash
|
||||
# Run Pyright type checker
|
||||
uv run pyright
|
||||
```
|
||||
|
||||
#### Testing
|
||||
#### Backend Testing
|
||||
|
||||
```bash
|
||||
# Run unit tests
|
||||
uv run pytest
|
||||
@@ -251,7 +274,8 @@ uv run pytest tests/integration/
|
||||
uv run xdoctest src/
|
||||
```
|
||||
|
||||
#### Documentation
|
||||
#### Backend Documentation
|
||||
|
||||
```bash
|
||||
# Check docstring style
|
||||
uv run pydoclint --config=pyproject.toml src/
|
||||
@@ -261,7 +285,8 @@ uv run pydoclint --config=pyproject.toml src/
|
||||
|
||||
Navigate to the `frontend/` directory for all frontend commands.
|
||||
|
||||
#### Code Formatting
|
||||
#### Frontend Code Formatting
|
||||
|
||||
```bash
|
||||
# Format code with Prettier
|
||||
yarn format
|
||||
@@ -271,6 +296,7 @@ yarn format:check
|
||||
```
|
||||
|
||||
#### Linting
|
||||
|
||||
```bash
|
||||
# Run ESLint
|
||||
yarn lint
|
||||
@@ -282,13 +308,15 @@ yarn lint:fix
|
||||
yarn lint:tsdoc
|
||||
```
|
||||
|
||||
#### Type Checking
|
||||
#### Frontend Type Checking
|
||||
|
||||
```bash
|
||||
# Run Vue TypeScript compiler
|
||||
yarn type-check
|
||||
```
|
||||
|
||||
#### Testing
|
||||
#### Frontend Testing
|
||||
|
||||
```bash
|
||||
# Run unit tests
|
||||
yarn test
|
||||
@@ -341,22 +369,26 @@ The CI/CD pipeline uses a **multi-stage build architecture** for optimal perform
|
||||
- **Stage 2**: Build complete image (project code and dependencies) - **rebuilt every time**
|
||||
|
||||
Pipeline triggers:
|
||||
|
||||
- Push to any branch
|
||||
- Pull requests to `main` or `develop`
|
||||
|
||||
### Multi-Stage Build Benefits ✅ **VALIDATED SUCCESSFUL**
|
||||
|
||||
**Performance Gains**:
|
||||
|
||||
- **85% build time improvement**: 3-5 minutes (down from 15-25 minutes)
|
||||
- Base image cached when `Dockerfile.cicd-base` unchanged (~95% of runs)
|
||||
- **100% success rate** achieved with optimized dependency management
|
||||
- Raspberry Pi 4GB workers handle builds efficiently with resource optimization
|
||||
|
||||
**Architecture**:
|
||||
|
||||
- `cicd-base:latest` - System dependencies (Python 3.14, Node.js 24, build tools, pre-installed dev packages)
|
||||
- `cicd:latest` - Complete environment (project code + optimized dependency installation)
|
||||
|
||||
**Recent Optimizations** (November 2025):
|
||||
|
||||
- **Dependency-first build pattern** prevents cache invalidation on code changes
|
||||
- **Yarn PnP state regeneration** ensures reliable frontend builds
|
||||
- **Network-resilient E2E testing** with simplified Docker operations
|
||||
|
||||
@@ -29,7 +29,7 @@ RUN --mount=type=secret,id=ssh_private_key \
|
||||
## 🏗️ CI/CD Pipeline Implementation
|
||||
|
||||
### Gitea Actions Workflow
|
||||
The `.gitea/workflows/cicd.yml` file now uses:
|
||||
The CI workflow files under `.gitea/workflows/` now use:
|
||||
|
||||
1. **Docker BuildKit Enabled**
|
||||
```yaml
|
||||
|
||||
56
docs/adr/ADR003-deployable_runtime_image_contract.md
Normal file
56
docs/adr/ADR003-deployable_runtime_image_contract.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# ADR003: Deployable Runtime Image Contract Boundaries
|
||||
|
||||
- Status: Accepted
|
||||
- Date: 2026-06-19
|
||||
|
||||
## Context
|
||||
|
||||
The repository has both deployable runtime artifacts and CI/development tooling
|
||||
artifacts. Without an explicit boundary, non-runtime concerns can drift into
|
||||
deployable images, making runtime behavior less predictable and increasing
|
||||
artifact complexity.
|
||||
|
||||
Issue PP-58 requires a clear, approved contract for minimal backend and frontend
|
||||
deployable images, including health and startup behavior and explicit exclusion
|
||||
of non-runtime tooling classes.
|
||||
|
||||
## Decision
|
||||
|
||||
Adopt a canonical deployable runtime contract at:
|
||||
|
||||
- `docs/DEPLOYABLE_RUNTIME_CONTRACT.md`
|
||||
|
||||
The contract defines, for backend and frontend deployable images:
|
||||
|
||||
1. Runtime artifact boundaries (final image intent and payload).
|
||||
2. Runtime entrypoint and exposed ports.
|
||||
3. Runtime health and startup behavior expectations.
|
||||
4. Runtime environment variable contract.
|
||||
5. Disallowed non-runtime tooling classes in final deployable images.
|
||||
|
||||
Scope guardrails for PP-58:
|
||||
|
||||
- Documentation and architectural decision codification only.
|
||||
- No CI workflow rewiring in this issue.
|
||||
- No test execution redesign in this issue.
|
||||
- Enforcement automation deferred to follow-up work under epic #66.
|
||||
|
||||
## Consequences
|
||||
|
||||
Positive:
|
||||
|
||||
- Deployable image intent is explicit and auditable.
|
||||
- Future tickets can implement automated checks against a stable policy.
|
||||
- Reduced ambiguity between runtime artifacts and CI/development environments.
|
||||
|
||||
Negative:
|
||||
|
||||
- Requires ongoing documentation maintenance when runtime contracts evolve.
|
||||
- Drift can still occur if future changes bypass policy review.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Rely on Dockerfiles only as implicit contract.
|
||||
- Rejected: too easy for intent drift and inconsistent interpretation.
|
||||
- Enforce contract immediately in CI without documentation-first baseline.
|
||||
- Rejected: increases implementation risk without agreed policy language.
|
||||
Reference in New Issue
Block a user