Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved moderate findings affect CI enforcement and endpoint error/cancellation handling.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Updates the Demo for .NET 10 container deployment, safer request handling, documented usage, and Docker CI coverage.
Changes:
- Adds a multi-stage, non-root Docker image and root
.dockerignore. - Improves database context creation, cancellation handling, and error responses.
- Adds local/Cloud Run documentation and Docker smoke-test automation.
File summaries
| File | Reviewed changes | Final review findings |
|---|---|---|
Csag.AzureSqlFederatedIdentity.Demo/README.md |
Documents configuration, schema, Docker, and Cloud Run usage. | Foreground Docker command blocks the following curl (nit, 1 vote); ADC mount may be unreadable by UID 1654 (nit, 1 vote). |
Csag.AzureSqlFederatedIdentity.Demo/Program.cs |
Adds cancellation propagation and generic problem responses. | Factory resolution can bypass endpoint error handling (moderate, 1 vote); expected cancellations are logged as errors and returned as 500 responses (moderate, 2 votes). |
Csag.AzureSqlFederatedIdentity.Demo/Dockerfile |
Defines the .NET 10 multi-stage, non-root image. | No final findings. |
Csag.AzureSqlFederatedIdentity.Demo/Database/AppDbContextFactory.cs |
Removes sync-over-async creation and validates connection settings. | Constructor validation can bypass endpoint logging and generic error handling (moderate, 1 vote). |
.github/workflows/ci.yml |
Adds Docker build and smoke testing. | Smoke test does not verify the effective non-root UID (moderate, 3 votes). |
.dockerignore |
Excludes build artifacts and unnecessary repository content. | No final findings. |
.changeset/demo-fixes.md |
Records the Demo maintenance changes. | No final findings. |
Review details
Suppressed comments (4)
Csag.AzureSqlFederatedIdentity.Demo/Database/AppDbContextFactory.cs:20
- Because
IAppDbContextFactoryis resolved as a Minimal API handler parameter, this constructor runs before the lambda enters itstry. With the shipped empty connection string, this new exception therefore bypasses the endpoint'sLogErrorandResults.Problem()(and Development's exception page can expose the raw message). Resolve the factory inside thetry, or move this validation into code called from inside it.
var connectionString = configuration.GetConnectionString("DefaultConnection");
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new InvalidOperationException("Connection string 'DefaultConnection' not set.");
Csag.AzureSqlFederatedIdentity.Demo/Program.cs:18
IAppDbContextFactoryis resolved by Minimal API before the handler body runs. Because its constructor now throws for a missing or blank connection string, that failure occurs before thistry, so the endpoint neither logs it nor returns the documented generic problem response (and Development can expose the exception). Resolve the factory inside thetry, or move the validation intoCreateDbContextAsync.
app.MapGet("/test", async ([FromServices] IAppDbContextFactory dbFactory, [FromServices] ILogger<Program> logger, CancellationToken cancellationToken) =>
Csag.AzureSqlFederatedIdentity.Demo/README.md:72
- This command runs the container in the foreground, so the shell blocks at
docker runand never reaches the followingcurl. Run it detached (with a container name), wait for readiness, and stop it after the request so the documented verification is actually executable.
docker run --rm -p 8080:8080 \
Csag.AzureSqlFederatedIdentity.Demo/README.md:78
- The bind mount preserves the host file's ownership and mode, while the image runs as uid 1654. A normal
gcloud auth application-default logincredentials file is commonly owner-only (0600), so this exact command can make/testfail because/adc.jsonis unreadable. Document a safe readable copy/ACL for uid 1654 (rather than mounting the private file as-is) before presenting this as the local Docker command.
-v "$HOME/.config/gcloud/application_default_credentials.json:/adc.json:ro" \
-e GOOGLE_APPLICATION_CREDENTIALS=/adc.json \
- Files reviewed: 7/7 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
5177b54 to
1dbb04c
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Factory validation can bypass generic error handling, and the documented IAM and execution flows need correction.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (4)
Csag.AzureSqlFederatedIdentity.Demo/Program.cs:18
IAppDbContextFactoryis resolved for this[FromServices]parameter before the endpoint delegate enters thetry. With the new whitespace validation, a missing or blankDefaultConnectiontherefore throws during parameter binding and bypasses bothLogErrorandResults.Problem(and can expose the exception under Development). Resolve the factory inside thetry, or move this validation to code executed inside it.
app.MapGet("/test", async ([FromServices] IAppDbContextFactory dbFactory, [FromServices] ILogger<Program> logger, CancellationToken cancellationToken) =>
Csag.AzureSqlFederatedIdentity.Demo/README.md:78
- The bind mount does not make the ADC file readable by the image's
appuser.gcloud auth application-default loginnormally creates this file mode 0600 for the host user, while the container runs as uid 1654, so this command fails with a permission error on a typical Linux workstation before authentication starts. Please document and implement a secure UID/temporary-file ownership workaround instead of only noting the requirement.
-v "$HOME/.config/gcloud/application_default_credentials.json:/adc.json:ro" \
-e GOOGLE_APPLICATION_CREDENTIALS=/adc.json \
Csag.AzureSqlFederatedIdentity.Demo/README.md:72
docker runis foreground here, so the shell blocks at this line and never executes the followingcurlin the same code block. Run detached with a readiness retry (and stop it afterward), or explicitly tell the reader to use a second terminal; otherwise the documented Docker run sequence cannot be followed as written.
docker run --rm -p 8080:8080 \
Csag.AzureSqlFederatedIdentity.Demo/README.md:57
- This code block also starts
dotnet runin the foreground, so the nextcurlis not reached when the commands are pasted into one shell. Add an explicit second-terminal instruction or provide a background/readiness/cleanup sequence so the documented source-run flow is executable as written.
dotnet run --project Csag.AzureSqlFederatedIdentity.Demo
- Files reviewed: 7/7 changed files
- Comments generated: 2
- Review effort level: Lite
1dbb04c to
f1361fc
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The README’s local execution examples and ADC Docker usage need correction.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (2)
Csag.AzureSqlFederatedIdentity.Demo/README.md:57
dotnet runkeeps the shell occupied, so the nextcurlcommand in this same code block is not executed. Show the commands in separate terminals (or otherwise background and wait for the server) so the documented source run actually reaches/test.
dotnet run --project Csag.AzureSqlFederatedIdentity.Demo
Csag.AzureSqlFederatedIdentity.Demo/README.md:72
- This
docker runis foregrounded, so it blocks at the container command and the followingcurlis never reached unless the user opens another terminal. Make the documented sequence runnable by starting the container detached (and stopping it after the request), or explicitly put the curl in a second-terminal example.
docker run --rm -p 8080:8080 \
- Files reviewed: 7/7 changed files
- Comments generated: 1
- Review effort level: Lite
The Demo's Dockerfile could not build from any context (it copied *.csproj from a folder holding only the Demo project, whose ProjectReference points outside that context) and its ENTRYPOINT named an assembly that does not exist. It is now a multi-stage build whose context is the repository root: it copies the shared build files, both project files and their lock files, restores the Demo in locked mode, publishes it on the .NET 10 SDK image and runs it on aspnet:10.0 as the non-root app user on port 8080. A root .dockerignore keeps host build output and repository metadata out of the context. CI gains a docker job that builds the image and checks that the container serves "/" (with placeholder settings, since the library validates them at startup), so neither breakage can regress unnoticed. AppDbContextFactory drops the sync-over-async CreateDbContext() and the unused IDbContextFactory<AppDbContext> implementation, treats a blank connection string as missing, and receives the request's cancellation token from the /test endpoint. /test logs failures and returns a generic problem response instead of echoing exception messages to anonymous callers. The Demo README explains ADC on Cloud Run and on a workstation, the four settings with their user-secrets and environment-variable keys, the SQL to create TestTable (the app's identity has no DDL rights, so the app never creates schema), and the docker build/run commands. The package itself is unchanged, hence the empty changeset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ntainer in CI logs The library only calls generateIdToken, for which roles/iam.serviceAccountOpenIdTokenCreator bound on the service account is sufficient. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n propagate Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…en the factory is The factory is constructed while the endpoint's parameters are bound, so a missing connection string escaped the handler's error handling. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ADC file is readable Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
f1361fc to
1c65d45
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
The changes span Docker, CI, runtime, and database behavior, and readiness assessments are not unanimous.
Review details
Suppressed comments (1)
Csag.AzureSqlFederatedIdentity.Demo/README.md:80
docker runis also foreground by default, so thiscurlis never executed when the shown block is pasted into one shell; the documented Docker test therefore cannot be run as written. Either add a detached/readiness flow, or explicitly mark this request as being run from a second terminal.
curl http://localhost:8080/test
- Files reviewed: 7/7 changed files
- Comments generated: 1
- Review effort level: Lite
…point from a second terminal Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
Makes the Demo buildable, runnable and honest.
sdk:10.0/aspnet:10.0: copiesglobal.json,nuget.config,Directory.*.props, the csproj +packages.lock.jsonfiles, restores in--locked-mode, publishes the Demo; runtime stage listens on 8080 (Cloud Run), runs as the non-rootappuser, and theENTRYPOINTfinally names the assembly that exists. Repo-root.dockerignorekeepsbin/,obj/,.git/,node_modules/out of the context.dockerjob that builds the image, starts it with placeholder identity settings, curls/and checks the container runs unprivileged — so the image cannot silently rot again.AppDbContextFactory: the sync-over-asyncCreateDbContext()and the unregisteredIDbContextFactory<T>implementation are gone; a blank connection string is treated as missing; the request'sCancellationTokenflows through./testlogs the exception and returns a generic problem response instead of the raw message.Csag.AzureSqlFederatedIdentity.Demo/README.md: how to run it locally (ADC, user-secrets / env vars with the exact keys) and on Cloud Run, the SQL to createTestTable(the app's identity normally has only reader/writer rights, so schema is not auto-created), the least-privilege IAM role bound on the service account, and the docker build/run commands.Stacked on the runtime PR.
Review findings addressed
1, 2 (plus
.dockerignore, the Demo's sync-over-async path, the exception leak, and the missing schema/run story).Validation
Proven on Docker 29.7.2 from the repo root:
docker build -f Csag.AzureSqlFederatedIdentity.Demo/Dockerfile -t csag-demo .✅docker runwith the three placeholder identity settings →GET /returns the greeting ✅;id -uinside the container is1654(non-root) ✅Note: the library validates the identity settings at startup, so the container needs
Csag.AzureSqlFederatedIdentity__TenantId,__ClientIdand__Google__ServiceAccountEmailset to start — the README says so, and the CI smoke test passes placeholders.🤖 Generated with Claude Code