From d2345a9b462f4dff51e8a6d9747c8abf1fe48a50 Mon Sep 17 00:00:00 2001 From: giaongo <83873333+giaongo@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:51:31 +0200 Subject: [PATCH 1/7] Move .nuget package installation to current running dir --- .diploi/helm/app.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.diploi/helm/app.yaml b/.diploi/helm/app.yaml index ca34610..e184e4a 100644 --- a/.diploi/helm/app.yaml +++ b/.diploi/helm/app.yaml @@ -78,6 +78,10 @@ spec: - name: {{ .identifier }} value: {{ .value | quote }} {{- end }} + {{- if eq .Values.stage "development" }} + - name: NUGET_PACKAGES + value: /app{{ .Values.folder }}/.nuget + {{- end }} volumeMounts: {{- if hasKey .Values.storage "code" }} - name: app-mount From c7789ab9fd5962c986d41e6bb3bb72b7de1b6803 Mon Sep 17 00:00:00 2001 From: giaongo <83873333+giaongo@users.noreply.github.com> Date: Tue, 6 Jan 2026 14:41:25 +0200 Subject: [PATCH 2/7] Add .nuget to .gitignore --- .gitignore | 8 +++++++- README.md | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 427a76b..f8038dd 100644 --- a/.gitignore +++ b/.gitignore @@ -483,4 +483,10 @@ docker-compose.override.yaml ## tmp/ -temp/ \ No newline at end of file +temp/ + +# NuGet packages +packages/ +*.nuget.props +*.nuget.targets +.nuget/ \ No newline at end of file diff --git a/README.md b/README.md index 8accb19..8cc9d10 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ dotnet add package Microsoft.EntityFrameworkCore.SqlServer The `libman.json` file manages client-side libraries, while the `.csproj` file tracks NuGet dependencies. Both are automatically restored during development and build. -> **Important:** After installing packages, open the Deployment page and restart the running Blazor container so it loads the new dependencies. +> **Important:** After installing packages, the development environment will automatically reload to include the new dependencies. ### Production From c5c1bcdd043d8d3ccf460d37c80e81ca7e856159 Mon Sep 17 00:00:00 2001 From: giaongo <83873333+giaongo@users.noreply.github.com> Date: Tue, 6 Jan 2026 15:59:55 +0200 Subject: [PATCH 3/7] Add dotnet restore in initContainers --- .diploi/helm/app.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.diploi/helm/app.yaml b/.diploi/helm/app.yaml index e184e4a..c256eeb 100644 --- a/.diploi/helm/app.yaml +++ b/.diploi/helm/app.yaml @@ -42,7 +42,7 @@ spec: - /bin/sh - -c - > - dotnet build || exit 1; + (dotnet restore && dotnet build) || exit 1; (dotnet tool restore && dotnet tool run libman restore) || echo "Libman restore failed, continuing..."; workingDir: /app{{ .Values.folder }} volumeMounts: From 03077b37d632832f743af7e2ac1958f77ba3f19c Mon Sep 17 00:00:00 2001 From: giaongo <83873333+giaongo@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:39:48 +0200 Subject: [PATCH 4/7] Set up dotnet watch script and nodemon watch for dependency update --- Dockerfile.dev | 13 ++++++++++-- Program.cs | 15 +++++++++++++ nodemon.json | 14 +++++++++++++ run-dotnet-watch.sh | 51 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 nodemon.json create mode 100644 run-dotnet-watch.sh diff --git a/Dockerfile.dev b/Dockerfile.dev index bc61c41..4d3c770 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,5 +1,10 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0 +RUN apt-get update \ + && apt-get install -y --no-install-recommends tini nodejs npm iproute2 net-tools \ + && npm install -g nodemon \ + && rm -rf /var/lib/apt/lists/* + ARG FOLDER=/app COPY --chown=1000:1000 . /app @@ -10,10 +15,14 @@ USER 1000:1000 EXPOSE 5054 -ENV ASPNETCORE_URLS=http://+:5054 +ENV ASPNETCORE_URLS=http://0.0.0.0:5054 ENV ASPNETCORE_ENVIRONMENT=Development ENV ASPNETCORE_HTTPS_PORT="" -CMD ["dotnet", "watch", "--no-launch-profile", "--hot-reload", "--non-interactive"] \ No newline at end of file +ENV DOTNET_USE_POLLING_FILE_WATCHER=1 + +ENTRYPOINT ["/usr/bin/tini", "--"] + +CMD ["nodemon"] \ No newline at end of file diff --git a/Program.cs b/Program.cs index bd6b76f..94655cf 100644 --- a/Program.cs +++ b/Program.cs @@ -1,5 +1,6 @@ using component_blazor.Components; using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Server.Kestrel.Core; var builder = WebApplication.CreateBuilder(args); @@ -7,6 +8,20 @@ builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); +builder.Services.Configure(options => +{ + options.ShutdownTimeout = TimeSpan.FromSeconds(1); +}); + +// Configure Kestrel to allow port reuse and SO_REUSEADDR +builder.WebHost.ConfigureKestrel((context, options) => +{ + options.ListenAnyIP(5054, listenOptions => + { + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); +}); + // Configure data protection for Docker builder.Services.AddDataProtection() .PersistKeysToFileSystem(new DirectoryInfo("/var/dpkeys")) diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 0000000..a86906a --- /dev/null +++ b/nodemon.json @@ -0,0 +1,14 @@ +{ + "watch": [ + "**/*.csproj" + ], + "ignore": [ + "**/bin/**", + "**/obj/**", + "**/*.json" + ], + "ext": "csproj", + "exec": "sh run-dotnet-watch.sh", + "signal": "SIGTERM", + "delay": "500" +} \ No newline at end of file diff --git a/run-dotnet-watch.sh b/run-dotnet-watch.sh new file mode 100644 index 0000000..a0be836 --- /dev/null +++ b/run-dotnet-watch.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -eu + +PORT=5054 + +echo "=== Ensuring clean shutdown ===" + +# Gracefully stop dotnet-related servers (Roslyn / MSBuild) +dotnet build-server shutdown 2>/dev/null || true + +# Try graceful shutdown first +if ss -ltnp 2>/dev/null | grep -q ":${PORT} "; then + echo "Port ${PORT} is in use, attempting graceful shutdown..." + pkill -TERM -f "dotnet watch" 2>/dev/null || true + pkill -TERM -f "dotnet run" 2>/dev/null || true + pkill -TERM -f "dotnet" 2>/dev/null || true +fi + +# Wait for port to be released (bounded wait) +echo "=== Waiting for port ${PORT} to be released ===" +for i in {1..50}; do + if ! ss -ltn 2>/dev/null | grep -q ":${PORT} "; then + echo "Port ${PORT} released." + break + fi + sleep 0.2 +done + +echo "=== Hard cleaning port ${PORT} if still busy ===" +# Hard kill only if still stuck +if ss -ltn 2>/dev/null | grep -q ":${PORT} "; then + echo "Port ${PORT} still busy, forcing release..." + fuser -k ${PORT}/tcp 2>/dev/null || true + sleep 1 +fi + +# Additional cleanup for TIME_WAIT sockets +echo "=== Checking for lingering TIME_WAIT sockets ===" +if ss -ltn 2>/dev/null | grep -q ":${PORT}.*TIME-WAIT"; then + echo "Waiting for TIME_WAIT sockets to clear..." + sleep 2 +fi + +echo "=== Starting to build ===" +dotnet clean +dotnet build + +echo "=== Starting dotnet watch ===" +exec dotnet watch run \ + --no-launch-profile \ + --non-interactive \ \ No newline at end of file From 04c0323bdf98bb8462e36188ec0885880054f4ae Mon Sep 17 00:00:00 2001 From: giaongo <83873333+giaongo@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:46:58 +0200 Subject: [PATCH 5/7] Refactoring and update README --- README.md | 10 ++++++---- run-dotnet-watch.sh | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8cc9d10..68d8ada 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,19 @@ Link to the full guide - upcoming ### Development -During development, the container uses `dotnet watch` to enable automatic reloads when files change. The development server is started with: +During development, the container uses the combination of `nodemon` and `dotnet watch` to enable automatic reloads when files and new dependencies change. The development server is started with: ```sh -dotnet watch --no-launch-profile --hot-reload --non-interactive +dotnet watch run \ + --no-launch-profile \ + --non-interactive ``` This will: - Use `dotnet watch` to monitor for changes to .cs, .razor, and .css files and restart the server when changes are detected. - Run the Blazor Server application with hot reload enabled. - Start the app on port 5054. -- Enable automatic browser refresh when Razor components or CSS files change. +- Avoid using any launch profile so environment variables are controlled by the container. ### Installing Packages @@ -64,7 +66,7 @@ dotnet add package Microsoft.EntityFrameworkCore.SqlServer The `libman.json` file manages client-side libraries, while the `.csproj` file tracks NuGet dependencies. Both are automatically restored during development and build. -> **Important:** After installing packages, the development environment will automatically reload to include the new dependencies. +`dotnet add package` updates the `.csproj` and restores the package to the local NuGet cache. `nodemon` notices the new package reference in `component-blazor.csproj` and restarts the runtime so the change is picked up immediately. ### Production diff --git a/run-dotnet-watch.sh b/run-dotnet-watch.sh index a0be836..6c2ed67 100644 --- a/run-dotnet-watch.sh +++ b/run-dotnet-watch.sh @@ -48,4 +48,4 @@ dotnet build echo "=== Starting dotnet watch ===" exec dotnet watch run \ --no-launch-profile \ - --non-interactive \ \ No newline at end of file + --non-interactive \ No newline at end of file From 5068eec2b7ac85fb50c1f29f2c1010ba35a37df5 Mon Sep 17 00:00:00 2001 From: giaongo <83873333+giaongo@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:09:09 +0200 Subject: [PATCH 6/7] Allow loading server automatically on app crash due to missing dependencies --- nodemon.json | 9 ++++----- run-dotnet-watch.sh | 28 +++++++++++----------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/nodemon.json b/nodemon.json index a86906a..e1cae3a 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,14 +1,13 @@ { "watch": [ - "**/*.csproj" + "." ], "ignore": [ "**/bin/**", - "**/obj/**", - "**/*.json" + "**/obj/**" ], - "ext": "csproj", + "ext": "cs,csproj,razor,cshtml,json,html,js,css", "exec": "sh run-dotnet-watch.sh", "signal": "SIGTERM", - "delay": "500" + "delay": "1000" } \ No newline at end of file diff --git a/run-dotnet-watch.sh b/run-dotnet-watch.sh index 6c2ed67..09cdf88 100644 --- a/run-dotnet-watch.sh +++ b/run-dotnet-watch.sh @@ -3,17 +3,15 @@ set -eu PORT=5054 -echo "=== Ensuring clean shutdown ===" - -# Gracefully stop dotnet-related servers (Roslyn / MSBuild) -dotnet build-server shutdown 2>/dev/null || true - # Try graceful shutdown first +echo "=== Checking if port ${PORT} is in use ===" if ss -ltnp 2>/dev/null | grep -q ":${PORT} "; then echo "Port ${PORT} is in use, attempting graceful shutdown..." pkill -TERM -f "dotnet watch" 2>/dev/null || true pkill -TERM -f "dotnet run" 2>/dev/null || true pkill -TERM -f "dotnet" 2>/dev/null || true + dotnet clean 2>/dev/null || true + sleep 2 fi # Wait for port to be released (bounded wait) @@ -23,7 +21,7 @@ for i in {1..50}; do echo "Port ${PORT} released." break fi - sleep 0.2 + sleep 0.25 done echo "=== Hard cleaning port ${PORT} if still busy ===" @@ -31,21 +29,17 @@ echo "=== Hard cleaning port ${PORT} if still busy ===" if ss -ltn 2>/dev/null | grep -q ":${PORT} "; then echo "Port ${PORT} still busy, forcing release..." fuser -k ${PORT}/tcp 2>/dev/null || true - sleep 1 + sleep 2 fi -# Additional cleanup for TIME_WAIT sockets +# Check for TIME_WAIT sockets (use ss -tan to see all TCP states, not just LISTEN) echo "=== Checking for lingering TIME_WAIT sockets ===" -if ss -ltn 2>/dev/null | grep -q ":${PORT}.*TIME-WAIT"; then - echo "Waiting for TIME_WAIT sockets to clear..." +if ss -tan state time-wait 2>/dev/null | grep -q ":${PORT} "; then + echo "TIME_WAIT sockets found on port ${PORT}, waiting briefly..." sleep 2 fi -echo "=== Starting to build ===" -dotnet clean -dotnet build - -echo "=== Starting dotnet watch ===" -exec dotnet watch run \ +echo "=== Starting running dotnet application" +exec dotnet run \ --no-launch-profile \ - --non-interactive \ No newline at end of file + --non-interactive \ No newline at end of file From 88312ab38dd0f7a5a7adcee1803d33e8c0460a2a Mon Sep 17 00:00:00 2001 From: giaongo <83873333+giaongo@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:16:06 +0200 Subject: [PATCH 7/7] Update README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 68d8ada..554a90a 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ Link to the full guide - upcoming ### Development -During development, the container uses the combination of `nodemon` and `dotnet watch` to enable automatic reloads when files and new dependencies change. The development server is started with: +During development, the container uses `nodemon` to enable automatic reloads when files and new dependencies change. The development server is started with: ```sh -dotnet watch run \ +dotnet run \ --no-launch-profile \ - --non-interactive + --non-interactive ``` This will: