-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from maisiesadler/config
Config
- Loading branch information
Showing
14 changed files
with
369 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,56 @@ | ||
# Dependency Tree | ||
|
||
Create a self contained assembly | ||
Dependency Tree will load an assembly then, starting with the root type, recursively read the constructor parameters to create a tree of dependencies. | ||
|
||
`dotnet publish -r osx.10.12-x64 -p:PublishSingleFile=true --self-contained true` | ||
By default it uses the Startup file to load and resolve which type is registered to an interface. | ||
|
||
## Using the github action | ||
|
||
The GitHub action uses the [Dockerfile](./Dockerfile) in the root of the project. | ||
|
||
| Name | Environment Variable | CLI setting | | Required | | ||
| -- | -- | -- | -- | -- | | ||
| Assembly Location | ASSEMBLY_LOCATION | -a --assembly | The location of the assembly to read | Yes | | ||
| Root types | ROOT_TYPES | -t --root-types | The root type to use for the dependency tree, multiple values can be used as a csv input | Yes | | ||
| Skip types | SKIP_TYPES | -s --skip-types | Types to skip, multiple values can be used as a csv input | No | | ||
| Assembly Config Location | ASSEMBLY_CONFIG_LOCATION | -n --assembly-config | The location of the configuration file required to build IConfiguration for Startup | No | | ||
| Interface Resolver | INTERFACE_RESOLVER | -i --interface-resolver | Method for resolving interfaces, Allowed Values: None, Startup. Default: Startup. | No | | ||
| Application Config Location | APPLICATION_CONFIG_LOCATION | -n --assembly-config | The location of application config file | No | | ||
|
||
### Application config | ||
|
||
There is support for passing in a config file for multiple `Skip` or `Generate` (root) types. | ||
|
||
[Example](./applicationconfig.json) | ||
|
||
### Interface resolver | ||
|
||
If `Interface Resolver` is `Startup` (default) then the application will look for a class named Startup. | ||
|
||
If the Startup file has an IConfiguration constructor then set the `Assembly Config Location` to the path of an example config file that has enough data in it to build the configuration. | ||
|
||
If there is no startup then set `Interface Resolver` to `None`. | ||
|
||
### Example applications | ||
|
||
#### Pokedex - dotnet5 web api | ||
|
||
Needed to publish a single file application | ||
|
||
`dotnet publish -r linux-x64 -p:PublishSingleFile=true -p:UseAppHost=true --self-contained` | ||
|
||
[Example output](https://github.com/maisiesadler/pokedex/blob/main/DependencyTree.md) | ||
|
||
<img src="http://yuml.me/diagram/scruffy/class/[PokemonController]->[BasicPokemonInformationRetriever], [PokemonController]->[TranslatedPokemonInformationRetriever], [PokemonController]->[ILogger`1], [BasicPokemonInformationRetriever]->[IPokemonQuery|PokemonQuery], [IPokemonQuery]-2>[ICache`1], [IPokemonQuery]-2>[IPokeApiClient], [TranslatedPokemonInformationRetriever]->[IPokemonQuery|PokemonQuery], [TranslatedPokemonInformationRetriever]->[ITranslationQuery|TranslationQuery], [ITranslationQuery]->[ICache`1], [ITranslationQuery]->[IFunTranslationsApiClient], [ITranslationQuery]->[ILogger`1]" /> | ||
|
||
[Example workflow](https://github.com/maisiesadler/pokedex/blob/main/.github/workflows/dependencytree.yml) | ||
|
||
- Need to add RuntimeIdentifiers [here](https://github.com/maisiesadler/pokedex/blob/main/src/Pokedex/Pokedex.csproj#L5). | ||
|
||
#### Endpoints - doesn't use Startup | ||
|
||
[Example output](https://github.com/maisiesadler/Endpoints/blob/master/Dependencies.md) | ||
|
||
<img src="http://yuml.me/diagram/scruffy/class/[MyModelRetriever]->[IDbThing]" /> | ||
|
||
[Example workflow action](https://github.com/maisiesadler/Endpoints/blob/master/.github/workflows/dependencytree.yml) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,12 @@ | ||
# dotnet build src/Example | ||
dotnet run -p src/DepTree.Console \ | ||
applicationconfig.json \ | ||
/Users/maisiesadler/comparison/property-service/src/PropertyService.Api/bin/Release/net5.0/osx.10.12-x64/PropertyService.Api.dll \ | ||
/Users/maisiesadler/comparison/property-service/src/PropertyService.Test.Integration/testsettings.json | ||
dotnet publish ./src/DepTree.Console/DepTree.Console.csproj -c Release -o out --no-self-contained | ||
|
||
ASSEMBLY_LOCATION=/Users/maisiesadler/repos/endpoints/src/Endpoints.Api/bin/Release/net5.0/osx.10.12-x64/Endpoints.Api.dll | ||
ROOT_TYPES=Pokedex.Controllers.PokemonController | ||
|
||
ASSEMBLY_LOCATION=$ASSEMBLY_LOCATION \ | ||
APPLICATION_CONFIG_LOCATION=$APPLICATION_CONFIG_LOCATION \ | ||
ROOT_TYPES=$ROOT_TYPES \ | ||
SKIP_TYPES=$SKIP_TYPES \ | ||
ASSEMBLY_CONFIG_LOCATION=$ASSEMBLY_CONFIG_LOCATION \ | ||
INTERFACE_RESOLVER=$INTERFACE_RESOLVER \ | ||
dotnet out/DepTree.Console.dll |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using Xunit; | ||
using DepTree.Console.Configuration; | ||
|
||
namespace DepTree.Console.Tests | ||
{ | ||
public class ConfigurationTests | ||
{ | ||
[Fact] | ||
public void CanBuildApplicationConfigFromArgs() | ||
{ | ||
var args = new[] { "-a", "assembly-location", "-t", "type", "-c", "config-location" }; | ||
|
||
var (config, ok) = ApplicationConfiguration.Build(args); | ||
|
||
Assert.NotNull(config); | ||
var type = Assert.Single(config.Generate); | ||
Assert.Equal("type", type); | ||
Assert.Equal("assembly-location", config.AssemblyLocation); | ||
} | ||
|
||
[Fact] | ||
public void InvalidAssemblyLocationProducesError() | ||
{ | ||
var args = new[] { "-a", "assembly-location", "-t", "type", "-c", "config-location" }; | ||
|
||
var (config, ok) = ApplicationConfiguration.Build(args); | ||
|
||
Assert.False(ok); | ||
Assert.Contains("Assembly Location 'assembly-location' is missing or invalid", config.Errors); | ||
} | ||
|
||
[Fact] | ||
public void ConfigFileIsParsed() | ||
{ | ||
var args = new[] { "-a", "assembly-location", "-c", "exampleappconfig.json" }; | ||
|
||
var (config, ok) = ApplicationConfiguration.Build(args); | ||
|
||
Assert.NotNull(config); | ||
Assert.Equal(2, config.Generate.Count); | ||
Assert.Equal("DepTree.DependencyTree", config.Generate[0]); | ||
Assert.Equal("DepTree.AnotherRootType", config.Generate[1]); | ||
var skip = Assert.Single(config.Skip); | ||
Assert.Equal("Serilog.IDiagnosticContext", skip); | ||
} | ||
|
||
[Fact] | ||
public void CsvTypesAreAdded() | ||
{ | ||
var args = new[] { "-a", "assembly-location", "-t", "type1,type2" }; | ||
|
||
var (config, ok) = ApplicationConfiguration.Build(args); | ||
|
||
Assert.NotNull(config); | ||
Assert.Equal(2, config.Generate.Count); | ||
Assert.Equal("type1", config.Generate[0]); | ||
Assert.Equal("type2", config.Generate[1]); | ||
Assert.Equal("assembly-location", config.AssemblyLocation); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net5.0</TargetFramework> | ||
|
||
<IsPackable>false</IsPackable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> | ||
<PackageReference Include="xunit" Version="2.4.1" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
<PackageReference Include="coverlet.collector" Version="1.3.0"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\DepTree.Console\DepTree.Console.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Content Include="exampleappconfig.json" CopyToOutputDirectory="Always" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"Skip": [ | ||
"Serilog.IDiagnosticContext" | ||
], | ||
"Generate": [ | ||
"DepTree.DependencyTree", | ||
"DepTree.AnotherRootType" | ||
] | ||
} |
136 changes: 136 additions & 0 deletions
136
src/DepTree.Console/Configuration/ApplicationConfiguration.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Text.Json; | ||
using CommandLine; | ||
using Microsoft.Extensions.Configuration; | ||
|
||
namespace DepTree.Console.Configuration | ||
{ | ||
public class ApplicationConfiguration | ||
{ | ||
private ApplicationConfiguration() { } | ||
|
||
private string _assemblyConfigLocation; | ||
private string _configLocation; | ||
|
||
public IConfiguration AssemblyConfiguration { get; private set; } | ||
public string AssemblyLocation { get; private set; } | ||
public string InterfaceResolver { get; private set; } | ||
public HashSet<string> Skip { get; private set; } = new HashSet<string>(); | ||
public List<string> Generate { get; private set; } = new List<string>(); | ||
public List<string> Errors { get; private set; } = new List<string>(); | ||
public bool IsValid => Errors.Count == 0; | ||
|
||
public static (ApplicationConfiguration, bool) Build(string[] args) | ||
{ | ||
var config = new ApplicationConfiguration(); | ||
|
||
config.ReadEnvironmentVariables(); | ||
|
||
config.TryReadArgs(args); | ||
config.TryReadFileConfig(); | ||
|
||
config.TryBuildAssemblyConfiguration(); | ||
|
||
config.Validate(); | ||
|
||
return (config, config.IsValid); | ||
} | ||
|
||
private void Validate() | ||
{ | ||
if (string.IsNullOrEmpty(AssemblyLocation) || !File.Exists(AssemblyLocation)) | ||
{ | ||
Errors.Add($"Assembly Location '{AssemblyLocation}' is missing or invalid"); | ||
} | ||
} | ||
|
||
private void ReadEnvironmentVariables() | ||
{ | ||
AssemblyLocation = Environment.GetEnvironmentVariable("ASSEMBLY_LOCATION"); | ||
_assemblyConfigLocation = Environment.GetEnvironmentVariable("ASSEMBLY_CONFIG_LOCATION"); | ||
_configLocation = Environment.GetEnvironmentVariable("APPLICATION_CONFIG_LOCATION"); | ||
InterfaceResolver = Environment.GetEnvironmentVariable("INTERFACE_RESOLVER"); | ||
|
||
var rootTypes = Environment.GetEnvironmentVariable("ROOT_TYPES"); | ||
if (!string.IsNullOrWhiteSpace(rootTypes)) | ||
{ | ||
var types = rootTypes.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); | ||
Generate.AddRange(types); | ||
} | ||
|
||
var skipTypes = Environment.GetEnvironmentVariable("SKIP_TYPES"); | ||
if (!string.IsNullOrWhiteSpace(skipTypes)) | ||
{ | ||
var skip = skipTypes.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); | ||
foreach (var s in skip) Skip.Add(s); | ||
} | ||
} | ||
|
||
private void TryReadArgs(string[] args) | ||
{ | ||
var parser = Parser.Default.ParseArguments<CommandLineInputs>(() => new(), args); | ||
parser.WithParsed(inputs => | ||
{ | ||
if (!string.IsNullOrWhiteSpace(inputs.AssemblyLocation)) | ||
AssemblyLocation = inputs.AssemblyLocation; | ||
|
||
if (!string.IsNullOrWhiteSpace(inputs.InterfaceResolver)) | ||
InterfaceResolver = inputs.InterfaceResolver; | ||
|
||
if (!string.IsNullOrWhiteSpace(inputs.RootTypes)) | ||
{ | ||
var types = inputs.RootTypes.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); | ||
Generate.AddRange(types); | ||
} | ||
|
||
if (!string.IsNullOrWhiteSpace(inputs.SkipTypes)) | ||
{ | ||
var skip = inputs.SkipTypes.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); | ||
foreach (var s in skip) Skip.Add(s); | ||
} | ||
|
||
if (!string.IsNullOrWhiteSpace(inputs.AssemblyConfigLocation)) | ||
_assemblyConfigLocation = inputs.AssemblyConfigLocation; | ||
if (!string.IsNullOrWhiteSpace(inputs.ConfigLocation)) | ||
_configLocation = inputs.ConfigLocation; | ||
}); | ||
|
||
parser.WithNotParsed(errors => | ||
{ | ||
Errors.Add("Invalid CLI inputs"); | ||
}); | ||
} | ||
|
||
private void TryReadFileConfig() | ||
{ | ||
try | ||
{ | ||
if (string.IsNullOrWhiteSpace(_configLocation)) return; | ||
var configContents = File.ReadAllText(_configLocation); | ||
var fileConfig = JsonSerializer.Deserialize<FileConfiguration>(configContents); | ||
|
||
if (fileConfig.Skip != null) | ||
foreach (var s in fileConfig.Skip) Skip.Add(s); | ||
|
||
if (fileConfig.Generate != null) | ||
Generate.AddRange(fileConfig.Generate); | ||
} | ||
catch (Exception ex) | ||
{ | ||
System.Console.WriteLine($"Exception reading config: {ex.Message}."); | ||
} | ||
} | ||
|
||
private void TryBuildAssemblyConfiguration() | ||
{ | ||
if (!string.IsNullOrWhiteSpace(_assemblyConfigLocation) && File.Exists(_assemblyConfigLocation)) | ||
{ | ||
var cfgBuilder = new ConfigurationBuilder(); | ||
cfgBuilder.AddJsonFile(_assemblyConfigLocation, optional: false, reloadOnChange: false); | ||
AssemblyConfiguration = cfgBuilder.Build(); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.