Skip to content

JasperFx.Aspire: startup gates (A2)#404

Merged
jeremydmiller merged 1 commit into
mainfrom
feat-jasperfx-aspire-startup-gates
May 31, 2026
Merged

JasperFx.Aspire: startup gates (A2)#404
jeremydmiller merged 1 commit into
mainfrom
feat-jasperfx-aspire-startup-gates

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Implements A2 from the design doc (.claude/docs/jasperfx-aspire-startup-gates-design.md), the orchestration-time complement to A1 (merged in #402). Ships in the same JasperFx.Aspire package.

Run a JasperFx provisioning verb as a run-to-completion resource that finishes before the service starts, wired via Aspire's WaitForCompletion:

var db = builder.AddPostgres("pg").AddDatabase("appdb");

builder.AddProject<Projects.Api>("api")
    .WithReference(db)
    .WaitFor(db)
    .WithJasperFxStartup("resources", "setup"); // runs to completion before "api" starts

Why this is low-risk vs A1

A1's hard problem was resolving a resource's environment inside a callback and spawning a child by hand. A2 has no such problem — each gate is a first-class Aspire resource pointing at the same project with the verb as args, so Aspire injects connection strings/env natively. The gate inherits the parent's references (declared before WithJasperFxStartup).

What's here

  • WithJasperFxStartup (single-verb + fluent forms) + JasperFxStartupGate (ResourceName / Parallel / BlockOnFailure / RunWhen / ConfigureGate) + JasperFxStartupBuilder (Run / Check). Parent constraint is IResourceWithEnvironment, IResourceWithWaitSupport.
  • Gate factoryAddProject(name, sameProjectPath) + WithArgs(verb, arg); clones the parent's reference/env annotations (EnvironmentCallbackAnnotation / WaitAnnotation / ResourceRelationshipAnnotation) onto the gate. Annotations are snapshotted before any wiring so the WaitForCompletion annotations added to the parent don't leak onto later gates.
  • Ordering — sequential WaitForCompletion chaining in declaration order; Parallel opt-out.
  • check-env is an opt-in blocking gate (Check() / WithJasperFxStartup("check-env")); RunWhen makes a gate environment-conditional; non-zero exit fails fast.
  • Defaults: resourcessetup, codegenwrite (distinct from A1's codegenpreview).

Verification

  • dotnet build jasperfx.slnx -c Release clean; VitePress docs build clean.
  • 36 unit tests (20 new): pure helpers + app-model assertions over AddProject / WaitForCompletion / annotation-clone wiring via DistributedApplication.CreateBuilderno Docker.
  • ✅ Sample AppHost gains a resources setup + blocking check-env gate and compiles.
  • ⚠️ Manual step: the live dashboard run (dotnet run --project src/AspireSample/AspireSample.AppHost) — confirm the gates show Finished and api starts only after them. The app-model tests cover the wiring; a live run can't be automated headlessly.

Spike findings (design step 0)

Validated against real Aspire 13.3.1: run-to-completion modeling of a project-with-verb-args, WaitForCompletion gating, and annotation cloning. Two corrections vs the draft API: the parent needs the IResourceWithWaitSupport constraint (not just IResourceWithEnvironment), and the wait annotation type is WaitAnnotation.

Docs

New "Startup gates" section on the Aspire dashboard integration page (/cli/aspire).

Downstream docs

Tracking issues for Marten / Wolverine / Polecat filed separately (design §9).

🤖 Generated with Claude Code

…rvice starts (A2)

Extends JasperFx.Aspire (same package as A1) with run-to-completion startup gates.
One line runs a JasperFx provisioning verb to completion BEFORE the owning service
starts, wired via Aspire's WaitForCompletion:

    builder.AddProject<Projects.Api>("api")
        .WithReference(db).WaitFor(db)
        .WithJasperFxStartup("resources", "setup");

Unlike A1's on-demand buttons, a gate is a first-class Aspire resource pointing at
the SAME project with the verb as args — so Aspire injects connection strings/env
natively (no callback-spawn or env-resolution trick; A2 sidesteps A1's main risk).

- WithJasperFxStartup (single-verb + fluent forms) + JasperFxStartupGate
  (ResourceName/Parallel/BlockOnFailure/RunWhen/ConfigureGate) + JasperFxStartupBuilder
  (Run/Check). Parent constraint is IResourceWithEnvironment + IResourceWithWaitSupport.
- Gate factory: AddProject(name, sameProjectPath) + WithArgs(verb, arg); clones the
  parent's reference/env annotations (EnvironmentCallbackAnnotation/WaitAnnotation/
  ResourceRelationshipAnnotation) onto the gate, snapshotting them BEFORE wiring so
  WaitForCompletion annotations don't leak onto later gates.
- Sequential WaitForCompletion chaining in declaration order; Parallel opt-out;
  check-env opt-in blocking; RunWhen environment gating; fail-fast on non-zero exit.

Defaults: resources -> setup, codegen -> write (distinct from A1's codegen -> preview).
20 new tests (pure helpers + app-model assertions over AddProject/WaitForCompletion/
clone wiring via DistributedApplication.CreateBuilder, no Docker). Sample AppHost gains
a resources-setup + blocking check-env gate. Docs: new "Startup gates" section on the
Aspire dashboard integration page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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