Skip to content

Commit 1f0d24b

Browse files
committed
feat: plugin isolation, TDD, test coverage, and modular build improvements
- Implemented plugin sandboxing and interface contract enforcement - Added TDD tests for plugin loader and sandbox - Integrated nuke test - Refactored build tasks for modularity and CI/CD readiness - Updated stories and docs for new plugin architecture // All code changes use English comments
1 parent 79645ba commit 1f0d24b

39 files changed

+1691
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,3 +399,4 @@ FodyWeavers.xsd
399399

400400
# JetBrains Rider
401401
*.sln.iml
402+
docs/Story/.keep

.nuke/build.schema.json

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"definitions": {
4+
"Host": {
5+
"type": "string",
6+
"enum": [
7+
"AppVeyor",
8+
"AzurePipelines",
9+
"Bamboo",
10+
"Bitbucket",
11+
"Bitrise",
12+
"GitHubActions",
13+
"GitLab",
14+
"Jenkins",
15+
"Rider",
16+
"SpaceAutomation",
17+
"TeamCity",
18+
"Terminal",
19+
"TravisCI",
20+
"VisualStudio",
21+
"VSCode"
22+
]
23+
},
24+
"ExecutableTarget": {
25+
"type": "string",
26+
"enum": [
27+
"Build",
28+
"BuildAll",
29+
"Clean",
30+
"Default",
31+
"Pack",
32+
"Restore",
33+
"Run",
34+
"Test"
35+
]
36+
},
37+
"Verbosity": {
38+
"type": "string",
39+
"description": "",
40+
"enum": [
41+
"Verbose",
42+
"Normal",
43+
"Minimal",
44+
"Quiet"
45+
]
46+
},
47+
"NukeBuild": {
48+
"properties": {
49+
"Continue": {
50+
"type": "boolean",
51+
"description": "Indicates to continue a previously failed build attempt"
52+
},
53+
"Help": {
54+
"type": "boolean",
55+
"description": "Shows the help text for this build assembly"
56+
},
57+
"Host": {
58+
"description": "Host for execution. Default is 'automatic'",
59+
"$ref": "#/definitions/Host"
60+
},
61+
"NoLogo": {
62+
"type": "boolean",
63+
"description": "Disables displaying the NUKE logo"
64+
},
65+
"Partition": {
66+
"type": "string",
67+
"description": "Partition to use on CI"
68+
},
69+
"Plan": {
70+
"type": "boolean",
71+
"description": "Shows the execution plan (HTML)"
72+
},
73+
"Profile": {
74+
"type": "array",
75+
"description": "Defines the profiles to load",
76+
"items": {
77+
"type": "string"
78+
}
79+
},
80+
"Root": {
81+
"type": "string",
82+
"description": "Root directory during build execution"
83+
},
84+
"Skip": {
85+
"type": "array",
86+
"description": "List of targets to be skipped. Empty list skips all dependencies",
87+
"items": {
88+
"$ref": "#/definitions/ExecutableTarget"
89+
}
90+
},
91+
"Target": {
92+
"type": "array",
93+
"description": "List of targets to be invoked. Default is '{default_target}'",
94+
"items": {
95+
"$ref": "#/definitions/ExecutableTarget"
96+
}
97+
},
98+
"Verbosity": {
99+
"description": "Logging verbosity during build execution. Default is 'Normal'",
100+
"$ref": "#/definitions/Verbosity"
101+
}
102+
}
103+
}
104+
},
105+
"allOf": [
106+
{
107+
"properties": {
108+
"Configuration": {
109+
"type": "string",
110+
"description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
111+
"enum": [
112+
"Debug",
113+
"Release"
114+
]
115+
},
116+
"Solution": {
117+
"type": "string",
118+
"description": "Path to a solution file that is automatically loaded"
119+
}
120+
}
121+
},
122+
{
123+
"$ref": "#/definitions/NukeBuild"
124+
}
125+
]
126+
}

.nuke/parameters.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"$schema": "build.schema.json",
3+
"Solution": "Modulus.sln",
4+
"NoLogo": true
5+
}

Modulus.sln

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
Microsoft Visual Studio Solution File, Format Version 12.00
2+
# Visual Studio Version 17
3+
VisualStudioVersion = 17.0.31903.59
4+
MinimumVisualStudioVersion = 10.0.40219.1
5+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}"
6+
EndProject
7+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
8+
EndProject
9+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modulus.PluginHost", "src\Modulus.PluginHost\Modulus.PluginHost.csproj", "{13583974-8926-4153-8A6E-B8724B767D45}"
10+
EndProject
11+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modulus.App", "src\Modulus.App\Modulus.App.csproj", "{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}"
12+
EndProject
13+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modulus.App.Desktop", "src\Modulus.App.Desktop\Modulus.App.Desktop.csproj", "{5E969E96-57A7-B6D8-7475-5E543D716563}"
14+
EndProject
15+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modulus.PluginHost.Tests", "tests\Modulus.PluginHost.Tests\Modulus.PluginHost.Tests.csproj", "{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}"
16+
EndProject
17+
Global
18+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
19+
Debug|Any CPU = Debug|Any CPU
20+
Debug|x64 = Debug|x64
21+
Debug|x86 = Debug|x86
22+
Release|Any CPU = Release|Any CPU
23+
Release|x64 = Release|x64
24+
Release|x86 = Release|x86
25+
EndGlobalSection
26+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
27+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Debug|x64.ActiveCfg = Debug|Any CPU
29+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Debug|x64.Build.0 = Debug|Any CPU
30+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Debug|x86.ActiveCfg = Debug|Any CPU
31+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Debug|x86.Build.0 = Debug|Any CPU
32+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
33+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Release|x64.ActiveCfg = Release|Any CPU
34+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Release|x64.Build.0 = Release|Any CPU
35+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Release|x86.ActiveCfg = Release|Any CPU
36+
{F90DD49A-A54E-41DE-8461-3FC1AAABDC4D}.Release|x86.Build.0 = Release|Any CPU
37+
{13583974-8926-4153-8A6E-B8724B767D45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38+
{13583974-8926-4153-8A6E-B8724B767D45}.Debug|Any CPU.Build.0 = Debug|Any CPU
39+
{13583974-8926-4153-8A6E-B8724B767D45}.Debug|x64.ActiveCfg = Debug|Any CPU
40+
{13583974-8926-4153-8A6E-B8724B767D45}.Debug|x64.Build.0 = Debug|Any CPU
41+
{13583974-8926-4153-8A6E-B8724B767D45}.Debug|x86.ActiveCfg = Debug|Any CPU
42+
{13583974-8926-4153-8A6E-B8724B767D45}.Debug|x86.Build.0 = Debug|Any CPU
43+
{13583974-8926-4153-8A6E-B8724B767D45}.Release|Any CPU.ActiveCfg = Release|Any CPU
44+
{13583974-8926-4153-8A6E-B8724B767D45}.Release|Any CPU.Build.0 = Release|Any CPU
45+
{13583974-8926-4153-8A6E-B8724B767D45}.Release|x64.ActiveCfg = Release|Any CPU
46+
{13583974-8926-4153-8A6E-B8724B767D45}.Release|x64.Build.0 = Release|Any CPU
47+
{13583974-8926-4153-8A6E-B8724B767D45}.Release|x86.ActiveCfg = Release|Any CPU
48+
{13583974-8926-4153-8A6E-B8724B767D45}.Release|x86.Build.0 = Release|Any CPU
49+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
51+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Debug|x64.ActiveCfg = Debug|Any CPU
52+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Debug|x64.Build.0 = Debug|Any CPU
53+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Debug|x86.ActiveCfg = Debug|Any CPU
54+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Debug|x86.Build.0 = Debug|Any CPU
55+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
56+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Release|Any CPU.Build.0 = Release|Any CPU
57+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Release|x64.ActiveCfg = Release|Any CPU
58+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Release|x64.Build.0 = Release|Any CPU
59+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Release|x86.ActiveCfg = Release|Any CPU
60+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2}.Release|x86.Build.0 = Release|Any CPU
61+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Debug|Any CPU.Build.0 = Debug|Any CPU
63+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Debug|x64.ActiveCfg = Debug|Any CPU
64+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Debug|x64.Build.0 = Debug|Any CPU
65+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Debug|x86.ActiveCfg = Debug|Any CPU
66+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Debug|x86.Build.0 = Debug|Any CPU
67+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Release|Any CPU.ActiveCfg = Release|Any CPU
68+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Release|Any CPU.Build.0 = Release|Any CPU
69+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Release|x64.ActiveCfg = Release|Any CPU
70+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Release|x64.Build.0 = Release|Any CPU
71+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Release|x86.ActiveCfg = Release|Any CPU
72+
{5E969E96-57A7-B6D8-7475-5E543D716563}.Release|x86.Build.0 = Release|Any CPU
73+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
75+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Debug|x64.ActiveCfg = Debug|Any CPU
76+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Debug|x64.Build.0 = Debug|Any CPU
77+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Debug|x86.ActiveCfg = Debug|Any CPU
78+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Debug|x86.Build.0 = Debug|Any CPU
79+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
80+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Release|Any CPU.Build.0 = Release|Any CPU
81+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Release|x64.ActiveCfg = Release|Any CPU
82+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Release|x64.Build.0 = Release|Any CPU
83+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Release|x86.ActiveCfg = Release|Any CPU
84+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1}.Release|x86.Build.0 = Release|Any CPU
85+
EndGlobalSection
86+
GlobalSection(SolutionProperties) = preSolution
87+
HideSolutionNode = FALSE
88+
EndGlobalSection
89+
GlobalSection(NestedProjects) = preSolution
90+
{13583974-8926-4153-8A6E-B8724B767D45} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
91+
{568C5B1A-2FD2-446D-8D5B-CD4A55C31EE2} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
92+
{5E969E96-57A7-B6D8-7475-5E543D716563} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
93+
{B1B2C3D4-E5F6-7890-1234-56789ABCDEF1} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
94+
EndGlobalSection
95+
EndGlobal

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,36 @@
11
# Modulus
22
Modulus - Modular Application Template for Cross-Platform Tooling
3+
4+
**Modulus** 是一个现代化的跨平台插件式工具应用模板,旨在帮助开发者快速构建可扩展、可维护、支持 AI 的桌面工具类软件。
5+
6+
该模板具备模块化架构、热插拔插件、配置系统、依赖注入、本地化、多版本兼容、签名验证等关键特性,帮助你专注业务开发而非基础设施。
7+
8+
---
9+
10+
## ✨ 特性亮点
11+
12+
- 🔌 插件热更新与动态卸载(基于 AssemblyLoadContext)
13+
- ⚙️ 插件配置支持(JSON-based)
14+
- 📦 插件依赖注入(DI 容器隔离)
15+
- 🌐 多语言本地化(支持自动切换)
16+
- 🔐 插件签名验证与版本控制
17+
- 🧠 AI Agent 插件支持(可嵌入 LLM)
18+
- 🛠️ 提供插件开发 SDK 与模板工程
19+
- 🖥️ 跨平台支持:Windows / macOS(Avalonia UI)
20+
21+
---
22+
23+
## 📦 用途场景
24+
25+
- 构建桌面数据工具 / UI 自动化工具
26+
- 快速构建开发者辅助类应用(Log Viewer、Code Generator)
27+
- 面向 AI 插件开发的任务框架
28+
- 内部工具平台(多团队协作)
29+
30+
---
31+
32+
## 🚀 快速开始
33+
34+
```bash
35+
dotnet new --install Modulus.Templates
36+
dotnet new modulus-plugin -n MyPlugin

build/.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[*.cs]
2+
dotnet_style_qualification_for_field = false:warning
3+
dotnet_style_qualification_for_property = false:warning
4+
dotnet_style_qualification_for_method = false:warning
5+
dotnet_style_qualification_for_event = false:warning
6+
dotnet_style_require_accessibility_modifiers = never:warning
7+
8+
csharp_style_expression_bodied_methods = true:silent
9+
csharp_style_expression_bodied_properties = true:warning
10+
csharp_style_expression_bodied_indexers = true:warning
11+
csharp_style_expression_bodied_accessors = true:warning

build/BuildTasks.cs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using Nuke.Common;
5+
using Nuke.Common.CI;
6+
using Nuke.Common.Execution;
7+
using Nuke.Common.IO;
8+
using Nuke.Common.ProjectModel;
9+
using Nuke.Common.Tooling;
10+
using Nuke.Common.Utilities.Collections;
11+
using static Nuke.Common.EnvironmentInfo;
12+
using static Nuke.Common.IO.PathConstruction;
13+
using Nuke.Common.Tools.DotNet;
14+
using System.Collections.Generic;
15+
16+
class BuildTasks : NukeBuild
17+
{
18+
/// Support plugins are available for:
19+
/// - JetBrains ReSharper https://nuke.build/resharper
20+
/// - JetBrains Rider https://nuke.build/rider
21+
/// - Microsoft VisualStudio https://nuke.build/visualstudio
22+
/// - Microsoft VSCode https://nuke.build/vscode
23+
24+
public static int Main ()
25+
{
26+
return Execute<BuildTasks>(x => x.BuildAll);
27+
}
28+
29+
[Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
30+
readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
31+
32+
[Solution] readonly Solution Solution;
33+
34+
AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts";
35+
36+
Target Clean => _ => _
37+
.Executes(() =>
38+
{
39+
if (Directory.Exists(ArtifactsDirectory))
40+
Directory.Delete(ArtifactsDirectory, true);
41+
var binDirs = Directory.GetDirectories(RootDirectory / "src", "bin", SearchOption.AllDirectories);
42+
var objDirs = Directory.GetDirectories(RootDirectory / "src", "obj", SearchOption.AllDirectories);
43+
foreach (var dir in binDirs.Concat(objDirs))
44+
{
45+
Directory.Delete(dir, true);
46+
}
47+
});
48+
49+
Target Restore => _ => _
50+
.Executes(() =>
51+
{
52+
DotNetTasks.DotNetRestore(s => s
53+
.SetProjectFile(Solution));
54+
});
55+
56+
Target Build => _ => _
57+
.DependsOn(Restore)
58+
.Executes(() =>
59+
{
60+
DotNetTasks.DotNetBuild(s => s
61+
.SetProjectFile(Solution)
62+
.SetConfiguration(Configuration)
63+
.EnableNoRestore());
64+
});
65+
66+
Target Pack => _ => _
67+
.DependsOn(Build)
68+
.Executes(() =>
69+
{
70+
foreach (var project in Solution.AllProjects.Where(p => p.Name.Contains("Plugin") || p.Name.Contains("App")))
71+
{
72+
DotNetTasks.DotNetPack(s => s
73+
.SetProject(project)
74+
.SetConfiguration(Configuration)
75+
.SetOutputDirectory(ArtifactsDirectory)
76+
.EnableNoBuild());
77+
}
78+
});
79+
80+
Target Run => _ => _
81+
.Executes(() =>
82+
{
83+
var desktopProject = Solution.AllProjects.FirstOrDefault(p => p.Name == "Modulus.App.Desktop");
84+
if (desktopProject == null)
85+
throw new Exception("Modulus.App.Desktop project cannot find");
86+
DotNetTasks.DotNetRun(s => s
87+
.SetProjectFile(desktopProject)
88+
.SetConfiguration(Configuration));
89+
});
90+
91+
Target Test => _ => _
92+
.DependsOn(Build)
93+
.Executes(() =>
94+
{
95+
DotNetTasks.DotNetTest(s => s
96+
.SetProjectFile(Solution)
97+
.SetConfiguration(Configuration)
98+
.SetNoBuild(true));
99+
});
100+
101+
Target BuildAll => _ => _
102+
.DependsOn(Build, Test);
103+
104+
Target Default => _ => _
105+
.DependsOn(Build);
106+
}

0 commit comments

Comments
 (0)