Skip to content

Commit 4532ced

Browse files
committed
Add identity web composition and docs workflow
1 parent 2fffb3a commit 4532ced

File tree

65 files changed

+1962
-50
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1962
-50
lines changed

.nuke/build.schema.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
"Build",
2929
"Clean",
3030
"CoverageReport",
31+
"Doc",
32+
"DocsBuild",
3133
"Format",
3234
"GenerateReleaseManifest",
3335
"Init",
@@ -142,6 +144,11 @@
142144
"description": "Minimum line coverage percentage (0-100). CoverageReport fails if below this threshold",
143145
"format": "int32"
144146
},
147+
"DocsPort": {
148+
"type": "integer",
149+
"description": "Port used by Doc target",
150+
"format": "int32"
151+
},
145152
"NuGetApiKey": {
146153
"type": "string",
147154
"description": "NuGet API key for PushNuGetPackages target. Defaults to NUGET_API_KEY environment variable"
@@ -150,6 +157,10 @@
150157
"type": "string",
151158
"description": "New project name for Init target (e.g. Acme.Payments)"
152159
},
160+
"PublishHost": {
161+
"type": "string",
162+
"description": "Host to publish - supported values are 'web' and 'cli'"
163+
},
153164
"ResetGit": {
154165
"type": "boolean",
155166
"description": "Reset git history to a fresh commit during Init"

ChengYuan.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<Project Path="src/Applications/ChengYuan.Identity/ChengYuan.Identity.Domain/ChengYuan.Identity.Domain.csproj" />
4040
<Project Path="src/Applications/ChengYuan.Identity/ChengYuan.Identity.Application/ChengYuan.Identity.Application.csproj" />
4141
<Project Path="src/Applications/ChengYuan.Identity/ChengYuan.Identity.Persistence/ChengYuan.Identity.Persistence.csproj" />
42+
<Project Path="src/Applications/ChengYuan.Identity/ChengYuan.Identity.Web/ChengYuan.Identity.Web.csproj" />
4243
<Project Path="src/Applications/ChengYuan.PermissionManagement/ChengYuan.PermissionManagement.Contracts/ChengYuan.PermissionManagement.Contracts.csproj" />
4344
<Project Path="src/Applications/ChengYuan.PermissionManagement/ChengYuan.PermissionManagement.Application/ChengYuan.PermissionManagement.Application.csproj" />
4445
<Project Path="src/Applications/ChengYuan.PermissionManagement/ChengYuan.PermissionManagement.Persistence/ChengYuan.PermissionManagement.Persistence.csproj" />

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
1111
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
1212
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
13+
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="10.0.0" />
1314
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />
1415
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.0" />
1516
<!-- Analyzers -->

build/BuildTask.Helpers.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using Nuke.Common;
15
using Nuke.Common.Tools.DotNet;
26

37
partial class BuildTask
@@ -13,4 +17,58 @@ DotNetPublishSettings ApplyVersionSuffix(DotNetPublishSettings settings) =>
1317

1418
DotNetTestSettings ApplyVersionSuffix(DotNetTestSettings settings) =>
1519
string.IsNullOrWhiteSpace(VersionSuffix) ? settings : settings.SetProperty("VersionSuffix", VersionSuffix);
20+
21+
void EnsureDocsDependencies()
22+
{
23+
if (Directory.Exists(DocsNodeModulesDirectory))
24+
{
25+
Serilog.Log.Information("Skipping docs dependency install because node_modules already exists.");
26+
return;
27+
}
28+
29+
var installCommand = File.Exists(DocsPackageLockFile) ? "ci" : "install";
30+
Serilog.Log.Information("Installing docs dependencies with 'npm {InstallCommand}'.", installCommand);
31+
RunNpmCommandAndAssert(installCommand, DocsDirectory);
32+
}
33+
34+
void BuildDocsSite()
35+
{
36+
Serilog.Log.Information("Building docs site.");
37+
RunNpmCommandAndAssert("run docs:build", DocsDirectory);
38+
}
39+
40+
void RunNpmCommandAndAssert(string arguments, string workingDirectory)
41+
{
42+
var (fileName, resolvedArguments) = CreateNpmInvocation(arguments);
43+
RunCommandAndAssert(fileName, resolvedArguments, workingDirectory);
44+
}
45+
46+
(string FileName, string Arguments) CreateNpmInvocation(string arguments)
47+
{
48+
return OperatingSystem.IsWindows()
49+
? ("cmd.exe", $"/c npm.cmd {arguments}")
50+
: ("npm", arguments);
51+
}
52+
53+
void RunCommandAndAssert(string fileName, string arguments, string workingDirectory)
54+
{
55+
using var process = Process.Start(new ProcessStartInfo
56+
{
57+
FileName = fileName,
58+
Arguments = arguments,
59+
WorkingDirectory = workingDirectory,
60+
UseShellExecute = false,
61+
RedirectStandardOutput = false,
62+
RedirectStandardError = false,
63+
CreateNoWindow = true
64+
}) ?? throw new InvalidOperationException($"Failed to start process '{fileName}'.");
65+
66+
process.WaitForExit();
67+
68+
if (process.ExitCode != 0)
69+
{
70+
throw new InvalidOperationException($"Process '{fileName} {arguments}' exited with code {process.ExitCode}.");
71+
}
72+
}
73+
1674
}

build/BuildTask.Parameters.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ partial class BuildTask
1313
readonly string VersionSuffix = string.Empty;
1414

1515
[Parameter("Host to publish - supported values are 'web' and 'cli'")]
16-
new readonly string Host = string.Empty;
16+
readonly string PublishHost = string.Empty;
1717

1818
// Internal path conventions controlled by build tasks (not external parameters).
1919
readonly string BuildPath = "ChengYuan.slnx";
2020
readonly string TestPath = "ChengYuan.slnx";
2121
readonly string PackPath = "ChengYuan.slnx";
22-
string NormalizedHost => Host.Trim().ToLowerInvariant();
22+
string NormalizedHost => PublishHost.Trim().ToLowerInvariant();
2323
string PublishPath => NormalizedHost switch
2424
{
2525
"web" => "src/Hosts/ChengYuan.WebHost/ChengYuan.WebHost.csproj",
@@ -34,6 +34,9 @@ partial class BuildTask
3434
[Parameter("Publish self-contained output in Publish target")]
3535
readonly bool SelfContained;
3636

37+
[Parameter("Port used by Doc target")]
38+
readonly int DocsPort = 5173;
39+
3740
[Parameter("NuGet API key for PushNuGetPackages target. Defaults to NUGET_API_KEY environment variable.")]
3841
readonly string NuGetApiKey = string.Empty;
3942

@@ -58,6 +61,9 @@ partial class BuildTask
5861
AbsolutePath PackagesDirectory => ArtifactsDirectory / "packages";
5962
AbsolutePath PublishDirectory => ArtifactsDirectory / "publish";
6063
AbsolutePath InstallersDirectory => ArtifactsDirectory / "installers";
64+
AbsolutePath DocsDirectory => RootDirectory / "docs";
65+
AbsolutePath DocsNodeModulesDirectory => DocsDirectory / "node_modules";
66+
AbsolutePath DocsPackageLockFile => DocsDirectory / "package-lock.json";
6167

6268
AbsolutePath ReleaseManifestFile => PackagesDirectory / "release-manifest.json";
6369
AbsolutePath BuildOutputsMarkerFile => ArtifactsDirectory / ".build-outputs" / "build-outputs.json";

build/BuildTask.Targets.Docs.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Nuke.Common;
2+
3+
partial class BuildTask
4+
{
5+
Target Doc => _ => _
6+
.DependsOn(DocsBuild)
7+
.Executes(() =>
8+
{
9+
Serilog.Log.Information("Starting docs development server on http://localhost:{DocsPort}.", DocsPort);
10+
RunNpmCommandAndAssert($"run docs:dev:restart -- --port {DocsPort}", DocsDirectory);
11+
});
12+
13+
Target DocsBuild => _ => _
14+
.Executes(() =>
15+
{
16+
EnsureDocsDependencies();
17+
BuildDocsSite();
18+
});
19+
}

docs/contributing/development.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ All build logic lives in `build/`. Workflows call these targets instead of raw `
5555
| **Build** | Restore | Build the solution |
5656
| **Test** | Build | Run tests with coverage (trx + Coverlet) |
5757
| **Pack** | Test | Pack NuGet packages (.nupkg + .snupkg) |
58-
| **Publish** | Restore | Publish the selected host for a given `--Host` and `--Runtime` |
58+
| **Publish** | Restore | Publish the selected host for a given `--PublishHost` and `--Runtime` |
5959
| **PackageApp** | Publish | Zip published output to `app-{host}-{runtime}.zip` |
6060
| **Format** | Restore | `dotnet format --verify-no-changes` |
6161
| **CoverageReport** | Test | Generate HTML + Cobertura coverage report |
62+
| **Doc** | DocsBuild | Clear any same-port docs server and start the docs dev server |
63+
| **DocsBuild** || Install docs dependencies when needed and run `vitepress build` |
6264
| **ShowVersion** || Print current `VersionPrefix` |
6365
| **UpdateVersion** || Bump patch or set `--VersionPrefix` explicitly |
6466
| **GenerateReleaseManifest** | Pack | Create `release-manifest.json` with SHA256 hashes |
@@ -68,8 +70,11 @@ All build logic lives in `build/`. Workflows call these targets instead of raw `
6870
```bash
6971
./build.sh Test # Build + Test
7072
./build.sh Pack # Build + Test + Pack
71-
./build.sh Publish --Host web --Runtime linux-x64 --SelfContained # Publish self-contained web host
73+
./build.sh Publish --PublishHost web --Runtime linux-x64 --SelfContained # Publish self-contained web host
7274
./build.sh CoverageReport # Generate coverage HTML
75+
./build.sh DocsBuild # Build the docs site
76+
./build.sh Doc # Install docs deps, kill any existing server, and start docs dev server on port 5173
77+
./build.sh Doc --DocsPort 8080 # Same on a custom port (Ctrl+C to stop)
7378
./build.sh ShowVersion # Print version
7479
./build.sh UpdateVersion # Patch bump (e.g. 0.2.0 → 0.2.1)
7580
./build.sh UpdateVersion --VersionPrefix 1.0.0 # Set version explicitly
@@ -82,9 +87,10 @@ All build logic lives in `build/`. Workflows call these targets instead of raw `
8287
| `--Configuration` | `Debug` (local) / `Release` (CI) | Build configuration |
8388
| `--VersionPrefix` || Version to set (used by `UpdateVersion`) |
8489
| `--VersionSuffix` || Prerelease suffix (e.g. `ci.42`) |
85-
| `--Host` || Host to publish (`web` or `cli`) |
90+
| `--PublishHost` || Host to publish (`web` or `cli`) |
8691
| `--Runtime` || Target RID for `Publish` (e.g. `linux-x64`) |
8792
| `--SelfContained` | `false` | Produce self-contained output |
93+
| `--DocsPort` | `5173` | Port used by `Doc` |
8894

8995
### Artifacts
9096

@@ -106,6 +112,9 @@ npm install
106112
npm run docs:dev # Local dev server at http://localhost:5173
107113
npm run docs:build # Production build
108114
npm run docs:preview # Preview production build
115+
116+
# Or use NUKE from the repository root (blocks until Ctrl+C)
117+
build.ps1 Doc
109118
```
110119

111120
## Next Steps

docs/package-lock.json

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
"type": "module",
44
"scripts": {
55
"docs:dev": "vitepress dev",
6+
"docs:dev:restart": "node ./scripts/docs-dev-restart.mjs",
67
"docs:build": "vitepress build",
78
"docs:preview": "vitepress preview"
89
},
910
"devDependencies": {
11+
"kill-port": "^2.0.1",
1012
"vitepress": "^1.6.3"
1113
}
1214
}

docs/reference/api.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,18 @@ First full application bounded context for user management.
179179
| Type | Description |
180180
|---|---|
181181
| `IdentityModule` | Registers the Identity application services on top of the core runtime module. |
182+
| `RoleRecord` | Immutable role snapshot exposed through application contracts. |
182183
| `UserRecord` | Immutable user snapshot exposed through application contracts. |
184+
| `IRoleReader` | Reads roles by id, name, or ordered list. |
185+
| `IRoleManager` | Creates, updates, and removes roles while enforcing unique role-name rules. |
183186
| `IUserReader` | Reads users by id, user name, email, or ordered list. |
184-
| `IUserManager` | Creates, updates, and removes users while enforcing unique user name and email rules. |
187+
| `IUserManager` | Creates, updates, removes users, and manages their role assignments while enforcing unique user name and email rules. |
188+
| `IdentityRole` | Aggregate root for roles with normalized name and soft-delete behavior. |
185189
| `IdentityUser` | Aggregate root for users with normalized user name and email plus soft-delete behavior. |
190+
| `IdentityUserRole` | Domain-owned assignment record that keeps user-role membership inside the Identity model. |
191+
| `IIdentityRoleRepository` | Domain repository contract for identity-role lookup and persistence. |
186192
| `IIdentityUserRepository` | Domain repository contract for identity-user lookup and persistence. |
193+
| `RoleManager` | Application service that coordinates role uniqueness, lifecycle, and assignment cleanup. |
187194
| `UserManager` | Application service that coordinates uniqueness checks and persistence. |
188195

189196
### `ChengYuan.Identity.Persistence`
@@ -196,11 +203,25 @@ Persistence facet for identity-user storage backed by EF Core.
196203
|---|---|
197204
| `IdentityPersistenceModule` | Registers the EF-backed identity-user repository on top of the Identity application module. |
198205
| `IdentityDbContext` | Independent module DbContext for identity-user tables within the shared physical database. |
206+
| `IdentityRoleConfiguration` | EF Core model configuration for the identity-role aggregate. |
199207
| `IdentityUserConfiguration` | EF Core model configuration for the identity-user aggregate. |
208+
| `IdentityUserRoleConfiguration` | EF Core model configuration for the user-role assignment table. |
209+
| `EfIdentityRoleRepository` | EF Core implementation of `IIdentityRoleRepository`. |
200210
| `EfIdentityUserRepository` | EF Core implementation of `IIdentityUserRepository`. |
201211
| `AddIdentityDbContext()` | Registers the module DbContext against a caller-provided provider and connection. |
202212
| `AddIdentityPersistence()` | Registers the EF-backed identity repository and unit of work services. |
203213

214+
### `ChengYuan.Identity.Web`
215+
216+
Minimal HTTP management facet for Identity.
217+
218+
#### Key Types
219+
220+
| Type | Description |
221+
|---|---|
222+
| `IdentityWebModule` | Registers the Identity Web facet on top of the Identity application module. |
223+
| `MapIdentityManagementEndpoints()` | Maps the minimal HTTP management surface for users, roles, and user-role assignment operations. |
224+
204225
### `ChengYuan.Authorization`
205226

206227
Definition-driven runtime permission checking.

0 commit comments

Comments
 (0)