diff --git a/.appveyor.yml b/.appveyor.yml index 0b365f1..eee769f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,13 +1,14 @@ #---------------------------------# # Build Image # #---------------------------------# -image: Visual Studio 2017 +image: Visual Studio 2019 #---------------------------------# # Build Script # #---------------------------------# build_script: - - ps: .\build.ps1 -Target AppVeyor + - ps: .\build.ps1 --bootstrap + - ps: .\build.ps1 --target=CI #---------------------------------# # Tests @@ -33,18 +34,18 @@ branches: only: - develop - master - - /release\/.*/ - - /hotfix\/.*/ + - /release/.*/ + - /hotfix/.*/ #---------------------------------# # Build Cache # #---------------------------------# cache: -- tools -> recipe.cake, tools/packages.config +- tools -> recipe.cake #---------------------------------# # Skip builds for doc changes # #---------------------------------# skip_commits: # Regex for matching commit message - message: /\(doc\).*/ + message: /\(doc\).*/ \ No newline at end of file diff --git a/README.md b/README.md index 0e44057..3e2c9b9 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ Cake.DotNetCoreEf is an Addin that extends [Cake](http://cakebuild.net/) for exe - [Documentation](https://cake-contrib.github.io/Cake.DotNetCoreEf/) -## Chat Room +## Discussion -Come join in the conversation about Cake.DotNetCoreEf in our Gitter Chat Room +For questions and to discuss ideas & feature requests, use the [GitHub discussions on the Cake GitHub repository](https://github.com/cake-build/cake/discussions), under the [Extension Q&A](https://github.com/cake-build/cake/discussions/categories/extension-q-a) category. -[![Join the chat at https://gitter.im/cake-contrib/Lobby](https://badges.gitter.im/cake-contrib/Lobby.svg)](https://gitter.im/cake-contrib/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Join in the discussion on the Cake repository](https://img.shields.io/badge/GitHub-Discussions-green?logo=github)](https://github.com/cake-build/cake/discussions) ## Contribution GuideLines diff --git a/docs/input/blog/new-release-0.2.0.md b/docs/input/blog/new-release-0.2.0.md index 23c84c1..407ef5f 100644 --- a/docs/input/blog/new-release-0.2.0.md +++ b/docs/input/blog/new-release-0.2.0.md @@ -21,4 +21,4 @@ Within this release, the following things have been done... - [__#8__](https://github.com/cake-contrib/Cake.DotNetCoreEf/issues/8) Add Wyam Documentation generation -Please do not hesitate to reach out in the [Gitter Channel](https://gitter.im/cake-contrib/Lobby) if you have any issues using this addin. \ No newline at end of file +Please do not hesitate to reach out in the [GitHub discussions](https://github.com/cake-build/cake/discussions) if you have any issues using this addin. \ No newline at end of file diff --git a/docs/input/blog/new-release-0.3.0.md b/docs/input/blog/new-release-0.3.0.md index 7c8de7f..f0634c8 100644 --- a/docs/input/blog/new-release-0.3.0.md +++ b/docs/input/blog/new-release-0.3.0.md @@ -17,4 +17,4 @@ Within this release, the following things have been done... - [__#13__](https://github.com/cake-contrib/Cake.DotNetCoreEf/issues/13) Support for migrations remove - [__#14__](https://github.com/cake-contrib/Cake.DotNetCoreEf/issues/14) Support for migrations script -Please do not hesitate to reach out in the [Gitter Channel](https://gitter.im/cake-contrib/Lobby) if you have any issues using this addin. \ No newline at end of file +Please do not hesitate to reach out in the [GitHub discussions](https://github.com/cake-build/cake/discussions), if you have any issues using this addin. \ No newline at end of file diff --git a/recipe.cake b/recipe.cake index 274cc72..edeb1f6 100644 --- a/recipe.cake +++ b/recipe.cake @@ -1,4 +1,4 @@ -#load nuget:?package=Cake.Recipe&version=1.0.0 +#load nuget:?package=Cake.Recipe&version=2.2.1 Environment.SetVariableNames(); @@ -8,18 +8,20 @@ BuildParameters.SetParameters(context: Context, title: "Cake.DotNetCoreEf", repositoryOwner: "cake-contrib", repositoryName: "Cake.DotNetCoreEf", - shouldRunDotNetCorePack: true, - shouldRunDupFinder: false, - shouldRunInspectCode: false, + shouldRunCodecov: false, + shouldGenerateDocumentation: false, // until wyam oin recipe is fixed appVeyorAccountName: "cakecontrib", - shouldRunGitVersion: true); + shouldRunDotNetCorePack: true); BuildParameters.PrintParameters(Context); ToolSettings.SetToolSettings(context: Context, dupFinderExcludePattern: new string[] { - BuildParameters.RootDirectoryPath + "/src/Cake.DotNetCoreEf.Tests/**/*.cs" }, - testCoverageFilter: "+[*]* -[xunit.*]* -[Cake.Core]* -[Cake.Testing]* -[*.Tests]* ", + BuildParameters.RootDirectoryPath + "/src/**/*.AssemblyInfo.cs", + BuildParameters.RootDirectoryPath + "/src/Cake.DotNetCoreEf/Migration/DotNetCoreEfMigrationLister.cs", + BuildParameters.RootDirectoryPath + "/src/Cake.DotNetCoreEf/DotNetCoreEfTool.cs", + BuildParameters.RootDirectoryPath + "/src/Cake.DotNetCoreEf.UnitTests/**/*.cs" }, + testCoverageFilter: "+[*]* -[xunit.*]* -[Cake.Core]* -[Cake.Testing]* -[*.Tests]* -[FakeItEasy]* -[FluentAssertions]* -[FluentAssertions.Core]*", testCoverageExcludeByAttribute: "*.ExcludeFromCodeCoverage*", testCoverageExcludeByFile: "*/*Designer.cs;*/*.g.cs;*/*.g.i.cs"); diff --git a/src/Cake.DotNetCoreEf.UnitTests/Cake.DotNetCoreEf.UnitTests.csproj b/src/Cake.DotNetCoreEf.UnitTests/Cake.DotNetCoreEf.UnitTests.csproj index 132b908..dea439e 100644 --- a/src/Cake.DotNetCoreEf.UnitTests/Cake.DotNetCoreEf.UnitTests.csproj +++ b/src/Cake.DotNetCoreEf.UnitTests/Cake.DotNetCoreEf.UnitTests.csproj @@ -1,24 +1,29 @@ - netcoreapp2.1 + net5.0 - false + + false - - - - - - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - + all runtime; build; native; contentfiles; analyzers - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Cake.DotNetCoreEf.UnitTests/Fixtures/Migration/DotNetCoreEfMigrationListerFixture.cs b/src/Cake.DotNetCoreEf.UnitTests/Fixtures/Migration/DotNetCoreEfMigrationListerFixture.cs new file mode 100644 index 0000000..575172f --- /dev/null +++ b/src/Cake.DotNetCoreEf.UnitTests/Fixtures/Migration/DotNetCoreEfMigrationListerFixture.cs @@ -0,0 +1,17 @@ +using Cake.DotNetCoreEf.Migration; + +namespace Cake.DotNetCoreEf.Tests.Fixtures.Migration +{ + internal sealed class DotNetCoreEfMigrationListerFixture : DotNetCoreEfFixture + { + public string Project { get; set; } + + public string Arguments { get; set; } + + protected override void RunTool() + { + var tool = new DotNetCoreEfMigrationLister(FileSystem, Environment, ProcessRunner, Tools); + tool.Script(Project, Arguments, Settings); + } + } +} \ No newline at end of file diff --git a/src/Cake.DotNetCoreEf.UnitTests/Unit/Migration/DotNetCoreEfMigrationScriptListerTests.cs b/src/Cake.DotNetCoreEf.UnitTests/Unit/Migration/DotNetCoreEfMigrationScriptListerTests.cs new file mode 100644 index 0000000..13f0598 --- /dev/null +++ b/src/Cake.DotNetCoreEf.UnitTests/Unit/Migration/DotNetCoreEfMigrationScriptListerTests.cs @@ -0,0 +1,102 @@ +using Cake.DotNetCoreEf.Tests.Fixtures.Migration; +using Cake.Testing; +using Xunit; + +namespace Cake.DotNetCoreEf.Tests.Unit.Migration +{ + public sealed class DotNetCoreEfMigrationScriptListerTests + { + + [Fact] + public void Should_Throw_If_Settings_Are_Null() + { + // Given + var fixture = new DotNetCoreEfMigrationListerFixture(); + fixture.Project = "./src/"; + fixture.Settings = null; + fixture.GivenDefaultToolDoNotExist(); + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + AssertExtensions.IsArgumentNullException(result, "settings"); + } + + [Fact] + public void Should_Throw_If_Process_Was_Not_Started() + { + // Given + var fixture = new DotNetCoreEfMigrationListerFixture(); + fixture.Project = "./src/"; + fixture.GivenProcessCannotStart(); + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + AssertExtensions.IsCakeException(result, ".NET Core CLI: Process was not started."); + } + + // Removed. Process returns json + //[Fact] + //public void Should_Throw_If_Process_Has_A_Non_Zero_Exit_Code() + //{ + // // Given + // var fixture = new DotNetCoreEfMigrationScriptListerFixture(); + // fixture.Project = "./src/"; + // fixture.GivenProcessExitsWithCode(1); + + // // When + // var result = Record.Exception(() => fixture.Run()); + + // // Then + // AssertExtensions.IsCakeException(result, ".NET Core CLI: Process returned an error (exit code 1)."); + //} + + [Fact] + public void Should_Add_Mandatory_Arguments() + { + // Given + var fixture = new DotNetCoreEfMigrationListerFixture(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("ef migrations list --no-build --json", result.Args); + } + + [Fact] + public void Should_Add_Path_Arguments() + { + // Given + var fixture = new DotNetCoreEfMigrationListerFixture(); + fixture.Project = "./tools/tool/"; + fixture.Arguments = "--args=\"value\""; + // When + var result = fixture.Run(); + + // Then + Assert.Equal("ef migrations list --no-build --json --args=\"value\"", result.Args); + Assert.Equal("/Working/tools/tool", result.Process.WorkingDirectory.FullPath); + } + + [Fact] + public void Should_Add_Additional_Settings() + { + // Given + var fixture = new DotNetCoreEfMigrationListerFixture(); + fixture.Settings.StartupProject = "./src/MyMvcProject"; + fixture.Settings.Project = "./src/MyDataProject"; + fixture.Settings.Configuration = "release"; + fixture.Settings.PrefixOutput = true; + fixture.Settings.NoBuild = true; + // When + var result = fixture.Run(); + + // Then + Assert.Equal("ef migrations list --project \"./src/MyDataProject\" --startup-project \"./src/MyMvcProject\" --prefix-output --no-build --json", result.Args); + } + } +} \ No newline at end of file diff --git a/src/Cake.DotNetCoreEf.sln b/src/Cake.DotNetCoreEf.sln index 7a47b40..91c4870 100644 --- a/src/Cake.DotNetCoreEf.sln +++ b/src/Cake.DotNetCoreEf.sln @@ -1,41 +1,41 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2041 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cake.DotNetCoreEf", "Cake.DotNetCoreEf\Cake.DotNetCoreEf.csproj", "{EF4D86EB-7401-4E38-A02B-AB4884DCB168}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{77CAC347-B714-4AD6-9932-328C59DC9B2B}" - ProjectSection(SolutionItems) = preProject - ..\.appveyor.yml = ..\.appveyor.yml - ..\build.ps1 = ..\build.ps1 - ..\nuspec\Cake.DotNetCoreEf.nuspec = ..\nuspec\Cake.DotNetCoreEf.nuspec - ..\LICENSE = ..\LICENSE - ..\README.md = ..\README.md - ..\recipe.cake = ..\recipe.cake - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cake.DotNetCoreEf.UnitTests", "Cake.DotNetCoreEf.UnitTests\Cake.DotNetCoreEf.UnitTests.csproj", "{8C502FAA-A13C-4051-9716-1C41A4596BED}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EF4D86EB-7401-4E38-A02B-AB4884DCB168}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF4D86EB-7401-4E38-A02B-AB4884DCB168}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF4D86EB-7401-4E38-A02B-AB4884DCB168}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF4D86EB-7401-4E38-A02B-AB4884DCB168}.Release|Any CPU.Build.0 = Release|Any CPU - {8C502FAA-A13C-4051-9716-1C41A4596BED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C502FAA-A13C-4051-9716-1C41A4596BED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C502FAA-A13C-4051-9716-1C41A4596BED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C502FAA-A13C-4051-9716-1C41A4596BED}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {732C4874-8EF7-42F1-82E0-DFF23CD78A46} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2041 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cake.DotNetCoreEf", "Cake.DotNetCoreEf\Cake.DotNetCoreEf.csproj", "{EF4D86EB-7401-4E38-A02B-AB4884DCB168}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{77CAC347-B714-4AD6-9932-328C59DC9B2B}" + ProjectSection(SolutionItems) = preProject + ..\.appveyor.yml = ..\.appveyor.yml + ..\build.ps1 = ..\build.ps1 + ..\nuspec\Cake.DotNetCoreEf.nuspec = ..\nuspec\Cake.DotNetCoreEf.nuspec + ..\LICENSE = ..\LICENSE + ..\README.md = ..\README.md + ..\recipe.cake = ..\recipe.cake + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cake.DotNetCoreEf.UnitTests", "Cake.DotNetCoreEf.UnitTests\Cake.DotNetCoreEf.UnitTests.csproj", "{8C502FAA-A13C-4051-9716-1C41A4596BED}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EF4D86EB-7401-4E38-A02B-AB4884DCB168}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF4D86EB-7401-4E38-A02B-AB4884DCB168}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF4D86EB-7401-4E38-A02B-AB4884DCB168}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF4D86EB-7401-4E38-A02B-AB4884DCB168}.Release|Any CPU.Build.0 = Release|Any CPU + {8C502FAA-A13C-4051-9716-1C41A4596BED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C502FAA-A13C-4051-9716-1C41A4596BED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C502FAA-A13C-4051-9716-1C41A4596BED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C502FAA-A13C-4051-9716-1C41A4596BED}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {732C4874-8EF7-42F1-82E0-DFF23CD78A46} + EndGlobalSection +EndGlobal diff --git a/src/Cake.DotNetCoreEf/Cake.DotNetCoreEf.csproj b/src/Cake.DotNetCoreEf/Cake.DotNetCoreEf.csproj index 4c56584..a0f8486 100644 --- a/src/Cake.DotNetCoreEf/Cake.DotNetCoreEf.csproj +++ b/src/Cake.DotNetCoreEf/Cake.DotNetCoreEf.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.0;net5.0;net461 true Library pdbonly @@ -11,22 +11,22 @@ Cake.DotNetCoreEf Cake.DotNetCoreEf .NET Core EntityFrameworkCore addin for cake build. - Cake addin for executing commands with the dotnet ef command line interface (CLI). + Cake addin for executing commands with the dotnet ef command line interface (CLI). mvput mvput,cake-contrib https://github.com/cake-contrib/Cake.DotNetCoreEf - https://cdn.jsdelivr.net/gh/cake-contrib/graphics/png/cake-contrib-medium.png + https://cdn.jsdelivr.net/gh/cake-contrib/graphics/png/addin/cake-contrib-addin-medium.png MIT git https://github.com/cake-contrib/Cake.DotNetCoreEf.git - cake script cli netcore efcore + cake script cli netcore efcore cake-addin Copyright (c) Cake Contributions 2016 - Present Cake Contributions - - + + diff --git a/src/Cake.DotNetCoreEf/DotNetCoreEfAliases.cs b/src/Cake.DotNetCoreEf/DotNetCoreEfAliases.cs index abfd264..880cc16 100644 --- a/src/Cake.DotNetCoreEf/DotNetCoreEfAliases.cs +++ b/src/Cake.DotNetCoreEf/DotNetCoreEfAliases.cs @@ -403,5 +403,43 @@ public static void DotNetCoreEfMigrationScript(this ICakeContext context, string var runner = new DotNetCoreEfMigrationScripter(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); runner.Script(project, arguments, settings); } + + /// + /// List all migrations + /// + /// The context. + /// The project path. + /// The arguments. + /// The settings. + /// + /// + /// var settings = new DotNetCoreEfMigrationScriptListerSettings + /// { + /// Context = SchoolContext, + /// StartupProject = "./src/MvcProject", + /// NoBuild = true + /// }; + /// + /// DotNetCoreEfMigrationScript("./src/Project", "--args", settings); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("MigrationScript")] + [CakeNamespaceImport("Cake.DotNetCoreEf.Migration")] + public static string DotNetCoreEfMigrationList(this ICakeContext context, string project, ProcessArgumentBuilder arguments, DotNetCoreEfMigrationListerSettings settings) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (settings == null) + { + settings = new DotNetCoreEfMigrationListerSettings(); + } + + var runner = new DotNetCoreEfMigrationLister(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); + return runner.Script(project, arguments, settings); + } } } \ No newline at end of file diff --git a/src/Cake.DotNetCoreEf/Migration/DotNetCoreEfMigrationLister.cs b/src/Cake.DotNetCoreEf/Migration/DotNetCoreEfMigrationLister.cs new file mode 100644 index 0000000..52b3cbc --- /dev/null +++ b/src/Cake.DotNetCoreEf/Migration/DotNetCoreEfMigrationLister.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Cake.Core; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.DotNetCoreEf.Migration +{ + /// + /// Support for script migrations using .NET Core cli tooling + /// + public class DotNetCoreEfMigrationLister : DotNetCoreEfTool + { + private readonly ICakeEnvironment _environment; + + /// + /// Initializes a new instance of the . + /// + /// The file system. + /// The environment. + /// The process runner. + /// The tool locator. + public DotNetCoreEfMigrationLister( + IFileSystem fileSystem, + ICakeEnvironment environment, + IProcessRunner processRunner, + IToolLocator tools) + : base(fileSystem, environment, processRunner, tools) + { + this._environment = environment; + } + + /// + /// Script migrations for the project using the specified path with arguments and settings. + /// + /// The target project path. + /// The arguments. + /// The settings. + public string Script(string project, ProcessArgumentBuilder arguments, DotNetCoreEfMigrationListerSettings settings) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + var result = RunProcess(settings, GetArguments(project, arguments, settings)).GetStandardOutput(); + + return result?.FirstOrDefault(); + } + + private ProcessArgumentBuilder GetArguments(string project, ProcessArgumentBuilder arguments, DotNetCoreEfMigrationListerSettings settings) + { + ProcessArgumentBuilder builder = new ProcessArgumentBuilder(); + ProcessArgumentBuilder builderArguments = CreateArgumentBuilder(settings); + + builder.Append("ef"); + builder.Append("migrations"); + builder.Append("list"); + + settings.SetProject(project); + + if (!string.IsNullOrWhiteSpace(settings.Project)) + { + builder.Append("--project"); + builder.AppendQuoted(settings.Project); + } + + if (!string.IsNullOrWhiteSpace(settings.StartupProject)) + { + builder.Append("--startup-project"); + builder.AppendQuoted(settings.StartupProject); + } + + if (settings.PrefixOutput) + { + builder.Append("--prefix-output"); + } + + if (settings.NoBuild) + { + builder.Append("--no-build"); + } + + // return json output + builder.Append("--json"); + + // Arguments + if (!arguments.IsNullOrEmpty()) + { + arguments.CopyTo(builder); + } + + return builder; + } + } +} \ No newline at end of file diff --git a/src/Cake.DotNetCoreEf/Migration/DotNetCoreEfMigrationListerSettings.cs b/src/Cake.DotNetCoreEf/Migration/DotNetCoreEfMigrationListerSettings.cs new file mode 100644 index 0000000..2061f00 --- /dev/null +++ b/src/Cake.DotNetCoreEf/Migration/DotNetCoreEfMigrationListerSettings.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Cake.DotNetCoreEf.Migration +{ + /// + /// Contains settings used by . + /// + public class DotNetCoreEfMigrationListerSettings : DotNetCoreEfSettings + { + /// + /// Prefix characters in output. + /// + public bool PrefixOutput { get; set; } = false; + + /// + /// Gets or sets a value indicating whether to not to build the project before publishing. + /// This makes build faster, but requires build to be done before publish is executed. + /// + public bool NoBuild { get; set; } = true; + } +} \ No newline at end of file diff --git a/tools/packages.config b/tools/packages.config index f27ab48..a36acb6 100644 --- a/tools/packages.config +++ b/tools/packages.config @@ -1,4 +1,4 @@ - +