Development setup
Use this page to install the tools, build a plugin, and run it on a local server.
Prerequisites
.NET 10 SDK. Every plugin csproj targets
net10.0.EF Core CLI tools (
dotnet-ef), for plugins with aDbContext(Schulware, OdaOrg). See migrations.shdotnet tool install --global dotnet-ef dotnet tool update --global dotnet-efKiota, to regenerate the Schulware API client. See Kiota client.
A running SchulyServer with PostgreSQL, to load and test a plugin end to end.
The abstractions dependency
Schuly.Plugin.Abstractions is a NuGet PackageReference, not a project reference:
<PackageReference Include="Schuly.Plugin.Abstractions" Version="0.11.*" />It supplies the whole contract, including ISchulyPlugin, IPluginBackgroundTask, IPluginUserContext, IPluginLogin, ISchoolData and its records, IPluginVault, IPluginFileStore, ILinkedAccounts, ILinkProvider, and PluginServiceContext. See the contract reference.
Plugins reference nothing from the backend. The host registers the service implementations in each plugin's container at runtime.
The backend refuses to load a plugin built against an incompatible contract version. Before 1.0, that means a different MAJOR.MINOR. The publish workflow records the version as contract in index.json, so the backend never updates to a build it can't load. See versioning.
How a plugin is structured
A plugin is a class library with one ISchulyPlugin implementation, the composition root. The larger plugins keep HTTP routes in Controllers/, discovered as an MVC ApplicationPart, instead of mapping them in ConfigureEndpoints. They split sync logic across small scoped services driven by one IPluginBackgroundTask.
See Add a plugin for the full lifecycle.
Build a plugin
# Restore and build one plugin
dotnet build src/Schuly.Plugin.Schulware/Schuly.Plugin.Schulware.csproj -c Release
# Produce the loadable output (the same command CI uses)
dotnet publish src/Schuly.Plugin.Schulware/Schuly.Plugin.Schulware.csproj -c Release -o ./outThe Example and Schulware plugins also have a .slnx file, which opens the plugin standalone in an IDE.
Run a plugin on a local server
The server loads a plugin only if the plugin is listed in plugins.yml and has a manifest in the plugins folder. It removes installed plugins that aren't listed.
These paths are relative to the server's app folder, <app>. With dotnet run, that's the build output folder of Schuly.API. In the container, it's /app.
| File | Default | Setting |
|---|---|---|
| Plugin list | <app>/plugins.yml | Plugins:File |
| Plugin DLLs and manifests | <app>/plugins/ | Plugins:Directory |
| Plugin config | <app>/plugins-config/ | Plugins:ConfigDirectory |
Start SchulyServer and Postgres. See the server's development setup.
Run
dotnet publishas shown above.Copy
out/Schuly.Plugin.<Name>.dlland its third-party dependency DLLs, such as Kiota or AngleSharp, to the plugins folder. Leave out what the server provides:Microsoft.AspNetCore.*,Microsoft.Extensions.*,Microsoft.EntityFrameworkCore.*,Npgsql.*,Mediator.*, andSchuly.Plugin.Abstractions.dll.In the plugins folder, create the manifest
Schuly.Plugin.<Name>.plugin.json. List every file you copied infiles:json{ "name": "Schuly.Plugin.<Name>", "version": "1.0.0", "dll": "Schuly.Plugin.<Name>.dll", "files": ["Schuly.Plugin.<Name>.dll"] }Add the plugin to
plugins.ymlwith the same version as the manifest. If the version differs and the plugin is also in the registry, the server replaces your build with the registry's.yamlplugins: - name: Schuly.Plugin.<Name> version: 1.0.0If the plugin needs config, save it as
Schuly.Plugin.<Name>.yml(or.yaml) in the plugins-config folder.- Schulware requires
SchulwareApi.BaseUrl. Seeconfig.ymlfor the schema. - Microsoft requires
MicrosoftApi.BaseUrl.
Without its required value, the plugin's
ConfigureServicesthrows and the plugin doesn't load.- Schulware requires
Restart the server.
On startup, the host calls ConfigureServices, then MigrateAsync, which creates or upgrades the plugin's Postgres database, then ConfigureEndpoints. It then schedules any IPluginBackgroundTask on its Schedule.
To see how plugins get published, read distribution.
