├── .editorconfig ├── .github ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── dotnet.yml │ ├── publish.yaml │ └── releasedrafter.yml ├── .gitignore ├── InfinityFlow.Aspire.Temporal.sln ├── LICENSE ├── README.md ├── docs ├── aspire-dashboard-exe.png ├── aspire-dashboard-temporal-metrics.png └── aspire-dashboard-temporal-tracing.png ├── sample ├── Api │ ├── Api.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ └── appsettings.json ├── AppHost │ ├── AppHost.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ └── appsettings.json ├── ServiceDefaults │ ├── Extensions.cs │ └── ServiceDefaults.csproj └── Worker │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Worker.csproj │ └── appsettings.json └── src └── InfinityFlow.Aspire.Temporal ├── Enums.cs ├── InfinityFlow.Aspire.Temporal.csproj ├── TemporalServerContainerBuilderExtensions.cs ├── TemporalServerExecutableBuilderExtensions.cs ├── TemporalServerExecutableResource.cs ├── TemporalServerResourceArguments.cs ├── TemporalServerResourceBuilder.cs └── assets ├── LICENSE ├── README.md └── packageIcon.png /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # All files 4 | [*] 5 | indent_style = space 6 | 7 | # Xml files 8 | [*.xml] 9 | indent_size = 2 10 | 11 | # C# files 12 | [*.cs] 13 | 14 | #### Core EditorConfig Options #### 15 | 16 | # Indentation and spacing 17 | indent_size = 4 18 | tab_width = 4 19 | 20 | # New line preferences 21 | end_of_line = crlf 22 | insert_final_newline = false 23 | 24 | #### .NET Coding Conventions #### 25 | [*.{cs,vb}] 26 | 27 | # Organize usings 28 | dotnet_separate_import_directive_groups = true 29 | dotnet_sort_system_directives_first = true 30 | file_header_template = unset 31 | 32 | # this. and Me. preferences 33 | dotnet_style_qualification_for_event = false:silent 34 | dotnet_style_qualification_for_field = false:silent 35 | dotnet_style_qualification_for_method = false:silent 36 | dotnet_style_qualification_for_property = false:silent 37 | 38 | # Language keywords vs BCL types preferences 39 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 40 | dotnet_style_predefined_type_for_member_access = true:silent 41 | 42 | # Parentheses preferences 43 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 44 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 45 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 46 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 47 | 48 | # Modifier preferences 49 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 50 | 51 | # Expression-level preferences 52 | dotnet_style_coalesce_expression = true:suggestion 53 | dotnet_style_collection_initializer = true:suggestion 54 | dotnet_style_explicit_tuple_names = true:suggestion 55 | dotnet_style_null_propagation = true:suggestion 56 | dotnet_style_object_initializer = true:suggestion 57 | dotnet_style_operator_placement_when_wrapping = beginning_of_line 58 | dotnet_style_prefer_auto_properties = true:suggestion 59 | dotnet_style_prefer_compound_assignment = true:suggestion 60 | dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion 61 | dotnet_style_prefer_conditional_expression_over_return = true:suggestion 62 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 63 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 64 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 65 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion 66 | dotnet_style_prefer_simplified_interpolation = true:suggestion 67 | 68 | # Field preferences 69 | dotnet_style_readonly_field = true:warning 70 | 71 | # Parameter preferences 72 | dotnet_code_quality_unused_parameters = all:suggestion 73 | 74 | # Suppression preferences 75 | dotnet_remove_unnecessary_suppression_exclusions = none 76 | 77 | #### C# Coding Conventions #### 78 | [*.cs] 79 | 80 | # var preferences 81 | csharp_style_var_elsewhere = false:silent 82 | csharp_style_var_for_built_in_types = false:silent 83 | csharp_style_var_when_type_is_apparent = false:silent 84 | 85 | # Expression-bodied members 86 | csharp_style_expression_bodied_accessors = true:silent 87 | csharp_style_expression_bodied_constructors = false:silent 88 | csharp_style_expression_bodied_indexers = true:silent 89 | csharp_style_expression_bodied_lambdas = true:suggestion 90 | csharp_style_expression_bodied_local_functions = false:silent 91 | csharp_style_expression_bodied_methods = false:silent 92 | csharp_style_expression_bodied_operators = false:silent 93 | csharp_style_expression_bodied_properties = true:silent 94 | 95 | # Pattern matching preferences 96 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 97 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 98 | csharp_style_prefer_not_pattern = true:suggestion 99 | csharp_style_prefer_pattern_matching = true:silent 100 | csharp_style_prefer_switch_expression = true:suggestion 101 | 102 | # Null-checking preferences 103 | csharp_style_conditional_delegate_call = true:suggestion 104 | 105 | # Modifier preferences 106 | csharp_prefer_static_local_function = true:warning 107 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent 108 | 109 | # Code-block preferences 110 | csharp_prefer_braces = true:silent 111 | csharp_prefer_simple_using_statement = true:suggestion 112 | 113 | # Expression-level preferences 114 | csharp_prefer_simple_default_expression = true:suggestion 115 | csharp_style_deconstructed_variable_declaration = true:suggestion 116 | csharp_style_inlined_variable_declaration = true:suggestion 117 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 118 | csharp_style_prefer_index_operator = true:suggestion 119 | csharp_style_prefer_range_operator = true:suggestion 120 | csharp_style_throw_expression = true:suggestion 121 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 122 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 123 | 124 | # 'using' directive preferences 125 | csharp_using_directive_placement = outside_namespace:silent 126 | 127 | #### C# Formatting Rules #### 128 | 129 | # New line preferences 130 | csharp_new_line_before_catch = true 131 | csharp_new_line_before_else = true 132 | csharp_new_line_before_finally = true 133 | csharp_new_line_before_members_in_anonymous_types = true 134 | csharp_new_line_before_members_in_object_initializers = true 135 | csharp_new_line_before_open_brace = all 136 | csharp_new_line_between_query_expression_clauses = true 137 | 138 | # Indentation preferences 139 | csharp_indent_block_contents = true 140 | csharp_indent_braces = false 141 | csharp_indent_case_contents = true 142 | csharp_indent_case_contents_when_block = true 143 | csharp_indent_labels = one_less_than_current 144 | csharp_indent_switch_labels = true 145 | 146 | # Space preferences 147 | csharp_space_after_cast = false 148 | csharp_space_after_colon_in_inheritance_clause = true 149 | csharp_space_after_comma = true 150 | csharp_space_after_dot = false 151 | csharp_space_after_keywords_in_control_flow_statements = true 152 | csharp_space_after_semicolon_in_for_statement = true 153 | csharp_space_around_binary_operators = before_and_after 154 | csharp_space_around_declaration_statements = false 155 | csharp_space_before_colon_in_inheritance_clause = true 156 | csharp_space_before_comma = false 157 | csharp_space_before_dot = false 158 | csharp_space_before_open_square_brackets = false 159 | csharp_space_before_semicolon_in_for_statement = false 160 | csharp_space_between_empty_square_brackets = false 161 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 162 | csharp_space_between_method_call_name_and_opening_parenthesis = false 163 | csharp_space_between_method_call_parameter_list_parentheses = false 164 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 165 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 166 | csharp_space_between_method_declaration_parameter_list_parentheses = false 167 | csharp_space_between_parentheses = false 168 | csharp_space_between_square_brackets = false 169 | 170 | # Wrapping preferences 171 | csharp_preserve_single_line_blocks = true 172 | csharp_preserve_single_line_statements = true 173 | 174 | #### Naming styles #### 175 | [*.{cs,vb}] 176 | 177 | # Naming rules 178 | 179 | dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion 180 | dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces 181 | dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase 182 | 183 | dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion 184 | dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces 185 | dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase 186 | 187 | dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion 188 | dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters 189 | dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase 190 | 191 | dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion 192 | dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods 193 | dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase 194 | 195 | dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion 196 | dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties 197 | dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase 198 | 199 | dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion 200 | dotnet_naming_rule.events_should_be_pascalcase.symbols = events 201 | dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase 202 | 203 | dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion 204 | dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables 205 | dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase 206 | 207 | dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion 208 | dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants 209 | dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase 210 | 211 | dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion 212 | dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters 213 | dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase 214 | 215 | dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion 216 | dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields 217 | dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase 218 | 219 | dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion 220 | dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields 221 | dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase 222 | 223 | dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion 224 | dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields 225 | dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase 226 | 227 | dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion 228 | dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields 229 | dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase 230 | 231 | dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion 232 | dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields 233 | dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase 234 | 235 | dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion 236 | dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields 237 | dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase 238 | 239 | dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion 240 | dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields 241 | dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase 242 | 243 | dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion 244 | dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums 245 | dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase 246 | 247 | dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion 248 | dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions 249 | dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase 250 | 251 | dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion 252 | dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members 253 | dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase 254 | 255 | # Symbol specifications 256 | 257 | dotnet_naming_symbols.interfaces.applicable_kinds = interface 258 | dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 259 | dotnet_naming_symbols.interfaces.required_modifiers = 260 | 261 | dotnet_naming_symbols.enums.applicable_kinds = enum 262 | dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 263 | dotnet_naming_symbols.enums.required_modifiers = 264 | 265 | dotnet_naming_symbols.events.applicable_kinds = event 266 | dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 267 | dotnet_naming_symbols.events.required_modifiers = 268 | 269 | dotnet_naming_symbols.methods.applicable_kinds = method 270 | dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 271 | dotnet_naming_symbols.methods.required_modifiers = 272 | 273 | dotnet_naming_symbols.properties.applicable_kinds = property 274 | dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 275 | dotnet_naming_symbols.properties.required_modifiers = 276 | 277 | dotnet_naming_symbols.public_fields.applicable_kinds = field 278 | dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal 279 | dotnet_naming_symbols.public_fields.required_modifiers = 280 | 281 | dotnet_naming_symbols.private_fields.applicable_kinds = field 282 | dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected 283 | dotnet_naming_symbols.private_fields.required_modifiers = 284 | 285 | dotnet_naming_symbols.private_static_fields.applicable_kinds = field 286 | dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected 287 | dotnet_naming_symbols.private_static_fields.required_modifiers = static 288 | 289 | dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum 290 | dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 291 | dotnet_naming_symbols.types_and_namespaces.required_modifiers = 292 | 293 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 294 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 295 | dotnet_naming_symbols.non_field_members.required_modifiers = 296 | 297 | dotnet_naming_symbols.type_parameters.applicable_kinds = namespace 298 | dotnet_naming_symbols.type_parameters.applicable_accessibilities = * 299 | dotnet_naming_symbols.type_parameters.required_modifiers = 300 | 301 | dotnet_naming_symbols.private_constant_fields.applicable_kinds = field 302 | dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected 303 | dotnet_naming_symbols.private_constant_fields.required_modifiers = const 304 | 305 | dotnet_naming_symbols.local_variables.applicable_kinds = local 306 | dotnet_naming_symbols.local_variables.applicable_accessibilities = local 307 | dotnet_naming_symbols.local_variables.required_modifiers = 308 | 309 | dotnet_naming_symbols.local_constants.applicable_kinds = local 310 | dotnet_naming_symbols.local_constants.applicable_accessibilities = local 311 | dotnet_naming_symbols.local_constants.required_modifiers = const 312 | 313 | dotnet_naming_symbols.parameters.applicable_kinds = parameter 314 | dotnet_naming_symbols.parameters.applicable_accessibilities = * 315 | dotnet_naming_symbols.parameters.required_modifiers = 316 | 317 | dotnet_naming_symbols.public_constant_fields.applicable_kinds = field 318 | dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal 319 | dotnet_naming_symbols.public_constant_fields.required_modifiers = const 320 | 321 | dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field 322 | dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal 323 | dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static 324 | 325 | dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field 326 | dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected 327 | dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static 328 | 329 | dotnet_naming_symbols.local_functions.applicable_kinds = local_function 330 | dotnet_naming_symbols.local_functions.applicable_accessibilities = * 331 | dotnet_naming_symbols.local_functions.required_modifiers = 332 | 333 | # Naming styles 334 | 335 | dotnet_naming_style.pascalcase.required_prefix = 336 | dotnet_naming_style.pascalcase.required_suffix = 337 | dotnet_naming_style.pascalcase.word_separator = 338 | dotnet_naming_style.pascalcase.capitalization = pascal_case 339 | 340 | dotnet_naming_style.ipascalcase.required_prefix = I 341 | dotnet_naming_style.ipascalcase.required_suffix = 342 | dotnet_naming_style.ipascalcase.word_separator = 343 | dotnet_naming_style.ipascalcase.capitalization = pascal_case 344 | 345 | dotnet_naming_style.tpascalcase.required_prefix = T 346 | dotnet_naming_style.tpascalcase.required_suffix = 347 | dotnet_naming_style.tpascalcase.word_separator = 348 | dotnet_naming_style.tpascalcase.capitalization = pascal_case 349 | 350 | dotnet_naming_style._camelcase.required_prefix = _ 351 | dotnet_naming_style._camelcase.required_suffix = 352 | dotnet_naming_style._camelcase.word_separator = 353 | dotnet_naming_style._camelcase.capitalization = camel_case 354 | 355 | dotnet_naming_style.camelcase.required_prefix = 356 | dotnet_naming_style.camelcase.required_suffix = 357 | dotnet_naming_style.camelcase.word_separator = 358 | dotnet_naming_style.camelcase.capitalization = camel_case 359 | 360 | dotnet_naming_style.s_camelcase.required_prefix = s_ 361 | dotnet_naming_style.s_camelcase.required_suffix = 362 | dotnet_naming_style.s_camelcase.word_separator = 363 | dotnet_naming_style.s_camelcase.capitalization = camel_case 364 | 365 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | - package-ecosystem: "nuget" # See documentation for possible values 13 | directory: "/" # Location of package manifests 14 | schedule: 15 | interval: "daily" 16 | labels: 17 | - "dependencies" 18 | open-pull-requests-limit: 10 19 | groups: 20 | aspire: 21 | patterns: 22 | - "Aspire" 23 | - "Aspire.*" 24 | dotnet: 25 | patterns: 26 | - "Microsoft.*" 27 | - "System.*" 28 | OpenTelemetry: 29 | patterns: 30 | - "OpenTelemetry.*" 31 | - package-ecosystem: "gitsubmodule" # See documentation for possible values 32 | directory: "/" # Location of package manifests 33 | schedule: 34 | interval: "daily" 35 | labels: 36 | - "dependencies" 37 | open-pull-requests-limit: 10 38 | - package-ecosystem: "devcontainers" 39 | directory: "/.devcontainer" # Location of package manifests 40 | schedule: 41 | interval: "daily" 42 | labels: 43 | - "dependencies" 44 | 45 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: "InfinityFlow.Aspire.Temporal $RESOLVED_VERSION" 2 | tag-template: "$RESOLVED_VERSION" 3 | change-template: "- $TITLE by @$AUTHOR (#$NUMBER)" 4 | no-changes-template: "- No changes" 5 | categories: 6 | - title: "📚 Documentation" 7 | labels: 8 | - "documentation" 9 | - title: "🚀 New Features" 10 | labels: 11 | - "enhancement" 12 | - title: "🐛 Bug Fixes" 13 | labels: 14 | - "bug" 15 | - title: "☸️ Deployment Resources" 16 | labels: 17 | - "deployment-resources" 18 | version-resolver: 19 | major: 20 | labels: 21 | - "major" 22 | minor: 23 | labels: 24 | - "minor" 25 | patch: 26 | labels: 27 | - "patch" 28 | default: patch 29 | template: | 30 | $CHANGES 31 | 32 | ## 👨🏼‍💻 Contributors 33 | 34 | $CONTRIBUTORS 35 | autolabeler: 36 | - label: "documentation" 37 | files: 38 | - "docs/**/*" 39 | - label: "maintenance" 40 | files: 41 | - ".github/**/*" 42 | - ".devcontainer/**/*" 43 | - "test/**/*" 44 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: .NET 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | concurrency: 12 | group: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || github.ref }} 13 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref_type != 'tag' }} 14 | 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Setup .NET 24 | uses: actions/setup-dotnet@v4 25 | with: 26 | dotnet-version: 9.0.x 27 | - name: dotnet workload restore 28 | run: dotnet workload restore 29 | - name: Restore dependencies 30 | run: dotnet restore 31 | - name: Build 32 | run: dotnet build --no-restore 33 | - name: Test 34 | run: dotnet test --no-build --verbosity normal 35 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | 3 | name: publish 4 | on: 5 | workflow_dispatch: # Allow running the workflow manually from the GitHub UI 6 | push: 7 | branches: 8 | - "main" # Run the workflow when pushing to the main branch 9 | release: 10 | types: 11 | - published # Run the workflow when a new GitHub release is published 12 | 13 | env: 14 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 15 | DOTNET_NOLOGO: true 16 | NuGetDirectory: ${{ github.workspace}}/nuget 17 | TargetProject: "./src/InfinityFlow.Aspire.Temporal/InfinityFlow.Aspire.Temporal.csproj" 18 | 19 | defaults: 20 | run: 21 | shell: pwsh 22 | 23 | jobs: 24 | create_nuget: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: checkout 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | 32 | - name: install dotnet 33 | uses: actions/setup-dotnet@v4 34 | with: 35 | dotnet-version: 9.0.x 36 | - name: dotnet pack 37 | if: ${{ github.event_name != 'release' }} 38 | run: dotnet pack ${{ env.TargetProject }} --configuration Release --output ${{ env.NuGetDirectory }} 39 | 40 | - name: dotnet pack (release) 41 | if: ${{ github.event_name == 'release' && github.ref_type == 'tag' || github.event.release.tag_name }} 42 | run: dotnet pack ${{ env.TargetProject }} --configuration Release -p:PackageVersion="${{ github.event.release.tag_name || github.ref_name }}" --output ${{ env.NuGetDirectory }} 43 | 44 | 45 | - name: store artifact 46 | uses: actions/upload-artifact@v4 47 | with: 48 | name: nuget 49 | if-no-files-found: error 50 | retention-days: 7 51 | path: ${{ env.NuGetDirectory }}/*.nupkg # used in the following jobs 52 | 53 | deploy: 54 | if: github.event_name == 'release' 55 | runs-on: ubuntu-latest 56 | needs: [create_nuget] 57 | steps: 58 | - name: download artifact 59 | uses: actions/download-artifact@v4 60 | with: 61 | name: nuget 62 | path: ${{ env.NuGetDirectory }} 63 | 64 | - name: install dotnet 65 | uses: actions/setup-dotnet@v4 66 | 67 | - name: nuget push 68 | run: | 69 | foreach($file in (Get-ChildItem "${{ env.NuGetDirectory }}" -Recurse -Include *.nupkg)) { 70 | dotnet nuget push $file --api-key "${{ secrets.NUGET_APIKEY }}" --source https://api.nuget.org/v3/index.json 71 | } 72 | -------------------------------------------------------------------------------- /.github/workflows/releasedrafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request_target: 8 | types: 9 | - edited 10 | - opened 11 | - reopened 12 | - synchronize 13 | workflow_dispatch: 14 | concurrency: 15 | group: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || github.ref }} 16 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref_type != 'tag' }} 17 | 18 | 19 | jobs: 20 | update_release_draft: 21 | permissions: 22 | contents: write 23 | pull-requests: write 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: "Draft Release" 27 | uses: release-drafter/release-drafter@v6.1.0 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from `dotnet new gitignore` 5 | 6 | # dotenv files 7 | .env 8 | 9 | # User-specific files 10 | *.rsuser 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Mono auto generated files 20 | mono_crash.* 21 | 22 | # Build results 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | [Ww][Ii][Nn]32/ 30 | [Aa][Rr][Mm]/ 31 | [Aa][Rr][Mm]64/ 32 | bld/ 33 | [Bb]in/ 34 | [Oo]bj/ 35 | [Ll]og/ 36 | [Ll]ogs/ 37 | 38 | # Visual Studio 2015/2017 cache/options directory 39 | .vs/ 40 | # Uncomment if you have tasks that create the project's static files in wwwroot 41 | #wwwroot/ 42 | 43 | # Visual Studio 2017 auto generated files 44 | Generated\ Files/ 45 | 46 | # MSTest test Results 47 | [Tt]est[Rr]esult*/ 48 | [Bb]uild[Ll]og.* 49 | 50 | # NUnit 51 | *.VisualState.xml 52 | TestResult.xml 53 | nunit-*.xml 54 | 55 | # Build Results of an ATL Project 56 | [Dd]ebugPS/ 57 | [Rr]eleasePS/ 58 | dlldata.c 59 | 60 | # Benchmark Results 61 | BenchmarkDotNet.Artifacts/ 62 | 63 | # .NET 64 | project.lock.json 65 | project.fragment.lock.json 66 | artifacts/ 67 | 68 | # Tye 69 | .tye/ 70 | 71 | # ASP.NET Scaffolding 72 | ScaffoldingReadMe.txt 73 | 74 | # StyleCop 75 | StyleCopReport.xml 76 | 77 | # Files built by Visual Studio 78 | *_i.c 79 | *_p.c 80 | *_h.h 81 | *.ilk 82 | *.meta 83 | *.obj 84 | *.iobj 85 | *.pch 86 | *.pdb 87 | *.ipdb 88 | *.pgc 89 | *.pgd 90 | *.rsp 91 | *.sbr 92 | *.tlb 93 | *.tli 94 | *.tlh 95 | *.tmp 96 | *.tmp_proj 97 | *_wpftmp.csproj 98 | *.log 99 | *.tlog 100 | *.vspscc 101 | *.vssscc 102 | .builds 103 | *.pidb 104 | *.svclog 105 | *.scc 106 | 107 | # Chutzpah Test files 108 | _Chutzpah* 109 | 110 | # Visual C++ cache files 111 | ipch/ 112 | *.aps 113 | *.ncb 114 | *.opendb 115 | *.opensdf 116 | *.sdf 117 | *.cachefile 118 | *.VC.db 119 | *.VC.VC.opendb 120 | 121 | # Visual Studio profiler 122 | *.psess 123 | *.vsp 124 | *.vspx 125 | *.sap 126 | 127 | # Visual Studio Trace Files 128 | *.e2e 129 | 130 | # TFS 2012 Local Workspace 131 | $tf/ 132 | 133 | # Guidance Automation Toolkit 134 | *.gpState 135 | 136 | # ReSharper is a .NET coding add-in 137 | _ReSharper*/ 138 | *.[Rr]e[Ss]harper 139 | *.DotSettings.user 140 | 141 | # TeamCity is a build add-in 142 | _TeamCity* 143 | 144 | # DotCover is a Code Coverage Tool 145 | *.dotCover 146 | 147 | # AxoCover is a Code Coverage Tool 148 | .axoCover/* 149 | !.axoCover/settings.json 150 | 151 | # Coverlet is a free, cross platform Code Coverage Tool 152 | coverage*.json 153 | coverage*.xml 154 | coverage*.info 155 | 156 | # Visual Studio code coverage results 157 | *.coverage 158 | *.coveragexml 159 | 160 | # NCrunch 161 | _NCrunch_* 162 | .*crunch*.local.xml 163 | nCrunchTemp_* 164 | 165 | # MightyMoose 166 | *.mm.* 167 | AutoTest.Net/ 168 | 169 | # Web workbench (sass) 170 | .sass-cache/ 171 | 172 | # Installshield output folder 173 | [Ee]xpress/ 174 | 175 | # DocProject is a documentation generator add-in 176 | DocProject/buildhelp/ 177 | DocProject/Help/*.HxT 178 | DocProject/Help/*.HxC 179 | DocProject/Help/*.hhc 180 | DocProject/Help/*.hhk 181 | DocProject/Help/*.hhp 182 | DocProject/Help/Html2 183 | DocProject/Help/html 184 | 185 | # Click-Once directory 186 | publish/ 187 | 188 | # Publish Web Output 189 | *.[Pp]ublish.xml 190 | *.azurePubxml 191 | # Note: Comment the next line if you want to checkin your web deploy settings, 192 | # but database connection strings (with potential passwords) will be unencrypted 193 | *.pubxml 194 | *.publishproj 195 | 196 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 197 | # checkin your Azure Web App publish settings, but sensitive information contained 198 | # in these scripts will be unencrypted 199 | PublishScripts/ 200 | 201 | # NuGet Packages 202 | *.nupkg 203 | # NuGet Symbol Packages 204 | *.snupkg 205 | # The packages folder can be ignored because of Package Restore 206 | **/[Pp]ackages/* 207 | # except build/, which is used as an MSBuild target. 208 | !**/[Pp]ackages/build/ 209 | # Uncomment if necessary however generally it will be regenerated when needed 210 | #!**/[Pp]ackages/repositories.config 211 | # NuGet v3's project.json files produces more ignorable files 212 | *.nuget.props 213 | *.nuget.targets 214 | 215 | # Microsoft Azure Build Output 216 | csx/ 217 | *.build.csdef 218 | 219 | # Microsoft Azure Emulator 220 | ecf/ 221 | rcf/ 222 | 223 | # Windows Store app package directories and files 224 | AppPackages/ 225 | BundleArtifacts/ 226 | Package.StoreAssociation.xml 227 | _pkginfo.txt 228 | *.appx 229 | *.appxbundle 230 | *.appxupload 231 | 232 | # Visual Studio cache files 233 | # files ending in .cache can be ignored 234 | *.[Cc]ache 235 | # but keep track of directories ending in .cache 236 | !?*.[Cc]ache/ 237 | 238 | # Others 239 | ClientBin/ 240 | ~$* 241 | *~ 242 | *.dbmdl 243 | *.dbproj.schemaview 244 | *.jfm 245 | *.pfx 246 | *.publishsettings 247 | orleans.codegen.cs 248 | 249 | # Including strong name files can present a security risk 250 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 251 | #*.snk 252 | 253 | # Since there are multiple workflows, uncomment next line to ignore bower_components 254 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 255 | #bower_components/ 256 | 257 | # RIA/Silverlight projects 258 | Generated_Code/ 259 | 260 | # Backup & report files from converting an old project file 261 | # to a newer Visual Studio version. Backup files are not needed, 262 | # because we have git ;-) 263 | _UpgradeReport_Files/ 264 | Backup*/ 265 | UpgradeLog*.XML 266 | UpgradeLog*.htm 267 | ServiceFabricBackup/ 268 | *.rptproj.bak 269 | 270 | # SQL Server files 271 | *.mdf 272 | *.ldf 273 | *.ndf 274 | 275 | # Business Intelligence projects 276 | *.rdl.data 277 | *.bim.layout 278 | *.bim_*.settings 279 | *.rptproj.rsuser 280 | *- [Bb]ackup.rdl 281 | *- [Bb]ackup ([0-9]).rdl 282 | *- [Bb]ackup ([0-9][0-9]).rdl 283 | 284 | # Microsoft Fakes 285 | FakesAssemblies/ 286 | 287 | # GhostDoc plugin setting file 288 | *.GhostDoc.xml 289 | 290 | # Node.js Tools for Visual Studio 291 | .ntvs_analysis.dat 292 | node_modules/ 293 | 294 | # Visual Studio 6 build log 295 | *.plg 296 | 297 | # Visual Studio 6 workspace options file 298 | *.opt 299 | 300 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 301 | *.vbw 302 | 303 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 304 | *.vbp 305 | 306 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 307 | *.dsw 308 | *.dsp 309 | 310 | # Visual Studio 6 technical files 311 | *.ncb 312 | *.aps 313 | 314 | # Visual Studio LightSwitch build output 315 | **/*.HTMLClient/GeneratedArtifacts 316 | **/*.DesktopClient/GeneratedArtifacts 317 | **/*.DesktopClient/ModelManifest.xml 318 | **/*.Server/GeneratedArtifacts 319 | **/*.Server/ModelManifest.xml 320 | _Pvt_Extensions 321 | 322 | # Paket dependency manager 323 | .paket/paket.exe 324 | paket-files/ 325 | 326 | # FAKE - F# Make 327 | .fake/ 328 | 329 | # CodeRush personal settings 330 | .cr/personal 331 | 332 | # Python Tools for Visual Studio (PTVS) 333 | __pycache__/ 334 | *.pyc 335 | 336 | # Cake - Uncomment if you are using it 337 | # tools/** 338 | # !tools/packages.config 339 | 340 | # Tabs Studio 341 | *.tss 342 | 343 | # Telerik's JustMock configuration file 344 | *.jmconfig 345 | 346 | # BizTalk build output 347 | *.btp.cs 348 | *.btm.cs 349 | *.odx.cs 350 | *.xsd.cs 351 | 352 | # OpenCover UI analysis results 353 | OpenCover/ 354 | 355 | # Azure Stream Analytics local run output 356 | ASALocalRun/ 357 | 358 | # MSBuild Binary and Structured Log 359 | *.binlog 360 | 361 | # NVidia Nsight GPU debugger configuration file 362 | *.nvuser 363 | 364 | # MFractors (Xamarin productivity tool) working folder 365 | .mfractor/ 366 | 367 | # Local History for Visual Studio 368 | .localhistory/ 369 | 370 | # Visual Studio History (VSHistory) files 371 | .vshistory/ 372 | 373 | # BeatPulse healthcheck temp database 374 | healthchecksdb 375 | 376 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 377 | MigrationBackup/ 378 | 379 | # Ionide (cross platform F# VS Code tools) working folder 380 | .ionide/ 381 | 382 | # Fody - auto-generated XML schema 383 | FodyWeavers.xsd 384 | 385 | # VS Code files for those working on multiple tools 386 | .vscode/* 387 | !.vscode/settings.json 388 | !.vscode/tasks.json 389 | !.vscode/launch.json 390 | !.vscode/extensions.json 391 | *.code-workspace 392 | 393 | # Local History for Visual Studio Code 394 | .history/ 395 | 396 | # Windows Installer files from build outputs 397 | *.cab 398 | *.msi 399 | *.msix 400 | *.msm 401 | *.msp 402 | 403 | # JetBrains Rider 404 | *.sln.iml 405 | .idea 406 | 407 | ## 408 | ## Visual studio for Mac 409 | ## 410 | 411 | 412 | # globs 413 | Makefile.in 414 | *.userprefs 415 | *.usertasks 416 | config.make 417 | config.status 418 | aclocal.m4 419 | install-sh 420 | autom4te.cache/ 421 | *.tar.gz 422 | tarballs/ 423 | test-results/ 424 | 425 | # Mac bundle stuff 426 | *.dmg 427 | *.app 428 | 429 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 430 | # General 431 | .DS_Store 432 | .AppleDouble 433 | .LSOverride 434 | 435 | # Icon must end with two \r 436 | Icon 437 | 438 | 439 | # Thumbnails 440 | ._* 441 | 442 | # Files that might appear in the root of a volume 443 | .DocumentRevisions-V100 444 | .fseventsd 445 | .Spotlight-V100 446 | .TemporaryItems 447 | .Trashes 448 | .VolumeIcon.icns 449 | .com.apple.timemachine.donotpresent 450 | 451 | # Directories potentially created on remote AFP share 452 | .AppleDB 453 | .AppleDesktop 454 | Network Trash Folder 455 | Temporary Items 456 | .apdisk 457 | 458 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 459 | # Windows thumbnail cache files 460 | Thumbs.db 461 | ehthumbs.db 462 | ehthumbs_vista.db 463 | 464 | # Dump file 465 | *.stackdump 466 | 467 | # Folder config file 468 | [Dd]esktop.ini 469 | 470 | # Recycle Bin used on file shares 471 | $RECYCLE.BIN/ 472 | 473 | # Windows Installer files 474 | *.cab 475 | *.msi 476 | *.msix 477 | *.msm 478 | *.msp 479 | 480 | # Windows shortcuts 481 | *.lnk 482 | 483 | # Vim temporary swap files 484 | *.swp 485 | 486 | appsettings.Development.json 487 | temporal.sqlite -------------------------------------------------------------------------------- /InfinityFlow.Aspire.Temporal.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppHost", "sample\AppHost\AppHost.csproj", "{52057B80-B01E-46C3-96F5-228D0B3BCB48}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceDefaults", "sample\ServiceDefaults\ServiceDefaults.csproj", "{4B893ACC-7DDF-43CE-95A6-104CA8AEAB04}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{0815F1E4-424A-4A05-A12E-B2BDAF2AA142}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{70F8209F-4A68-4F27-AF37-3A76C8EA0642}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InfinityFlow.Aspire.Temporal", "src\InfinityFlow.Aspire.Temporal\InfinityFlow.Aspire.Temporal.csproj", "{88EE3FBD-5AF8-4F75-A0C7-BC593EC8F90F}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api", "sample\Api\Api.csproj", "{1FFA9DCE-3368-4AFA-960A-0AD2C501CA1B}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker", "sample\Worker\Worker.csproj", "{8B68A40A-674D-4989-B366-1EE0A633F0D9}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {52057B80-B01E-46C3-96F5-228D0B3BCB48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {52057B80-B01E-46C3-96F5-228D0B3BCB48}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {52057B80-B01E-46C3-96F5-228D0B3BCB48}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {52057B80-B01E-46C3-96F5-228D0B3BCB48}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {4B893ACC-7DDF-43CE-95A6-104CA8AEAB04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {4B893ACC-7DDF-43CE-95A6-104CA8AEAB04}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {4B893ACC-7DDF-43CE-95A6-104CA8AEAB04}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {4B893ACC-7DDF-43CE-95A6-104CA8AEAB04}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {88EE3FBD-5AF8-4F75-A0C7-BC593EC8F90F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {88EE3FBD-5AF8-4F75-A0C7-BC593EC8F90F}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {88EE3FBD-5AF8-4F75-A0C7-BC593EC8F90F}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {88EE3FBD-5AF8-4F75-A0C7-BC593EC8F90F}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {1FFA9DCE-3368-4AFA-960A-0AD2C501CA1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {1FFA9DCE-3368-4AFA-960A-0AD2C501CA1B}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {1FFA9DCE-3368-4AFA-960A-0AD2C501CA1B}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {1FFA9DCE-3368-4AFA-960A-0AD2C501CA1B}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {8B68A40A-674D-4989-B366-1EE0A633F0D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {8B68A40A-674D-4989-B366-1EE0A633F0D9}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {8B68A40A-674D-4989-B366-1EE0A633F0D9}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {8B68A40A-674D-4989-B366-1EE0A633F0D9}.Release|Any CPU.Build.0 = Release|Any CPU 46 | EndGlobalSection 47 | GlobalSection(SolutionProperties) = preSolution 48 | HideSolutionNode = FALSE 49 | EndGlobalSection 50 | GlobalSection(NestedProjects) = preSolution 51 | {52057B80-B01E-46C3-96F5-228D0B3BCB48} = {0815F1E4-424A-4A05-A12E-B2BDAF2AA142} 52 | {4B893ACC-7DDF-43CE-95A6-104CA8AEAB04} = {0815F1E4-424A-4A05-A12E-B2BDAF2AA142} 53 | {88EE3FBD-5AF8-4F75-A0C7-BC593EC8F90F} = {70F8209F-4A68-4F27-AF37-3A76C8EA0642} 54 | {1FFA9DCE-3368-4AFA-960A-0AD2C501CA1B} = {0815F1E4-424A-4A05-A12E-B2BDAF2AA142} 55 | {8B68A40A-674D-4989-B366-1EE0A633F0D9} = {0815F1E4-424A-4A05-A12E-B2BDAF2AA142} 56 | EndGlobalSection 57 | GlobalSection(ExtensibilityGlobals) = postSolution 58 | SolutionGuid = {C4C2C7A9-980A-4B6D-A624-60A0FBD99DE0} 59 | EndGlobalSection 60 | EndGlobal 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 sains1, InfinityFlow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InfinityFlow.Aspire.Temporal 2 | 3 | [![NuGet](https://img.shields.io/nuget/v/InfinityFlow.Aspire.Temporal.svg?style=flat)](https://www.nuget.org/packages/InfinityFlow.Aspire.Temporal) 4 | 5 | [![Discord](https://discordapp.com/api/guilds/1148334798524383292/widget.png?style=banner2)](https://discord.gg/PXJFbP7PKk) 6 | 7 | Aspire extension to start the temporal cli dev server as an container or executable resource. 8 | **Note: Only container works as expected. See https://github.com/dotnet/aspire/issues/1637 and https://github.com/temporalio/cli/issues/316** 9 | 10 | 11 | ## Contents: 12 | - [Pre-Requisites](#pre-requisites) 13 | - [Getting Started](#getting-started) 14 | - [Observability](#observability) 15 | - [Configuration](#configuration) 16 | 17 | ## Pre-requisites 18 | 19 | - [Temporal CLI](https://github.com/temporalio/cli) (ensure the binary is in your PATH) 20 | - An Aspire project. See [Aspire docs](https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview) to get started. 21 | 22 | ## Getting Started 23 | 24 | ### 1. Install the nuget package 25 | 26 | ```sh 27 | dotnet add package InfinityFlow.Aspire.Temporal 28 | ``` 29 | 30 | ### 2. Add Temporal dev server to your Aspire AppHost Program.cs 31 | 32 | ```csharp 33 | // AppHost/Program.cs 34 | using Aspire.Temporal.Server; 35 | 36 | var builder = DistributedApplication.CreateBuilder(args); 37 | 38 | // Use the default server options 39 | var temporal = await builder.AddTemporalServerContainer("temporal") 40 | 41 | // OR customise server options with builder 42 | // see config section for details 43 | var temporal = await builder.AddTemporalServerContainer("temporal", x => x 44 | .WithLogFormat(LogFormat.Json) 45 | .WithLogLevel(LogLevel.Info) 46 | .WithNamespace("test1", "test2")); 47 | ``` 48 | 49 | ### 3. Run the Aspire application 50 | 51 | You should see Temporal running under the Executables tab. 52 | 53 | Temporal will be available on its default ports: 54 | - Server: http://localhost:7233 55 | - UI: http://localhost:8233 56 | 57 | ![Aspire dashboard temporal exe](./docs/aspire-dashboard-exe.png) 58 | 59 | ### 4. Configure Client/Worker Applications 60 | 61 | The Temporal client can then be added to a .NET project as normal using the instructions from the [temporal dotnet sdk repo](https://github.com/temporalio/sdk-dotnet/) 62 | 63 | It can be included in Aspire orchestration like below and can optionally take a reference to the Temporal resource. 64 | 65 | ```csharp 66 | // ./samples/AppHost/Program.cs 67 | 68 | // ... 69 | 70 | var temporal = builder.AddTemporalServerExecutable("temporal"); 71 | 72 | builder.AddProject("worker") // my custom project 73 | .WithReference(temporal); 74 | 75 | // ... 76 | ``` 77 | 78 | If using [Temporalio.Extensions.Hosting](https://github.com/temporalio/sdk-dotnet/blob/main/src/Temporalio.Extensions.Hosting/README.md) the client registration might look something like below. If we took the reference to the Temporal Aspire resource, then the TargetHost property is automatically injected under the key `ConnectionStrings:`. (e.g., this will be `builder.Configuration["ConnectionStrings:temporal"]` for a resource named "temporal" as above) 79 | 80 | ```csharp 81 | // register a client - ./samples/Api/Program.cs 82 | builder.Services 83 | .AddTemporalClient(opts => 84 | { 85 | opts.TargetHost = builder.Configuration["ConnectionStrings:temporal"]; // or just self-configure localhost:7233 86 | opts.Namespace = "default"; 87 | }) 88 | 89 | // or 90 | 91 | // register a worker - ./samples/Worker/Program.cs 92 | builder.Services 93 | .AddTemporalClient(opts => 94 | { 95 | opts.TargetHost = builder.Configuration["ConnectionStrings:temporal"]; // or just self-configure localhost:7233 96 | opts.Namespace = "default"; 97 | }) 98 | .AddHostedTemporalWorker("my-task-queue") 99 | .AddScopedActivities() 100 | .AddWorkflow(); 101 | ``` 102 | 103 | ## Observability 104 | 105 | The extension doesn't provide any setup for observability, but you can follow [Temporalio.Extensions.DiagnosticSource](https://github.com/temporalio/sdk-dotnet/blob/main/src/Temporalio.Extensions.DiagnosticSource/README.md) and [Temporalio.Extensions.Hosting](https://github.com/temporalio/sdk-dotnet/blob/main/src/Temporalio.Extensions.Hosting/TemporalHostingServiceCollectionExtensions.cs) to configure this on the temporal client. If using the Aspire Service Defaults, you'll need to configure the metrics and tracing accordingly. 106 | 107 | The sample folder has an example for configuring this with the Aspire Dashboard 108 | 109 | - [sample/Api/Program.cs](./sample/Api/Program.cs) for an example client 110 | - [sample/Worker/Program.cs](./sample/Worker/Program.cs) for an example worker 111 | - [sample/ServiceDefaults/Extensions.cs](./sample/ServiceDefaults/Extensions.cs) for an example of adding the custom meter and tracing sources to the service defaults. 112 | 113 | If done correctly, you should tracing and metrics on the Aspire dashboard: 114 | 115 | #### Tracing 116 | 117 | ![aspire dashboard temporal tracing](./docs/aspire-dashboard-temporal-tracing.png) 118 | 119 | #### Metrics 120 | 121 | ![aspire dashboard temporal metrics](./docs/aspire-dashboard-temporal-metrics.png) 122 | 123 | 124 | ## Configuration 125 | 126 | The dev server can be configured with a fluent builder 127 | 128 | ```csharp 129 | await builder.AddTemporalServerContainer("temporal", builder => builder.WithPort(1234)) 130 | ``` 131 | 132 | You can run `temporal server start-dev --help` to get more information about the CLI flags on the dev server. All available flags are mapped to a method on the builder. 133 | 134 | Available methods: 135 | 136 | ```csharp 137 | builder 138 | .WithDbFileName("/location/of/persistent/file") // --db-filename 139 | .WithNamespace("namespace-name", ...) // --namespace 140 | .WithPort(7233) // --port 141 | .WithHttpPort(7234) // --http-port 142 | .WithMetricsPort(7235) // --metrics-port 143 | .UiPort(8233) // --ui-port 144 | .WithHeadlessUi(true) // --headless 145 | .WithIp("127.0.0.1") // --ip 146 | .WithUiIp("127.0.0.1") // --ui-ip 147 | .WithUiAssetPath("/location/of/custom/assets") // --ui-asset-path 148 | .WithUiCodecEndpoint("http://localhost:8080") // --ui-codec-endpoint 149 | .WithLogFormat(LogFormat.Pretty) // --log-format 150 | .WithLogLevel(LogLevel.Info) // --log-level 151 | .WithSQLitePragma(SQLitePragma.JournalMode) // --sqlite-pragma 152 | ``` 153 | -------------------------------------------------------------------------------- /docs/aspire-dashboard-exe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfinityFlowApp/aspire-temporal/8bdd63b60da4ea9530bc766d7e1d58ccebd0973c/docs/aspire-dashboard-exe.png -------------------------------------------------------------------------------- /docs/aspire-dashboard-temporal-metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfinityFlowApp/aspire-temporal/8bdd63b60da4ea9530bc766d7e1d58ccebd0973c/docs/aspire-dashboard-temporal-metrics.png -------------------------------------------------------------------------------- /docs/aspire-dashboard-temporal-tracing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfinityFlowApp/aspire-temporal/8bdd63b60da4ea9530bc766d7e1d58ccebd0973c/docs/aspire-dashboard-temporal-tracing.png -------------------------------------------------------------------------------- /sample/Api/Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /sample/Api/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.Metrics; 2 | 3 | using Microsoft.AspNetCore.Http.HttpResults; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | using Temporalio.Client; 7 | using Temporalio.Extensions.DiagnosticSource; 8 | using Temporalio.Extensions.OpenTelemetry; 9 | using Temporalio.Runtime; 10 | 11 | var builder = WebApplication.CreateBuilder(args); 12 | 13 | using var meter = new Meter("Temporal.Client"); 14 | 15 | var runtime = new TemporalRuntime(new() 16 | { 17 | Telemetry = new() 18 | { 19 | Metrics = new() { CustomMetricMeter = new CustomMetricMeter(meter) }, 20 | }, 21 | }); 22 | 23 | builder.AddServiceDefaults(); 24 | 25 | builder.Services.AddTemporalClient(opts => 26 | { 27 | opts.TargetHost = builder.Configuration["ConnectionStrings:temporal"]; 28 | opts.Namespace = Constants.Namespace; 29 | opts.Interceptors = [new TracingInterceptor()]; 30 | opts.Runtime = runtime; 31 | }); 32 | 33 | var app = builder.Build(); 34 | 35 | app.MapDefaultEndpoints(); 36 | app.UseHttpsRedirection(); 37 | 38 | app.MapGet("/say-hello", async Task, BadRequest>> ( 39 | HttpContext ctx, 40 | [FromQuery] string? name, 41 | [FromServices] ITemporalClient client) => 42 | { 43 | if (name is null or { Length: > 1000 }) 44 | { 45 | return TypedResults.BadRequest($"Pass a name using query param {nameof(name)}"); 46 | } 47 | 48 | var result = await client.ExecuteWorkflowAsync(x => x.RunAsync(name), 49 | new WorkflowOptions 50 | { 51 | Id = name, 52 | TaskQueue = Constants.TaskQueueName 53 | }); 54 | 55 | return TypedResults.Ok(result); 56 | }); 57 | 58 | app.Run(); -------------------------------------------------------------------------------- /sample/Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "https://localhost:53494", 8 | "sslPort": 44374 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "say-hello", 17 | "applicationUrl": "https://localhost:5195", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "say-hello", 27 | "applicationUrl": "https://localhost:7244;http://localhost:5195", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "say-hello", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sample/Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /sample/AppHost/AppHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sample/AppHost/Program.cs: -------------------------------------------------------------------------------- 1 | using InfinityFlow.Aspire.Temporal; 2 | 3 | var builder = DistributedApplication.CreateBuilder(args); 4 | 5 | var temporal = await builder.AddTemporalServerContainer("temporal", x => x 6 | .WithLogFormat(LogFormat.Json) 7 | .WithLogLevel(LogLevel.Info) 8 | .WithNamespace("test1", "test2") 9 | .WithDynamicConfigValue("frontend.enableUpdateWorkflowExecution", true) 10 | ); 11 | 12 | builder.AddProject("api") 13 | .WithReference(temporal); 14 | 15 | builder.AddProject("worker") 16 | .WithReference(temporal); 17 | 18 | builder.Build().Run(); 19 | -------------------------------------------------------------------------------- /sample/AppHost/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "https": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": true, 8 | "applicationUrl": "https://localhost:15264", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development", 11 | "DOTNET_ENVIRONMENT": "Development", 12 | "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16224", 13 | "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", 14 | "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sample/AppHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning", 6 | "Aspire.Hosting.Dcp": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/ServiceDefaults/Extensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Diagnostics.HealthChecks; 5 | using Microsoft.Extensions.Logging; 6 | 7 | using OpenTelemetry.Logs; 8 | using OpenTelemetry.Metrics; 9 | using OpenTelemetry.Trace; 10 | 11 | using Temporalio.Extensions.OpenTelemetry; 12 | 13 | namespace Microsoft.Extensions.Hosting; 14 | 15 | public static class Extensions 16 | { 17 | public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) 18 | { 19 | builder.ConfigureOpenTelemetry(); 20 | 21 | builder.AddDefaultHealthChecks(); 22 | 23 | builder.Services.AddServiceDiscovery(); 24 | 25 | builder.Services.ConfigureHttpClientDefaults(http => 26 | { 27 | // Turn on resilience by default 28 | http.AddStandardResilienceHandler(); 29 | 30 | // Turn on service discovery by default 31 | http.AddServiceDiscovery(); 32 | }); 33 | 34 | return builder; 35 | } 36 | 37 | public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) 38 | { 39 | builder.Logging.AddOpenTelemetry(logging => 40 | { 41 | logging.IncludeFormattedMessage = true; 42 | logging.IncludeScopes = true; 43 | }); 44 | 45 | builder.Services.AddOpenTelemetry() 46 | .WithMetrics(metrics => metrics.AddRuntimeInstrumentation() 47 | .AddBuiltInMeters() 48 | .AddMeter("Temporal.Client")) 49 | .WithTracing(tracing => 50 | { 51 | if (builder.Environment.IsDevelopment()) 52 | { 53 | // We want to view all traces in development 54 | tracing.SetSampler(new AlwaysOnSampler()); 55 | } 56 | 57 | tracing.AddAspNetCoreInstrumentation() 58 | .AddGrpcClientInstrumentation() 59 | .AddHttpClientInstrumentation() 60 | .AddSource( 61 | TracingInterceptor.ClientSource.Name, 62 | TracingInterceptor.WorkflowsSource.Name, 63 | TracingInterceptor.ActivitiesSource.Name); 64 | }); 65 | 66 | builder.AddOpenTelemetryExporters(); 67 | 68 | return builder; 69 | } 70 | 71 | private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) 72 | { 73 | var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); 74 | 75 | if (useOtlpExporter) 76 | { 77 | builder.Services.Configure(logging => logging.AddOtlpExporter()); 78 | builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter()); 79 | builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter()); 80 | } 81 | 82 | // Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) 83 | // builder.Services.AddOpenTelemetry() 84 | // .WithMetrics(metrics => metrics.AddPrometheusExporter()); 85 | 86 | // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.Exporter package) 87 | // builder.Services.AddOpenTelemetry() 88 | // .UseAzureMonitor(); 89 | 90 | return builder; 91 | } 92 | 93 | public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) 94 | { 95 | builder.Services.AddHealthChecks() 96 | // Add a default liveness check to ensure app is responsive 97 | .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); 98 | 99 | return builder; 100 | } 101 | 102 | public static WebApplication MapDefaultEndpoints(this WebApplication app) 103 | { 104 | // Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) 105 | // app.MapPrometheusScrapingEndpoint(); 106 | 107 | // All health checks must pass for app to be considered ready to accept traffic after starting 108 | app.MapHealthChecks("/health"); 109 | 110 | // Only health checks tagged with the "live" tag must pass for app to be considered alive 111 | app.MapHealthChecks("/alive", new HealthCheckOptions 112 | { 113 | Predicate = r => r.Tags.Contains("live") 114 | }); 115 | 116 | return app; 117 | } 118 | 119 | private static MeterProviderBuilder AddBuiltInMeters(this MeterProviderBuilder meterProviderBuilder) => 120 | meterProviderBuilder.AddMeter( 121 | "Microsoft.AspNetCore.Hosting", 122 | "Microsoft.AspNetCore.Server.Kestrel", 123 | "System.Net.Http"); 124 | } 125 | -------------------------------------------------------------------------------- /sample/ServiceDefaults/ServiceDefaults.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net9.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sample/Worker/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.Metrics; 2 | 3 | using Temporalio.Activities; 4 | using Temporalio.Extensions.DiagnosticSource; 5 | using Temporalio.Extensions.Hosting; 6 | using Temporalio.Extensions.OpenTelemetry; 7 | using Temporalio.Runtime; 8 | using Temporalio.Workflows; 9 | 10 | var builder = Host.CreateApplicationBuilder(args); 11 | 12 | using var meter = new Meter("Temporal.Client"); 13 | 14 | var runtime = new TemporalRuntime(new() 15 | { 16 | Telemetry = new() 17 | { 18 | Metrics = new() { CustomMetricMeter = new CustomMetricMeter(meter) }, 19 | }, 20 | }); 21 | 22 | builder.AddServiceDefaults(); 23 | 24 | builder.Services 25 | .AddTemporalClient(opts => 26 | { 27 | opts.TargetHost = builder.Configuration.GetConnectionString("temporal"); 28 | opts.Namespace = Constants.Namespace; 29 | opts.Interceptors = new[] { new TracingInterceptor() }; 30 | opts.Runtime = runtime; 31 | }) 32 | .AddHostedTemporalWorker(Constants.TaskQueueName) 33 | .AddScopedActivities() 34 | .AddWorkflow(); 35 | 36 | var host = builder.Build(); 37 | host.Run(); 38 | 39 | public static class Constants 40 | { 41 | public const string TaskQueueName = "aspire-worker-task-queue"; 42 | public const string Namespace = "test1"; 43 | } 44 | 45 | [Workflow] 46 | public class HelloWorkflow 47 | { 48 | [WorkflowRun] 49 | public async Task RunAsync(string name) 50 | { 51 | return await Workflow.ExecuteActivityAsync((HelloActivities act) => act.SayHello(name), 52 | new() { ScheduleToCloseTimeout = TimeSpan.FromMinutes(5) }); 53 | } 54 | } 55 | 56 | public class HelloActivities 57 | { 58 | [Activity] 59 | public Task SayHello(string name) 60 | { 61 | return Task.FromResult("Hello " + name); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sample/Worker/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "Worker": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "environmentVariables": { 8 | "DOTNET_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /sample/Worker/Worker.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | dotnet-Worker-0059691c-eb4b-48c3-980f-e2478ee155e9 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /sample/Worker/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.Hosting.Lifetime": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/Enums.cs: -------------------------------------------------------------------------------- 1 | namespace InfinityFlow.Aspire.Temporal; 2 | 3 | public enum LogFormat 4 | { 5 | Json, 6 | Pretty 7 | } 8 | 9 | public enum LogLevel 10 | { 11 | Debug, 12 | Info, 13 | Warn, 14 | Error, 15 | Fatal 16 | } 17 | 18 | public enum SQLitePragma 19 | { 20 | JournalMode, 21 | Synchronous 22 | } 23 | 24 | public static class EnumHelpers 25 | { 26 | public static string LogFormatToString(LogFormat logFormat) => logFormat switch 27 | { 28 | LogFormat.Pretty => "pretty", 29 | LogFormat.Json => "json", 30 | _ => throw new ArgumentOutOfRangeException(nameof(logFormat), logFormat, null), 31 | }; 32 | 33 | public static string LogLevelToString(LogLevel level) => level switch 34 | { 35 | LogLevel.Debug => "debug", 36 | LogLevel.Info => "info", 37 | LogLevel.Warn => "warn", 38 | LogLevel.Error => "error", 39 | LogLevel.Fatal => "fatal", 40 | _ => throw new ArgumentOutOfRangeException(nameof(level), level, null), 41 | }; 42 | 43 | public static string SQLitePragmaToString(SQLitePragma pragma) => pragma switch 44 | { 45 | SQLitePragma.JournalMode => "journal_mode", 46 | SQLitePragma.Synchronous => "synchronous", 47 | _ => throw new ArgumentOutOfRangeException(nameof(pragma), pragma, null), 48 | }; 49 | } -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/InfinityFlow.Aspire.Temporal.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | enable 5 | enable 6 | packageIcon.png 7 | true 8 | true 9 | InfinityFlow.Aspire.Temporal 10 | 0.5.2 11 | true 12 | README.md 13 | LICENSE 14 | true 15 | dotnet;aspire;temporal 16 | Aspire extension to start the temporal cli dev server as an container or executable resource. Only container is well-supported currently. 17 | https://github.com/InfinityFlowApp/aspire-temporal-server 18 | InfinityFlow 19 | Copyright ©️ InfinityFlow 20 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 21 | true 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/TemporalServerContainerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Aspire.Hosting.ApplicationModel; 2 | 3 | using InfinityFlow.Aspire.Temporal; 4 | 5 | namespace Aspire.Hosting; 6 | public static class TemporalServerContainerBuilderExtensions 7 | { 8 | /// 9 | /// Adds a temporal server resource instance to the Aspire host. Requires the Temporal Container location to be in your path. 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | public static Task> AddTemporalServerContainer(this IDistributedApplicationBuilder builder, string name, 16 | Action callback) 17 | { 18 | var rb = new TemporalServerResourceBuilder(); 19 | callback(rb); 20 | var args = rb.Build(); 21 | 22 | return builder.AddTemporalServerContainer(name, args); 23 | } 24 | 25 | /// 26 | /// Adds a temporal server resource instance to the Aspire host. Requires the Temporal Container location to be in your path. 27 | /// 28 | /// 29 | /// 30 | /// 31 | public static Task> AddTemporalServerContainer(this IDistributedApplicationBuilder builder, string name) 32 | { 33 | return builder.AddTemporalServerContainer(name, new TemporalServerResourceArguments()); 34 | } 35 | private static async Task> AddTemporalServerContainer(this IDistributedApplicationBuilder builder, string name, TemporalServerResourceArguments args) 36 | { 37 | var container = new TemporalServerContainerResource(name, args); 38 | 39 | var resourceBuilder = builder.AddResource(container) 40 | .WithAnnotation(new ContainerImageAnnotation() { Image = "temporalio/admin-tools", Tag = "latest" }) 41 | .WithArgs(args.GetArgs()) 42 | .WithEntrypoint("temporal") 43 | .WithHttpsEndpoint(name: "server", port: args.Port, targetPort: 7233).AsHttp2Service(); // Internal port is always 7233 44 | 45 | if (args.Headless is not true) 46 | { 47 | resourceBuilder.WithHttpEndpoint(name: "ui", port: args.UiPort, targetPort: 8233); // Internal port is always 8233 48 | } 49 | 50 | if (args.MetricsPort is not null) 51 | { 52 | resourceBuilder.WithHttpEndpoint(name: "metrics", port: args.MetricsPort, targetPort: 7235); // Internal port is always 7235 53 | } 54 | 55 | if (args.HttpPort is not null) 56 | { 57 | resourceBuilder.WithHttpEndpoint(name: "http", port: args.HttpPort, targetPort: 7234); // Internal port is always 7234 58 | } 59 | 60 | return resourceBuilder; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/TemporalServerExecutableBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Aspire.Hosting.ApplicationModel; 2 | 3 | using InfinityFlow.Aspire.Temporal; 4 | 5 | namespace Aspire.Hosting; 6 | public static class TemporalServerExecutableBuilderExtensions 7 | { 8 | /// 9 | /// Adds a temporal server resource instance to the Aspire host. Requires the Temporal executable location to be in your path. 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | public static IResourceBuilder AddTemporalServerExecutable(this IDistributedApplicationBuilder builder, string name, 16 | Action callback) 17 | { 18 | var rb = new TemporalServerResourceBuilder(); 19 | callback(rb); 20 | var args = rb.Build(); 21 | 22 | return builder.AddTemporalServerExecutable(name, args); 23 | } 24 | 25 | /// 26 | /// Adds a temporal server resource instance to the Aspire host. Requires the Temporal executable location to be in your path. 27 | /// 28 | /// 29 | /// 30 | /// 31 | public static IResourceBuilder AddTemporalServerExecutable(this IDistributedApplicationBuilder builder, string name) 32 | { 33 | return builder.AddTemporalServerExecutable(name, new TemporalServerResourceArguments()); 34 | } 35 | 36 | private static IResourceBuilder AddTemporalServerExecutable(this IDistributedApplicationBuilder builder, string name, 37 | TemporalServerResourceArguments args) 38 | { 39 | var resourceBuilder = builder.AddResource(new TemporalServerExecutableResource(name, args)); 40 | 41 | resourceBuilder.WithHttpEndpoint(port: args.Port, name: "server").AsHttp2Service(); 42 | 43 | if (args.Headless is not true) 44 | { 45 | resourceBuilder.WithHttpEndpoint(port: args.UiPort ?? args.Port + 1000, name: "ui"); 46 | } 47 | 48 | if (args.MetricsPort is not null) 49 | { 50 | resourceBuilder.WithHttpEndpoint(port: args.MetricsPort?? 9000, name: "metrics"); 51 | } 52 | 53 | if (args.HttpPort is not null) 54 | { 55 | resourceBuilder.WithHttpEndpoint(port: args.HttpPort ?? 8080, name: "http"); 56 | } 57 | 58 | return resourceBuilder; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/TemporalServerExecutableResource.cs: -------------------------------------------------------------------------------- 1 | using InfinityFlow.Aspire.Temporal; 2 | using Aspire.Hosting.ApplicationModel; 3 | 4 | 5 | namespace Aspire.Hosting; 6 | 7 | public class TemporalServerExecutableResource(string name, TemporalServerResourceArguments arguments) : ExecutableResource(name, command: "temporal", workingDirectory: ""), IResourceWithConnectionString 8 | { 9 | public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create($"{GetConnectionString()}"); 10 | 11 | public string? GetConnectionString() 12 | { 13 | var endpoints = this.GetEndpoints().Where(e => e.IsAllocated).ToList(); 14 | if (endpoints.Count==0) 15 | { 16 | return null; 17 | } 18 | 19 | var server = endpoints.SingleOrDefault(x => x.EndpointName == "server"); 20 | 21 | return $"{server?.Host}:{server?.Port}"; 22 | } 23 | } 24 | 25 | public class TemporalServerContainerResource(string name, TemporalServerResourceArguments arguments) : ContainerResource(name,entrypoint: "/temporal"), IResourceWithConnectionString, IResourceWithEnvironment 26 | { 27 | public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create($"{GetConnectionString()}"); 28 | 29 | public TemporalServerResourceArguments Arguments { get; } = arguments; 30 | 31 | public string? GetConnectionString() 32 | { 33 | var endpoints = this.GetEndpoints().Where(e => e.IsAllocated).ToList(); 34 | if (endpoints.Count == 0) 35 | { 36 | return null; 37 | } 38 | 39 | var server = endpoints.SingleOrDefault(x => x.EndpointName == "server"); 40 | 41 | return $"{server?.Host}:{server?.Port}"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/TemporalServerResourceArguments.cs: -------------------------------------------------------------------------------- 1 | namespace InfinityFlow.Aspire.Temporal; 2 | 3 | /// 4 | /// Represents the arguments required to configure and start a Temporal server. 5 | /// 6 | public class TemporalServerResourceArguments 7 | { 8 | /// 9 | /// Gets or sets the command to execute. Default is "temporal". 10 | /// 11 | public string Command { get; set; } = "temporal"; 12 | 13 | /// 14 | /// Gets or sets the file name where the Temporal state will be persisted. If not set, workflows are lost when the process dies. 15 | /// 16 | public string? DbFileName { get; set; } 17 | 18 | /// 19 | /// Gets or sets the port for the frontend gRPC service. Default is 7233. 20 | /// 21 | public int Port { get; set; } = 7233; 22 | 23 | /// 24 | /// Gets or sets the port for the frontend HTTP service. If not set, the service is disabled. 25 | /// 26 | public int? HttpPort { get; set; } 27 | 28 | /// 29 | /// Gets or sets the port for metrics. If not set, metrics are disabled. 30 | /// 31 | public int? MetricsPort { get; set; } 32 | 33 | /// 34 | /// Gets or sets the port for the Web UI. Default is 8233. 35 | /// 36 | public int? UiPort { get; set; } = 8233; 37 | 38 | /// 39 | /// Gets or sets a value indicating whether the Web UI is headless. If not set, defaults to false. 40 | /// 41 | public bool? Headless { get; set; } 42 | 43 | /// 44 | /// Gets or sets the IPv4 address to bind the frontend service to. Default is 0.0.0.0. 45 | /// 46 | public string? Ip { get; set; } = "0.0.0.0"; 47 | 48 | /// 49 | /// Gets or sets the IPv4 address to bind the Web UI to. Default is null, which means it uses the same as --ip. 50 | /// 51 | public string? UiIp { get; set; } 52 | 53 | /// 54 | /// Gets or sets the path to the UI custom assets. 55 | /// 56 | public string? UiAssetPath { get; set; } 57 | 58 | /// 59 | /// Gets or sets the UI remote codec HTTP endpoint. 60 | /// 61 | public string? UiCodecEndpoint { get; set; } 62 | 63 | /// 64 | /// Gets or sets the log format. Options are "json" and "pretty". Default is null. 65 | /// 66 | public LogFormat? LogFormat { get; set; } 67 | 68 | /// 69 | /// Gets or sets the log level. Options are "debug", "info", "warn", "error", and "fatal". Default is null. 70 | /// 71 | public LogLevel? LogLevel { get; set; } 72 | 73 | /// 74 | /// Gets or sets the SQLite pragma. Options are "journal_mode" and "synchronous". Default is null. 75 | /// 76 | public SQLitePragma? SQLitePragma { get; set; } 77 | 78 | /// 79 | /// Gets the list of namespaces that should be pre-created. The "default" namespace is always created. 80 | /// 81 | public List Namespaces { get; set; } = []; 82 | 83 | /// 84 | /// Gets the list of dynamic config values. 85 | /// 86 | public Dictionary DynamicConfigValues { get; set; } = []; 87 | 88 | /// 89 | /// Converts the current instance's properties to an array of command-line arguments for starting the Temporal server. 90 | /// 91 | /// An array of strings representing the command-line arguments. 92 | public string[] GetArgs() 93 | { 94 | var result = new List 95 | { 96 | "server", 97 | "start-dev" 98 | }; 99 | 100 | AddIfNotNull(result, "--db-filename", DbFileName); 101 | AddAlways(result, "--port", Port.ToString()); 102 | 103 | AddIfNotNull(result, "--http-port", HttpPort?.ToString()); 104 | AddIfNotNull(result, "--metrics-port", MetricsPort?.ToString()); 105 | AddIfNotNull(result, "--ui-port", UiPort?.ToString()); 106 | AddIfNotNull(result, "--headless", Headless?.ToString().ToLowerInvariant()); 107 | AddIfNotNull(result, "--ip", Ip); 108 | AddIfNotNull(result, "--ui-ip", UiIp); 109 | AddIfNotNull(result, "--ui-asset-path", UiAssetPath); 110 | AddIfNotNull(result, "--ui-codec-endpoint", UiCodecEndpoint); 111 | 112 | if (LogFormat.HasValue) 113 | { 114 | result.Add("--log-format"); 115 | result.Add(EnumHelpers.LogFormatToString(LogFormat.Value)); 116 | } 117 | 118 | if (LogLevel.HasValue) 119 | { 120 | result.Add("--log-level"); 121 | result.Add(EnumHelpers.LogLevelToString(LogLevel.Value)); 122 | } 123 | 124 | if (SQLitePragma.HasValue) 125 | { 126 | result.Add("--sqlite-pragma"); 127 | result.Add(EnumHelpers.SQLitePragmaToString(SQLitePragma.Value)); 128 | } 129 | 130 | foreach (var name in Namespaces) 131 | { 132 | result.Add("--namespace"); 133 | result.Add(name); 134 | } 135 | 136 | foreach (var (k, v) in DynamicConfigValues) 137 | { 138 | result.Add("--dynamic-config-value"); 139 | 140 | result.Add($"{k}={v switch 141 | { 142 | string s => $""" "{v}" """, 143 | bool b => b.ToString().ToLowerInvariant(), 144 | int i => i.ToString(), 145 | float f => f.ToString("F"), 146 | double d => d.ToString("F"), 147 | long l => l.ToString(), 148 | _ => null, 149 | }}"); 150 | } 151 | 152 | return [.. result]; 153 | } 154 | 155 | private static void AddIfNotNull(List list, string argument, string? value) 156 | { 157 | if (value is not null) 158 | { 159 | list.Add(argument); 160 | list.Add(value); 161 | } 162 | } 163 | 164 | private static void AddAlways(List list, string argument, string value) 165 | { 166 | list.Add(argument); 167 | list.Add(value); 168 | } 169 | } -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/TemporalServerResourceBuilder.cs: -------------------------------------------------------------------------------- 1 | using InfinityFlow.Aspire.Temporal; 2 | 3 | namespace Aspire.Hosting; 4 | 5 | public class TemporalServerResourceBuilder 6 | { 7 | private TemporalServerResourceArguments Args { get; } = new(); 8 | 9 | /// 10 | /// Command to execute (default: "temporal") 11 | /// 12 | /// 13 | /// 14 | public TemporalServerResourceBuilder WithCommand(string command = "temporal") 15 | { 16 | Args.Command = command; 17 | return this; 18 | } 19 | 20 | /// 21 | /// File in which to persist Temporal state (by default, Workflows are lost when the process dies) 22 | /// 23 | /// 24 | public TemporalServerResourceBuilder WithDbFileName(string dbFileName) 25 | { 26 | Args.DbFileName = dbFileName; 27 | return this; 28 | } 29 | 30 | /// 31 | /// Port for the frontend gRPC service (default: 7233) 32 | /// 33 | /// 34 | /// 35 | public TemporalServerResourceBuilder WithPort(int port) 36 | { 37 | Args.Port = port; 38 | return this; 39 | } 40 | 41 | /// 42 | /// Port for the frontend HTTP service (default: disabled) 43 | /// 44 | /// 45 | /// 46 | public TemporalServerResourceBuilder WithHttpPort(int httpPort) 47 | { 48 | Args.HttpPort = httpPort; 49 | return this; 50 | } 51 | 52 | /// 53 | /// Port for /metrics (default: disabled) 54 | /// 55 | /// 56 | /// 57 | public TemporalServerResourceBuilder WithMetricsPort(int metricsPort) 58 | { 59 | Args.MetricsPort = metricsPort; 60 | return this; 61 | } 62 | 63 | /// 64 | /// Port for the Web UI (default: --port + 1000, e.g. 8233) 65 | /// 66 | /// 67 | /// 68 | public TemporalServerResourceBuilder WithUiPort(int uiPort) 69 | { 70 | Args.UiPort = uiPort; 71 | return this; 72 | } 73 | 74 | /// 75 | /// Disable the Web UI(default: false) 76 | /// 77 | /// 78 | /// 79 | public TemporalServerResourceBuilder WithHeadlessUi(bool headless) 80 | { 81 | Args.Headless = headless; 82 | return this; 83 | } 84 | 85 | /// 86 | /// IPv4 address to bind the frontend service to (default: "127.0.0.1") 87 | /// 88 | /// 89 | /// 90 | public TemporalServerResourceBuilder WithIp(string ip) 91 | { 92 | Args.Ip = ip; 93 | return this; 94 | } 95 | 96 | /// 97 | /// IPv4 address to bind the Web UI to (default: same as --ip) 98 | /// 99 | /// 100 | /// 101 | public TemporalServerResourceBuilder WithUiIp(string uiIp) 102 | { 103 | Args.UiIp = uiIp; 104 | return this; 105 | } 106 | 107 | /// 108 | /// UI custom assets path 109 | /// 110 | /// 111 | /// 112 | public TemporalServerResourceBuilder WithUiAssetsPath(string assetsPath) 113 | { 114 | Args.UiAssetPath = assetsPath; 115 | return this; 116 | } 117 | 118 | /// 119 | /// UI remote codec HTTP endpoint 120 | /// 121 | /// 122 | /// 123 | public TemporalServerResourceBuilder WithUiCodecEndpoint(string codecEndpoint) 124 | { 125 | Args.UiCodecEndpoint = codecEndpoint; 126 | return this; 127 | } 128 | 129 | /// 130 | /// Set the log formatting. Options: ["json", "pretty"]. (default: "json") 131 | /// 132 | /// 133 | /// 134 | public TemporalServerResourceBuilder WithLogFormat(LogFormat format) 135 | { 136 | Args.LogFormat = format; 137 | return this; 138 | } 139 | 140 | /// 141 | /// Set the log level. Options: ["debug" "info" "warn" "error" "fatal"]. (default: "info") 142 | /// 143 | /// 144 | /// 145 | public TemporalServerResourceBuilder WithLogLevel(LogLevel level) 146 | { 147 | Args.LogLevel = level; 148 | return this; 149 | } 150 | 151 | /// 152 | /// Specify SQLite pragma statements in pragma=value format. Pragma options: ["journal_mode" "synchronous"] 153 | /// 154 | /// 155 | /// 156 | public TemporalServerResourceBuilder WithSQLitePragma(SQLitePragma pragma) 157 | { 158 | Args.SQLitePragma = pragma; 159 | return this; 160 | } 161 | 162 | /// 163 | /// Specify namespaces that should be pre-created (namespace "default" is always created) 164 | /// 165 | /// 166 | /// 167 | public TemporalServerResourceBuilder WithNamespace(params string[] namespaces) 168 | { 169 | Args.Namespaces.AddRange(namespaces); 170 | return this; 171 | } 172 | 173 | public TemporalServerResourceArguments Build() => Args; 174 | 175 | /// 176 | /// Specify dynamic config values that should be configured. 177 | /// 178 | /// 179 | /// 180 | /// 181 | public TemporalServerResourceBuilder WithDynamicConfigValue(string key, object value) 182 | { 183 | Args.DynamicConfigValues.Add(key, value); 184 | return this; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/assets/LICENSE: -------------------------------------------------------------------------------- 1 | ../../../LICENSE -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/assets/README.md: -------------------------------------------------------------------------------- 1 | ../../../README.md -------------------------------------------------------------------------------- /src/InfinityFlow.Aspire.Temporal/assets/packageIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfinityFlowApp/aspire-temporal/8bdd63b60da4ea9530bc766d7e1d58ccebd0973c/src/InfinityFlow.Aspire.Temporal/assets/packageIcon.png --------------------------------------------------------------------------------