-
Notifications
You must be signed in to change notification settings - Fork 173
Add proposal for apps resolving a deployed shared set of dependencies #371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # Shared dependencies across applications | ||
|
|
||
| The .NET deployment model optionally shares the runtime across apps, but offers no sharing model for other libraries. When multiple applications share many of the same dependencies, each carries its own copies, inflating the overall deployment and requiring every app to be updated to pick up a dependency change. If that common set of libraries is owned and versioned together, it is a natural set to deploy once and share. | ||
|
|
||
| **Goals** | ||
| - Apps can reference the shared set at build time without deploying it into each app's output | ||
| - The shared set lives in a shared location on disk and is resolved at run time by apps that referenced it at build time | ||
| - A single deployment of the shared set is used by all the applications | ||
| - The shared set can be updated independently of the apps | ||
|
|
||
| **Non-goals** | ||
| - Extension of the shared framework concept (global install, name-based framework references, framework versioning) to third parties | ||
| - Machine-wide assembly store cache | ||
| - Deployment of the applications and their shared set(s) | ||
|
|
||
| ## Proposed approach | ||
|
|
||
| ### Runtime resolution | ||
|
|
||
| The host reads a `sharedDependencies` array in `runtimeconfig.json`. Each entry points at a shared set's `.deps.json`. | ||
|
|
||
| `App.runtimeconfig.json`: | ||
|
|
||
| ```json | ||
| { | ||
| "runtimeOptions": { | ||
| "tfm": "net11.0", | ||
| "framework": { | ||
| "name": "Microsoft.NETCore.App", | ||
| "version": "11.0.0" | ||
| }, | ||
| "sharedDependencies": [ | ||
| "/opt/AppSuite.Shared/AppSuite.Shared.deps.json" | ||
| ] | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| - Each `sharedDependencies` entry is either an absolute path or relative path (based on the app's directory) to the shared set's `.deps.json`. Environment variables are not supported. | ||
| - The host merges each shared set's `deps.json` into the app's dependencies at startup. The shared set is treated as part of the app, so it resolves with the same priority as assets directly in the app's `deps.json` (that is, before frameworks). This is similar to an [additional `deps.json` via `--additional-deps`/`DOTNET_ADDITIONAL_DEPS`](https://github.com/dotnet/runtime/blob/main/docs/design/features/additional-deps.md). | ||
| - The assets in a shared set's `deps.json` are resolved relative to that `deps.json`'s folder (`/opt/AppSuite.Shared/` in the above example). The presence of `sharedDependencies` does not trigger file existence checks at startup. This differs from `--additional-deps`/`DOTNET_ADDITIONAL_DEPS`, which resolves an additional deps file's assets relative to the app directory and enables those file existence checks. | ||
|
|
||
| ### Developer experience | ||
|
|
||
| An app can reference a shared set so it compiles against the libraries without copying them into its output, and the build records the deployed `.deps.json` in the app's `sharedDependencies`. The shared set can be a project that aggregates the libraries to be shared. | ||
|
|
||
| `App.csproj` (app): | ||
|
|
||
| ```xml | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net11.0</TargetFramework> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- SharedDependencyLocation metadata (no copy-local + emits the entry) --> | ||
| <ItemGroup> | ||
| <ProjectReference Include="../AppSuite.Shared/AppSuite.Shared.csproj" | ||
| SharedDependencyLocation="/opt/AppSuite.Shared/" /> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we had External=True from nuget, would I assume you wouldn't use SharedDependencyLocation on any packagereference?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That seems like it would fit. What is the mechanism for run time resolution for External=True? Is there a doc around that feature we can link to?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I asked @nkolev92 offline if he could add more details and share any specs they might have with you. We should update this design to include an example using that new feature once we have the details. |
||
| </ItemGroup> | ||
|
|
||
| <!-- SharedDependency item (emits the entry) + ProjectReference with Private=false (no copy-local) --> | ||
| <ItemGroup> | ||
| <ProjectReference Include="../AppSuite.Utilities/AppSuite.Utilities.csproj" Private="false" /> | ||
| <SharedDependency Include="/opt/AppSuite.Utilities/AppSuite.Utilities.deps.json" /> | ||
| </ItemGroup> | ||
| </Project> | ||
| ``` | ||
|
|
||
| `AppSuite.Shared.csproj` (shared set): | ||
|
|
||
| ```xml | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <TargetFramework>net11.0</TargetFramework> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <PackageReference Include="Serilog" Version="4.0.0" /> | ||
| <ProjectReference Include="../AppSuite.Core/AppSuite.Core.csproj" /> | ||
| <!-- Other libraries to be shared --> | ||
| </ItemGroup> | ||
| </Project> | ||
| ``` | ||
|
|
||
| - `SharedDependencyLocation` metadata on the reference turns copy-local off and emits the `sharedDependencies` entry (`<SharedDependencyLocation>/<depsFileName>`). | ||
| - `SharedDependency` item points at a deployed `.deps.json` and emits the entry. | ||
| - Publishing the shared set produces a layout with the dependency assemblies and a `deps.json`. That layout is deployed to the shared location and the `deps.json` is pointed at by `sharedDependencies`. | ||
|
|
||
| ## Related | ||
|
|
||
| - [dotnet/runtime#53834](https://github.com/dotnet/runtime/issues/53834) — Support deploying multiple exes as a single self-contained set | ||
| - [`--additional-deps`/`DOTNET_ADDITIONAL_DEPS`](https://github.com/dotnet/runtime/blob/main/docs/design/features/additional-deps.md) — existing support for additional deps files via command line or environment variable | ||
| - Not directly related, but in the space of configuring how dependencies can be laid out and found | ||
| - [Configure .NET install search behavior](https://learn.microsoft.com/dotnet/core/deploying#configure-net-install-search-behavior) — existing configuration for how the apphost finds the .NET install | ||
| - [dotnet/sdk#48406](https://github.com/dotnet/sdk/issues/48406) — `localPath` in `deps.json` is used for asset resolution and can be customized via `DestinationSubDirectory`: [dotnet/runtime#117682](https://github.com/dotnet/runtime/issues/117682), [dotnet/runtime#118297](https://github.com/dotnet/runtime/pull/118297), [dotnet/sdk#50120](https://github.com/dotnet/sdk/pull/50120) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just kind of put something that somewhat made sense to me here. Happy to pull in whatever experience others folks think makes more sense.