-
Notifications
You must be signed in to change notification settings - Fork 0
152 lines (132 loc) · 6.99 KB
/
Copy pathci-python-zensical.yml
File metadata and controls
152 lines (132 loc) · 6.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# ============================================================
# .github/workflows/ci-python-zensical.yml (ALL-PY-SRC-REPOS)
# ============================================================
# Updated: 2026-09-25
#
# WHY: Continuous Integration (CI) ensures
# Python correctness, testing, and documentation builds
# before merging changes into the main branch.
# REQ: CI SHOULD NOT introduce rules that are not reproducible locally.
# OBS: CI validates only; it SHOULD NOT make file changes or deploy documentation.
# OBS: yaml lint config lives at .github/.yamllint.yml (not in the repo root).
# Name shown in the GitHub repo Actions tab.
name: CI (Python + Zensical)
on:
push:
branches: [main] # WHY: Validate on every push to GitHub branch `main`.
pull_request:
branches: [main] # WHY: Validate pull requests before merge.
workflow_dispatch: # WHY: Allow manual trigger from Actions tab.
permissions: # WHY: Least privilege; this job only needs repository read access.
contents: read # WHY: CI validates repository contents and never pushes changes.
concurrency:
group: ci-python-zensical-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# WHY: Cancel stale CI runs for the same workflow/ref when a newer commit arrives.
# OBS: Different PRs and branches still run independently.
env:
PYTHONUNBUFFERED: "1" # WHY: Real-time log output in CI.
PYTHONIOENCODING: "utf-8" # WHY: Consistent encoding across platforms.
UV_FROZEN: "1" # WHY: CI MUST use the committed lockfile without updating it.
jobs:
ci:
name: Repo checks and check Zensical build
runs-on: ubuntu-latest # WHY: Linux matches most production deployments.
timeout-minutes: 30 # WHY: Fail fast if a step hangs unexpectedly.
env:
UV_PYTHON: "3.14" # WHY: Pin Python version for all steps in this job.
steps:
# ============================================================
# A) ASSEMBLE: Checkout code and set up environment
# ============================================================
- name: A1) Checkout repository code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
# WHY: Required so all subsequent steps can access repo files.
- name: A2) Install uv (with caching)
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
# WHY: Cache uv-managed artifacts for faster subsequent runs.
cache-dependency-glob: "uv.lock"
# WHY: Invalidate dependency cache when the committed lockfile changes.
- name: A3) Install project Python
run: uv python install
# WHY: Ensures the Python version declared in .python-version is available.
# OBS: uv manages the interpreter locally in the CI environment.
- name: A4) Install dependencies from committed lockfile
run: uv sync --frozen
# WHY: Install all configured dependency groups exactly from uv.lock.
# REQ: CI MUST fail rather than update a stale or incompatible lockfile.
- name: A5) Show tool versions
run: |
uv --version
uv run python --version
uv run python -m ruff --version
uv run ty --version
if [ -f "zensical.toml" ]; then
uv run python -m zensical --version
fi
uv run --no-sync prek --version 2>/dev/null \
|| uv run --no-sync pre-commit --version 2>/dev/null \
|| echo "No hook runner installed"
# WHY: Version output makes CI logs easier to debug when tools change.
- name: A6) Run repository hooks on all files (prek or pre-commit)
run: |
if uv run --no-sync prek --version > /dev/null 2>&1; then
echo "Hook runner: prek"
uv run --no-sync prek run --all-files
elif uv run --no-sync pre-commit --version > /dev/null 2>&1; then
echo "Hook runner: pre-commit"
uv run --no-sync pre-commit run --all-files
else
echo "::error::Neither prek nor pre-commit is installed in the project environment. Add one to the dev dependency group in pyproject.toml."
exit 1
fi
# WHY: Run the same repository hygiene checks used locally.
# OBS: Prefers prek when both are installed; both read .pre-commit-config.yaml.
# OBS: The runner is detected in the project environment installed by A4,
# so CI uses whichever runner the repository declares.
# OBS: --no-sync reuses the environment from A4 without re-syncing.
# OBS: A clean commit should already pass without file changes.
# OBS: Autofixing hooks may modify the ephemeral CI checkout.
# If they do, the hook runner fails because the committed files
# were not already in their required final state.
# FIX: Run the same hook runner locally, review any autofixes, fix
# remaining issues by hand, then git add, commit, and push again.
# ============================================================
# B) BASELINE CHECKS: Tools not covered by pre-commit
# ============================================================
- name: B1) Validate pyproject.toml schema
run: uvx "validate-pyproject[all]" pyproject.toml
# WHY: Catch malformed project metadata before downstream CI or release work.
- name: B2) Run ty type checker
run: uv run ty check
continue-on-error: true
# WHY: Catch type errors that Ruff does not check.
# OBS: ty uses the installed project environment for analysis.
# OBS: Not included in pre-commit because it requires the full environment.
# OBS: continue-on-error keeps type checking non-blocking in this workflow.
# ============================================================
# C) COVERAGE & TESTING: Python tests
# ============================================================
- name: C1) Run pytest
run: uv run python -m pytest
# WHY: Confirm all Python tests pass in a clean CI environment.
# OBS: pytest config lives in pyproject.toml [tool.pytest.ini_options].
# OBS: Not included in pre-commit because tests can be slow.
# ============================================================
# D) DOCS: Build documentation without deployment
# ============================================================
- name: D1) Build documentation with Zensical
run: |
if [ -f "zensical.toml" ]; then
uv run python -m zensical build
else
echo "No zensical.toml found; skipping docs build."
fi
# WHY: Confirm documentation builds without errors.
# OBS: This workflow builds only; deployment is handled separately.
# OBS: Conditional on zensical.toml so this workflow remains reusable
# across src-layout Python repos that do not require documentation.