Skip to content

refactor: Move demo user seeding to post-provisioning hook#2137

Merged
bjcoombs merged 3 commits intodevelopfrom
fix-demo-user-provisioning-hook
Apr 6, 2026
Merged

refactor: Move demo user seeding to post-provisioning hook#2137
bjcoombs merged 3 commits intodevelopfrom
fix-demo-user-provisioning-hook

Conversation

@bjcoombs
Copy link
Copy Markdown
Collaborator

@bjcoombs bjcoombs commented Apr 6, 2026

Summary

Move demo user seeding from a startup call (which races with schema provisioning) to a post-provisioning hook that runs after tenant schemas are fully migrated.

Problem

`SeedDemoUsers` ran at app startup before the provisioning worker finished creating tenant schemas. On deploys that drop and recreate schemas (every deploy since PR #2133), provisioning takes 30-60s. The startup seeder fired immediately, found no identity table, logged a warning, and gave up. The `operator@volterra.energy` demo user was never created, breaking Dex login on demo.

Fix

Register `AsDemoUserHook` as hook #7 in the post-provisioning chain. The hook:

  • Loads `DEMO_OPERATOR_*` env vars once at registration time
  • Builds a tenant lookup set from `DEMO_OPERATOR_TENANT` (comma-separated)
  • For each provisioned tenant, checks membership and seeds the demo user only for matching tenants
  • No-op when env vars are unset (production-safe)
  • Runs after admin-identity hook so the identity schema is guaranteed to exist

`SeedDemoUsers` remains exported for tests and manual use but is no longer called from the startup path.

Changes

services/identity/bootstrap/bootstrap.go

  • Add `AsDemoUserHook(repo) func(ctx, tenantID) error` following the `AsPostProvisioningHook` pattern

cmd/meridian/wire_services.go

  • Remove the startup `SeedDemoUsers` call (lines 100-104)
  • Register `"demo-operator"` hook after `"self-registered-admin"`
  • Update hook list comment to include hooks 6 and 7

Test plan

  • `go build ./...` clean
  • `go test -short ./services/identity/bootstrap/... ./cmd/meridian/...` passes
  • CI green
  • After merge + deploy to demo: `operator@volterra.energy` is created during provisioning, not at startup. Dex login works without manual restart.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 495b3721-9770-40cf-9722-c7e9071f60de

📥 Commits

Reviewing files that changed from the base of the PR and between 4009a7a and 2475bbb.

📒 Files selected for processing (1)
  • cmd/seed-dev/cmd/root.go

📝 Walkthrough

Walkthrough

Demo user seeding was relocated from server startup in wire_services.go to the seed-dev command in cmd/seed-dev/cmd/root.go. The change introduces a new seedDemoOperator function that runs after tenant provisioning completes, addressing a timing race condition where seeding previously occurred before tenant schemas were provisioned.

Changes

Cohort / File(s) Summary
Server startup seeding removal
cmd/meridian/wire_services.go
Removed the call to identitybootstrap.SeedDemoUsers(ctx, identityRepo) and its error handling from the registerServices flow; added a comment explaining demo user seeding is now handled by seed-dev after provisioning.
Seed-dev command enhancement
cmd/seed-dev/cmd/root.go
Added imports for identity seeding dependencies; introduced package-level ErrDatabaseURLRequired error; added seedDemoOperator(ctx) function to read demo operator credentials from environment variables, establish a direct database connection, and invoke the seeding logic; integrated the function into the runSeed control flow after tenant provisioning.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: moving demo user seeding from startup to a post-provisioning hook.
Description check ✅ Passed The description is directly related to the changeset, providing context about the problem, the solution, and the changes made to move demo user seeding to post-provisioning.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-demo-user-provisioning-hook

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link
Copy Markdown

claude Bot commented Apr 6, 2026

Claude Code Review

Commit: 2475bbb | CI: running (dependency review, multi-asset purity, proto freshness, secret scanning, shell lint, Tiltfile, Trivy pass; tests, lint, CodeQL, Docker build still pending)

Summary

Clean, well-scoped fix for a timing race where SeedDemoUsers ran at server startup before tenant schemas were provisioned. The fix moves demo user seeding to seed-dev, which already polls GetTenantProvisioningStatus until ACTIVE before proceeding — eliminating the race by design.

The implementation is minimal and correct: remove the startup call from wire_services.go, add seedDemoOperator() to seed-dev/cmd/root.go that opens a direct DB connection (necessary because the identity SetPassword RPC requires an invitation token) and delegates to the existing idempotent SeedDemoUsers function. No-op when env vars are unset (production-safe). DB connection properly closed via defer sqlDB.Close().

Risk Assessment

Area Level Detail
Blast radius Low Only affects demo user seeding; production is no-op when env vars unset
Rollback Safe Revert restores startup seeding; worst case is the original race returns
Scale Low Runs once per deploy, not per request
Cross-system Low Self-contained within identity bootstrap + seed-dev CLI
Migration N/A No schema changes

Findings

No actionable findings. Code is clean.

Bot Review Notes

All 4 review threads (2 CodeRabbit, 1 claude[bot], 1 CodeRabbit on DB close) are resolved. The DB connection leak flagged by both claude[bot] and CodeRabbit was fixed in commit 2475bbb.

Previously Flagged

Severity Location Description Status
Improvement cmd/seed-dev/cmd/root.go:413 DB connection not closed Resolved in 2475bbb
Note PR description PR body describes old AsDemoUserHook approach vs actual seed-dev implementation Informational

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Clean fix for the startup race — post-provisioning hook is the right pattern. See summary comment for details.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/meridian/wire_services.go`:
- Around line 257-260: The demo-user seeding hook is only registered inside
startProvisioningWorker and is skipped when prov == nil (i.e.,
SCHEMA_PROVISIONING_ENABLED=false), so DEMO_OPERATOR_* env vars are ignored; fix
by adding a fallback when prov == nil that executes the demo-seeding hook at
startup (or registers/executes identitybootstrap.AsDemoUserHook(identityRepo)
immediately) instead of returning early—modify startProvisioningWorker to detect
prov == nil and call/run the AsDemoUserHook (or equivalent demo-seed invocation)
against identityRepo so demo users are created even with provisioning disabled,
while keeping the existing w.RegisterPostProvisioningHook("demo-operator",
identitybootstrap.AsDemoUserHook(identityRepo)) path for the normal provisioned
flow.

In `@services/identity/bootstrap/bootstrap.go`:
- Around line 433-448: AsDemoUserHook currently calls seedDemoUser(repo, ...)
without verifying repo; add the same nil-repo guard used elsewhere: at the start
of AsDemoUserHook (or inside the returned closure) check if repo == nil and
return tenant.ErrNilRepository (or ErrNilRepository the project uses) to avoid
panics when miswired or in tests; reference the AsDemoUserHook function and
seedDemoUser so the guard mirrors other bootstrap entry points' behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f8a3d790-d2c2-409a-8590-734a370f1f00

📥 Commits

Reviewing files that changed from the base of the PR and between 28e0e04 and 817ec45.

📒 Files selected for processing (2)
  • cmd/meridian/wire_services.go
  • services/identity/bootstrap/bootstrap.go

Comment thread cmd/meridian/wire_services.go Outdated
Comment thread services/identity/bootstrap/bootstrap.go Outdated
SeedDemoUsers ran at server startup (wire_services.go:102) before
tenant schemas were provisioned. On deploys that drop and recreate
schemas, the provisioning worker takes 30-60s after boot, so the
startup seeder consistently failed with "relation identity does not
exist" and the demo operator user was never created.

Move demo user seeding to seed-dev, which already runs after
provisioning completes (it polls GetTenantProvisioningStatus until
ACTIVE). seed-dev is the correct home because:
- It only runs in deploy workflows, never in production
- It already handles all demo/dev data seeding (fixtures)
- It runs inside the container (access to DATABASE_URL and
  DEMO_OPERATOR_* env vars)
- It executes after provisioning, eliminating the timing race

The seeder uses a direct DB connection (not gRPC) because the
identity service's SetPassword RPC requires an invitation token by
design - there is no admin "create user with password" API. This
is consistent with how SeedDemoUsers worked before; only the call
site moved from server boot to seed-dev.

No-op when DEMO_OPERATOR_EMAIL or DEMO_OPERATOR_PASSWORD are unset,
making it safe for any environment including production.
@bjcoombs bjcoombs force-pushed the fix-demo-user-provisioning-hook branch from 817ec45 to 4009a7a Compare April 6, 2026 08:51
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See summary comment. 1 inline suggestion.

Comment thread cmd/seed-dev/cmd/root.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/seed-dev/cmd/root.go`:
- Around line 408-422: The DB connection returned by bootstrap.NewDatabase
(variable db) is never closed; after successfully creating db call db.DB() to
obtain the underlying *sql.DB (handle the error) and close it when done (e.g.,
defer sqlDB.Close() immediately after obtaining it or close it just before
returning), ensuring the connection pool is properly released around the calls
to identitypersistence.NewRepository and identitybootstrap.SeedDemoUsers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f1031598-4ea6-4af2-a9a4-a323e5b3f33d

📥 Commits

Reviewing files that changed from the base of the PR and between 817ec45 and 4009a7a.

📒 Files selected for processing (3)
  • .github/workflows/deploy-demo.yml
  • cmd/meridian/wire_services.go
  • cmd/seed-dev/cmd/root.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/meridian/wire_services.go

Comment thread cmd/seed-dev/cmd/root.go
Address review feedback from claude[bot] and CodeRabbit: the
bootstrap.NewDatabase connection pool was never closed. Add
defer sqlDB.Close() after obtaining the underlying sql.DB.
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. The seed-dev approach cleanly eliminates the timing race. All previous review findings resolved. See summary comment for details.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@bjcoombs bjcoombs merged commit 8d9b639 into develop Apr 6, 2026
31 checks passed
@bjcoombs bjcoombs deleted the fix-demo-user-provisioning-hook branch April 6, 2026 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant