Skip to content

Commit cb93802

Browse files
authored
Merge pull request #3 from elsa-workflows/002-package-manifest-generator
Fix package catalog main build
2 parents a87b3bc + 8d1dec5 commit cb93802

37 files changed

Lines changed: 955 additions & 9 deletions

.gitignore

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*.userosscache
1111
*.sln.docstates
1212
*.env
13+
.azure/
1314
.DS_Store
1415
Thumbs.db
1516

@@ -22,7 +23,7 @@ mono_crash.*
2223
# Build results
2324
bin/
2425
obj/
25-
packages/
26+
/packages/
2627
[Dd]ebug/
2728
[Dd]ebugPublic/
2829
[Rr]elease/
@@ -96,6 +97,7 @@ project.lock.json
9697
project.fragment.lock.json
9798
artifacts/
9899
.artifacts/
100+
src/Elsa.Catalog.AppHost/artifacts/
99101

100102
# ASP.NET Scaffolding
101103
ScaffoldingReadMe.txt
@@ -236,9 +238,9 @@ PublishScripts/
236238
# NuGet Symbol Packages
237239
*.snupkg
238240
# The packages folder can be ignored because of Package Restore
239-
**/[Pp]ackages/*
241+
/[Pp]ackages/*
240242
# except build/, which is used as an MSBuild target.
241-
!**/[Pp]ackages/build/
243+
!/[Pp]ackages/build/
242244
# Uncomment if necessary however generally it will be regenerated when needed
243245
#!**/[Pp]ackages/repositories.config
244246
# NuGet v3's project.json files produces more ignorable files

Directory.Packages.props

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,29 @@
55

66
<ItemGroup>
77
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
8+
<PackageVersion Include="Aspire.Hosting.Azure.AppService" Version="13.3.2" />
9+
<PackageVersion Include="Aspire.Hosting.Azure.Sql" Version="13.3.2" />
810
<PackageVersion Include="FluentAssertions" Version="8.8.0" />
911
<PackageVersion Include="JsonSchema.Net" Version="9.2.0" />
1012
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.1" />
1113
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.1" />
1214
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.8" />
1315
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.8" />
16+
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.8" />
1417
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.8" />
18+
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.7" />
19+
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.5.0" />
20+
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.5.0" />
1521
<PackageVersion Include="Microsoft.Build.Framework" Version="17.14.8" />
1622
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.14.8" />
1723
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
1824
<PackageVersion Include="NuGet.Protocol" Version="7.6.0" />
1925
<PackageVersion Include="NuGet.Versioning" Version="7.6.0" />
26+
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
27+
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
28+
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
29+
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
30+
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
2031
<PackageVersion Include="System.Reflection.MetadataLoadContext" Version="10.0.1" />
2132
<PackageVersion Include="xunit" Version="2.9.3" />
2233
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.4" />

Elsa.PackageCatalog.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution", "solution", "{9F
5252
AGENTS.md = AGENTS.md
5353
EndProjectSection
5454
EndProject
55+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Catalog.AppHost", "src\Elsa.Catalog.AppHost\Elsa.Catalog.AppHost.csproj", "{60DAE03C-A730-4125-B310-656D64BA28F0}"
56+
EndProject
57+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Catalog.ServiceDefaults", "src\Elsa.Catalog.ServiceDefaults\Elsa.Catalog.ServiceDefaults.csproj", "{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}"
58+
EndProject
5559
Global
5660
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5761
Debug|Any CPU = Debug|Any CPU
@@ -278,6 +282,30 @@ Global
278282
{060E3A4E-FD2E-41BB-936C-B91AF91BB628}.Release|x64.Build.0 = Release|Any CPU
279283
{060E3A4E-FD2E-41BB-936C-B91AF91BB628}.Release|x86.ActiveCfg = Release|Any CPU
280284
{060E3A4E-FD2E-41BB-936C-B91AF91BB628}.Release|x86.Build.0 = Release|Any CPU
285+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
286+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
287+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Debug|x64.ActiveCfg = Debug|Any CPU
288+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Debug|x64.Build.0 = Debug|Any CPU
289+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Debug|x86.ActiveCfg = Debug|Any CPU
290+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Debug|x86.Build.0 = Debug|Any CPU
291+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
292+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Release|Any CPU.Build.0 = Release|Any CPU
293+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Release|x64.ActiveCfg = Release|Any CPU
294+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Release|x64.Build.0 = Release|Any CPU
295+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Release|x86.ActiveCfg = Release|Any CPU
296+
{60DAE03C-A730-4125-B310-656D64BA28F0}.Release|x86.Build.0 = Release|Any CPU
297+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
298+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
299+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Debug|x64.ActiveCfg = Debug|Any CPU
300+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Debug|x64.Build.0 = Debug|Any CPU
301+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Debug|x86.ActiveCfg = Debug|Any CPU
302+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Debug|x86.Build.0 = Debug|Any CPU
303+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
304+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Release|Any CPU.Build.0 = Release|Any CPU
305+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Release|x64.ActiveCfg = Release|Any CPU
306+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Release|x64.Build.0 = Release|Any CPU
307+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Release|x86.ActiveCfg = Release|Any CPU
308+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA}.Release|x86.Build.0 = Release|Any CPU
281309
EndGlobalSection
282310
GlobalSection(SolutionProperties) = preSolution
283311
HideSolutionNode = FALSE
@@ -301,5 +329,7 @@ Global
301329
{3A90AE29-33DB-4241-9BB5-8578067C9A49} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
302330
{10D68F1A-23BB-43BF-BC90-1BC0D73F0BFB} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
303331
{060E3A4E-FD2E-41BB-936C-B91AF91BB628} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
332+
{60DAE03C-A730-4125-B310-656D64BA28F0} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
333+
{1906CE1B-FB94-4E2B-8EF7-44A927277BEA} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
304334
EndGlobalSection
305335
EndGlobal

azure.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: elsa-package-catalog
2+
services:
3+
api:
4+
project: src/Elsa.Catalog.AppHost/Elsa.Catalog.AppHost.csproj
5+
host: containerapp
6+
language: dotnet
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Aspire Deployment to Azure App Service
2+
3+
## Recommendation
4+
5+
Use Aspire and Azure Developer CLI (`azd`) as the deployment path. The AppHost
6+
declares the API, Azure App Service environment, and Azure SQL database;
7+
Aspire/azd provisions the resources, builds the app container image, pushes it
8+
to ACR, and deploys the Web App.
9+
10+
The previous manually provisioned Web App can be deleted once anything important
11+
has been backed up.
12+
13+
## Local Tooling
14+
15+
Install the current .NET 10 SDK before deploying. As of May 15, 2026, the
16+
current .NET 10 SDK line is `10.0.300`, released May 12, 2026.
17+
18+
The official Aspire templates are installed with:
19+
20+
```bash
21+
dotnet new install Aspire.ProjectTemplates
22+
dotnet tool install -g Aspire.Cli
23+
```
24+
25+
On this machine, `~/.dotnet/tools/aspire` is `13.3.2`. If `aspire --version`
26+
prints an older version, move `~/.dotnet/tools` before `~/.aspire/bin` in
27+
`PATH`, or run `~/.dotnet/tools/aspire` explicitly.
28+
29+
## Deploy
30+
31+
```bash
32+
azd auth login
33+
azd init
34+
azd env set admin-api-key <strong-secret>
35+
azd up
36+
```
37+
38+
When `azd init` asks how to initialize the app, scan the current directory and
39+
confirm the detected Aspire AppHost.
40+
41+
## Removing Existing Resources
42+
43+
If the old resources are in a dedicated resource group, delete the group:
44+
45+
```bash
46+
az group delete --name <old-resource-group>
47+
```
48+
49+
If the group has shared resources, delete only the old Web App, plan, registry,
50+
storage account, and related managed identities after confirming they are not
51+
used elsewhere.
52+
53+
## Database Provider
54+
55+
The API supports two EF Core providers:
56+
57+
- `Database:Provider=Sqlite`
58+
- `Database:Provider=SqlServer`
59+
60+
Local development defaults to SQLite. Aspire publish mode provisions Azure SQL,
61+
injects `ConnectionStrings__Catalog`, and sets `Database__Provider=SqlServer`.
62+
63+
SQLite remains fine for local development and single-process test runs. For
64+
production and App Service scale-out, use Azure SQL. SQLite on shared App
65+
Service storage or Azure Files is not a good production target because SQLite
66+
depends on filesystem locking, and WAL mode does not support clients on
67+
different machines through a network filesystem.

global.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"sdk": {
3+
"version": "10.0.300",
4+
"rollForward": "latestFeature"
5+
}
6+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Elsa.Catalog.Api.Authentication;
2+
using Elsa.Catalog.Core.Approvals;
3+
using Elsa.Catalog.Core.Packages;
4+
using System.Security.Claims;
5+
6+
namespace Elsa.Catalog.Api.Admin.Packages;
7+
8+
public static class AdminApprovalEndpoints
9+
{
10+
public static IEndpointRouteBuilder MapAdminApprovalEndpoints(this IEndpointRouteBuilder endpoints)
11+
{
12+
var group = endpoints.MapGroup("/api/admin/packages")
13+
.RequireAuthorization(AdminAuthorization.Policy)
14+
.WithTags("Admin Approval");
15+
16+
group.MapPost("/{packageId}/approve", async (string packageId, ApprovalRequest? request, HttpContext httpContext, ApprovalService approvals, CancellationToken cancellationToken) =>
17+
await approvals.SetPackageApprovalAsync(packageId, PackageApprovalStatus.Approved, GetActor(httpContext), request?.Reason, cancellationToken) ? Results.NoContent() : Results.NotFound());
18+
19+
group.MapPost("/{packageId}/reject", async (string packageId, ApprovalRequest? request, HttpContext httpContext, ApprovalService approvals, CancellationToken cancellationToken) =>
20+
await approvals.SetPackageApprovalAsync(packageId, PackageApprovalStatus.Rejected, GetActor(httpContext), request?.Reason, cancellationToken) ? Results.NoContent() : Results.NotFound());
21+
22+
group.MapPost("/{packageId}/versions/{version}/approve", async (string packageId, string version, ApprovalRequest? request, HttpContext httpContext, ApprovalService approvals, CancellationToken cancellationToken) =>
23+
await approvals.SetVersionApprovalAsync(packageId, version, PackageApprovalStatus.Approved, GetActor(httpContext), request?.Reason, cancellationToken) ? Results.NoContent() : Results.NotFound());
24+
25+
group.MapPost("/{packageId}/versions/{version}/reject", async (string packageId, string version, ApprovalRequest? request, HttpContext httpContext, ApprovalService approvals, CancellationToken cancellationToken) =>
26+
await approvals.SetVersionApprovalAsync(packageId, version, PackageApprovalStatus.Rejected, GetActor(httpContext), request?.Reason, cancellationToken) ? Results.NoContent() : Results.NotFound());
27+
28+
return endpoints;
29+
}
30+
31+
private static string GetActor(HttpContext httpContext) =>
32+
httpContext.User.FindFirstValue(ClaimTypes.NameIdentifier)
33+
?? httpContext.User.Identity?.Name
34+
?? "unknown";
35+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Elsa.Catalog.Core.Packages;
2+
3+
namespace Elsa.Catalog.Api.Admin.Packages;
4+
5+
public sealed record AdminPackageResponse(
6+
string PackageId,
7+
bool Approved,
8+
bool Listed,
9+
string? LatestVersion,
10+
IReadOnlyList<AdminPackageVersionResponse> Versions);
11+
12+
public sealed record AdminPackageVersionResponse(
13+
string Version,
14+
ValidationStatus ValidationStatus,
15+
PackageApprovalStatus ApprovalStatus,
16+
bool IsListed,
17+
bool SuspiciousChangeDetected,
18+
string? SchemaVersion);
19+
20+
public sealed record ApprovalRequest(string? Reason);
21+
22+
public sealed record AdminValidationResultResponse(
23+
Guid Id,
24+
string? SchemaVersion,
25+
ValidationStatus Status,
26+
string ErrorsJson,
27+
string WarningsJson,
28+
DateTimeOffset ValidatedAt,
29+
string? ValidatorVersion);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Elsa.Catalog.Api.Authentication;
2+
using Elsa.Catalog.Core.Approvals;
3+
using Elsa.Catalog.Core.Packages;
4+
5+
namespace Elsa.Catalog.Api.Admin.Packages;
6+
7+
public static class AdminPackageEndpoints
8+
{
9+
public static IEndpointRouteBuilder MapAdminPackageEndpoints(this IEndpointRouteBuilder endpoints)
10+
{
11+
var group = endpoints.MapGroup("/api/admin/packages")
12+
.RequireAuthorization(AdminAuthorization.Policy)
13+
.WithTags("Admin Packages");
14+
15+
group.MapGet("/", async (ApprovalService approvals, CancellationToken cancellationToken) =>
16+
Results.Ok((await approvals.ListPackagesAsync(cancellationToken)).Select(ToResponse)));
17+
18+
group.MapGet("/{packageId}", async (string packageId, ApprovalService approvals, CancellationToken cancellationToken) =>
19+
{
20+
var package = await approvals.GetPackageAsync(packageId, cancellationToken);
21+
return package is null ? Results.NotFound() : Results.Ok(ToResponse(package));
22+
});
23+
24+
return endpoints;
25+
}
26+
27+
internal static AdminPackageResponse ToResponse(Package package) =>
28+
new(
29+
package.PackageId,
30+
package.Approved,
31+
package.Listed,
32+
package.LatestVersion,
33+
package.Versions.Select(version => new AdminPackageVersionResponse(
34+
version.Version,
35+
version.ValidationStatus,
36+
version.ApprovalStatus,
37+
version.IsListed,
38+
version.SuspiciousChangeDetected,
39+
version.SchemaVersion)).ToList());
40+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Elsa.Catalog.Api.Authentication;
2+
using Elsa.Catalog.Core.Approvals;
3+
4+
namespace Elsa.Catalog.Api.Admin.Packages;
5+
6+
public static class AdminValidationEndpoints
7+
{
8+
public static IEndpointRouteBuilder MapAdminValidationEndpoints(this IEndpointRouteBuilder endpoints)
9+
{
10+
var group = endpoints.MapGroup("/api/admin/packages")
11+
.RequireAuthorization(AdminAuthorization.Policy)
12+
.WithTags("Admin Validation");
13+
14+
group.MapGet("/{packageId}/versions/{version}/validation", async (string packageId, string version, IApprovalStore store, CancellationToken cancellationToken) =>
15+
{
16+
var results = await store.GetValidationResultsAsync(packageId, version, cancellationToken);
17+
return Results.Ok(results.Select(x => new AdminValidationResultResponse(x.Id, x.SchemaVersion, x.Status, x.ErrorsJson, x.WarningsJson, x.ValidatedAt, x.ValidatorVersion)));
18+
});
19+
20+
return endpoints;
21+
}
22+
}

0 commit comments

Comments
 (0)