yavsc/contrib/Makefile

184 lines
7.5 KiB
Makefile
Raw Normal View History

2026-09-20 18:35:27 +01:00
DESTDIR=
DOTNET_FRAMEWORK=net10.0
2026-08-30 23:55:09 +01:00
APP_PROJECT_NAMES=Org Blogs Api
2026-02-28 14:22:33 +00:00
SLNDIR=..
2026-09-20 18:35:27 +01:00
APP_INSTALL_ENV=preprod
2026-09-21 10:37:39 +01:00
APP_INSTALL_PREFIX=preprod-
2026-09-20 18:35:27 +01:00
include .$(APP_INSTALL_ENV).env
2026-02-28 14:22:33 +00:00
2026-09-21 12:49:38 +01:00
reinstall: copy-binaries
@for project in $(APP_PROJECT_NAMES); do \
2026-09-21 14:58:40 +01:00
echo "🚀 Starting $(APP_INSTALL_PREFIX)yavsc$${project}" ; \
2026-09-21 12:49:38 +01:00
sudo systemctl start $(APP_INSTALL_PREFIX)yavsc$${project} ; done
@echo "💯 backend started"
reinstall-prod:
2026-09-21 14:58:40 +01:00
make reinstall APP_INSTALL_ENV= APP_INSTALL_PREFIX= APP_INSTALL_ENV=production
2026-02-28 14:22:33 +00:00
2026-09-20 18:35:27 +01:00
generated/$(APP_INSTALL_PREFIX)yavscOrg.service:
generated/$(APP_INSTALL_PREFIX)yavscBlogs.service:
generated/$(APP_INSTALL_PREFIX)yavscApi.service:
2026-06-06 21:10:48 +01:00
2026-09-20 18:35:27 +01:00
generated/$(APP_INSTALL_PREFIX)yavsc%.service: generated/ template.service $(SLNDIR)/.env
2026-06-06 21:10:48 +01:00
@cat template.service | APP_NAME="$*" \
2026-08-30 23:55:09 +01:00
DESTDIR="$(DESTDIR)" \
2026-06-14 18:26:40 +01:00
HTTP_HOST="$(HTTP_HOST)" \
2026-09-25 13:27:00 +01:00
HTTP_PORT="$($*_PORT)" \
2026-09-20 18:35:27 +01:00
APPDIR="$(APPDIR)" \
APP_SYSLOG_ID="$(APP_INSTALL_PREFIX)yavsc$*" \
2026-06-06 21:10:48 +01:00
POSTGRES_HOST="$(POSTGRES_HOST)" \
POSTGRES_PORT="$(POSTGRES_PORT)" \
POSTGRES_DB="$(POSTGRES_DB)" \
POSTGRES_USER="$(POSTGRES_USER)" \
POSTGRES_PASSWORD="$(POSTGRES_PASSWORD)" \
ANTHROPIC_API_KEY="$(ANTHROPIC_API_KEY)" \
ANTHROPIC_MAX_TOKENS="$(ANTHROPIC_MAX_TOKENS)" \
ASPNETCORE_Smtp__Host="$(ASPNETCORE_Smtp__Host)" \
ASPNETCORE_Smtp__Port="$(ASPNETCORE_Smtp__Port)" \
ASPNETCORE_Smtp__SenderName=$(ASPNETCORE_Smtp__SenderName) \
ASPNETCORE_Smtp__SenderEmail="$(ASPNETCORE_Smtp__SenderEmail)" \
ASPNETCORE_Smtp__UserName="$(ASPNETCORE_Smtp__UserName)" \
ASPNETCORE_Smtp__Password="$(ASPNETCORE_Smtp__Password)" \
2026-06-06 21:10:48 +01:00
envsubst > $@
@echo Created service file: $@
2026-09-20 18:35:27 +01:00
generate-service-units: generated/$(APP_INSTALL_PREFIX)yavscOrg.service generated/$(APP_INSTALL_PREFIX)yavscBlogs.service generated/$(APP_INSTALL_PREFIX)yavscApi.service
2026-09-25 13:27:00 +01:00
# --- Vhosts nginx (générés par envsubst, à l'image des unités systemd) ---
# make generate-nginx -> generated/nginx/preprod-yavsc{Org,Blogs,Api}
# make generate-nginx APP_INSTALL_ENV=production APP_INSTALL_PREFIX= -> prod (yavsc/api/blogs.pschneider.fr)
#
# HTTP/3 (QUIC) termine au edge nginx (plan contrib/quic.md). Requiert nginx
# avec http_v3_module + UDP 443 ouvert. reuseport sur Org uniquement
# (QUIC_OPTS). Les vhosts générés vont dans generated/nginx/ (gitignoré).
2026-09-25 13:27:00 +01:00
NGINX_TEMPLATE := template.vhost
NGINX_OUT_DIR := generated/nginx
NGINX_HOSTS := Org Blogs Api
DOMAIN_ROOT ?= pschneider.fr
# Schéma backend Kestrel : https en prod (Kestrel écoute HTTPS, cert Let's
# Encrypt), http en preprod (Kestrel écoute HTTP simple, cf. .preprod.env).
BACKEND_SCHEME ?= $(if $(filter production,$(APP_INSTALL_ENV)),https,http)
# sous-domaine par hôte : Org -> yavsc, Api -> api, Blogs -> blogs
nginx_subdomain = $(if $(filter Org,$1),yavsc,$(if $(filter Api,$1),api,$(if $(filter Blogs,$1),blogs,$1)))
# domaine complet : <prefix><subdomain>.<DOMAIN_ROOT>
# prod Org -> yavsc.pschneider.fr
# preprod Org -> preprod-yavsc.pschneider.fr
nginx_domain = $(APP_INSTALL_PREFIX)$(call nginx_subdomain,$1).$(DOMAIN_ROOT)
$(NGINX_OUT_DIR)/$(APP_INSTALL_PREFIX)yavsc%: $(NGINX_OUT_DIR) $(NGINX_TEMPLATE) .$(APP_INSTALL_ENV).env
@cat $(NGINX_TEMPLATE) \
| DOMAIN="$(call nginx_domain,$*)" \
BACKEND_SCHEME="$(BACKEND_SCHEME)" \
HTTP_HOST="$(HTTP_HOST)" \
BACKEND_PORT="$($*_PORT)" \
QUIC_OPTS="$(if $(filter Org,$*),reuseport,)" \
envsubst '$$DOMAIN $$BACKEND_SCHEME $$HTTP_HOST $$BACKEND_PORT $$QUIC_OPTS' > $@
@echo Created nginx vhost: $@
generate-nginx: $(addprefix $(NGINX_OUT_DIR)/$(APP_INSTALL_PREFIX)yavsc,$(NGINX_HOSTS))
generate-nginx-prod:
make generate-nginx APP_INSTALL_ENV=production APP_INSTALL_PREFIX=
$(NGINX_OUT_DIR):
@mkdir -p $@
# --- appsettings (générés depuis template.appsettings-*.json) ---
# make generate-appsettings -> generated/appsettings/appsettings-{org,api,blogs}.json
#
# Substitue $DOMAIN (racine du domaine, cf. DOMAIN_ROOT) dans toutes les
# chaînes, et $SMTP_PASSWORD (secret du .env) dans Smtp.Password.
# On utilise jq (et non envsubst) car envsubst n'échappe pas le JSON : un
# secret contenant \ ou " casserait le JSON (le mot de passe SMTP actuel
# contient un \). jq lit le secret depuis l'environnement (export) ->
# échappement JSON correct, aucun shell-quoting ni exposition en ligne de cmd.
# Les templates actuels sont issus de la preprod (préfixe preprod-, ports
# 88-93) ; pour la prod il faudra un template prod (ports 82-87, sans préfixe).
export ASPNETCORE_Smtp__Password
APPSETTINGS_OUT_DIR := generated/appsettings
APPSETTINGS_HOSTS := org api blogs
$(APPSETTINGS_OUT_DIR)/appsettings-%.json: $(APPSETTINGS_OUT_DIR) template.appsettings-%.json .$(APP_INSTALL_ENV).env
@jq --arg domain "$(DOMAIN_ROOT)" \
'walk(if type=="string" then gsub("\\$$DOMAIN"; $$domain) else . end) | if has("Smtp") then .Smtp.Password = env["ASPNETCORE_Smtp__Password"] else . end' \
template.appsettings-$*.json > $@
@echo Created appsettings: $@
generate-appsettings: $(addprefix $(APPSETTINGS_OUT_DIR)/appsettings-,$(addsuffix .json,$(APPSETTINGS_HOSTS)))
$(APPSETTINGS_OUT_DIR):
@mkdir -p $@
2026-09-20 18:35:27 +01:00
install-service-%: /etc/systemd/system/$(APP_INSTALL_PREFIX)yavsc%.service
sudo systemctl daemon-reload
sudo systemctl enable $(APP_INSTALL_PREFIX)yavsc$*
2026-06-06 21:10:48 +01:00
2026-09-20 18:35:27 +01:00
install-services: install-service-Org install-service-Blogs install-service-Api
2026-06-06 21:10:48 +01:00
2026-08-30 23:55:09 +01:00
copy-binaries: build_publish_Org build_publish_Blogs build_publish_Api stop-services
2026-06-19 16:18:39 +01:00
@for project in $(APP_PROJECT_NAMES); \
do LCAPI=$$(echo $${project}|tr [:upper:] [:lower:]) ; \
echo "$${project} -> $${LCAPI}" ; \
BINDIR="$(SLNDIR)/src/Yavsc.$${project}/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish" ; \
echo BINDIR: $${BINDIR} ; \
2026-06-19 21:13:17 +01:00
rm -f $${BINDIR}/appsettings-$${LCAPI}-dist.json ; \
mv -f $${BINDIR}/appsettings-$${LCAPI}.json \
2026-06-19 16:18:39 +01:00
$${BINDIR}/appsettings-$${LCAPI}-dist.json ; \
2026-06-19 21:13:17 +01:00
rm -f $${BINDIR}/appsettings-$${LCAPI}.json ; \
sync ; \
ls $${BINDIR}/appsettings* 2>&1 ; \
2026-09-20 18:35:27 +01:00
sudo cp -a $${BINDIR}/* $(DESTDIR)$(APPDIR) ; \
2026-06-06 21:10:48 +01:00
done
2026-09-20 18:35:27 +01:00
@sudo chown -R $(USER_AND_GROUP) $(DESTDIR)$(APPDIR)
@sync
$(DESTDIR)/etc/systemd/system/$(APP_INSTALL_PREFIX)yavsc%.service: generated/$(APP_INSTALL_PREFIX)yavsc%.service
2026-02-28 14:22:33 +00:00
sudo cp $^ $@
2026-06-06 21:10:48 +01:00
sudo chown root:root $@
2026-02-28 14:22:33 +00:00
2026-06-19 21:13:17 +01:00
build_publish_%: clean_publish_dir_%
2026-06-19 16:18:39 +01:00
@ASPNETCORE_ENV=$(CONFIGURATION) dotnet publish $(SLNDIR)/src/Yavsc.$*/Yavsc.$*.csproj
2026-06-13 20:57:30 +01:00
2026-06-19 21:13:17 +01:00
clean_publish_dir_%:
@rm -rf $(SLNDIR)/src/Yavsc.$*/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish
2026-09-20 18:35:27 +01:00
install: copy-binaries copy-services
2026-06-19 16:18:39 +01:00
@sudo systemctl daemon-reload
@for project in $(APP_PROJECT_NAMES); \
2026-06-06 21:10:48 +01:00
do \
2026-09-21 14:58:40 +01:00
echo "🖒 enable $(APP_INSTALL_PREFIX)yavsc$${project}" ; \
sudo systemctl enable $(APP_INSTALL_PREFIX)yavsc$${project} ; \
echo "🚀 start $(APP_INSTALL_PREFIX)yavsc$${project}" ; \
sudo systemctl start $(APP_INSTALL_PREFIX)yavsc$${project} ; \
2026-06-06 21:10:48 +01:00
done
feat(postit): circles+ACL UI, blog fixture→SQLite, seed default user Bundled end-of-branch commit on feat/postit-acl-members. PostIt UI for circles + per-post ACL - Reorganise PostIt.Tests into Auth/ and Blogs/ subfolders (Bearer/OIDC scope tests vs. blog API fakes live where they belong) and introduces PostItHeadlessCollection so the Avalonia.Headless tests share a single xUnit collection instead of contending with the EF-Core test host. - Adds BlogAclApiTests (a brand-new behavioural layer over POST /api/v1/blogacl) and the fakes it relies on (BlogApiTestFakes, BlogPostAuthorDtoTests, AddCircleMember DialogTests); pulls UserId-through-OIDC-sub path into BearerScopeTests / FakeAuthorizingBrowser / OidcStubAuthority. - App.axaml.cs gets a small PushPageAsync touch-up the new tests rely on. - Drops UnitTest1.cs (xUnit scaffold, never used). Yavsc.Blogs.Tests — SQLite instead of InMemory - Bumps Yavsc.Blogs.Tests.csproj on Microsoft.EntityFrameworkCore.Sqlite and rewrites BlogsWebServerFixture to hold a single shared SqliteConnection (Cache=Shared) for the fixture lifetime, with a sync Dispose close to dodge async teardown hangs. Reason: the EF Core InMemory provider silently ignores FKs, which masked the kind of bug we are about to pin in the ACL tests. SQLite enforces them, so any future INSERT that forgets to seed its parent rows fails loudly here instead of passing the test and breaking prod. - PublishEndpointTests and BlogApiSmokeTests get a one-line tweak to follow the new connection lifecycle. Foreign-key fallout: seed the default user in the fixture - Adds BlogsWebServerFixture.SeedUser(userName). Now that SQLite enforces BlogPost.AuthorId → AspNetUsers.Id, every test that POST/PUT/DELETE a BlogPost and sends AuthorId= 'tester' in the payload needs an AspNetUsers row to satisfy the FK or it returns 500 with SQLite Error 19. - BlogApiTests wraps the existing ResetDatabase with a ResetAndSeedDefaultUser helper for the six mutating tests; the four GET-only and ModelState-only tests keep the bare ResetDatabase. - Side benefit: every test in Yavsc.Blogs.Tests now finishes cleanly instead of hanging at teardown — previously a stuck test held the shared SqliteConnection open and the next tests waited indefinitely. Verified: dotnet test src/Yavsc.Blogs.Tests passes 25/25 green from a clean run, no fixture teardown hang.
2026-08-20 23:59:21 +01:00
2026-06-14 18:26:40 +01:00
stop-services:
2026-06-19 16:18:39 +01:00
@for project in $(APP_PROJECT_NAMES); \
2026-06-06 21:10:48 +01:00
do \
2026-09-21 14:58:40 +01:00
echo "🔥 Stopping $(APP_INSTALL_PREFIX)yavsc$${project}" ; \
2026-09-21 12:49:38 +01:00
sudo systemctl stop $(APP_INSTALL_PREFIX)yavsc$${project} ; \
2026-02-28 14:22:33 +00:00
done
2026-06-13 20:57:30 +01:00
$(SLNDIR)/src/Yavsc.Org/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish: build_publish
$(SLNDIR)/src/Yavsc.Blogs/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish: build_publish
2026-08-30 23:55:09 +01:00
$(SLNDIR)/src/Yavsc.Api/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish: build_publish
2026-06-13 20:57:30 +01:00
feat(postit): circles+ACL UI, blog fixture→SQLite, seed default user Bundled end-of-branch commit on feat/postit-acl-members. PostIt UI for circles + per-post ACL - Reorganise PostIt.Tests into Auth/ and Blogs/ subfolders (Bearer/OIDC scope tests vs. blog API fakes live where they belong) and introduces PostItHeadlessCollection so the Avalonia.Headless tests share a single xUnit collection instead of contending with the EF-Core test host. - Adds BlogAclApiTests (a brand-new behavioural layer over POST /api/v1/blogacl) and the fakes it relies on (BlogApiTestFakes, BlogPostAuthorDtoTests, AddCircleMember DialogTests); pulls UserId-through-OIDC-sub path into BearerScopeTests / FakeAuthorizingBrowser / OidcStubAuthority. - App.axaml.cs gets a small PushPageAsync touch-up the new tests rely on. - Drops UnitTest1.cs (xUnit scaffold, never used). Yavsc.Blogs.Tests — SQLite instead of InMemory - Bumps Yavsc.Blogs.Tests.csproj on Microsoft.EntityFrameworkCore.Sqlite and rewrites BlogsWebServerFixture to hold a single shared SqliteConnection (Cache=Shared) for the fixture lifetime, with a sync Dispose close to dodge async teardown hangs. Reason: the EF Core InMemory provider silently ignores FKs, which masked the kind of bug we are about to pin in the ACL tests. SQLite enforces them, so any future INSERT that forgets to seed its parent rows fails loudly here instead of passing the test and breaking prod. - PublishEndpointTests and BlogApiSmokeTests get a one-line tweak to follow the new connection lifecycle. Foreign-key fallout: seed the default user in the fixture - Adds BlogsWebServerFixture.SeedUser(userName). Now that SQLite enforces BlogPost.AuthorId → AspNetUsers.Id, every test that POST/PUT/DELETE a BlogPost and sends AuthorId= 'tester' in the payload needs an AspNetUsers row to satisfy the FK or it returns 500 with SQLite Error 19. - BlogApiTests wraps the existing ResetDatabase with a ResetAndSeedDefaultUser helper for the six mutating tests; the four GET-only and ModelState-only tests keep the bare ResetDatabase. - Side benefit: every test in Yavsc.Blogs.Tests now finishes cleanly instead of hanging at teardown — previously a stuck test held the shared SqliteConnection open and the next tests waited indefinitely. Verified: dotnet test src/Yavsc.Blogs.Tests passes 25/25 green from a clean run, no fixture teardown hang.
2026-08-20 23:59:21 +01:00
showConfig:
2026-02-28 14:22:33 +00:00
@echo CONFIGURATION: $(CONFIGURATION)
2026-09-20 18:35:27 +01:00
@echo APPDIR: $(APPDIR)
2026-02-28 14:22:33 +00:00
2026-09-13 18:54:27 +01:00
showApiLogs:
@sudo journalctl -u yavscApi.service -S "2 min ago" | tee yavscApi.log
showOrgLogs:
@sudo journalctl -u yavscOrg.service -S "2 min ago" | tee yavscOrg.log
showBlogsLogs:
@sudo journalctl -u yavscBlogs.service -S "2 min ago" | tee yavscBlogs.log
2026-06-06 21:10:48 +01:00
clean:
2026-06-19 16:18:39 +01:00
@rm -rf generated
2026-06-06 21:10:48 +01:00
2026-09-21 12:49:38 +01:00
generated/:
@mkdir -p $@