From 21902ab384a84a3c413c526d931d1e56c8cd9b9d Mon Sep 17 00:00:00 2001 From: Maisie Date: Tue, 4 May 2021 13:29:23 +0100 Subject: [PATCH 01/12] Update config --- .github/workflows/release.yml | 43 ++++++- .../ConfigurationTests.cs | 61 ++++++++++ .../DepTree.Console.Tests.csproj | 30 +++++ .../exampleappconfig.json | 9 ++ .../Configuration/ApplicationConfiguration.cs | 111 ++++++++++++++++++ .../Configuration/CommandLineInputs.cs | 37 ++++++ .../FileConfiguration.cs} | 4 +- src/DepTree.Console/DepTree.Console.csproj | 1 + src/DepTree.Console/Program.cs | 67 +---------- 9 files changed, 297 insertions(+), 66 deletions(-) create mode 100644 src/DepTree.Console.Tests/ConfigurationTests.cs create mode 100644 src/DepTree.Console.Tests/DepTree.Console.Tests.csproj create mode 100644 src/DepTree.Console.Tests/exampleappconfig.json create mode 100644 src/DepTree.Console/Configuration/ApplicationConfiguration.cs create mode 100644 src/DepTree.Console/Configuration/CommandLineInputs.cs rename src/DepTree.Console/{ApplicationConfig.cs => Configuration/FileConfiguration.cs} (67%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index edfd110..864b412 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,8 +5,25 @@ on: branches: [ main ] jobs: + create-tag: + name: Create version tag + runs-on: ubuntu-latest + outputs: + new_tag: ${{ steps.tag_version.outputs.new_tag }} + new_version: ${{ steps.tag_version.outputs.new_version }} + + steps: + - name: Bump version and push tag + id: tag_version + uses: mathieudutour/github-tag-action@v5.3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + build: runs-on: ubuntu-latest + needs: create-tag + env: + VERSION: ${{ needs.create-tag.outputs.new_version }} steps: - uses: actions/checkout@v2 @@ -18,7 +35,7 @@ jobs: - name: 'Print version' run: | - echo 'Creating package version 0.0.${{github.run_number}}' + echo 'Creating package version ${{ needs.create-tag.outputs.new_version }} - $VERSION' - name: Test run: dotnet test @@ -28,7 +45,7 @@ jobs: run: dotnet build -c Release - name: Generate a NuGet package - run: dotnet pack --no-build -c Release -o . /p:PackageVersion=0.0.${{github.run_number}} + run: dotnet pack --no-build -c Release -o . /p:PackageVersion=${{ needs.create-tag.outputs.new_version }} working-directory: ./src/DepTree - name: Push to GitHub package registry @@ -36,9 +53,29 @@ jobs: working-directory: ./src/DepTree - name: Generate a NuGet package - run: dotnet pack --no-build -c Release -o . /p:PackageVersion=0.0.${{github.run_number}} + run: dotnet pack --no-build -c Release -o . /p:PackageVersion=0.0.${{ needs.create-tag.outputs.new_version }} working-directory: ./src/DepTree.Diagrams - name: Push to GitHub package registry run: dotnet nuget push *.nupkg --api-key ${{secrets.NUGET_AUTH_TOKEN}} working-directory: ./src/DepTree.Diagrams + + create-release: + name: Create Release + runs-on: ubuntu-latest + needs: [create-tag, build] + steps: + - name: Checkout code + uses: actions/checkout@master + - name: Create Release + id: create_release + uses: actions/create-release@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ needs.create-tag.outputs.new_tag }} + release_name: Release ${{ needs.create-tag.outputs.new_tag }} + body: | + New version + draft: false + prerelease: false \ No newline at end of file diff --git a/src/DepTree.Console.Tests/ConfigurationTests.cs b/src/DepTree.Console.Tests/ConfigurationTests.cs new file mode 100644 index 0000000..ca957e0 --- /dev/null +++ b/src/DepTree.Console.Tests/ConfigurationTests.cs @@ -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); + } + } +} diff --git a/src/DepTree.Console.Tests/DepTree.Console.Tests.csproj b/src/DepTree.Console.Tests/DepTree.Console.Tests.csproj new file mode 100644 index 0000000..e053f7a --- /dev/null +++ b/src/DepTree.Console.Tests/DepTree.Console.Tests.csproj @@ -0,0 +1,30 @@ + + + + net5.0 + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/src/DepTree.Console.Tests/exampleappconfig.json b/src/DepTree.Console.Tests/exampleappconfig.json new file mode 100644 index 0000000..b1e4536 --- /dev/null +++ b/src/DepTree.Console.Tests/exampleappconfig.json @@ -0,0 +1,9 @@ +{ + "Skip": [ + "Serilog.IDiagnosticContext" + ], + "Generate": [ + "DepTree.DependencyTree", + "DepTree.AnotherRootType" + ] +} diff --git a/src/DepTree.Console/Configuration/ApplicationConfiguration.cs b/src/DepTree.Console/Configuration/ApplicationConfiguration.cs new file mode 100644 index 0000000..bbd874f --- /dev/null +++ b/src/DepTree.Console/Configuration/ApplicationConfiguration.cs @@ -0,0 +1,111 @@ +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 Skip { get; private set; } = new HashSet(); + public List Generate { get; private set; } = new List(); + public List Errors { get; private set; } = new List(); + 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() + { + _assemblyConfigLocation = Environment.GetEnvironmentVariable("ASSEMBLY_CONFIG_LOCATION"); + _configLocation = Environment.GetEnvironmentVariable("CONFIG_LOCATION"); + InterfaceResolver = Environment.GetEnvironmentVariable("INTERFACE_RESOLVER"); + } + + private void TryReadArgs(string[] args) + { + var parser = Parser.Default.ParseArguments(() => new(), args); + parser.WithParsed(inputs => + { + AssemblyLocation = inputs.AssemblyLocation; + InterfaceResolver = inputs.InterfaceResolver; + + if (!string.IsNullOrWhiteSpace(inputs.Type)) + { + var types = inputs.Type.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + Generate.AddRange(types); + } + + if (!string.IsNullOrWhiteSpace(inputs.Skip)) + { + var skip = inputs.Skip.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + foreach (var s in skip) Skip.Add(s); + } + + _assemblyConfigLocation = inputs.AssemblyConfigLocation; + _configLocation = inputs.ConfigLocation; + }); + } + + private void TryReadFileConfig() + { + try + { + if (_configLocation == null) return; + var configContents = File.ReadAllText(_configLocation); + var fileConfig = JsonSerializer.Deserialize(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(); + } + } + } +} diff --git a/src/DepTree.Console/Configuration/CommandLineInputs.cs b/src/DepTree.Console/Configuration/CommandLineInputs.cs new file mode 100644 index 0000000..c20b98a --- /dev/null +++ b/src/DepTree.Console/Configuration/CommandLineInputs.cs @@ -0,0 +1,37 @@ +using CommandLine; + +namespace DepTree.Console.Configuration +{ + public class CommandLineInputs + { + [Option('a', "assembly", + Required = false, + HelpText = "The location of the assembly to read")] + public string AssemblyLocation { get; set; } = null!; + + [Option('n', "assembly-config", + Required = false, + HelpText = "The location of the configuration file required to build IConfiguration for Startup")] + public string AssemblyConfigLocation { get; set; } = null!; + + [Option('t', "type", + Required = false, + HelpText = "The root type to use for the dependency tree, multiple values can be used as a csv input")] + public string Type { get; set; } = null!; + + [Option('c', "config", + Required = false, + HelpText = "The location of application config file")] + public string ConfigLocation { get; set; } = null!; + + [Option('s', "skip", + Required = false, + HelpText = "Types to skip, multiple values can be used as a csv input")] + public string Skip { get; set; } = null!; + + [Option('i', "interface-resolver", + Required = false, + HelpText = "Interface Resolver type to use, Allowed Values: None, Startup. Default: Startup.")] + public string InterfaceResolver { get; set; } = null!; + } +} diff --git a/src/DepTree.Console/ApplicationConfig.cs b/src/DepTree.Console/Configuration/FileConfiguration.cs similarity index 67% rename from src/DepTree.Console/ApplicationConfig.cs rename to src/DepTree.Console/Configuration/FileConfiguration.cs index c89523d..71ae057 100644 --- a/src/DepTree.Console/ApplicationConfig.cs +++ b/src/DepTree.Console/Configuration/FileConfiguration.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -namespace DepTree.Console +namespace DepTree.Console.Configuration { - public class ApplicationConfig + public class FileConfiguration { public HashSet Skip { get; set; } public IList Generate { get; set; } diff --git a/src/DepTree.Console/DepTree.Console.csproj b/src/DepTree.Console/DepTree.Console.csproj index 99140a5..56b78e9 100644 --- a/src/DepTree.Console/DepTree.Console.csproj +++ b/src/DepTree.Console/DepTree.Console.csproj @@ -6,6 +6,7 @@ + diff --git a/src/DepTree.Console/Program.cs b/src/DepTree.Console/Program.cs index 9505d5e..64258cf 100644 --- a/src/DepTree.Console/Program.cs +++ b/src/DepTree.Console/Program.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; -using System.IO; +using System.Collections.Generic; using System.Reflection; -using System.Text.Json; using DepTree.Diagrams; -using Microsoft.Extensions.Configuration; +using DepTree.Console.Configuration; namespace DepTree.Console { @@ -12,39 +9,15 @@ class Program { static int Main(string[] args) { - var expectedArgs = "Expected ."; - if (args.Length != 2 && args.Length != 3) - { - System.Console.WriteLine($"Incorrect number of args. {expectedArgs}"); - return -1; - } - - var configLocation = args[0]; - if (!File.Exists(configLocation)) - { - System.Console.WriteLine($"Application Config location '{configLocation}' does not exist'. {expectedArgs}"); - return -1; - } - - var (applicationConfig, ok) = ReadConfig(configLocation); + var (applicationConfig, ok) = ApplicationConfiguration.Build(args); if (!ok) { - System.Console.WriteLine($"Application Config at '{configLocation}' not valid'. {expectedArgs}"); - return -1; - } - - var assemblyLocation = args[1]; - if (!File.Exists(assemblyLocation)) - { - System.Console.WriteLine($"Assembly location '{assemblyLocation}' does not exist'. {expectedArgs}"); + System.Console.WriteLine($"Application Config is not valid. {string.Join(", ", applicationConfig.Errors)}."); return -1; } - var iconfiguration = TryBuildConfiguration(args); - - var ass = Assembly.LoadFrom(assemblyLocation); - var config = new DependencyTreeConfig(ass, iconfiguration, skipAssemblies: applicationConfig.Skip); - config.InterfaceResolverType = Resolvers.InterfaceResolverType.None; + var assembly = Assembly.LoadFrom(applicationConfig.AssemblyLocation); + var config = new DependencyTreeConfig(assembly, applicationConfig.AssemblyConfiguration, skipAssemblies: applicationConfig.Skip); var nodes = new List(); var tree = new DependencyTree(config); @@ -63,34 +36,6 @@ static int Main(string[] args) return 0; } - private static IConfiguration TryBuildConfiguration(string[] args) - { - if (args.Length > 2 - && args[2] is string configLocation - && File.Exists(configLocation)) - { - var cfgBuilder = new ConfigurationBuilder(); - cfgBuilder.AddJsonFile(configLocation, optional: false, reloadOnChange: false); - return cfgBuilder.Build(); - } - - return null; - } - - private static (ApplicationConfig, bool) ReadConfig(string configLocation) - { - try - { - var configContents = File.ReadAllText(configLocation); - return (JsonSerializer.Deserialize(configContents), true); - } - catch (Exception ex) - { - System.Console.WriteLine($"Could not read config. Exception: {ex.Message}."); - return (null, false); - } - } - private static void Print(DependencyTreeNode node, string indent = "") { System.Console.WriteLine($"{indent}{node.Name}"); From d6534500d9ab1bd715610bde1f41a6dd47f1c071 Mon Sep 17 00:00:00 2001 From: Maisie Date: Tue, 4 May 2021 13:39:04 +0100 Subject: [PATCH 02/12] allow interface resolver = none --- .github/workflows/generate-diagrams.yml | 2 +- .github/workflows/release.yml | 4 ++-- src/DepTree.Console/Program.cs | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/generate-diagrams.yml b/.github/workflows/generate-diagrams.yml index 26d2cc3..35bff66 100644 --- a/.github/workflows/generate-diagrams.yml +++ b/.github/workflows/generate-diagrams.yml @@ -16,7 +16,7 @@ jobs: run: dotnet build -c Release - name: Generate a NuGet package - run: dotnet run -p src/DepTree.Console applicationconfig.json ./src/DepTree/bin/Release/net5.0/DepTree.dll + run: dotnet run -p src/DepTree.Console --config applicationconfig.json -a ./src/DepTree/bin/Release/net5.0/DepTree.dll -i None - uses: EndBug/add-and-commit@v7 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 864b412..920997e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: - name: 'Print version' run: | - echo 'Creating package version ${{ needs.create-tag.outputs.new_version }} - $VERSION' + echo 'Creating package version ${{ needs.create-tag.outputs.new_version }} - name: Test run: dotnet test @@ -53,7 +53,7 @@ jobs: working-directory: ./src/DepTree - name: Generate a NuGet package - run: dotnet pack --no-build -c Release -o . /p:PackageVersion=0.0.${{ needs.create-tag.outputs.new_version }} + run: dotnet pack --no-build -c Release -o . /p:PackageVersion=${{ needs.create-tag.outputs.new_version }} working-directory: ./src/DepTree.Diagrams - name: Push to GitHub package registry diff --git a/src/DepTree.Console/Program.cs b/src/DepTree.Console/Program.cs index 64258cf..e7c587b 100644 --- a/src/DepTree.Console/Program.cs +++ b/src/DepTree.Console/Program.cs @@ -18,6 +18,8 @@ static int Main(string[] args) var assembly = Assembly.LoadFrom(applicationConfig.AssemblyLocation); var config = new DependencyTreeConfig(assembly, applicationConfig.AssemblyConfiguration, skipAssemblies: applicationConfig.Skip); + if (applicationConfig.InterfaceResolver == "None") + config.InterfaceResolverType = Resolvers.InterfaceResolverType.None; var nodes = new List(); var tree = new DependencyTree(config); From a0bdb1e3aa8d1e10d5a2a43866c76ed59b67f420 Mon Sep 17 00:00:00 2001 From: Maisie Date: Tue, 4 May 2021 13:45:47 +0100 Subject: [PATCH 03/12] add env variables --- Dockerfile | 8 ++++++++ runindocker.sh | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fe8a3a3..ed47937 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,14 @@ ARG ASSEMBLY_LOCATION ENV ASSEMBLY_LOCATION=$ASSEMBLY_LOCATION ARG APPLICATION_CONFIG_LOCATION ENV APPLICATION_CONFIG_LOCATION=$APPLICATION_CONFIG_LOCATION +ARG ROOT_TYPE +ENV ROOT_TYPE=$ROOT_TYPE +ARG SKIP_TYPE +ENV SKIP_TYPE=$SKIP_TYPE +ARG ASSEMBLY_CONFIG_LOCATION +ENV ASSEMBLY_CONFIG_LOCATION=$ASSEMBLY_CONFIG_LOCATION +ARG INTERFACE_RESOLVER +ENV INTERFACE_RESOLVER=$INTERFACE_RESOLVER FROM mcr.microsoft.com/dotnet/aspnet:5.0-focal COPY --from=build-env /out . diff --git a/runindocker.sh b/runindocker.sh index c22c12d..0a93e25 100755 --- a/runindocker.sh +++ b/runindocker.sh @@ -1,6 +1,12 @@ #!/bin/bash -result=$(dotnet /DepTree.Console.dll $APPLICATION_CONFIG_LOCATION $ASSEMBLY_LOCATION) +result=$(dotnet /DepTree.Console.dll \ + -a $ASSEMBLY_LOCATION \ + -p $APPLICATION_CONFIG_LOCATION \ + -t $ROOT_TYPE \ + -s $SKIP_TYPE \ + -n $ASSEMBLY_CONFIG_LOCATION \ + -i $INTERFACE_RESOLVER) r=$? if [ $r -ne 0 ]; then From a306211bf48c3360d3b3a085f992dadb77397129 Mon Sep 17 00:00:00 2001 From: Maisie Date: Tue, 4 May 2021 13:46:23 +0100 Subject: [PATCH 04/12] missing ' --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 920997e..24cf653 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: - name: 'Print version' run: | - echo 'Creating package version ${{ needs.create-tag.outputs.new_version }} + echo 'Creating package version ${{ needs.create-tag.outputs.new_version }}' - name: Test run: dotnet test From b9f32bb0fa032d1713d8795a8f0c8b03c60a777a Mon Sep 17 00:00:00 2001 From: Maisie Date: Tue, 4 May 2021 13:57:51 +0100 Subject: [PATCH 05/12] correct config --- .github/workflows/release.yml | 2 +- runindocker.sh | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 24cf653..d13c7e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -78,4 +78,4 @@ jobs: body: | New version draft: false - prerelease: false \ No newline at end of file + prerelease: false diff --git a/runindocker.sh b/runindocker.sh index 0a93e25..a94ece8 100755 --- a/runindocker.sh +++ b/runindocker.sh @@ -1,8 +1,12 @@ #!/bin/bash +echo $ASSEMBLY_LOCATION + +echo 'running' + result=$(dotnet /DepTree.Console.dll \ -a $ASSEMBLY_LOCATION \ - -p $APPLICATION_CONFIG_LOCATION \ + --config $APPLICATION_CONFIG_LOCATION \ -t $ROOT_TYPE \ -s $SKIP_TYPE \ -n $ASSEMBLY_CONFIG_LOCATION \ From a2717320fb118254494d0ec34623bf49bf6f2d1a Mon Sep 17 00:00:00 2001 From: Maisie Date: Tue, 4 May 2021 17:54:54 +0100 Subject: [PATCH 06/12] logging --- README.md | 3 +++ runindocker.sh | 4 ---- .../Configuration/ApplicationConfiguration.cs | 14 ++++++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3d16642..d303983 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Dependency Tree +[Example workflow action](https://github.com/maisiesadler/Endpoints/blob/master/.github/workflows/dependencytree.yml) +[example result](https://github.com/maisiesadler/Endpoints/blob/master/Dependencies.md) + Create a self contained assembly `dotnet publish -r osx.10.12-x64 -p:PublishSingleFile=true --self-contained true` diff --git a/runindocker.sh b/runindocker.sh index a94ece8..68cef8e 100755 --- a/runindocker.sh +++ b/runindocker.sh @@ -1,9 +1,5 @@ #!/bin/bash -echo $ASSEMBLY_LOCATION - -echo 'running' - result=$(dotnet /DepTree.Console.dll \ -a $ASSEMBLY_LOCATION \ --config $APPLICATION_CONFIG_LOCATION \ diff --git a/src/DepTree.Console/Configuration/ApplicationConfiguration.cs b/src/DepTree.Console/Configuration/ApplicationConfiguration.cs index bbd874f..20a7027 100644 --- a/src/DepTree.Console/Configuration/ApplicationConfiguration.cs +++ b/src/DepTree.Console/Configuration/ApplicationConfiguration.cs @@ -58,8 +58,12 @@ private void TryReadArgs(string[] args) var parser = Parser.Default.ParseArguments(() => new(), args); parser.WithParsed(inputs => { - AssemblyLocation = inputs.AssemblyLocation; - InterfaceResolver = inputs.InterfaceResolver; + System.Console.WriteLine("CLI inputs: " + JsonSerializer.Serialize(inputs)); + if (!string.IsNullOrWhiteSpace(inputs.AssemblyLocation)) + AssemblyLocation = inputs.AssemblyLocation; + + if (!string.IsNullOrWhiteSpace(inputs.InterfaceResolver)) + InterfaceResolver = inputs.InterfaceResolver; if (!string.IsNullOrWhiteSpace(inputs.Type)) { @@ -73,8 +77,10 @@ private void TryReadArgs(string[] args) foreach (var s in skip) Skip.Add(s); } - _assemblyConfigLocation = inputs.AssemblyConfigLocation; - _configLocation = inputs.ConfigLocation; + if (!string.IsNullOrWhiteSpace(inputs.AssemblyConfigLocation)) + _assemblyConfigLocation = inputs.AssemblyConfigLocation; + if (!string.IsNullOrWhiteSpace(inputs.ConfigLocation)) + _configLocation = inputs.ConfigLocation; }); } From bd083030a94977e00a502b08d6d4fbeb2a222aa8 Mon Sep 17 00:00:00 2001 From: Maisie Date: Tue, 4 May 2021 18:49:28 +0100 Subject: [PATCH 07/12] Use environment variables --- runindocker.sh | 14 +++++++------- .../Configuration/ApplicationConfiguration.cs | 18 ++++++++++++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/runindocker.sh b/runindocker.sh index 68cef8e..efe942d 100755 --- a/runindocker.sh +++ b/runindocker.sh @@ -1,12 +1,12 @@ #!/bin/bash -result=$(dotnet /DepTree.Console.dll \ - -a $ASSEMBLY_LOCATION \ - --config $APPLICATION_CONFIG_LOCATION \ - -t $ROOT_TYPE \ - -s $SKIP_TYPE \ - -n $ASSEMBLY_CONFIG_LOCATION \ - -i $INTERFACE_RESOLVER) +result=$(ASSEMBLY_LOCATION=$ASSEMBLY_LOCATION \ + APPLICATION_CONFIG_LOCATION=$APPLICATION_CONFIG_LOCATION \ + ROOT_TYPE=$ROOT_TYPE \ + SKIP_TYPE=$SKIP_TYPE \ + ASSEMBLY_CONFIG_LOCATION=$ASSEMBLY_CONFIG_LOCATION \ + INTERFACE_RESOLVER=$INTERFACE_RESOLVER \ + dotnet /DepTree.Console.dll) r=$? if [ $r -ne 0 ]; then diff --git a/src/DepTree.Console/Configuration/ApplicationConfiguration.cs b/src/DepTree.Console/Configuration/ApplicationConfiguration.cs index 20a7027..2407d5f 100644 --- a/src/DepTree.Console/Configuration/ApplicationConfiguration.cs +++ b/src/DepTree.Console/Configuration/ApplicationConfiguration.cs @@ -48,9 +48,24 @@ private void Validate() private void ReadEnvironmentVariables() { + AssemblyLocation = Environment.GetEnvironmentVariable("ASSEMBLY_LOCATION"); _assemblyConfigLocation = Environment.GetEnvironmentVariable("ASSEMBLY_CONFIG_LOCATION"); - _configLocation = Environment.GetEnvironmentVariable("CONFIG_LOCATION"); + _configLocation = Environment.GetEnvironmentVariable("APPLICATION_CONFIG_LOCATION"); InterfaceResolver = Environment.GetEnvironmentVariable("INTERFACE_RESOLVER"); + + var rootType = Environment.GetEnvironmentVariable("ROOT_TYPE"); + if (!string.IsNullOrWhiteSpace(rootType)) + { + var types = rootType.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + Generate.AddRange(types); + } + + var skipType = Environment.GetEnvironmentVariable("SKIP_TYPE"); + if (!string.IsNullOrWhiteSpace(skipType)) + { + var skip = skipType.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + foreach (var s in skip) Skip.Add(s); + } } private void TryReadArgs(string[] args) @@ -58,7 +73,6 @@ private void TryReadArgs(string[] args) var parser = Parser.Default.ParseArguments(() => new(), args); parser.WithParsed(inputs => { - System.Console.WriteLine("CLI inputs: " + JsonSerializer.Serialize(inputs)); if (!string.IsNullOrWhiteSpace(inputs.AssemblyLocation)) AssemblyLocation = inputs.AssemblyLocation; From 6904bb05310582714a274215a481bfe2048bb0f0 Mon Sep 17 00:00:00 2001 From: Maisie Date: Wed, 5 May 2021 07:19:17 +0100 Subject: [PATCH 08/12] log args --- src/DepTree.Console/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DepTree.Console/Program.cs b/src/DepTree.Console/Program.cs index e7c587b..f3c1b31 100644 --- a/src/DepTree.Console/Program.cs +++ b/src/DepTree.Console/Program.cs @@ -9,6 +9,7 @@ class Program { static int Main(string[] args) { + System.Console.WriteLine(args); var (applicationConfig, ok) = ApplicationConfiguration.Build(args); if (!ok) { From df66951142d51580b3d5998d3a49ed40f7d447e1 Mon Sep 17 00:00:00 2001 From: Maisie Date: Wed, 5 May 2021 07:22:47 +0100 Subject: [PATCH 09/12] test --- src/DepTree.Console/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DepTree.Console/Program.cs b/src/DepTree.Console/Program.cs index f3c1b31..36cf264 100644 --- a/src/DepTree.Console/Program.cs +++ b/src/DepTree.Console/Program.cs @@ -10,6 +10,7 @@ class Program static int Main(string[] args) { System.Console.WriteLine(args); + var (applicationConfig, ok) = ApplicationConfiguration.Build(args); if (!ok) { From 6b08d3e0c7f08822122bb38140332ba8729a07a7 Mon Sep 17 00:00:00 2001 From: Maisie Date: Wed, 5 May 2021 07:26:07 +0100 Subject: [PATCH 10/12] remove ' ' --- src/DepTree.Console/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/DepTree.Console/Program.cs b/src/DepTree.Console/Program.cs index 36cf264..f3c1b31 100644 --- a/src/DepTree.Console/Program.cs +++ b/src/DepTree.Console/Program.cs @@ -10,7 +10,6 @@ class Program static int Main(string[] args) { System.Console.WriteLine(args); - var (applicationConfig, ok) = ApplicationConfiguration.Build(args); if (!ok) { From bf2a0e0fa0b18d8678abf75b9d84ce5ec1ef9bc8 Mon Sep 17 00:00:00 2001 From: Maisie Date: Wed, 5 May 2021 08:19:17 +0100 Subject: [PATCH 11/12] Use root_types and skip_types --- runindocker.sh | 4 +-- .../Configuration/ApplicationConfiguration.cs | 27 +++++++++++-------- .../Configuration/CommandLineInputs.cs | 8 +++--- src/DepTree.Console/Program.cs | 4 ++- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/runindocker.sh b/runindocker.sh index efe942d..468d6d6 100755 --- a/runindocker.sh +++ b/runindocker.sh @@ -2,8 +2,8 @@ result=$(ASSEMBLY_LOCATION=$ASSEMBLY_LOCATION \ APPLICATION_CONFIG_LOCATION=$APPLICATION_CONFIG_LOCATION \ - ROOT_TYPE=$ROOT_TYPE \ - SKIP_TYPE=$SKIP_TYPE \ + ROOT_TYPES=$ROOT_TYPES \ + SKIP_TYPES=$SKIP_TYPES \ ASSEMBLY_CONFIG_LOCATION=$ASSEMBLY_CONFIG_LOCATION \ INTERFACE_RESOLVER=$INTERFACE_RESOLVER \ dotnet /DepTree.Console.dll) diff --git a/src/DepTree.Console/Configuration/ApplicationConfiguration.cs b/src/DepTree.Console/Configuration/ApplicationConfiguration.cs index 2407d5f..0fd7d5e 100644 --- a/src/DepTree.Console/Configuration/ApplicationConfiguration.cs +++ b/src/DepTree.Console/Configuration/ApplicationConfiguration.cs @@ -53,17 +53,17 @@ private void ReadEnvironmentVariables() _configLocation = Environment.GetEnvironmentVariable("APPLICATION_CONFIG_LOCATION"); InterfaceResolver = Environment.GetEnvironmentVariable("INTERFACE_RESOLVER"); - var rootType = Environment.GetEnvironmentVariable("ROOT_TYPE"); - if (!string.IsNullOrWhiteSpace(rootType)) + var rootTypes = Environment.GetEnvironmentVariable("ROOT_TYPES"); + if (!string.IsNullOrWhiteSpace(rootTypes)) { - var types = rootType.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var types = rootTypes.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); Generate.AddRange(types); } - var skipType = Environment.GetEnvironmentVariable("SKIP_TYPE"); - if (!string.IsNullOrWhiteSpace(skipType)) + var skipTypes = Environment.GetEnvironmentVariable("SKIP_TYPES"); + if (!string.IsNullOrWhiteSpace(skipTypes)) { - var skip = skipType.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var skip = skipTypes.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); foreach (var s in skip) Skip.Add(s); } } @@ -79,15 +79,15 @@ private void TryReadArgs(string[] args) if (!string.IsNullOrWhiteSpace(inputs.InterfaceResolver)) InterfaceResolver = inputs.InterfaceResolver; - if (!string.IsNullOrWhiteSpace(inputs.Type)) + if (!string.IsNullOrWhiteSpace(inputs.RootTypes)) { - var types = inputs.Type.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var types = inputs.RootTypes.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); Generate.AddRange(types); } - if (!string.IsNullOrWhiteSpace(inputs.Skip)) + if (!string.IsNullOrWhiteSpace(inputs.SkipTypes)) { - var skip = inputs.Skip.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var skip = inputs.SkipTypes.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); foreach (var s in skip) Skip.Add(s); } @@ -96,13 +96,18 @@ private void TryReadArgs(string[] args) if (!string.IsNullOrWhiteSpace(inputs.ConfigLocation)) _configLocation = inputs.ConfigLocation; }); + + parser.WithNotParsed(errors => + { + Errors.Add("Invalid CLI inputs"); + }); } private void TryReadFileConfig() { try { - if (_configLocation == null) return; + if (string.IsNullOrWhiteSpace(_configLocation)) return; var configContents = File.ReadAllText(_configLocation); var fileConfig = JsonSerializer.Deserialize(configContents); diff --git a/src/DepTree.Console/Configuration/CommandLineInputs.cs b/src/DepTree.Console/Configuration/CommandLineInputs.cs index c20b98a..e92c77d 100644 --- a/src/DepTree.Console/Configuration/CommandLineInputs.cs +++ b/src/DepTree.Console/Configuration/CommandLineInputs.cs @@ -14,20 +14,20 @@ public class CommandLineInputs HelpText = "The location of the configuration file required to build IConfiguration for Startup")] public string AssemblyConfigLocation { get; set; } = null!; - [Option('t', "type", + [Option('t', "root-types", Required = false, HelpText = "The root type to use for the dependency tree, multiple values can be used as a csv input")] - public string Type { get; set; } = null!; + public string RootTypes { get; set; } = null!; [Option('c', "config", Required = false, HelpText = "The location of application config file")] public string ConfigLocation { get; set; } = null!; - [Option('s', "skip", + [Option('s', "skip-types", Required = false, HelpText = "Types to skip, multiple values can be used as a csv input")] - public string Skip { get; set; } = null!; + public string SkipTypes { get; set; } = null!; [Option('i', "interface-resolver", Required = false, diff --git a/src/DepTree.Console/Program.cs b/src/DepTree.Console/Program.cs index f3c1b31..4ffc341 100644 --- a/src/DepTree.Console/Program.cs +++ b/src/DepTree.Console/Program.cs @@ -9,7 +9,7 @@ class Program { static int Main(string[] args) { - System.Console.WriteLine(args); + // System.Console.WriteLine(string.Join(", ", args)); var (applicationConfig, ok) = ApplicationConfiguration.Build(args); if (!ok) { @@ -17,6 +17,8 @@ static int Main(string[] args) return -1; } + // System.Console.WriteLine(JsonSerializer.Serialize(applicationConfig)); + var assembly = Assembly.LoadFrom(applicationConfig.AssemblyLocation); var config = new DependencyTreeConfig(assembly, applicationConfig.AssemblyConfiguration, skipAssemblies: applicationConfig.Skip); if (applicationConfig.InterfaceResolver == "None") From 88661fe742b420a2e00a31672bf6d21b48847541 Mon Sep 17 00:00:00 2001 From: Maisie Date: Wed, 5 May 2021 20:41:15 +0100 Subject: [PATCH 12/12] add readme --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++---- run.sh | 17 ++++++++++++----- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d303983..5f2cd8e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,56 @@ # Dependency Tree -[Example workflow action](https://github.com/maisiesadler/Endpoints/blob/master/.github/workflows/dependencytree.yml) -[example result](https://github.com/maisiesadler/Endpoints/blob/master/Dependencies.md) +Dependency Tree will load an assembly then, starting with the root type, recursively read the constructor parameters to create a tree of dependencies. + +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`. -Create a self contained assembly +### Example applications -`dotnet publish -r osx.10.12-x64 -p:PublishSingleFile=true --self-contained true` +#### 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) + + + +[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) + + + +[Example workflow action](https://github.com/maisiesadler/Endpoints/blob/master/.github/workflows/dependencytree.yml) diff --git a/run.sh b/run.sh index 51b407f..966ea4a 100755 --- a/run.sh +++ b/run.sh @@ -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