Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ env:
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 5
permissions:
contents: read
Expand Down Expand Up @@ -72,7 +72,7 @@ jobs:
name: Backend checks
needs: changes
if: needs.changes.outputs.all == 'true' || needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 15
services:
postgres:
Expand Down Expand Up @@ -122,7 +122,7 @@ jobs:
name: Frontend checks
needs: changes
if: needs.changes.outputs.all == 'true' || needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -145,7 +145,7 @@ jobs:
pre-commit:
name: Repository hygiene
needs: changes
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -162,15 +162,15 @@ jobs:
- name: pre-commit (yaml, eol, large files, pyupgrade, bandit, uv lock, pyproject)
env:
SKIP: ruff-check,ruff-format,ty
run: uvx --python 3.14 pre-commit@4.6.2 run --all-files --show-diff-on-failure
run: uvx --python 3.15 pre-commit@4.7.0 run --all-files --show-diff-on-failure

image:
name: Image build and smoke test
needs: changes
if: >-
needs.changes.outputs.all == 'true' || needs.changes.outputs.backend == 'true' ||
needs.changes.outputs.frontend == 'true' || needs.changes.outputs.infra == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -244,7 +244,7 @@ jobs:
name: CI gate
if: always()
needs: [changes, backend, frontend, pre-commit, image]
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 5
steps:
- uses: re-actors/alls-green@b5b5b37504aa4183270bd3d855c52a67f212be35 # v1.3.0
Expand All @@ -256,7 +256,7 @@ jobs:
name: Release
needs: gate
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 10
permissions:
contents: write
Expand Down Expand Up @@ -368,7 +368,7 @@ jobs:
needs: release
# Off until the registry is configured (repository variables YC_REGISTRY_ID and IMAGE_NAME).
if: needs.release.outputs.released == 'true' && vars.YC_REGISTRY_ID != '' && vars.IMAGE_NAME != ''
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 30
permissions:
contents: read
Expand Down Expand Up @@ -476,7 +476,7 @@ jobs:
needs: [release, publish]
# Off until the server is configured (repository variable DEPLOY_ENABLED=true).
if: needs.publish.result == 'success' && vars.DEPLOY_ENABLED == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 15
environment: production
concurrency:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-title.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:
jobs:
conventional-title:
name: Conventional PR title
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 5
steps:
# Reads only the PR title through the API; never checks out PR code.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rollback.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
rollback:
name: Redeploy ${{ inputs.tag }}
if: vars.DEPLOY_ENABLED == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 15
permissions:
contents: read
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
secrets:
name: Secret scan (gitleaks)
if: github.event_name != 'schedule'
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -51,7 +51,7 @@ jobs:
dependencies:
name: Dependency and config scan (trivy)
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
runs-on: ubuntu-26.04
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24
26
12 changes: 7 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
exclude: backend/migrations/.*

# Hooks parse backend code, which uses Python 3.14 syntax.
# Hooks parse backend code, which uses Python 3.15 syntax.
default_language_version:
python: python3.14
python: python3.15

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-yaml
# pnpm 12 writes pnpm-lock.yaml as several YAML documents.
args: [--allow-multiple-documents]
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-added-large-files

- repo: https://github.com/asottile/pyupgrade
rev: v3.21.2
rev: v3.22.0
hooks:
- id: pyupgrade

Expand All @@ -33,14 +35,14 @@ repos:
args: ["--config", "backend/pyproject.toml"]

- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.12.5
rev: 0.13.5
hooks:
- id: uv-lock
files: backend/pyproject.toml
args: ["--locked", "--project", "backend"]

- repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.28.0
rev: v2.30.3
hooks:
- id: pyproject-fmt
args: ["--keep-full-version", "--indent", "4"]
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.14
3.15
21 changes: 12 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
# Base images, pinned here once (Dependabot updates these FROM lines). Builder and runtime share
# the Python image so the venv's interpreter path matches; Node (builder and the copied binary)
# uses the same Debian release, so the same glibc, as the runtime.
FROM python:3.14.9-slim-trixie AS python
FROM node:24.19.0-trixie-slim AS node
FROM python:3.15.1-slim-trixie AS python
FROM node:26.12.1-trixie-slim AS node
FROM caddy:2.11.7-alpine AS caddy
FROM ghcr.io/astral-sh/uv:0.13.5 AS uv

Expand Down Expand Up @@ -54,11 +54,11 @@ RUN --mount=type=cache,target=/root/.cache/uv \

# ===== Frontend: Nuxt build output in /app/frontend/.output =====
FROM node AS frontend-builder
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
NUXT_TELEMETRY_DISABLED=1
RUN corepack enable
ENV NUXT_TELEMETRY_DISABLED=1
WORKDIR /app/frontend
COPY frontend/package.json frontend/pnpm-lock.yaml ./
COPY frontend/package.json frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml ./
# Node 26 no longer bundles corepack: install the pnpm version pinned in `packageManager`.
RUN npm install --global --no-fund --no-audit "$(node -p "require('./package.json').packageManager")"
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
pnpm fetch --frozen-lockfile --store-dir /pnpm/store
COPY frontend/ ./
Expand All @@ -79,9 +79,12 @@ COPY --from=s6 /s6-root/ /
COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=caddy /usr/bin/caddy /usr/local/bin/caddy

# Node needs only glibc and libstdc++, both in the slim Python image; fail the build if that changes.
# s6-overlay runs as the unprivileged user: /run holds its supervision tree.
RUN node --version && caddy version \
# Node 26 also links libatomic, which the slim Python image lacks; the version check fails the build if another
# shared library goes missing. s6-overlay runs as the unprivileged user: /run holds its supervision tree.
RUN apt-get update \
&& apt-get install -y --no-install-recommends libatomic1 \
&& rm -rf /var/lib/apt/lists/* \
&& node --version && caddy version \
&& chown app:app /run

COPY --from=backend-builder /app/backend /app/backend
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ What every product inherits: [docs/template-core.md](docs/template-core.md). Wha

| Layer | Technologies |
|---|---|
| Backend | Python 3.14, FastAPI, Tortoise ORM, fastapi-ronin, Taskiq, Redis, PostgreSQL |
| Backend | Python 3.15, FastAPI, Tortoise ORM, fastapi-ronin, Taskiq, Redis, PostgreSQL |
| Frontend | Nuxt 4, Vue 3, Pinia, shadcn-vue / Reka UI, Tailwind CSS |
| Infra | One Docker image (s6-overlay: Caddy, uvicorn, Nuxt, Taskiq), Docker Compose, GitHub Actions, Makefile |

Expand Down Expand Up @@ -55,7 +55,7 @@ Generates an organization-scoped CRUD entity end to end — backend domain (mode

### 1. Environment

Requirements: Python 3.14 (`.python-version`) with [uv](https://docs.astral.sh/uv/), Node 24 (`.nvmrc`; `^22.18 || ^24.11` supported) and pnpm from `packageManager` in `frontend/package.json` (`corepack enable`).
Requirements: Python 3.15 (`.python-version`) with [uv](https://docs.astral.sh/uv/) 0.13+, Node 26 (`.nvmrc`) and pnpm 12 from `packageManager` in `frontend/package.json` (`npm install -g pnpm@12`; Node 26 no longer ships corepack).

```bash
cp .env.example .env
Expand Down
8 changes: 4 additions & 4 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ name = "fastapi-nuxt-template"
version = "0.8.0"
description = "Full-stack starter template with FastAPI and Nuxt"
readme = "README.md"
requires-python = ">=3.14"
requires-python = ">=3.15"
classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
]
dependencies = [
"asyncpg>=0.32.0",
Expand Down Expand Up @@ -44,7 +44,7 @@ dev = [
]

[tool.ruff]
target-version = "py314"
target-version = "py315"
line-length = 120
indent-width = 4
extend-exclude = [ "migrations" ]
Expand All @@ -70,7 +70,7 @@ ini_options.python_files = [ "test_*.py" ]
ini_options.asyncio_mode = "auto"

[tool.pyupgrade]
py314-plus = true
py315-plus = true

[tool.tortoise]
tortoise_orm = "app.core.database.TORTOISE_ORM"
Loading
Loading