Skip to content
This repository was archived by the owner on Jun 28, 2026. It is now read-only.

Commit 2cbcb3a

Browse files
committed
Add checklist to README.md 📝
1 parent beee11f commit 2cbcb3a

1 file changed

Lines changed: 202 additions & 37 deletions

File tree

README.md

Lines changed: 202 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,15 @@ Both models share the same configuration system, logging baseline, metrics, heal
3939
- Pre-commit (mypy, ruff, YAML/TOML/JSON checks)
4040
- GitHub Actions: lint + test + docker build + smoke test
4141
- Dockerfile with build cache support
42+
- Image name for GHCR is derived automatically from the repository name and lowercased in the CI workflow.
4243

4344
---
4445

4546
# 2. When to use Sync vs. Async?
4647

4748
### Use **sync** when:
4849
- You have a simple worker loop ("do something every X seconds").
49-
- You use blocking libraries (e.g. `requests`, psycopg2) and don’t need concurrency.
50+
- You use blocking libraries (e.g. `requests`, psycopg2) and don’t need high concurrency.
5051
- You want minimal complexity.
5152

5253
### Use **async** when:
@@ -60,8 +61,7 @@ The boilerplate lets you choose the right model per project without changing the
6061

6162
# 3. Directory Structure
6263

63-
```
64-
64+
```text
6565
src/app
6666
6767
├── config.py # unified configuration loading
@@ -74,26 +74,25 @@ src/app
7474
7575
├── main_sync.py # entry point for sync workers
7676
└── main_async.py # entry point for async workers
77-
78-
```
77+
````
7978
8079
---
8180
8281
# 4. Running a Synchronous Service
8382
8483
Sync entry point is installed as:
8584
86-
```
87-
85+
```bash
8886
poetry run app-sync
87+
```
8988

90-
````
91-
92-
or via Docker:
89+
or via Docker Compose:
9390

9491
```yaml
95-
command: ["poetry", "run", "app-sync"]
96-
````
92+
services:
93+
app:
94+
command: ["poetry", "run", "app-sync"]
95+
```
9796
9897
The sync service uses a classic blocking loop:
9998
@@ -110,14 +109,16 @@ service.start()
110109

111110
Async entry point is:
112111

113-
```
112+
```bash
114113
poetry run app-async
115114
```
116115

117-
or via Docker:
116+
or via Docker Compose:
118117

119118
```yaml
120-
command: ["poetry", "run", "app-async"]
119+
services:
120+
app:
121+
command: ["poetry", "run", "app-async"]
121122
```
122123
123124
The async service runs on asyncio:
@@ -129,7 +130,7 @@ service = AsyncService(config)
129130
await service.start()
130131
```
131132

132-
### Perfect for:
133+
### Typical use cases:
133134

134135
* Discord bots
135136
* Async HTTP workers
@@ -142,7 +143,7 @@ await service.start()
142143

143144
Every container can be validated via:
144145

145-
```
146+
```bash
146147
poetry run app-health
147148
```
148149

@@ -153,22 +154,35 @@ Exit codes:
153154

154155
This is the recommended Docker healthcheck command.
155156

157+
Example Docker Compose:
158+
159+
```yaml
160+
healthcheck:
161+
test: ["CMD", "poetry", "run", "app-health"]
162+
interval: 30s
163+
timeout: 5s
164+
retries: 3
165+
start_period: 10s
166+
```
167+
156168
---
157169
158170
# 7. Metrics
159171
160172
If enabled (`metrics_enabled=true`), the service exposes:
161173

162-
```
174+
```text
163175
http://localhost:<metrics_port>/metrics
164176
```
165177

166-
Baseline metrics give you:
178+
Baseline metrics provide:
179+
180+
* service liveness (`app_up`)
181+
* total loop iterations (`app_iterations_total`)
182+
* timestamp of last successful iteration (`app_last_iteration_timestamp_seconds`)
183+
* build information (`app_build_info{version,commit,env}`)
167184

168-
* uptime
169-
* iteration frequency
170-
* last-loop freshness
171-
* deployment info (version, commit, env)
185+
These can be scraped by Prometheus and visualized in Grafana.
172186

173187
---
174188

@@ -178,79 +192,230 @@ All logs follow the unified JSON format:
178192

179193
```json
180194
{
181-
"ts": "...",
195+
"ts": "2025-11-22T20:01:23.123456Z",
182196
"level": "info",
183197
"service": "python-service",
184-
"logger": "app.main",
198+
"logger": "app.main_sync",
185199
"instance": "myhost",
186200
"env": "dev",
187201
"msg": "Service startup succeeded",
188202
"event": "startup_success"
189203
}
190204
```
191205

192-
Errors include stack traces automatically.
206+
* All logs are single-line JSON on stdout.
207+
* Errors automatically include stack traces (`stack`) and `exception_type`.
208+
* Lifecycle events are logged explicitly: `startup_success`, `shutting_down`, `crashed`.
209+
210+
This makes ingestion into Loki, Elastic, or any JSON-aware log pipeline straightforward.
193211

194212
---
195213

196214
# 9. Configuration
197215

198-
A `.env` file can override or supplement configuration:
216+
Configuration can come from:
217+
218+
1. Environment variables
219+
2. YAML file (`config.yml`)
220+
3. Defaults
221+
222+
### Example `.env` (see `.env.example`)
199223

200224
```env
225+
# Core service configuration
201226
APP_SERVICE_NAME=python-service
202227
APP_ENV=dev
203228
APP_LOG_LEVEL=INFO
229+
230+
# Build / version metadata (typically injected during CI build)
231+
APP_VERSION=0.1.0
232+
APP_COMMIT=local-dev
233+
234+
# Optional: override instance ID
235+
APP_INSTANCE=local-dev
236+
237+
# Optional: YAML config path (relative to working directory)
238+
APP_CONFIG_PATH=config.yml
239+
240+
# Metrics configuration
204241
APP_METRICS_ENABLED=1
205242
APP_METRICS_PORT=8000
243+
244+
# Main loop configuration
206245
APP_LOOP_SLEEP_SECONDS=5.0
207246
```
208247

209-
Optionally load YAML config:
248+
### Example `config.yml` (see `config.yml.example`)
210249

211250
```yaml
212251
service_name: python-service
252+
env: dev
253+
log_level: INFO
254+
213255
metrics_enabled: true
214256
metrics_port: 8000
257+
215258
loop_sleep_seconds: 5.0
259+
260+
version: 0.1.0
261+
commit: local-dev
216262
```
217263

264+
Environment variables override YAML values; YAML overrides hardcoded defaults.
265+
218266
---
219267

220268
# 10. Tests
221269

222-
Run the full suite:
270+
Run the full test suite:
223271

224-
```
272+
```bash
225273
poetry run pytest
226274
```
227275

228-
Coverage is preconfigured.
276+
Coverage is configured via `coverage.ini` / `[tool.coverage.*]` in `pyproject.toml`.
277+
Pre-commit hooks ensure:
278+
279+
* type checking (mypy)
280+
* linting/formatting (ruff)
281+
* basic file hygiene (YAML/TOML/JSON + whitespace checks)
282+
* an optional GUI-based repo state check before committing.
229283

230284
---
231285

232286
# 11. Docker
233287

234-
Build and run:
288+
Build and run locally:
235289

236-
```
290+
```bash
237291
docker build -t python-service .
238292
docker run --rm -p 8000:8000 python-service
239293
```
240294

241-
CI automatically:
295+
The GitHub Actions workflow:
242296

297+
* installs dependencies
243298
* runs tests
244-
* builds/pushes images
245-
* performs smoke tests
299+
* builds the Docker image
300+
* pushes to GHCR
301+
* performs a smoke test via `/metrics`
302+
303+
The image name is derived from the repository owner and name in CI, lowercased:
304+
305+
* `ghcr.io/<owner>/<repo>` → e.g. `ghcr.io/hartmannlight/python-boilerplate`
306+
307+
You can adjust the image name logic in `.github/workflows/build.yml` if needed.
246308

247309
---
248310

249311
# 12. Summary
250312

251313
This boilerplate lets you start any kind of Python backend quickly:
252314

253-
* **Sync workers** for polling, simple loops, data processing
315+
* **Sync workers** for polling, simple loops, and data processing
254316
* **Async services** for event-driven, high-concurrency environments
255317

256318
Everything else (logging, health, metrics, config, CI, Docker) is unified and ready to use.
319+
320+
---
321+
322+
# 13. Checklist: What you should change when using this boilerplate
323+
324+
When you create a new project from this boilerplate, you should review and adapt at least the following items.
325+
326+
### 13.1 Project metadata
327+
328+
* In `pyproject.toml`:
329+
330+
* `[tool.poetry].name` → your project/package name
331+
* `[tool.poetry].description` → short description of your service
332+
* `[tool.poetry].authors` → your name/email
333+
* `[tool.poetry].version` → initial version (e.g. `0.1.0` for your project)
334+
335+
### 13.2 README and docs
336+
337+
* Update the title and description in `README.md` to match your service.
338+
* If you keep `docs/observability-baseline.md`, you can:
339+
340+
* either reference it from the README
341+
* or adapt it to your organization’s standards.
342+
343+
### 13.3 Service entry point
344+
345+
Decide which runtime model you actually need:
346+
347+
* Sync:
348+
349+
* Use `app-sync` as the main entry point (`poetry run app-sync`).
350+
* In Docker/Docker Compose, use `["poetry", "run", "app-sync"]`.
351+
* Async:
352+
353+
* Use `app-async` (`poetry run app-async`).
354+
* In Docker/Docker Compose, use `["poetry", "run", "app-async"]`.
355+
356+
Optional cleanups:
357+
358+
* If your project will **never** use async, you can remove `service_async.py` and `main_async.py` and the `app-async` script.
359+
* If your project will **only** be async, you can remove `service_sync.py` and `main_sync.py` and the `app-sync` script.
360+
361+
### 13.4 Configuration defaults
362+
363+
Check and adapt:
364+
365+
* `.env.example`
366+
367+
* `APP_SERVICE_NAME` → canonical name of your service
368+
* `APP_ENV` → default environment (`dev`, `stg`, `prod`, etc.)
369+
* `APP_METRICS_PORT` → port that fits your stack
370+
* `APP_LOOP_SLEEP_SECONDS` → reasonable default for your loop
371+
* `config.yml.example`
372+
373+
* `service_name`, `env`, `log_level` as appropriate
374+
* `metrics_enabled`, `metrics_port`
375+
* `loop_sleep_seconds`
376+
* `version` / `commit` (cosmetic; usually overwritten by CI)
377+
378+
### 13.5 Docker & CI
379+
380+
* `.github/workflows/build.yml`:
381+
382+
* The image name is derived automatically from GitHub repository owner and name, lowercased.
383+
* If you want to push to a different registry or org (e.g. Docker Hub), adjust the `Prepare image name` step.
384+
385+
* `docker-compose.yml` / `docker-compose.override.yml`:
386+
387+
* Ensure `image:` and `command:` match your desired entry point and registry.
388+
* Update `container_name:` if you care about the container name.
389+
390+
### 13.6 License
391+
392+
* The template assumes `Apache-2.0` (see workflow labels and usually a `LICENSE` file).
393+
* If you use a different license:
394+
395+
* Update or replace `LICENSE`.
396+
* Adjust the `org.opencontainers.image.licenses` label in `.github/workflows/build.yml` if needed.
397+
398+
### 13.7 Repo-state check script (optional)
399+
400+
* `scripts/repo_state_check_gui.py`:
401+
402+
* This is optional tooling to show a small GUI before committing.
403+
* You can:
404+
405+
* Adapt the checks (e.g. enforce email domain, branch naming),
406+
* Or remove this hook from `.pre-commit-config.yaml` if you don’t want interactive confirmation dialogs.
407+
408+
### 13.8 Observability integration
409+
410+
* Ensure your logging/metrics/health expectations match:
411+
412+
* If you add HTTP endpoints, consider:
413+
414+
* `/healthz` and `/readyz`
415+
* HTTP metrics (`app_requests_total`, etc.)
416+
* If you use Prometheus/Grafana:
417+
418+
* Wire the metrics endpoint into your scrape config.
419+
* Optionally add dashboards around `app_up`, `app_iterations_total`, and `app_build_info`.
420+
421+
Once you have gone through this checklist and adapted these points, the boilerplate should behave as a first-class, project-specific service template in your environment.

0 commit comments

Comments
 (0)