baobab-auth-security 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- baobab_auth_security-0.1.0/.cursor/rules/000-core.mdc +23 -0
- baobab_auth_security-0.1.0/.editorconfig +18 -0
- baobab_auth_security-0.1.0/.env.example +9 -0
- baobab_auth_security-0.1.0/.gitattributes +18 -0
- baobab_auth_security-0.1.0/.github/ISSUE_TEMPLATE/01-user-story.yml +32 -0
- baobab_auth_security-0.1.0/.github/ISSUE_TEMPLATE/02-feature.yml +32 -0
- baobab_auth_security-0.1.0/.github/ISSUE_TEMPLATE/03-task.yml +34 -0
- baobab_auth_security-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- baobab_auth_security-0.1.0/.github/dependabot.yml +20 -0
- baobab_auth_security-0.1.0/.github/pull_request_template.md +21 -0
- baobab_auth_security-0.1.0/.github/workflows/ci.yml +145 -0
- baobab_auth_security-0.1.0/.github/workflows/release.yml +114 -0
- baobab_auth_security-0.1.0/.gitignore +52 -0
- baobab_auth_security-0.1.0/.pre-commit-config.yaml +46 -0
- baobab_auth_security-0.1.0/AGENTS.md +109 -0
- baobab_auth_security-0.1.0/CHANGELOG.md +33 -0
- baobab_auth_security-0.1.0/CLAUDE.md +15 -0
- baobab_auth_security-0.1.0/CONTRIBUTING.md +36 -0
- baobab_auth_security-0.1.0/LICENSE +21 -0
- baobab_auth_security-0.1.0/Makefile +43 -0
- baobab_auth_security-0.1.0/PKG-INFO +257 -0
- baobab_auth_security-0.1.0/README.md +196 -0
- baobab_auth_security-0.1.0/SECURITY.md +27 -0
- baobab_auth_security-0.1.0/docs/_static/.gitkeep +0 -0
- baobab_auth_security-0.1.0/docs/api/index.rst +10 -0
- baobab_auth_security-0.1.0/docs/conf.py +30 -0
- baobab_auth_security-0.1.0/docs/guides/how-to/ajouter-une-classe.rst +25 -0
- baobab_auth_security-0.1.0/docs/guides/how-to/architecture-ecosysteme.rst +52 -0
- baobab_auth_security-0.1.0/docs/guides/how-to/hash-mot-de-passe.rst +46 -0
- baobab_auth_security-0.1.0/docs/guides/how-to/jwks.rst +42 -0
- baobab_auth_security-0.1.0/docs/guides/how-to/jwt.rst +60 -0
- baobab_auth_security-0.1.0/docs/guides/how-to/refresh-tokens.rst +42 -0
- baobab_auth_security-0.1.0/docs/guides/how-to/revocation.rst +51 -0
- baobab_auth_security-0.1.0/docs/guides/how-to/rotation-cles.rst +47 -0
- baobab_auth_security-0.1.0/docs/guides/index.rst +26 -0
- baobab_auth_security-0.1.0/docs/guides/tutorials/premiers-pas.rst +26 -0
- baobab_auth_security-0.1.0/docs/index.rst +23 -0
- baobab_auth_security-0.1.0/docs/specifications/cahier-des-charges/README.md +13 -0
- baobab_auth_security-0.1.0/docs/specifications/cahier-des-charges/cahier_des_charges.md +2166 -0
- baobab_auth_security-0.1.0/docs/specifications/glossary.rst +16 -0
- baobab_auth_security-0.1.0/docs/specifications/index.rst +36 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-001-socle/FEAT-001.1-structure-package.rst +29 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-001-socle/index.rst +32 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-002-mots-de-passe/FEAT-002.1-modeles-password.rst +25 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-002-mots-de-passe/FEAT-002.2-argon2-password-hasher.rst +27 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-002-mots-de-passe/index.rst +32 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-003-refresh-tokens/FEAT-003.1-refresh-token-generator.rst +23 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-003-refresh-tokens/FEAT-003.2-refresh-token-hasher.rst +23 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-003-refresh-tokens/index.rst +32 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-004-cles/FEAT-004.1-modeles-cles.rst +23 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-004-cles/FEAT-004.2-key-providers.rst +25 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-004-cles/index.rst +32 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-005-jwks/FEAT-005.1-jwks-provider.rst +25 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-005-jwks/index.rst +30 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-006-jwt/FEAT-006.1-modeles-jwt.rst +23 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-006-jwt/FEAT-006.2-jwt-encoder-decoder.rst +24 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-006-jwt/FEAT-006.3-jwt-validator.rst +24 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-006-jwt/index.rst +33 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-007-rotation-cles/FEAT-007.1-key-rotation-service.rst +24 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-007-rotation-cles/index.rst +31 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-008-revocation/FEAT-008.1-token-revocation-checker.rst +24 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-008-revocation/index.rst +30 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-009-integration-core/FEAT-009.1-core-adapters.rst +24 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-009-integration-core/index.rst +31 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-010-documentation/FEAT-010.1-docs-et-exemples.rst +26 -0
- baobab_auth_security-0.1.0/docs/specifications/us/US-010-documentation/index.rst +31 -0
- baobab_auth_security-0.1.0/docs/specifications/us/index.rst +16 -0
- baobab_auth_security-0.1.0/docs/workflow/README.md +66 -0
- baobab_auth_security-0.1.0/docs/workflow/SETUP.md +109 -0
- baobab_auth_security-0.1.0/docs/workflow/gates.md +38 -0
- baobab_auth_security-0.1.0/docs/workflow/handoff.md +53 -0
- baobab_auth_security-0.1.0/docs/workflow/prompts/init.md +42 -0
- baobab_auth_security-0.1.0/docs/workflow/prompts/orchestration.md +25 -0
- baobab_auth_security-0.1.0/docs/workflow/roles/00-orchestrateur.md +24 -0
- baobab_auth_security-0.1.0/docs/workflow/roles/01-product-owner.md +22 -0
- baobab_auth_security-0.1.0/docs/workflow/roles/02-architecte.md +22 -0
- baobab_auth_security-0.1.0/docs/workflow/roles/03-developpeur.md +23 -0
- baobab_auth_security-0.1.0/docs/workflow/roles/04-relecteur.md +24 -0
- baobab_auth_security-0.1.0/docs/workflow/roles/05-securite.md +26 -0
- baobab_auth_security-0.1.0/docs/workflow/roles/06-release-manager.md +27 -0
- baobab_auth_security-0.1.0/docs/workflow/roles/07-support.md +22 -0
- baobab_auth_security-0.1.0/examples/generate_jwks.py +26 -0
- baobab_auth_security-0.1.0/examples/generate_token.py +40 -0
- baobab_auth_security-0.1.0/examples/rotate_key.py +48 -0
- baobab_auth_security-0.1.0/examples/validate_token.py +42 -0
- baobab_auth_security-0.1.0/examples/verify_password.py +22 -0
- baobab_auth_security-0.1.0/pyproject.toml +127 -0
- baobab_auth_security-0.1.0/scripts/bootstrap_github_issues.py +214 -0
- baobab_auth_security-0.1.0/scripts/setup_github.sh +58 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/__init__.py +10 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/config/__init__.py +7 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/config/jwt_security_settings.py +44 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/config/key_settings.py +32 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/config/refresh_token_settings.py +34 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/exceptions/__init__.py +5 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/exceptions/baobab_auth_security_error.py +10 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/integration/__init__.py +17 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/integration/claims_mapper.py +48 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/integration/core_integration_gaps.py +25 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/integration/core_password_hasher_adapter.py +50 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/integration/core_token_provider_adapter.py +25 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/__init__.py +51 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/exceptions.py +80 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/in_memory_key_provider.py +131 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/jwk.py +67 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/jwks.py +24 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/jwks_provider.py +87 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/key_algorithm.py +14 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/key_generator.py +65 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/key_pair.py +43 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/key_provider.py +35 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/key_rotation_service.py +130 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/key_status.py +15 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/keys/pem_loader.py +45 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/password/__init__.py +27 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/password/argon2_password_hasher.py +117 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/password/exceptions.py +38 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/password/password_hash_policy.py +45 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/password/password_hash_result.py +32 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/password/password_hasher.py +43 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/password/password_verification_result.py +22 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/refresh_tokens/__init__.py +21 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/refresh_tokens/exceptions.py +31 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/refresh_tokens/refresh_token_generator.py +43 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/refresh_tokens/refresh_token_hasher.py +81 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/refresh_tokens/refresh_token_result.py +40 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/revocation/__init__.py +8 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/revocation/in_memory_revocation_checker.py +75 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/revocation/token_revocation_checker.py +39 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/__init__.py +51 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/exceptions.py +101 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/jwt_decoder.py +162 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/jwt_encoder.py +71 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/jwt_token_provider.py +68 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/jwt_validation_result.py +30 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/jwt_validator.py +150 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/token_claims.py +78 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/token_pair.py +32 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/tokens/token_type.py +14 -0
- baobab_auth_security-0.1.0/src/baobab_auth_security/version.py +8 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/config/test_jwt_security_settings.py +37 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/config/test_key_settings.py +20 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/config/test_refresh_token_settings.py +28 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/exceptions/test_baobab_auth_security_error.py +18 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/integration/test_claims_mapper.py +50 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/integration/test_core_adapters.py +54 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/integration/test_key_rotation_integration.py +68 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/integration/test_revocation_integration.py +92 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/keys/test_in_memory_key_provider.py +57 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/keys/test_jwk.py +50 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/keys/test_jwks.py +33 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/keys/test_jwks_provider.py +51 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/keys/test_key_enums.py +18 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/keys/test_key_generator.py +27 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/keys/test_key_pair.py +41 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/keys/test_key_rotation_service.py +70 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/keys/test_pem_loader.py +31 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/password/test_argon2_password_hasher.py +57 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/password/test_exceptions.py +39 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/password/test_password_hash_policy.py +41 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/password/test_password_hash_result.py +45 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/password/test_password_hasher.py +47 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/password/test_password_verification_result.py +47 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/refresh_tokens/test_refresh_token_generator.py +38 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/refresh_tokens/test_refresh_token_hasher.py +62 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/refresh_tokens/test_refresh_token_result.py +49 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/revocation/test_in_memory_revocation_checker.py +43 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/revocation/test_token_revocation_checker.py +18 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/test_init.py +14 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/test_version.py +29 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/tokens/test_jwt_decoder.py +142 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/tokens/test_jwt_encoder.py +87 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/tokens/test_jwt_validation_result.py +39 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/tokens/test_jwt_validator.py +236 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/tokens/test_token_claims.py +103 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/tokens/test_token_pair.py +29 -0
- baobab_auth_security-0.1.0/tests/baobab_auth_security/tokens/test_token_type.py +17 -0
- baobab_auth_security-0.1.0/tests/examples/test_examples_runnable.py +34 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Règles de développement du projet (source unique de vérité)
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Règles de développement
|
|
8
|
+
|
|
9
|
+
La **source unique de vérité** des règles de ce projet est le fichier [`AGENTS.md`](../../AGENTS.md)
|
|
10
|
+
à la racine du dépôt. **Lis-le et applique-le intégralement.** Ne duplique pas son contenu.
|
|
11
|
+
|
|
12
|
+
Rappels prioritaires (le détail complet est dans `AGENTS.md`) :
|
|
13
|
+
|
|
14
|
+
- **Python ≥ 3.11, orienté objet.** 1 classe = 1 fichier (module nommé d'après la classe).
|
|
15
|
+
- **PEP 8** + **PEP 20** ; en cas de conflit, **PEP 8 prime**.
|
|
16
|
+
- **Type hints obligatoires** ; `ruff` (lint+format) ; `mypy` strict.
|
|
17
|
+
- **Docstrings en reStructuredText**, avec `:spec: <ID>`.
|
|
18
|
+
- **Tests `pytest` en arborescence miroir** ; 1 classe testée = 1 classe de test ;
|
|
19
|
+
classe abstraite testée via une classe concrète de test ; **couverture ≥ 90 %**.
|
|
20
|
+
- Doc **Sphinx/RST** ; dossier `docs/guides/` obligatoire.
|
|
21
|
+
- Environnement virtuel **`.venv`** (`python -m venv .venv`), non versionné.
|
|
22
|
+
- Aucun secret en clair (`.env` gitignoré + `.env.example`).
|
|
23
|
+
- **Conventional Commits** + chaîne d'ID **US-001 / FEAT-001.1 / TASK-001.1.1**.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
trim_trailing_whitespace = true
|
|
8
|
+
indent_style = space
|
|
9
|
+
|
|
10
|
+
[*.py]
|
|
11
|
+
indent_size = 4
|
|
12
|
+
max_line_length = 100
|
|
13
|
+
|
|
14
|
+
[*.{rst,md,yml,yaml,toml,cfg}]
|
|
15
|
+
indent_size = 2
|
|
16
|
+
|
|
17
|
+
[Makefile]
|
|
18
|
+
indent_style = tab
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Copiez ce fichier en `.env` et renseignez vos valeurs.
|
|
2
|
+
# `.env` est gitignoré : n'y mettez jamais de secret dans le dépôt.
|
|
3
|
+
# Chargé/validé via pydantic-settings (voir src/<package>/settings.py si présent).
|
|
4
|
+
|
|
5
|
+
APP_ENV=development
|
|
6
|
+
LOG_LEVEL=INFO
|
|
7
|
+
|
|
8
|
+
# Exemple de secret (ne jamais committer la vraie valeur)
|
|
9
|
+
# API_KEY=changeme
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Normalise les fins de ligne : LF dans le dépôt, quel que soit l'OS du dev.
|
|
2
|
+
* text=auto eol=lf
|
|
3
|
+
|
|
4
|
+
# Scripts Windows : conserver CRLF (requis par cmd.exe).
|
|
5
|
+
*.bat text eol=crlf
|
|
6
|
+
*.cmd text eol=crlf
|
|
7
|
+
|
|
8
|
+
# Fichiers binaires : aucune normalisation.
|
|
9
|
+
*.png binary
|
|
10
|
+
*.jpg binary
|
|
11
|
+
*.jpeg binary
|
|
12
|
+
*.gif binary
|
|
13
|
+
*.ico binary
|
|
14
|
+
*.pdf binary
|
|
15
|
+
*.zip binary
|
|
16
|
+
*.gz binary
|
|
17
|
+
*.woff binary
|
|
18
|
+
*.woff2 binary
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: "📘 User Story"
|
|
2
|
+
description: "Décrire un besoin utilisateur (US)"
|
|
3
|
+
title: "[US-XXX] "
|
|
4
|
+
labels: ["type:us"]
|
|
5
|
+
body:
|
|
6
|
+
- type: input
|
|
7
|
+
id: id
|
|
8
|
+
attributes:
|
|
9
|
+
label: Identifiant
|
|
10
|
+
placeholder: "US-001"
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: story
|
|
15
|
+
attributes:
|
|
16
|
+
label: Récit
|
|
17
|
+
description: "En tant que… je veux… afin de…"
|
|
18
|
+
placeholder: "En tant qu'utilisateur, je veux …, afin de …"
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: acceptance
|
|
23
|
+
attributes:
|
|
24
|
+
label: Critères d'acceptation
|
|
25
|
+
placeholder: "- [ ] …"
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
- type: input
|
|
29
|
+
id: spec
|
|
30
|
+
attributes:
|
|
31
|
+
label: Spécification (RST)
|
|
32
|
+
placeholder: "docs/specifications/us/US-001-.../index.rst"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: "🧩 Feature"
|
|
2
|
+
description: "Découper une US en fonctionnalité (FEAT)"
|
|
3
|
+
title: "[FEAT-XXX.Y] "
|
|
4
|
+
labels: ["type:feat"]
|
|
5
|
+
body:
|
|
6
|
+
- type: input
|
|
7
|
+
id: id
|
|
8
|
+
attributes:
|
|
9
|
+
label: Identifiant
|
|
10
|
+
placeholder: "FEAT-001.1"
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: input
|
|
14
|
+
id: parent
|
|
15
|
+
attributes:
|
|
16
|
+
label: US parente
|
|
17
|
+
placeholder: "US-001 (#numéro de l'issue)"
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: description
|
|
22
|
+
attributes:
|
|
23
|
+
label: Description
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
- type: textarea
|
|
27
|
+
id: acceptance
|
|
28
|
+
attributes:
|
|
29
|
+
label: Critères d'acceptation
|
|
30
|
+
placeholder: "- [ ] …"
|
|
31
|
+
validations:
|
|
32
|
+
required: true
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: "🔧 Task (backlog)"
|
|
2
|
+
description: "Tâche concrète rattachée à une Feature"
|
|
3
|
+
title: "[TASK-XXX.Y.Z] "
|
|
4
|
+
labels: ["type:task"]
|
|
5
|
+
body:
|
|
6
|
+
- type: input
|
|
7
|
+
id: id
|
|
8
|
+
attributes:
|
|
9
|
+
label: Identifiant
|
|
10
|
+
placeholder: "TASK-001.1.1"
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: input
|
|
14
|
+
id: parent
|
|
15
|
+
attributes:
|
|
16
|
+
label: Feature parente
|
|
17
|
+
placeholder: "FEAT-001.1 (#numéro de l'issue)"
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: description
|
|
22
|
+
attributes:
|
|
23
|
+
label: Description / étapes
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
- type: checkboxes
|
|
27
|
+
id: dod
|
|
28
|
+
attributes:
|
|
29
|
+
label: Definition of Done
|
|
30
|
+
options:
|
|
31
|
+
- label: "Code POO, 1 classe/fichier, type hints complets"
|
|
32
|
+
- label: "ruff + mypy strict passent"
|
|
33
|
+
- label: "Test miroir présent, couverture ≥ 90 %"
|
|
34
|
+
- label: "Docstrings RST / guide à jour si besoin"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "pip"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
open-pull-requests-limit: 5
|
|
8
|
+
commit-message:
|
|
9
|
+
prefix: "chore"
|
|
10
|
+
include: "scope"
|
|
11
|
+
labels:
|
|
12
|
+
- "type:task"
|
|
13
|
+
- "dependencies"
|
|
14
|
+
|
|
15
|
+
- package-ecosystem: "github-actions"
|
|
16
|
+
directory: "/"
|
|
17
|
+
schedule:
|
|
18
|
+
interval: "weekly"
|
|
19
|
+
commit-message:
|
|
20
|
+
prefix: "ci"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
|
|
3
|
+
<!-- Que fait cette PR ? Rattachez l'ID spec. -->
|
|
4
|
+
|
|
5
|
+
Closes #
|
|
6
|
+
|
|
7
|
+
## Type de changement
|
|
8
|
+
|
|
9
|
+
- [ ] `feat` — nouvelle fonctionnalité
|
|
10
|
+
- [ ] `fix` — correction de bug
|
|
11
|
+
- [ ] `docs` — documentation
|
|
12
|
+
- [ ] `refactor` / `test` / `chore`
|
|
13
|
+
|
|
14
|
+
## Checklist (Definition of Done)
|
|
15
|
+
|
|
16
|
+
- [ ] 1 classe = 1 fichier ; type hints complets ; docstrings RST avec `:spec:`
|
|
17
|
+
- [ ] Test miroir présent (classe abstraite testée via classe concrète de test)
|
|
18
|
+
- [ ] `ruff` + `mypy` strict passent
|
|
19
|
+
- [ ] Couverture ≥ 90 %
|
|
20
|
+
- [ ] Doc / guide mis à jour si le comportement public change
|
|
21
|
+
- [ ] Commits en Conventional Commits avec ID
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
# Annule les runs obsolètes sur une même branche/PR.
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0 # hatch-vcs a besoin de l'historique/tags
|
|
23
|
+
- uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
cache: pip
|
|
27
|
+
- run: pip install -e ".[dev]"
|
|
28
|
+
- name: Ruff (lint)
|
|
29
|
+
run: ruff check .
|
|
30
|
+
- name: Ruff (format)
|
|
31
|
+
run: ruff format --check .
|
|
32
|
+
|
|
33
|
+
type:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
with:
|
|
38
|
+
fetch-depth: 0
|
|
39
|
+
- uses: actions/setup-python@v5
|
|
40
|
+
with:
|
|
41
|
+
python-version: "3.12"
|
|
42
|
+
cache: pip
|
|
43
|
+
- run: pip install -e ".[dev]"
|
|
44
|
+
- name: Mypy (strict)
|
|
45
|
+
run: mypy
|
|
46
|
+
|
|
47
|
+
security:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
permissions:
|
|
50
|
+
contents: read
|
|
51
|
+
security-events: write # publier le SARIF dans l'onglet Security
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
with:
|
|
55
|
+
fetch-depth: 0
|
|
56
|
+
- uses: actions/setup-python@v5
|
|
57
|
+
with:
|
|
58
|
+
python-version: "3.12"
|
|
59
|
+
cache: pip
|
|
60
|
+
- run: pip install -e ".[dev]"
|
|
61
|
+
- name: Bandit (génère le SARIF)
|
|
62
|
+
run: bandit -c pyproject.toml -r src -f sarif -o bandit.sarif
|
|
63
|
+
continue-on-error: true
|
|
64
|
+
- name: Publier le SARIF dans l'onglet Security
|
|
65
|
+
if: always()
|
|
66
|
+
continue-on-error: true # tolère repo privé sans GitHub Advanced Security
|
|
67
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
68
|
+
with:
|
|
69
|
+
sarif_file: bandit.sarif
|
|
70
|
+
- name: Bandit (gate)
|
|
71
|
+
run: bandit -c pyproject.toml -r src
|
|
72
|
+
- name: pip-audit (dépendances)
|
|
73
|
+
run: pip-audit
|
|
74
|
+
|
|
75
|
+
build:
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v4
|
|
79
|
+
with:
|
|
80
|
+
fetch-depth: 0
|
|
81
|
+
- uses: actions/setup-python@v5
|
|
82
|
+
with:
|
|
83
|
+
python-version: "3.12"
|
|
84
|
+
- name: Build sdist + wheel (validation packaging)
|
|
85
|
+
run: |
|
|
86
|
+
python -m pip install --upgrade build
|
|
87
|
+
python -m build
|
|
88
|
+
- uses: actions/upload-artifact@v4
|
|
89
|
+
with:
|
|
90
|
+
name: dist-ci
|
|
91
|
+
path: dist/
|
|
92
|
+
retention-days: 7
|
|
93
|
+
|
|
94
|
+
docs:
|
|
95
|
+
runs-on: ubuntu-latest
|
|
96
|
+
steps:
|
|
97
|
+
- uses: actions/checkout@v4
|
|
98
|
+
with:
|
|
99
|
+
fetch-depth: 0
|
|
100
|
+
- uses: actions/setup-python@v5
|
|
101
|
+
with:
|
|
102
|
+
python-version: "3.12"
|
|
103
|
+
cache: pip
|
|
104
|
+
- run: pip install -e ".[docs]"
|
|
105
|
+
- name: Build Sphinx (validation stricte)
|
|
106
|
+
run: sphinx-build -b html -W docs docs/_build/html
|
|
107
|
+
- name: Upload doc HTML (aperçu, sans hébergement)
|
|
108
|
+
uses: actions/upload-artifact@v4
|
|
109
|
+
with:
|
|
110
|
+
name: docs-html
|
|
111
|
+
path: docs/_build/html/
|
|
112
|
+
retention-days: 14
|
|
113
|
+
|
|
114
|
+
test:
|
|
115
|
+
runs-on: ubuntu-latest
|
|
116
|
+
strategy:
|
|
117
|
+
fail-fast: false
|
|
118
|
+
matrix:
|
|
119
|
+
python-version: ["3.12", "3.13"]
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/checkout@v4
|
|
122
|
+
with:
|
|
123
|
+
fetch-depth: 0
|
|
124
|
+
- uses: actions/setup-python@v5
|
|
125
|
+
with:
|
|
126
|
+
python-version: ${{ matrix.python-version }}
|
|
127
|
+
cache: pip
|
|
128
|
+
- run: pip install -e ".[dev]"
|
|
129
|
+
- name: Tests + coverage (JUnit + HTML)
|
|
130
|
+
run: pytest --junitxml=junit-${{ matrix.python-version }}.xml --cov-report=html
|
|
131
|
+
- name: Upload rapports (couverture HTML + JUnit)
|
|
132
|
+
if: always()
|
|
133
|
+
uses: actions/upload-artifact@v4
|
|
134
|
+
with:
|
|
135
|
+
name: reports-py${{ matrix.python-version }}
|
|
136
|
+
path: |
|
|
137
|
+
junit-${{ matrix.python-version }}.xml
|
|
138
|
+
htmlcov/
|
|
139
|
+
retention-days: 14
|
|
140
|
+
- name: Upload coverage to Codecov
|
|
141
|
+
if: matrix.python-version == '3.12'
|
|
142
|
+
uses: codecov/codecov-action@v4
|
|
143
|
+
with:
|
|
144
|
+
files: coverage.xml
|
|
145
|
+
fail_ci_if_error: false
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Déclenché par un tag de version créé par le Release Manager (ex. v1.2.0).
|
|
4
|
+
# Tag de pré-release (ex. v1.2.0rc1) → TestPyPI ; tag final → PyPI public.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
meta:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
outputs:
|
|
16
|
+
prerelease: ${{ steps.detect.outputs.prerelease }}
|
|
17
|
+
steps:
|
|
18
|
+
- id: detect
|
|
19
|
+
run: |
|
|
20
|
+
if [[ "${GITHUB_REF_NAME}" =~ (rc|a|b|alpha|beta|dev)[0-9]*$ ]]; then
|
|
21
|
+
echo "prerelease=true" >> "$GITHUB_OUTPUT"
|
|
22
|
+
else
|
|
23
|
+
echo "prerelease=false" >> "$GITHUB_OUTPUT"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
build:
|
|
27
|
+
needs: meta
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
permissions:
|
|
30
|
+
id-token: write # attestation de provenance
|
|
31
|
+
attestations: write
|
|
32
|
+
contents: read
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
with:
|
|
36
|
+
fetch-depth: 0 # tag complet requis pour hatch-vcs
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.12"
|
|
40
|
+
- name: Build sdist + wheel
|
|
41
|
+
run: |
|
|
42
|
+
python -m pip install --upgrade build
|
|
43
|
+
python -m build
|
|
44
|
+
- name: Attestation de provenance (supply chain)
|
|
45
|
+
uses: actions/attest-build-provenance@v1
|
|
46
|
+
with:
|
|
47
|
+
subject-path: "dist/*"
|
|
48
|
+
- uses: actions/upload-artifact@v4
|
|
49
|
+
with:
|
|
50
|
+
name: dist
|
|
51
|
+
path: dist/
|
|
52
|
+
|
|
53
|
+
publish-testpypi:
|
|
54
|
+
needs: [meta, build]
|
|
55
|
+
if: needs.meta.outputs.prerelease == 'true'
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
environment: testpypi
|
|
58
|
+
permissions:
|
|
59
|
+
id-token: write # Trusted Publishing (OIDC)
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/download-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: dist
|
|
64
|
+
path: dist/
|
|
65
|
+
- name: Publish to TestPyPI
|
|
66
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
67
|
+
with:
|
|
68
|
+
repository-url: https://test.pypi.org/legacy/
|
|
69
|
+
|
|
70
|
+
publish-pypi:
|
|
71
|
+
needs: [meta, build]
|
|
72
|
+
if: needs.meta.outputs.prerelease == 'false'
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
environment: pypi
|
|
75
|
+
permissions:
|
|
76
|
+
id-token: write # Trusted Publishing (OIDC) — aucun token stocké
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/download-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
name: dist
|
|
81
|
+
path: dist/
|
|
82
|
+
- name: Publish to PyPI
|
|
83
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
84
|
+
|
|
85
|
+
github-release:
|
|
86
|
+
needs: [meta, build]
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
permissions:
|
|
89
|
+
contents: write # créer la Release + attacher les assets
|
|
90
|
+
steps:
|
|
91
|
+
- uses: actions/checkout@v4
|
|
92
|
+
with:
|
|
93
|
+
fetch-depth: 0 # hatch-vcs (install du paquet pour le SBOM)
|
|
94
|
+
- uses: actions/setup-python@v5
|
|
95
|
+
with:
|
|
96
|
+
python-version: "3.12"
|
|
97
|
+
- uses: actions/download-artifact@v4
|
|
98
|
+
with:
|
|
99
|
+
name: dist
|
|
100
|
+
path: dist/
|
|
101
|
+
- name: Générer le SBOM (CycloneDX)
|
|
102
|
+
run: |
|
|
103
|
+
python -m pip install pip-audit .
|
|
104
|
+
pip-audit -f cyclonedx-json -o sbom.json
|
|
105
|
+
continue-on-error: true
|
|
106
|
+
- name: Create GitHub Release (dist + SBOM attachés)
|
|
107
|
+
uses: softprops/action-gh-release@v2
|
|
108
|
+
with:
|
|
109
|
+
files: |
|
|
110
|
+
dist/*
|
|
111
|
+
sbom.json
|
|
112
|
+
generate_release_notes: true
|
|
113
|
+
prerelease: ${{ needs.meta.outputs.prerelease == 'true' }}
|
|
114
|
+
body: "Voir le CHANGELOG.md pour le détail des changements."
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Environnement virtuel (jamais versionné)
|
|
2
|
+
.venv/
|
|
3
|
+
venv/
|
|
4
|
+
env/
|
|
5
|
+
|
|
6
|
+
# Secrets
|
|
7
|
+
.env
|
|
8
|
+
.env.*
|
|
9
|
+
!.env.example
|
|
10
|
+
|
|
11
|
+
# Python
|
|
12
|
+
__pycache__/
|
|
13
|
+
*.py[cod]
|
|
14
|
+
*$py.class
|
|
15
|
+
*.egg-info/
|
|
16
|
+
.eggs/
|
|
17
|
+
build/
|
|
18
|
+
dist/
|
|
19
|
+
*.egg
|
|
20
|
+
|
|
21
|
+
# Tests & couverture
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
.coverage.*
|
|
25
|
+
htmlcov/
|
|
26
|
+
coverage.xml
|
|
27
|
+
junit*.xml
|
|
28
|
+
.tox/
|
|
29
|
+
.nox/
|
|
30
|
+
|
|
31
|
+
# Rapports d'analyse (générés en CI)
|
|
32
|
+
bandit.sarif
|
|
33
|
+
sbom.json
|
|
34
|
+
|
|
35
|
+
# Typage / lint caches
|
|
36
|
+
.mypy_cache/
|
|
37
|
+
.ruff_cache/
|
|
38
|
+
.dmypy.json
|
|
39
|
+
|
|
40
|
+
# Documentation construite
|
|
41
|
+
docs/_build/
|
|
42
|
+
docs/api/_autosummary/
|
|
43
|
+
|
|
44
|
+
# IDE / OS
|
|
45
|
+
.idea/
|
|
46
|
+
.vscode/
|
|
47
|
+
.DS_Store
|
|
48
|
+
Thumbs.db
|
|
49
|
+
|
|
50
|
+
# Claude Code (local uniquement)
|
|
51
|
+
CLAUDE.local.md
|
|
52
|
+
.claude/settings.local.json
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Garde-fous mécaniques : appliqués quoi que l'IA décide.
|
|
2
|
+
# Installation : pre-commit install
|
|
3
|
+
default_language_version:
|
|
4
|
+
python: python3
|
|
5
|
+
|
|
6
|
+
repos:
|
|
7
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
8
|
+
rev: v4.6.0
|
|
9
|
+
hooks:
|
|
10
|
+
- id: trailing-whitespace
|
|
11
|
+
- id: end-of-file-fixer
|
|
12
|
+
- id: check-yaml
|
|
13
|
+
- id: check-toml
|
|
14
|
+
- id: check-added-large-files
|
|
15
|
+
- id: detect-private-key
|
|
16
|
+
|
|
17
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
18
|
+
rev: v0.6.9
|
|
19
|
+
hooks:
|
|
20
|
+
- id: ruff # lint
|
|
21
|
+
args: [--fix]
|
|
22
|
+
- id: ruff-format # format
|
|
23
|
+
|
|
24
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
25
|
+
rev: v1.11.2
|
|
26
|
+
hooks:
|
|
27
|
+
- id: mypy
|
|
28
|
+
additional_dependencies: ["pydantic-settings"]
|
|
29
|
+
args: [--strict]
|
|
30
|
+
|
|
31
|
+
- repo: https://github.com/PyCQA/bandit
|
|
32
|
+
rev: 1.7.10
|
|
33
|
+
hooks:
|
|
34
|
+
- id: bandit
|
|
35
|
+
args: ["-c", "pyproject.toml"]
|
|
36
|
+
additional_dependencies: ["bandit[toml]"]
|
|
37
|
+
|
|
38
|
+
- repo: local
|
|
39
|
+
hooks:
|
|
40
|
+
- id: pytest-cov-90
|
|
41
|
+
name: pytest (couverture >= 90%)
|
|
42
|
+
entry: pytest
|
|
43
|
+
language: system
|
|
44
|
+
pass_filenames: false
|
|
45
|
+
always_run: true
|
|
46
|
+
stages: [pre-push]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# AGENTS.md — Règles de développement (source unique de vérité)
|
|
2
|
+
|
|
3
|
+
> Ce fichier est **la** source des règles pour **toutes** les IA de développement
|
|
4
|
+
> (Codex le lit nativement ; `CLAUDE.md` l'importe ; `.cursor/rules/000-core.mdc` le reflète).
|
|
5
|
+
> Toute modification de règle se fait **ici**, jamais en double.
|
|
6
|
+
|
|
7
|
+
## Langage & conception
|
|
8
|
+
|
|
9
|
+
- Langage : **Python ≥ 3.11**, **orienté objet**. Respect de **SOLID**, composition > héritage.
|
|
10
|
+
- **1 classe = 1 fichier.** Le module porte le nom de la classe en `snake_case`
|
|
11
|
+
(`class FactureClient` → `facture_client.py`).
|
|
12
|
+
- *Dérogation* : une **hiérarchie d'exceptions** (classes courtes) peut être regroupée
|
|
13
|
+
dans un sous-package `exceptions/` organisé **par catégorie** (un fichier par famille).
|
|
14
|
+
- Pas de logique exécutable au niveau module ; tout passe par des classes/méthodes.
|
|
15
|
+
|
|
16
|
+
## Librairie consommable (contrat d'API)
|
|
17
|
+
|
|
18
|
+
- Le livrable est une **librairie réutilisable**, susceptible d'être intégrée dans un
|
|
19
|
+
projet parent. Ce qui est exporté dans `__all__` est un **contrat**.
|
|
20
|
+
- Rupture du contrat (suppression/modification incompatible d'un symbole public) →
|
|
21
|
+
**bump SemVer majeur** + entrée `CHANGELOG` « BREAKING » + note de migration.
|
|
22
|
+
- Aucune hypothèse sur l'hôte : pas d'état global, config **injectée** (`pydantic-settings`).
|
|
23
|
+
Ce dépôt *expose*, il ne *dépend jamais* d'un projet parent.
|
|
24
|
+
|
|
25
|
+
## PEP 8 & PEP 20
|
|
26
|
+
|
|
27
|
+
- Respect de **PEP 8** (style) et de **PEP 20** (Zen of Python : explicite, simple, lisible).
|
|
28
|
+
- **En cas de conflit entre PEP 8 et PEP 20, la PEP 8 prime.**
|
|
29
|
+
|
|
30
|
+
## Typage & style
|
|
31
|
+
|
|
32
|
+
- **Type hints obligatoires** sur toutes les signatures (paramètres et retours).
|
|
33
|
+
- Lint + format : **`ruff`** (unique outil). Vérification de types : **`mypy`** (mode strict).
|
|
34
|
+
- **Docstrings en reStructuredText (RST)** sur tout élément public
|
|
35
|
+
(champs `:param:`, `:returns:`, `:raises:`, et `:spec: <ID>` pour la traçabilité).
|
|
36
|
+
|
|
37
|
+
## Tests
|
|
38
|
+
|
|
39
|
+
- Framework : **`pytest`**, structure **AAA** (Arrange / Act / Assert), tests déterministes.
|
|
40
|
+
- **Arborescence miroir** : `src/<pkg>/a/b/c.py` ⇒ `tests/<pkg>/a/b/test_c.py`.
|
|
41
|
+
- **Une classe testée = une classe de test** (`class FactureClient` ⇒ `class TestFactureClient`).
|
|
42
|
+
- **Classe abstraite** : on la teste via une **classe concrète de test** définie dans le fichier de test.
|
|
43
|
+
- Nom de test porteur de l'ID spec : `def test_FEAT_001_1_cas_nominal(...)`.
|
|
44
|
+
- **Couverture ≥ 90 %**, imposée par `--cov-fail-under=90` (voir `pyproject.toml`).
|
|
45
|
+
|
|
46
|
+
## Documentation
|
|
47
|
+
|
|
48
|
+
- **Sphinx** + **RST**. La doc API est générée par **`autodoc`** depuis les docstrings.
|
|
49
|
+
- Dossier **`docs/guides/` obligatoire**, organisé selon **Diátaxis** :
|
|
50
|
+
`tutorials/` (apprendre) et `how-to/` (résoudre un problème précis).
|
|
51
|
+
- **`README.md`** : suit la structure « 15 sections » (voir le fichier) et porte **tous les badges**
|
|
52
|
+
que le projet peut légitimement afficher (version, couverture, CI, licence, style, types, docs…).
|
|
53
|
+
|
|
54
|
+
## Arborescence (layout `src/`)
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
.
|
|
58
|
+
├── src/<package>/ # code (1 classe par fichier)
|
|
59
|
+
├── tests/<package>/ # tests en miroir de src/
|
|
60
|
+
├── docs/
|
|
61
|
+
│ ├── specifications/ # cahier des charges : US / FEAT (RST, stable)
|
|
62
|
+
│ ├── api/ # doc API (autodoc)
|
|
63
|
+
│ └── guides/ # tutorials/ + how-to/ (OBLIGATOIRE)
|
|
64
|
+
├── pyproject.toml # config unique (projet, ruff, mypy, pytest, coverage)
|
|
65
|
+
├── .pre-commit-config.yaml
|
|
66
|
+
└── .github/ # CI + templates d'issues (US/FEAT/Task) + PR
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Environnement & dépendances
|
|
70
|
+
|
|
71
|
+
- **Toujours un environnement virtuel** : `python -m venv .venv` dans le dossier **`.venv`**.
|
|
72
|
+
- `.venv/` n'est **jamais** versionné (cf. `.gitignore`).
|
|
73
|
+
- Config projet et dépendances dans **`pyproject.toml`** (PEP 621). Install dev : `pip install -e ".[dev,docs]"`.
|
|
74
|
+
|
|
75
|
+
## Sécurité
|
|
76
|
+
|
|
77
|
+
- **Aucun secret** dans le code ou Git. Variables via `.env` (gitignoré) + `.env.example` versionné.
|
|
78
|
+
- Chargement/validation de la config via **`pydantic-settings`**.
|
|
79
|
+
|
|
80
|
+
## Git & traçabilité
|
|
81
|
+
|
|
82
|
+
- **Conventional Commits** portant l'ID spec : `feat(FEAT-001.1): export PDF de la facture`.
|
|
83
|
+
- **SemVer** pour les versions.
|
|
84
|
+
- Chaîne d'identifiants propagée partout :
|
|
85
|
+
**US-001** → **FEAT-001.1** → **TASK-001.1.1**
|
|
86
|
+
(titres d'issues, branches, commits `Closes #<n>`, noms de tests, docstrings `:spec:`).
|
|
87
|
+
- **Provenance** : chaque US/FEAT porte un champ `:origin:` (cahier des charges, ou projet
|
|
88
|
+
externe demandeur) dans sa spec RST et son issue, pour la traçabilité inter-projets.
|
|
89
|
+
- **Fermeture au merge** : une issue ne se ferme qu'**après** le merge de sa PR sur `main`,
|
|
90
|
+
jamais avant (sinon le tracker dit « terminé » alors que le code dort sur une branche).
|
|
91
|
+
|
|
92
|
+
## Workflow
|
|
93
|
+
|
|
94
|
+
- Le processus de dev (rôles, machine à états, handoff, prompts) est décrit dans
|
|
95
|
+
**`docs/workflow/`**. L'IA endosse un rôle à la fois, de façon séquentielle, et reprend
|
|
96
|
+
via la **note de handoff** + le GitHub Project.
|
|
97
|
+
- **Format des fichiers** : les instructions opérationnelles (`AGENTS.md`, `CLAUDE.md`,
|
|
98
|
+
`docs/workflow/`) sont en **Markdown** ; la **documentation du projet**
|
|
99
|
+
(specifications, API, guides) est en **reStructuredText**.
|
|
100
|
+
- Le cahier des charges brut se dépose dans `docs/specifications/cahier-des-charges/` ;
|
|
101
|
+
le rôle PO en dérive `docs/specifications/us/` (RST).
|
|
102
|
+
|
|
103
|
+
## Definition of Done (une tâche n'est close que si)
|
|
104
|
+
|
|
105
|
+
1. Code POO, 1 classe/fichier, type hints complets.
|
|
106
|
+
2. `ruff` (lint+format) et `mypy` strict passent.
|
|
107
|
+
3. Tests présents en miroir, couverture ≥ 90 %.
|
|
108
|
+
4. Docstrings RST + guide mis à jour si le comportement public change.
|
|
109
|
+
5. Commit conforme (Conventional Commits + ID), PR fusionnée.
|