12 KiB
Contribuer à Yavsc
Pré-requis
- .NET 10 SDK
- PostgreSQL (≥ 14) — local ou via
docker compose up(cf.docker-compose.yaml) - Node.js (uniquement pour la toolchain Avalonia Browser/WASM)
- Pour Android : Android SDK + workload
dotnet workload install android
Premier build
git clone https://forgejo.pschneider.fr/notazof/yavsc.git
cd yavsc
dotnet restore
dotnet build
Pour exécuter Yavsc.Org en local :
cp src/Yavsc.Org/appsettings-org.json src/Yavsc.Org/appsettings-org.Development.json
$EDITOR src/Yavsc.Org/appsettings-org.Development.json # renseigner Site.Authority, ConnectionStrings.YavscConnection, …
ASPNETCORE_ENVIRONMENT=Development dotnet run --project src/Yavsc.Org
Cf. doc/Architecture.md section "Paramétrage" pour le détail des variables à renseigner.
Tests
dotnet test
Les tests sont répartis en :
src/Yavsc.Org.Tests/— tests d'intégration du front web (TestWebApplicationFactory<Program>+ EF InMemory). Comprend les smoke tests par BC sousSmoke/:AccountSmokeTests(GET /signin),BlogSmokeTests(GET /BlogSpot/Index). Chaque test couvre une bounded context DDD au sens dedoc/ddd-exploration-2026-06-14.md: il démarre le host en mémoire viaWebApplicationFactory<Program>et vérifie qu'une route publique de la BC répond en 2xx/3xx (ou 401/403 si elle exige une authentification). Cf. ROADMAP.md item « Tests d'intégration smoke par BC ».src/PostIt.Tests/— tests unitaires du client desktop PostIt.
Le CHANGELOG.md
Le CHANGELOG.md est un document de changement de version
Toutes les modifications notables de PostIt et de la plateforme Yavsc sont documentées dans ce fichier.
Le format suit Keep a Changelog, et ce projet adhère au Semantic Versioning.
À noter : la parité du numéro de patch porte une signification de canal :
- patch pair (ex.
1.0.0,1.0.2) → stable - patch impair (ex.
1.0.1,1.0.3) → preview - suffixe (ex.
1.0.0-rc1,1.0.0-alpha) → instable
Cette convention est partagée avec le dépôt
postit-debian
pour la production des paquets .deb.
Navigation (PostIt)
La navigation est centralisée dans
App.PushPageAsync(ViewModelBase vm) (src/PostIt/PostIt/App.axaml.cs).
Pour ouvrir un écran, un ViewModel (généralement dans une
commande [RelayCommand]) appelle
await ((App)App.Current!).PushPageAsync(targetVm).ConfigureAwait(true);.
PushPageAsync résout la Control correspondante via le
ViewLocator (un IDataTemplate enregistré dans
Application.DataTemplates au boot), l'identifie comme
Page, lui assigne le VM comme DataContext, et appelle
NavRoot.PushAsync(page). Une garde anti-empilement
compare par référence la nouvelle page au sommet courant
de la stack pour éviter un push doublon.
Pour qu'une nouvelle page soit navigable, il faut deux
enregistrements : la page dans le DI (AddTransient<TPage>
ou AddSingleton<TPage>) et une case dans le switch
de ViewLocator.Build. Si l'un manque, l'app affiche
"No view for X" sans crash.
Règles :
- On n'instancie jamais une
Viewà la main depuis un ViewModel, on ne récupère jamais uneViewdepuis la DI directement dans un ViewModel. - Le ViewModel qui déclenche la nav ne pousse pas lui-même
la page ; il appelle
App.PushPageAsync(vm)et laisseApporchestrer lePushAsyncphysique. - Le ViewModel qui déclenche la nav ne capture pas de
référence à
MainWindowouNavigationPage. Il passe parApp.Current(l'app Avalonia est un singleton).
Exemple canonique (depuis MainPageViewModel) :
[RelayCommand]
internal async Task OpenSettings()
{
var settingsVm = ((App)App.Current!).ServiceProvider
.GetRequiredService<Settings>();
await ((App)App.Current!).PushPageAsync(settingsVm)
.ConfigureAwait(true);
}
Cf. doc/architecture/postit.md
pour la topologie complète (host de navigation,
SessionStatusViewModel, signaux de cycle de vie vs nav
utilisateur).
Conventions de code
Le repo applique .editorconfig (UTF-8, LF, indent_size = 4 en
C#, 2 en Razor / XML / .axaml). Pas de formatter dédié — les EDI
(Visual Studio, Rider) appliquent automatiquement les règles Roslyn
- EditorConfig.
Quelques règles non capturées par .editorconfig :
usingtriés par groupe (System, puis packages, puis local). Pas de séparation visuelle entre groupes (cf.dotnet_separate_import_directive_groups = false).- Préférer les types BCL (
int,string) aux types framework (Int32,String). - Préférer les expressions de pattern matching aux casts explicites.
- Pas de
objectdans le code source applicatif. Types de retour, paramètres, champs, propriétés, variables locales : tout doit être typé statiquement.dynamicest interdit pour les mêmes raisons. Un cast enobjectest presque toujours le symptôme d'un contrat qu'on a laissé s'effriter (DTO, payload, handler) — refactore le contrat (record typé, DTO dédié, méthode dédiée) au lieu de shimer avec un cast.
Branches & commits
-
Trunk-based sur
main. Pas de branche longue durée pour le moment ; on lande directement surmainvia PR (quand le projet attire de l'attention) ou push direct (mode solo). -
Un commit = un changement logique. Regrouper les fichiers qui touchent au même flux dans un seul commit (login + tests + doc = un commit), mais séparer les commits de reformulation d'historique (« refactor », « typo ») des commits de feature dans la mesure du possible.
-
Messages de commit en anglais, format :
<scope>: <imperative summary> <body — what changed and why, not how>Le scope est le nom du sous-projet (
postit,doc,readme,yavsc.org,yavsc.api, …). Pour les commits qui touchent plusieurs sous-projets, préférer un scope générique (readme:,doc:).
Découpage des projets
Cf. doc/architecture/decoupage-organisation.md.
qui couvre à la fois le build, la publication et les images
runtime, plus un second Dockerfile minimal (Dockerfile.backend)
utilisé uniquement par le workflow de publication de l'image
de production.
| Dockerfile | Stages | Construit par |
|---|---|---|
Dockerfile |
build-env (default), publish-org/api/blogs, web-runtime, api-runtime, blogs-runtime |
docker compose build + .github/workflows/docker-publish-android.yml |
Dockerfile.backend |
Idem limité à build-env + publish-org (suffisant pour publier l'image pazof/yavsc) |
.github/workflows/docker-publish-backend.yml |
L'image de base est construite depuis le dépôt sibling
dotnet-android-build-image (Debian 12 + .NET 10 SDK + Android
SDK 36 + workload .NET Android). Elle est poussée sur Docker Hub
sous le tag pazof/yavsc-build-env:debian12-dotnet10-android36-v1.
Le tag est déclaré comme ARG BUILD_ENV_TAG au début du
Dockerfile (et de Dockerfile.backend) — il faut le bumper en
lockstep dans les deux fichiers et dans docker-compose.yaml
(chaque bloc build.args.BUILD_ENV_TAG) quand l'image de base
est reconstruite.
docker compose up
sudo docker compose up --build
Démarre 4 services : db (PostgreSQL 16), web (Yavsc.Org),
api (Yavsc.Api), blogs (Yavsc.Blogs). Chaque service runtime
pointe sur le stage correspondant du Dockerfile multi-stage via
build.target. Les services runtime attendent le healthcheck
pg_isready de db avant de démarrer.
Sur une machine vierge, db, api et blogs démarrent. Le
service web (Yavsc.Org) échoue avec :
Production IdentityServer requires a signing certificate. Configure Kestrel:Endpoints:Https:Certificate:{Path,KeyPath}.
C'est attendu : IdentityServer8 en mode Production exige un cert
HTTPS pour signer les tokens (cf. src/Yavsc.Org/Extensions/HostingExtensions.cs:374).
Le critère de sortie Jalon 0 « docker compose up vert sur machine
vierge » n'est donc pas « tout démarre sans rien » — c'est
« tout démarre après la procédure d'installation qui monte
un cert HTTPS valide, cf. HTTPS en production ci-dessous ».
Le montage de /etc/letsencrypt (volume commenté par défaut
dans docker-compose.yaml) est l'étape qui distingue une
machine configurée d'une machine vierge.
appsettings-org.json
Le fichier de configuration de prod (src/Yavsc.Org/appsettings-org.json)
n'est pas commité. Il est injecté dans chaque image runtime
via un BuildKit secret mount — le fichier reste sur l'hôte,
n'apparaît dans aucun layer :
secrets:
yavsc_appsettings:
file: ./src/Yavsc.Org/appsettings-org.json
Compose le passe automatiquement à docker build via le bloc
build.secrets de chaque service runtime.
HTTPS en production
En dev local les services runtime sont forcés à HTTP seul par
un bloc environment explicite dans docker-compose.yaml :
environment:
ASPNETCORE_URLS: "http://+:5000"
ASPNETCORE_HTTPS_PORT: ""
C'est nécessaire parce que appsettings-org.Development.json
positionne Site.Authority = https://localhost:5001, ce qui
pousse Kestrel à essayer de binder HTTPS même sans certificat
disponible — et échoue proprement avec « Unable to configure
HTTPS endpoint. No server certificate was specified ».
En production, sur chaque service runtime de docker-compose.yaml :
-
Remplacer l'
environment.ASPNETCORE_URLSpar la forme double-bindhttp://+:5000;https://+:5001(ou équivalent pour Api / Blogs). -
Décommenter le port HTTPS correspondant (
5001pour Org,5003pour Api,5005pour Blogs). -
Décommenter le volume
/etc/letsencrypt:/etc/letsencrypt:ro— monter le répertoire Let's Encrypt de l'hôte en lecture seule pour que Kestrel accède aux fichiers.pem. -
Dans
appsettings-org.json, ajouter un blocKestrel:Endpointspointant vers les chemins du volume monté. Exemple :"Kestrel": { "Endpoints": { "Https": { "Url": "https://+:5001", "Certificate": { "Path": "/etc/letsencrypt/live/yavsc.example/fullchain.pem", "KeyPath": "/etc/letsencrypt/live/yavsc.example/privkey.pem" } } } } -
Régénérer l'image runtime (les
appsettingssont baked dans l'image via BuildKit secret mount — cf. section appsettings-org.json).
Bumper l'image de build
Quand on bumpe Debian, .NET SDK ou Android SDK, reconstruire l'image de base :
cd ../dotnet-android-build-image
docker build -t pazof/yavsc-build-env:debian12-dotnet10-android36-v2 .
docker push pazof/yavsc-build-env:debian12-dotnet10-android36-v2
Puis bumper en lockstep dans :
Dockerfile(ARGBUILD_ENV_TAGen tête de fichier)Dockerfile.backend(idem)docker-compose.yaml(chaque blocbuild.args.BUILD_ENV_TAG).
Vérifier un build isolé d'un stage runtime
docker build \
--secret id=yavsc_appsettings,src=src/Yavsc.Org/appsettings-org.json \
--target web-runtime \
-t yavsc-org:dev .
docker run --rm -p 5000:5000 yavsc-org:dev
et une roadmap à jour dans ROADMAP.md. Toute modification de modèle doit être précédée d'une note DDD ; les BC (Conciliation, etc.) listés dans ces docs sont les cibles de conception.
Sessions DDD
Le repo tient un journal de design DDD sous doc/ddd-exploration-*.md
et une roadmap à jour dans ROADMAP.md. Toute
modification de modèle doit être précédée d'une note DDD ; les BC
(Conciliation, etc.) listés dans ces docs sont les cibles de
conception.
Sécurité
Cf. la section "🔒 Hygiène transverse (à tous les jalons)" de
ROADMAP.md. En particulier : ne jamais committer
de secret, utiliser dotnet user-secrets en développement et les
variables d'environnement ASPNETCORE_* en production.
Questions
Ouvrir une issue GitHub, ou — pour les questions de design —
démarrer une session DDD et la consigner dans doc/.