diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index c33ff7cb..da200cda 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "cake.tool": { - "version": "3.2.0", + "version": "4.0.0", "commands": [ "dotnet-cake" ] diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index b18bc56d..00000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,34 +0,0 @@ -# IF YOU DON'T ANSWER THIS TEMPLATE - THE BOT WILL AUTOMATICALLY CLOSE YOUR ISSUE! - -## Please provide as much detail as possible -* Output Window Messages -* Your Code (small reproducible sample) -* If there is an exception/crash - provide the full stack trace - -*IF BUG, INCLUDE THIS PART:* - -### Steps to reproduce - -1. -2. -3. - -Platform: -.NET version: - -### Expected behaviour - -Tell us what should happen - -### Actual behaviour - -Tell us what happens instead -Can you also include a screen shot? - -*IF IT IS A NEW FEATURE REQUEST, INCLUDE THIS PART:* - -### Feature description - -Write a description of the feature. How should it work? How should it look? -Include some graphics if this could help! - diff --git a/.github/ISSUE_TEMPLATE/---support-request.md b/.github/ISSUE_TEMPLATE/---support-request.md index e4d9b77e..e485a184 100644 --- a/.github/ISSUE_TEMPLATE/---support-request.md +++ b/.github/ISSUE_TEMPLATE/---support-request.md @@ -9,7 +9,7 @@ assignees: '' --- -ONLY active OSS contributors OR people who buy me a coffee can ask questions here. If you don't do either of these things - DO NOT FILE HERE OR I WILL SIMPLY REMOVE YOUR ACCOUNT :) +ONLY active OSS contributors OR people who buy us a coffee can ask questions here. If you don't do either of these things - DO NOT FILE HERE :) Give as much details as humanly possible if you want any sort of answer! diff --git a/.github/ISSUE_TEMPLATE/--bug.md b/.github/ISSUE_TEMPLATE/--bug.md index 6c1c323d..2990ca77 100644 --- a/.github/ISSUE_TEMPLATE/--bug.md +++ b/.github/ISSUE_TEMPLATE/--bug.md @@ -12,10 +12,7 @@ assignees: '' ## Please check all of the platforms you are having the issue on (if platform is not listed, it is not supported) - [ ] WPF - - [ ] UWP - - [ ] iOS - - [ ] Android - - [ ] .NET Standard + - [ ] Blazor WASM - [ ] .NET Core ## Component diff --git a/.github/ISSUE_TEMPLATE/--thank-you.md b/.github/ISSUE_TEMPLATE/--thank-you.md index 18cb2c61..932c781a 100644 --- a/.github/ISSUE_TEMPLATE/--thank-you.md +++ b/.github/ISSUE_TEMPLATE/--thank-you.md @@ -1,6 +1,6 @@ --- name: "❤️Thank You" -about: Just want to say thank you, this is the guy to do it in +about: Just want to say thank you, this is the one to do it in title: Thank You labels: '' assignees: '' diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0745d5a7..079180ad 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -27,7 +27,7 @@ None ### PR Checklist ### - [ ] I have included examples or tests -- [ ] I have updated the change log -- [ ] I am listed in the CONTRIBUTORS file +- [ ] I have updated the change log or created a GitHub ticket with the change +- [ ] I am listed in the CONTRIBUTORS file (if it exists) - [ ] Changes adhere to coding standard - [ ] I checked the licenses of Third Party software and discussed new dependencies with at least 1 other team member \ No newline at end of file diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 00000000..833291a1 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,36 @@ +name: Build and test + +on: + push: + branches: + - develop + - master + pull_request: + +#permissions: + #pull-requests: write + #contents: write + +jobs: + build-and-test: + runs-on: windows-latest # Required for some (WPF) projects + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + id: checkout + with: + fetch-depth: 0 + + - name: Setup .NET Core + id: setup-dotnet + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + with: + dotnet-version: '8.0.x' + + - name: Cake Action + id: cake-action + uses: cake-build/cake-action@1223b6fa067ad192159f43b50cd4f953679b0934 #v2.0.0 + with: + target: BuildAndTest + arguments: | + IsCiBuild: true \ No newline at end of file diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 1c11fe82..5214c1e1 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Dependabot metadata id: dependabot-metadata - uses: dependabot/fetch-metadata@c9c4182bf1b97f5224aee3906fd373f6b61b4526 #1.6.0 + uses: dependabot/fetch-metadata@5e5f99653a5b510e8555840e80cbf1514ad4af38 #2.1.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Approve Dependabot PR diff --git a/.gitignore b/.gitignore index f9cde13a..c3accbe7 100644 --- a/.gitignore +++ b/.gitignore @@ -132,7 +132,6 @@ build.cakeoverrides TestResults .vs/ .sonarqube/ -.github/ BundleArtifacts/ # docker / tye diff --git a/GitReleaseManager.yaml b/GitReleaseManager.yaml index 721c1ba4..47e42402 100644 --- a/GitReleaseManager.yaml +++ b/GitReleaseManager.yaml @@ -1,12 +1,14 @@ -issue-labels-include: -- Breaking change -- Feature -- Bug -- Improvement -- Documentation -issue-labels-exclude: -- Build -issue-labels-alias: - - name: Documentation - header: Documentation - plural: Documentation \ No newline at end of file +issue-labels-include: + - Breaking change + - Feature + - Bug + - Improvement + - Documentation + - Dependencies +issue-labels-exclude: + - Build + - Won't fix +issue-labels-alias: + - name: Documentation + header: Documentation + plural: Documentation \ No newline at end of file diff --git a/deployment/cake/components-tasks.cake b/deployment/cake/components-tasks.cake index 90891315..ca91e6f4 100644 --- a/deployment/cake/components-tasks.cake +++ b/deployment/cake/components-tasks.cake @@ -190,8 +190,10 @@ public class ComponentsProcessor : ProcessorBase // Special exception for Blazor projects var isBlazorProject = IsBlazorProject(BuildContext, component); + var isPackageContainerProject = IsPackageContainerProject(BuildContext, component); BuildContext.CakeContext.LogSeparator("Packaging component '{0}'", component); + CakeContext.Information("IsPackageContainerProject = '{0}'", isPackageContainerProject); var projectDirectory = GetProjectDirectory(component); var projectFileName = GetProjectFileName(BuildContext, component); @@ -287,6 +289,17 @@ public class ComponentsProcessor : ProcessorBase noBuild = false; } + if (isPackageContainerProject) + { + // In debug / local builds, automatic building of reference projects + // is enabled for convenience. If that is the case, noBuild must be + // set to false, but *only* in debug mode + if (BuildContext.General.IsLocalBuild) + { + noBuild = false; + } + } + // As described in the this issue: https://github.com/NuGet/Home/issues/4360 // we should not use IsTool, but set BuildOutputTargetFolder instead msBuildSettings.WithProperty("CopyLocalLockFileAssemblies", "true"); diff --git a/deployment/cake/docker-tasks.cake b/deployment/cake/docker-tasks.cake index 79350bf3..007572d7 100644 --- a/deployment/cake/docker-tasks.cake +++ b/deployment/cake/docker-tasks.cake @@ -1,7 +1,7 @@ #l "docker-variables.cake" #l "lib-octopusdeploy.cake" -#addin "nuget:?package=Cake.Docker&version=1.2.3" +#addin "nuget:?package=Cake.Docker&version=1.3.0" //------------------------------------------------------------- diff --git a/deployment/cake/generic-variables.cake b/deployment/cake/generic-variables.cake index a3a34d2a..246657fd 100644 --- a/deployment/cake/generic-variables.cake +++ b/deployment/cake/generic-variables.cake @@ -400,7 +400,7 @@ public class SonarQubeContext : BuildContextBase public string Url { get; set; } public string Organization { get; set; } public string Username { get; set; } - public string Password { get; set; } + public string Token { get; set; } public string Project { get; set; } protected override void ValidateContext() @@ -514,7 +514,7 @@ private GeneralContext InitializeGeneralContext(BuildContext buildContext, IBuil Url = buildContext.BuildServer.GetVariable("SonarUrl", showValue: true), Organization = buildContext.BuildServer.GetVariable("SonarOrganization", showValue: true), Username = buildContext.BuildServer.GetVariable("SonarUsername", showValue: false), - Password = buildContext.BuildServer.GetVariable("SonarPassword", showValue: false), + Token = buildContext.BuildServer.GetVariable("SonarToken", showValue: false), Project = buildContext.BuildServer.GetVariable("SonarProject", data.Solution.Name, showValue: true) }; diff --git a/deployment/cake/github-pages-tasks.cake b/deployment/cake/github-pages-tasks.cake index 9e008273..98aad9ae 100644 --- a/deployment/cake/github-pages-tasks.cake +++ b/deployment/cake/github-pages-tasks.cake @@ -1,6 +1,6 @@ #l "github-pages-variables.cake" -#addin "nuget:?package=Cake.Git&version=3.0.0" +#addin "nuget:?package=Cake.Git&version=4.0.0" //------------------------------------------------------------- diff --git a/deployment/cake/issuetrackers-github.cake b/deployment/cake/issuetrackers-github.cake index fcfbd798..efa409b9 100644 --- a/deployment/cake/issuetrackers-github.cake +++ b/deployment/cake/issuetrackers-github.cake @@ -1,4 +1,4 @@ -#tool "nuget:?package=gitreleasemanager&version=0.16.0" +#tool "nuget:?package=gitreleasemanager&version=0.17.0" //------------------------------------------------------------- @@ -8,13 +8,11 @@ public class GitHubIssueTracker : IIssueTracker { BuildContext = buildContext; - UserName = buildContext.BuildServer.GetVariable("GitHubUserName", showValue: true); ApiKey = buildContext.BuildServer.GetVariable("GitHubApiKey", showValue: false); OwnerName = buildContext.BuildServer.GetVariable("GitHubOwnerName", buildContext.General.Copyright.Company, showValue: true); ProjectName = buildContext.BuildServer.GetVariable("GitHubProjectName", buildContext.General.Solution.Name, showValue: true); - if (!string.IsNullOrWhiteSpace(UserName) && - !string.IsNullOrWhiteSpace(ApiKey) && + if (!string.IsNullOrWhiteSpace(ApiKey) && !string.IsNullOrWhiteSpace(OwnerName) && !string.IsNullOrWhiteSpace(ProjectName)) { @@ -24,7 +22,6 @@ public class GitHubIssueTracker : IIssueTracker public BuildContext BuildContext { get; private set; } - public string UserName { get; set; } public string ApiKey { get; set; } public string OwnerName { get; set; } public string ProjectName { get; set; } diff --git a/deployment/cake/issuetrackers.cake b/deployment/cake/issuetrackers.cake index 8c384163..7350b42d 100644 --- a/deployment/cake/issuetrackers.cake +++ b/deployment/cake/issuetrackers.cake @@ -34,7 +34,7 @@ public class IssueTrackerIntegration : IntegrationBase } catch (Exception ex) { - BuildContext.CakeContext.Warning(ex.Message); + BuildContext.CakeContext.Error(ex.Message); } } } diff --git a/deployment/cake/lib-generic.cake b/deployment/cake/lib-generic.cake index d0a5fc98..7ab65714 100644 --- a/deployment/cake/lib-generic.cake +++ b/deployment/cake/lib-generic.cake @@ -440,6 +440,35 @@ private static bool IsCppProject(string projectName) return projectName.EndsWith(".vcxproj"); } +//-------------------------------------------------------------- + +private static bool IsPackageContainerProject(BuildContext buildContext, string projectName) +{ + var isPackageContainer = false; + + var projectFileName = CreateInlinedProjectXml(buildContext, projectName); + + var projectFileContents = System.IO.File.ReadAllText(projectFileName); + + var xmlDocument = XDocument.Parse(projectFileContents); + var projectElement = xmlDocument.Root; + + foreach (var propertyGroupElement in projectElement.Elements("PropertyGroup")) + { + var packageContainerElement = propertyGroupElement.Element("PackageContainer"); + if (packageContainerElement != null) + { + if (packageContainerElement.Value.ToLower() == "true") + { + isPackageContainer = true; + } + break; + } + } + + return isPackageContainer; +} + //------------------------------------------------------------- private static bool IsBlazorProject(BuildContext buildContext, string projectName) @@ -595,6 +624,40 @@ private static bool ShouldProcessProject(BuildContext buildContext, string proje return true; } +private static string CreateInlinedProjectXml(BuildContext buildContext, string projectName) +{ + buildContext.CakeContext.Information($"Running 'msbuild /pp' for project '{projectName}'"); + + var projectInlinedFileName = System.IO.Path.Combine(GetProjectOutputDirectory(buildContext, projectName), + "..", $"{projectName}.inlined.xml"); + + // Note: disabled caching until we correctly clean up everything + //if (!buildContext.CakeContext.FileExists(projectInlinedFileName)) + { + // Run "msbuild /pp" to create a single project file + + var msBuildSettings = new MSBuildSettings + { + Verbosity = Verbosity.Quiet, + ToolVersion = MSBuildToolVersion.Default, + Configuration = buildContext.General.Solution.ConfigurationName, + MSBuildPlatform = MSBuildPlatform.x86, // Always require x86, see platform for actual target platform + PlatformTarget = PlatformTarget.MSIL + }; + + ConfigureMsBuild(buildContext, msBuildSettings, projectName, "pp"); + + msBuildSettings.Target = string.Empty; + msBuildSettings.ArgumentCustomization = args => args.Append($"/pp:{projectInlinedFileName}"); + + var projectFileName = GetProjectFileName(buildContext, projectName); + + RunMsBuild(buildContext, projectName, projectFileName, msBuildSettings, "pp"); + } + + return projectInlinedFileName; +} + //------------------------------------------------------------- private static List GetProjectRuntimesIdentifiers(BuildContext buildContext, Cake.Core.IO.FilePath solutionOrProjectFileName, List runtimeIdentifiersToInvestigate) diff --git a/deployment/cake/lib-msbuild.cake b/deployment/cake/lib-msbuild.cake index 9df7f61e..7a263738 100644 --- a/deployment/cake/lib-msbuild.cake +++ b/deployment/cake/lib-msbuild.cake @@ -1,5 +1,6 @@ -#addin "nuget:?package=Cake.Issues&version=3.0.0" -#addin "nuget:?package=Cake.Issues.MsBuild&version=3.0.0" +#addin "nuget:?package=Cake.Issues&version=4.5.1" +#addin "nuget:?package=Cake.Issues.MsBuild&version=4.5.1" +#addin "nuget:?package=System.Configuration.ConfigurationManager&version=8.0.0" #tool "nuget:?package=MSBuild.Extension.Pack&version=1.9.1" @@ -67,7 +68,7 @@ private static void ConfigureMsBuild(BuildContext buildContext, MSBuildSettings } else { - buildContext.CakeContext.Information("This is a local build, disabling building of project references"); + buildContext.CakeContext.Information("This is a local build, not disabling building of project references"); } // Continuous integration build @@ -154,7 +155,7 @@ private static void ConfigureMsBuildForDotNet(BuildContext buildContext, DotNetM } else { - buildContext.CakeContext.Information($"This is a local build, disabling building of project references"); + buildContext.CakeContext.Information($"This is a local build, not disabling building of project references"); } // Continuous integration build @@ -488,4 +489,4 @@ private static void InjectAssemblySearchPathsInProjectFile(BuildContext buildCon { buildContext.CakeContext.Error($"Failed to process assembly search paths for project '{projectFileName}': {ex.Message}"); } -} \ No newline at end of file +} diff --git a/deployment/cake/sourcecontrol-github.cake b/deployment/cake/sourcecontrol-github.cake index 74368106..859cd72c 100644 --- a/deployment/cake/sourcecontrol-github.cake +++ b/deployment/cake/sourcecontrol-github.cake @@ -1,5 +1,5 @@ #addin "nuget:?package=Cake.GitHub&version=0.1.0" -#addin "nuget:?package=Octokit&version=9.0.0" +#addin "nuget:?package=Octokit&version=12.0.0" //------------------------------------------------------------- @@ -9,13 +9,11 @@ public class GitHubSourceControl : ISourceControl { BuildContext = buildContext; - UserName = buildContext.BuildServer.GetVariable("GitHubUserName", buildContext.General.Repository.Username, showValue: true); ApiKey = buildContext.BuildServer.GetVariable("GitHubApiKey", buildContext.General.Repository.Password, showValue: false); OwnerName = buildContext.BuildServer.GetVariable("GitHubOwnerName", buildContext.General.Copyright.Company, showValue: true); ProjectName = buildContext.BuildServer.GetVariable("GitHubProjectName", buildContext.General.Solution.Name, showValue: true); - if (!string.IsNullOrWhiteSpace(UserName) && - !string.IsNullOrWhiteSpace(ApiKey) && + if (!string.IsNullOrWhiteSpace(ApiKey) && !string.IsNullOrWhiteSpace(OwnerName) && !string.IsNullOrWhiteSpace(ProjectName)) { @@ -25,7 +23,6 @@ public class GitHubSourceControl : ISourceControl public BuildContext BuildContext { get; private set; } - public string UserName { get; set; } public string ApiKey { get; set; } public string OwnerName { get; set; } public string ProjectName { get; set; } @@ -66,7 +63,8 @@ public class GitHubSourceControl : ISourceControl var commitSha = BuildContext.General.Repository.CommitId; - BuildContext.CakeContext.GitHubStatus(UserName, ApiKey, OwnerName, ProjectName, commitSha, new GitHubStatusSettings + // Note: UserName is not really required, use string.Empty, then only api key is needed + BuildContext.CakeContext.GitHubStatus(string.Empty, ApiKey, OwnerName, ProjectName, commitSha, new GitHubStatusSettings { State = state, TargetUrl = null,// "url-to-build-server", diff --git a/deployment/cake/tasks.cake b/deployment/cake/tasks.cake index 7fa530e1..9f4dc759 100644 --- a/deployment/cake/tasks.cake +++ b/deployment/cake/tasks.cake @@ -24,8 +24,8 @@ #l "tests.cake" #l "templates-tasks.cake" -#addin "nuget:?package=Cake.FileHelpers&version=6.1.3" -#addin "nuget:?package=Cake.Sonar&version=1.1.32" +#addin "nuget:?package=Cake.FileHelpers&version=7.0.0" +#addin "nuget:?package=Cake.Sonar&version=1.1.33" #addin "nuget:?package=MagicChunks&version=2.0.0.119" #addin "nuget:?package=Newtonsoft.Json&version=13.0.3" @@ -36,7 +36,7 @@ // It probably means the tool is not correctly installed. // `dotnet tool install --global dotnet-sonarscanner --ignore-failed-sources` //#tool "nuget:?package=MSBuild.SonarQube.Runner.Tool&version=4.8.0" -#tool "nuget:?package=dotnet-sonarscanner&version=6.0.0" +#tool "nuget:?package=dotnet-sonarscanner&version=6.2.0" //------------------------------------------------------------- // BACKWARDS COMPATIBILITY CODE - START @@ -397,9 +397,9 @@ Task("Build") sonarSettings.Login = buildContext.General.SonarQube.Username; } - if (!string.IsNullOrWhiteSpace(buildContext.General.SonarQube.Password)) + if (!string.IsNullOrWhiteSpace(buildContext.General.SonarQube.Token)) { - sonarSettings.Password = buildContext.General.SonarQube.Password; + sonarSettings.Token = buildContext.General.SonarQube.Token; } // see https://cakebuild.net/api/Cake.Sonar/SonarBeginSettings/ for more information on @@ -450,7 +450,7 @@ Task("Build") { await buildContext.SourceControl.MarkBuildAsPendingAsync("SonarQube"); - var sonarEndSettings = new SonarEndSettings + var sonarSettings = new SonarEndSettings { // Use core clr version of SonarQube UseCoreClr = true @@ -458,17 +458,17 @@ Task("Build") if (!string.IsNullOrWhiteSpace(buildContext.General.SonarQube.Username)) { - sonarEndSettings.Login = buildContext.General.SonarQube.Username; + sonarSettings.Login = buildContext.General.SonarQube.Username; } - if (!string.IsNullOrWhiteSpace(buildContext.General.SonarQube.Password)) + if (!string.IsNullOrWhiteSpace(buildContext.General.SonarQube.Token)) { - sonarEndSettings.Password = buildContext.General.SonarQube.Password; + sonarSettings.Token = buildContext.General.SonarQube.Token; } Information("Ending SonarQube"); - SonarEnd(sonarEndSettings); + SonarEnd(sonarSettings); await buildContext.SourceControl.MarkBuildAsSucceededAsync("SonarQube"); } diff --git a/deployment/cake/tests-nunit.cake b/deployment/cake/tests-nunit.cake index a9216a8e..59b559a3 100644 --- a/deployment/cake/tests-nunit.cake +++ b/deployment/cake/tests-nunit.cake @@ -1,4 +1,4 @@ -#tool "nuget:?package=NUnit.ConsoleRunner&version=3.16.3" +#tool "nuget:?package=NUnit.ConsoleRunner&version=3.17.0" //------------------------------------------------------------- diff --git a/src/.vsconfig b/src/.vsconfig index 98c42001..030707b4 100644 --- a/src/.vsconfig +++ b/src/.vsconfig @@ -1,66 +1,54 @@ -{ - "version": "1.0", - "components": [ - "Microsoft.VisualStudio.Component.CoreEditor", - "Microsoft.VisualStudio.Workload.CoreEditor", - "Microsoft.VisualStudio.Component.NuGet", - "Microsoft.VisualStudio.Component.Roslyn.Compiler", - "Microsoft.VisualStudio.Component.Roslyn.LanguageServices", - "Microsoft.VisualStudio.Component.FSharp", - "Microsoft.Net.Core.Component.SDK.2.1", - "Microsoft.NetCore.ComponentGroup.DevelopmentTools.2.1", - "Microsoft.VisualStudio.Component.FSharp.WebTemplates", - "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions", - "Microsoft.VisualStudio.Component.DockerTools", - "Microsoft.NetCore.ComponentGroup.Web.2.1", - "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites", - "Microsoft.VisualStudio.Component.TypeScript.3.4", - "Microsoft.VisualStudio.Component.JavaScript.TypeScript", - "Microsoft.VisualStudio.Component.JavaScript.Diagnostics", - "Microsoft.Component.MSBuild", - "Microsoft.VisualStudio.Component.TextTemplating", - "Component.Microsoft.VisualStudio.RazorExtension", - "Microsoft.VisualStudio.Component.IISExpress", - "Microsoft.VisualStudio.Component.SQL.ADAL", - "Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime", - "Microsoft.VisualStudio.Component.Common.Azure.Tools", - "Microsoft.VisualStudio.Component.SQL.CLR", - "Microsoft.VisualStudio.Component.MSODBC.SQL", - "Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils", - "Microsoft.VisualStudio.Component.ManagedDesktop.Core", - "Microsoft.VisualStudio.Component.SQL.SSDT", - "Microsoft.VisualStudio.Component.SQL.DataSources", - "Component.Microsoft.Web.LibraryManager", - "Microsoft.VisualStudio.ComponentGroup.Web", - "Microsoft.VisualStudio.Component.Web", - "Microsoft.Net.ComponentGroup.TargetingPacks.Common", - "Microsoft.VisualStudio.Component.CloudExplorer", - "Microsoft.VisualStudio.Component.Debugger.TimeTravel", - "Microsoft.VisualStudio.Component.Debugger.Snapshot", - "Microsoft.VisualStudio.ComponentGroup.Web.CloudTools", - "Microsoft.VisualStudio.Component.IntelliTrace.FrontEnd", - "Microsoft.VisualStudio.Component.DiagnosticTools", - "Microsoft.VisualStudio.Component.EntityFramework", - "Microsoft.VisualStudio.Component.LiveUnitTesting", - "Microsoft.VisualStudio.Component.AspNet45", - "Microsoft.VisualStudio.Component.AppInsights.Tools", - "Microsoft.VisualStudio.Component.WebDeploy", - "Microsoft.VisualStudio.Component.Debugger.JustInTime", - "Microsoft.VisualStudio.Component.GraphDocument", - "Microsoft.VisualStudio.Component.CodeMap", - "Microsoft.VisualStudio.Workload.NetWeb", - "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites", - "Microsoft.VisualStudio.Component.PortableLibrary", - "Microsoft.VisualStudio.Workload.ManagedDesktop", - "Microsoft.VisualStudio.Component.ClassDesigner", - "Microsoft.Component.NetFX.Native", - "Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard", - "Microsoft.VisualStudio.Component.Graphics", - "Microsoft.VisualStudio.Component.Windows10SDK.18362", - "Microsoft.VisualStudio.Workload.Universal", - "Microsoft.VisualStudio.Component.Merq", - "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.TemplateEngine", - "Microsoft.VisualStudio.Workload.NetCrossPlat", - "Microsoft.Component.CodeAnalysis.SDK" - ] +{ + "version": "1.0", + "components": [ + "Microsoft.VisualStudio.Component.CoreEditor", + "Microsoft.VisualStudio.Workload.CoreEditor", + "Microsoft.Net.Component.4.8.SDK", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites", + "Microsoft.VisualStudio.Component.TypeScript.TSServer", + "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions", + "Microsoft.VisualStudio.Component.JavaScript.TypeScript", + "Microsoft.VisualStudio.Component.JavaScript.Diagnostics", + "Microsoft.VisualStudio.Component.Roslyn.Compiler", + "Microsoft.Component.MSBuild", + "Microsoft.VisualStudio.Component.Roslyn.LanguageServices", + "Microsoft.VisualStudio.Component.TextTemplating", + "Component.Microsoft.VisualStudio.RazorExtension", + "Microsoft.VisualStudio.Component.IISExpress", + "Microsoft.VisualStudio.Component.NuGet", + "Microsoft.VisualStudio.Component.MSODBC.SQL", + "Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime", + "Microsoft.VisualStudio.Component.Common.Azure.Tools", + "Microsoft.VisualStudio.Component.SQL.CLR", + "Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils", + "Microsoft.VisualStudio.Component.ManagedDesktop.Core", + "Microsoft.VisualStudio.Component.SQL.SSDT", + "Microsoft.VisualStudio.Component.SQL.DataSources", + "Component.Microsoft.Web.LibraryManager", + "Component.Microsoft.WebTools.BrowserLink.WebLivePreview", + "Microsoft.VisualStudio.ComponentGroup.Web", + "Microsoft.NetCore.Component.Runtime.8.0", + "Microsoft.NetCore.Component.SDK", + "Microsoft.VisualStudio.Component.FSharp", + "Microsoft.NetCore.Component.DevelopmentTools", + "Microsoft.VisualStudio.Component.FSharp.WebTemplates", + "Microsoft.VisualStudio.Component.DockerTools", + "Microsoft.NetCore.Component.Web", + "Microsoft.VisualStudio.Component.WebDeploy", + "Microsoft.VisualStudio.Component.AppInsights.Tools", + "Microsoft.VisualStudio.Component.Web", + "Microsoft.VisualStudio.Component.AspNet45", + "Microsoft.VisualStudio.Component.AspNet", + "Component.Microsoft.VisualStudio.Web.AzureFunctions", + "Microsoft.VisualStudio.ComponentGroup.AzureFunctions", + "Microsoft.VisualStudio.ComponentGroup.Web.CloudTools", + "Microsoft.VisualStudio.Component.DiagnosticTools", + "Microsoft.VisualStudio.Component.Debugger.JustInTime", + "Microsoft.VisualStudio.Component.WslDebugging", + "Microsoft.VisualStudio.Workload.NetWeb", + "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites", + "Microsoft.VisualStudio.Workload.ManagedDesktop" + ], + "extensions": [] } \ No newline at end of file diff --git a/src/Directory.Build.analyzers.props b/src/Directory.Build.analyzers.props index ec23151b..595ea76b 100644 --- a/src/Directory.Build.analyzers.props +++ b/src/Directory.Build.analyzers.props @@ -16,7 +16,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Directory.Build.shared.explicit.props b/src/Directory.Build.shared.explicit.props index 66f893c3..01dc5af4 100644 --- a/src/Directory.Build.shared.explicit.props +++ b/src/Directory.Build.shared.explicit.props @@ -50,6 +50,11 @@ bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + + + false + + + + + direct + + low + + true diff --git a/src/Orc.ProjectManagement.Example/Orc.ProjectManagement.Example.csproj b/src/Orc.ProjectManagement.Example/Orc.ProjectManagement.Example.csproj index 75ba49ea..2d20543a 100644 --- a/src/Orc.ProjectManagement.Example/Orc.ProjectManagement.Example.csproj +++ b/src/Orc.ProjectManagement.Example/Orc.ProjectManagement.Example.csproj @@ -16,14 +16,14 @@ - + runtime; build; native; contentfiles; analyzers - + diff --git a/src/Orc.ProjectManagement.Tests/Orc.ProjectManagement.Tests.csproj b/src/Orc.ProjectManagement.Tests/Orc.ProjectManagement.Tests.csproj index bed9990a..04fd850d 100644 --- a/src/Orc.ProjectManagement.Tests/Orc.ProjectManagement.Tests.csproj +++ b/src/Orc.ProjectManagement.Tests/Orc.ProjectManagement.Tests.csproj @@ -17,13 +17,13 @@ runtime; build; native; contentfiles; analyzers - + - + - + diff --git a/src/Orc.ProjectManagement.Tests/PublicApiFacts.Orc_ProjectManagement_HasNoBreakingChanges_Async.verified.txt b/src/Orc.ProjectManagement.Tests/PublicApiFacts.Orc_ProjectManagement_HasNoBreakingChanges_Async.verified.txt index bd422113..0390c4ef 100644 --- a/src/Orc.ProjectManagement.Tests/PublicApiFacts.Orc_ProjectManagement_HasNoBreakingChanges_Async.verified.txt +++ b/src/Orc.ProjectManagement.Tests/PublicApiFacts.Orc_ProjectManagement_HasNoBreakingChanges_Async.verified.txt @@ -13,11 +13,6 @@ namespace Orc.ProjectManagement public override System.Threading.Tasks.Task ClosedAsync(Orc.ProjectManagement.IProject project) { } public override System.Threading.Tasks.Task LoadingFailedAsync(string location, System.Exception? exception, Catel.Data.IValidationContext validationContext) { } } - public static class AsyncEventHandlerExtensions - { - public static System.Threading.Tasks.Task SafeInvokeWithTimeoutAsync(this Catel.AsyncEventHandler? handler, string eventName, object sender, TEventArgs e, int timeout) - where TEventArgs : System.EventArgs { } - } public class CloseBeforeLoadProjectWatcher : Orc.ProjectManagement.ProjectWatcherBase { public CloseBeforeLoadProjectWatcher(Orc.ProjectManagement.IProjectManager projectManager, Catel.IoC.ITypeFactory typeFactory) { } @@ -107,6 +102,10 @@ namespace Orc.ProjectManagement void SetProjectsSource(System.Collections.Generic.IEnumerable projects); void SuspendUsingHistory(); } + public interface IProjectEventType + { + Orc.ProjectManagement.ProjectEventTypeStage Stage { get; } + } public interface IProjectInitializer { System.Threading.Tasks.Task> GetInitialLocationsAsync(); @@ -145,22 +144,31 @@ namespace Orc.ProjectManagement event Catel.AsyncEventHandler? ProjectSavingAsync; event Catel.AsyncEventHandler? ProjectSavingCanceledAsync; event Catel.AsyncEventHandler? ProjectSavingFailedAsync; + [System.Obsolete("Use extension method. Use `CloseActiveProjectAsync` instead. Will be removed in v" + + "ersion 6.0.0.", true)] System.Threading.Tasks.Task CloseAsync(); System.Threading.Tasks.Task CloseAsync(Orc.ProjectManagement.IProject project); System.Threading.Tasks.Task InitializeAsync(); System.Threading.Tasks.Task LoadAsync(string location); System.Threading.Tasks.Task LoadInactiveAsync(string location); + [System.Obsolete("Use extension method. Use `RefreshActiveProjectAsync` instead. Will be removed in" + + " version 6.0.0.", true)] System.Threading.Tasks.Task RefreshAsync(); System.Threading.Tasks.Task RefreshAsync(Orc.ProjectManagement.IProject project); + [System.Obsolete("Use extension method. Use `SaveActiveProjectAsync` instead. Will be removed in ve" + + "rsion 6.0.0.", true)] System.Threading.Tasks.Task SaveAsync(string? location = null); System.Threading.Tasks.Task SaveAsync(Orc.ProjectManagement.IProject project, string? location = null); System.Threading.Tasks.Task SetActiveProjectAsync(Orc.ProjectManagement.IProject? project); } public static class IProjectManagerExtensions { + public static System.Threading.Tasks.Task CloseActiveProjectAsync(this Orc.ProjectManagement.IProjectManager projectManager) { } public static TProject? GetActiveProject(this Orc.ProjectManagement.IProjectManager projectManager) where TProject : Orc.ProjectManagement.IProject { } public static string? GetActiveProjectLocation(this Orc.ProjectManagement.IProjectManager projectManager) { } + public static System.Threading.Tasks.Task RefreshActiveProjectAsync(this Orc.ProjectManagement.IProjectManager projectManager) { } + public static System.Threading.Tasks.Task SaveActiveProjectAsync(this Orc.ProjectManagement.IProjectManager projectManager, string? location = null) { } } public interface IProjectManagerWorkflowItem { @@ -244,6 +252,12 @@ namespace Orc.ProjectManagement public MdiProjectManagementConfigurationService() { } public override Orc.ProjectManagement.ProjectManagementType GetProjectManagementType() { } } + public class ProjectActivationEvent : Orc.ProjectManagement.IProjectEventType + { + public ProjectActivationEvent(Orc.ProjectManagement.ProjectEventTypeStage stage, System.EventArgs eventArgs) { } + public System.EventArgs EventArgs { get; } + public Orc.ProjectManagement.ProjectEventTypeStage Stage { get; } + } public class ProjectActivationEventArgs : Orc.ProjectManagement.ProjectLocationEventArgs { public ProjectActivationEventArgs(Orc.ProjectManagement.IProject? project) { } @@ -283,6 +297,12 @@ namespace Orc.ProjectManagement public string? Location { get; } public Orc.ProjectManagement.IProject? Project { get; } } + public class ProjectCloseEvent : Orc.ProjectManagement.IProjectEventType + { + public ProjectCloseEvent(Orc.ProjectManagement.ProjectEventTypeStage stage, System.EventArgs eventArgs) { } + public System.EventArgs EventArgs { get; } + public Orc.ProjectManagement.ProjectEventTypeStage Stage { get; } + } public class ProjectErrorEventArgs : Orc.ProjectManagement.ProjectLocationEventArgs { public ProjectErrorEventArgs(Orc.ProjectManagement.IProject? project, System.Exception? exception = null, Catel.Data.IValidationContext? validationContext = null) { } @@ -296,6 +316,14 @@ namespace Orc.ProjectManagement public ProjectEventArgs(Orc.ProjectManagement.IProject project) { } public Orc.ProjectManagement.IProject Project { get; } } + public enum ProjectEventTypeStage + { + Required = 0, + Before = 1, + After = 2, + Cancelled = 3, + Failed = 4, + } public class ProjectException : System.Exception { public ProjectException(Orc.ProjectManagement.IProject project, string message) { } @@ -309,6 +337,12 @@ namespace Orc.ProjectManagement public ProjectFileSystemEventArgs(string location, params string[] fileNames) { } public string[] FileNames { get; } } + public class ProjectLoadEvent : Orc.ProjectManagement.IProjectEventType + { + public ProjectLoadEvent(Orc.ProjectManagement.ProjectEventTypeStage stage, System.EventArgs eventArgs) { } + public System.EventArgs EventArgs { get; } + public Orc.ProjectManagement.ProjectEventTypeStage Stage { get; } + } public class ProjectLocationEventArgs : System.EventArgs { public ProjectLocationEventArgs(string? location) { } @@ -358,17 +392,26 @@ namespace Orc.ProjectManagement public event Catel.AsyncEventHandler? ProjectSavingAsync; public event Catel.AsyncEventHandler? ProjectSavingCanceledAsync; public event Catel.AsyncEventHandler? ProjectSavingFailedAsync; + [System.Obsolete("Use extension method. Use `CloseActiveProjectAsync` instead. Will be removed in v" + + "ersion 6.0.0.", true)] public System.Threading.Tasks.Task CloseAsync() { } - public System.Threading.Tasks.Task CloseAsync(Orc.ProjectManagement.IProject project) { } - public System.Threading.Tasks.Task InitializeAsync() { } - public System.Threading.Tasks.Task LoadAsync(string location) { } - public System.Threading.Tasks.Task LoadInactiveAsync(string location) { } + public virtual System.Threading.Tasks.Task CloseAsync(Orc.ProjectManagement.IProject project) { } + public virtual System.Threading.Tasks.Task InitializeAsync() { } + protected virtual System.Threading.Tasks.Task InvokeEventListenerAsync(string eventName, object sender, TEventArgs args, Catel.AsyncEventHandler eventListener) + where TEventArgs : System.EventArgs { } + public virtual System.Threading.Tasks.Task LoadAsync(string location) { } + public virtual System.Threading.Tasks.Task LoadInactiveAsync(string location) { } + protected virtual System.Threading.Tasks.Task RaiseEventAsync(Orc.ProjectManagement.IProjectEventType projectEventType) { } protected virtual System.Threading.Tasks.Task ReadProjectAsync(string location) { } + [System.Obsolete("Use extension method. Use `RefreshActiveProjectAsync` instead. Will be removed in" + + " version 6.0.0.", true)] public System.Threading.Tasks.Task RefreshAsync() { } - public System.Threading.Tasks.Task RefreshAsync(Orc.ProjectManagement.IProject project) { } + public virtual System.Threading.Tasks.Task RefreshAsync(Orc.ProjectManagement.IProject project) { } + [System.Obsolete("Use extension method. Use `SaveActiveProjectAsync` instead. Will be removed in ve" + + "rsion 6.0.0.", true)] public System.Threading.Tasks.Task SaveAsync(string? location = null) { } - public System.Threading.Tasks.Task SaveAsync(Orc.ProjectManagement.IProject project, string? location = null) { } - public System.Threading.Tasks.Task SetActiveProjectAsync(Orc.ProjectManagement.IProject? project) { } + public virtual System.Threading.Tasks.Task SaveAsync(Orc.ProjectManagement.IProject project, string? location = null) { } + public virtual System.Threading.Tasks.Task SetActiveProjectAsync(Orc.ProjectManagement.IProject? project) { } protected virtual System.Threading.Tasks.Task WriteProjectAsync(Orc.ProjectManagement.IProject project, string location) { } } public abstract class ProjectManagerWorkflowItemBase : Orc.ProjectManagement.IProjectManagerWorkflowItem @@ -436,6 +479,12 @@ namespace Orc.ProjectManagement public System.Exception? Exception { get; } public Catel.Data.IValidationContext ValidationContext { get; } } + public class ProjectRefreshEvent : Orc.ProjectManagement.IProjectEventType + { + public ProjectRefreshEvent(Orc.ProjectManagement.ProjectEventTypeStage stage, System.EventArgs eventArgs) { } + public System.EventArgs EventArgs { get; } + public Orc.ProjectManagement.ProjectEventTypeStage Stage { get; } + } public abstract class ProjectRefresherBase : Orc.ProjectManagement.IProjectRefresher { protected ProjectRefresherBase(string projectLocation) { } @@ -453,6 +502,12 @@ namespace Orc.ProjectManagement public void Unsubscribe() { } protected abstract void UnsubscribeFromLocation(string location); } + public class ProjectSaveEvent : Orc.ProjectManagement.IProjectEventType + { + public ProjectSaveEvent(Orc.ProjectManagement.ProjectEventTypeStage stage, System.EventArgs eventArgs) { } + public System.EventArgs EventArgs { get; } + public Orc.ProjectManagement.ProjectEventTypeStage Stage { get; } + } public class ProjectState { public ProjectState() { } diff --git a/src/Orc.ProjectManagement/Extensions/AsyncEventHandlerExtensions.cs b/src/Orc.ProjectManagement/Extensions/AsyncEventHandlerExtensions.cs deleted file mode 100644 index e061efc2..00000000 --- a/src/Orc.ProjectManagement/Extensions/AsyncEventHandlerExtensions.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Orc.ProjectManagement; - -using System; -using System.Linq; -using System.Threading.Tasks; -using Catel; -using Catel.Logging; - -public static class AsyncEventHandlerExtensions -{ - private static readonly ILog Log = LogManager.GetCurrentClassLogger(); - - public static async Task SafeInvokeWithTimeoutAsync(this AsyncEventHandler? handler, string eventName, object sender, TEventArgs e, int timeout) - where TEventArgs : EventArgs - { - if (handler is null) - { - return false; - } - - try - { - Log.DebugIfAttached($"Handling project management event '{eventName}'"); - - var task = SafeInvokeAsync(handler, sender, e); - var completedTask = await Task.WhenAny(task, Task.Delay(timeout)); - - if (completedTask != task) - { - Log.Warning($"Handling project management event '{eventName}' has timed out"); - } - else - { - Log.DebugIfAttached($"Handled project management event '{eventName}'"); - } - - return await task.ConfigureAwait(false); - } - catch (Exception ex) - { - Log.Error(ex, "Failed to handle project management event '{eventName}'"); - throw; - } - } - - private static async Task SafeInvokeAsync(AsyncEventHandler? handler, object sender, TEventArgs e) - where TEventArgs : EventArgs - { - if (handler is null) - { - return false; - } - - var eventListeners = handler.GetInvocationList().Cast>().ToArray(); - - foreach (var eventListener in eventListeners) - { - try - { - Log.DebugIfAttached($"Executing event handler: target '{eventListener.Target}', method '{eventListener.Method.Name}'"); - - await eventListener(sender, e).ConfigureAwait(false); - - Log.DebugIfAttached($"Event handler successfully executed: target '{eventListener.Target}', method '{eventListener.Method.Name}'"); - } - catch (Exception ex) - { - - Log.Error(ex, $"Failed to invoke event handler handler: target '{eventListener.Target}', method '{eventListener.Method.Name}'"); - throw; - } - } - - return true; - } -} \ No newline at end of file diff --git a/src/Orc.ProjectManagement/Extensions/IProjectManagerExtensions.cs b/src/Orc.ProjectManagement/Extensions/IProjectManagerExtensions.cs index 7ad0519c..c65cd9ab 100644 --- a/src/Orc.ProjectManagement/Extensions/IProjectManagerExtensions.cs +++ b/src/Orc.ProjectManagement/Extensions/IProjectManagerExtensions.cs @@ -1,6 +1,7 @@ namespace Orc.ProjectManagement; using System; +using System.Threading.Tasks; public static class IProjectManagerExtensions { @@ -19,4 +20,33 @@ public static class IProjectManagerExtensions var activeProject = projectManager.ActiveProject; return activeProject?.Location; } + + public static Task CloseActiveProjectAsync(this IProjectManager projectManager) + { + var project = projectManager.ActiveProject; + + return project is null + ? Task.FromResult(false) + : projectManager.CloseAsync(project); + } + + public static Task RefreshActiveProjectAsync(this IProjectManager projectManager) + { + var project = projectManager.ActiveProject; + + return project is null + ? Task.FromResult(false) + : projectManager.RefreshAsync(project); + } + + public static Task SaveActiveProjectAsync(this IProjectManager projectManager, string? location = null) + { + var project = projectManager.ActiveProject; + if (project is null) + { + return Task.FromResult(false); + } + + return projectManager.SaveAsync(project, location); + } } diff --git a/src/Orc.ProjectManagement/Managers/IProjectEventType.cs b/src/Orc.ProjectManagement/Managers/IProjectEventType.cs new file mode 100644 index 00000000..58715118 --- /dev/null +++ b/src/Orc.ProjectManagement/Managers/IProjectEventType.cs @@ -0,0 +1,6 @@ +namespace Orc.ProjectManagement; + +public interface IProjectEventType +{ + ProjectEventTypeStage Stage { get; } +} diff --git a/src/Orc.ProjectManagement/Managers/Interfaces/IProjectManager.cs b/src/Orc.ProjectManagement/Managers/Interfaces/IProjectManager.cs index 8475eb9e..317ca3b1 100644 --- a/src/Orc.ProjectManagement/Managers/Interfaces/IProjectManager.cs +++ b/src/Orc.ProjectManagement/Managers/Interfaces/IProjectManager.cs @@ -39,13 +39,16 @@ public interface IProjectManager event AsyncEventHandler? ProjectActivationFailedAsync; Task InitializeAsync(); + [ObsoleteEx(Message = "Use extension method", ReplacementTypeOrMember = "RefreshActiveProjectAsync", RemoveInVersion = "6.0.0")] Task RefreshAsync(); Task RefreshAsync(IProject project); Task LoadAsync(string location); Task LoadInactiveAsync(string location); + [ObsoleteEx(Message = "Use extension method", ReplacementTypeOrMember = "SaveActiveProjectAsync", RemoveInVersion = "6.0.0")] Task SaveAsync(string? location = null); Task SaveAsync(IProject project, string? location = null); + [ObsoleteEx(Message = "Use extension method", ReplacementTypeOrMember = "CloseActiveProjectAsync", RemoveInVersion = "6.0.0")] Task CloseAsync(); Task CloseAsync(IProject project); Task SetActiveProjectAsync(IProject? project); -} \ No newline at end of file +} diff --git a/src/Orc.ProjectManagement/Managers/ProjectActivationEvent.cs b/src/Orc.ProjectManagement/Managers/ProjectActivationEvent.cs new file mode 100644 index 00000000..99674c3d --- /dev/null +++ b/src/Orc.ProjectManagement/Managers/ProjectActivationEvent.cs @@ -0,0 +1,15 @@ +namespace Orc.ProjectManagement; + +using System; + +public class ProjectActivationEvent : IProjectEventType +{ + public ProjectActivationEvent(ProjectEventTypeStage stage, EventArgs eventArgs) + { + Stage = stage; + EventArgs = eventArgs; + } + + public ProjectEventTypeStage Stage { get; } + public EventArgs EventArgs { get; } +} diff --git a/src/Orc.ProjectManagement/Managers/ProjectCloseEvent.cs b/src/Orc.ProjectManagement/Managers/ProjectCloseEvent.cs new file mode 100644 index 00000000..ed32bcba --- /dev/null +++ b/src/Orc.ProjectManagement/Managers/ProjectCloseEvent.cs @@ -0,0 +1,15 @@ +namespace Orc.ProjectManagement; + +using System; + +public class ProjectCloseEvent : IProjectEventType +{ + public ProjectCloseEvent(ProjectEventTypeStage stage, EventArgs eventArgs) + { + Stage = stage; + EventArgs = eventArgs; + } + + public ProjectEventTypeStage Stage { get; } + public EventArgs EventArgs { get; } +} diff --git a/src/Orc.ProjectManagement/Managers/ProjectEventTypeStage.cs b/src/Orc.ProjectManagement/Managers/ProjectEventTypeStage.cs new file mode 100644 index 00000000..a2348a71 --- /dev/null +++ b/src/Orc.ProjectManagement/Managers/ProjectEventTypeStage.cs @@ -0,0 +1,10 @@ +namespace Orc.ProjectManagement; + +public enum ProjectEventTypeStage +{ + Required = 0, + Before = 1, + After = 2, + Cancelled = 3, + Failed = 4 +} diff --git a/src/Orc.ProjectManagement/Managers/ProjectLoadEvent.cs b/src/Orc.ProjectManagement/Managers/ProjectLoadEvent.cs new file mode 100644 index 00000000..3fe6690c --- /dev/null +++ b/src/Orc.ProjectManagement/Managers/ProjectLoadEvent.cs @@ -0,0 +1,15 @@ +namespace Orc.ProjectManagement; + +using System; + +public class ProjectLoadEvent : IProjectEventType +{ + public ProjectLoadEvent(ProjectEventTypeStage stage, EventArgs eventArgs) + { + Stage = stage; + EventArgs = eventArgs; + } + + public ProjectEventTypeStage Stage { get; } + public EventArgs EventArgs { get; } +} diff --git a/src/Orc.ProjectManagement/Managers/ProjectManager.cs b/src/Orc.ProjectManagement/Managers/ProjectManager.cs index 42801157..150e2418 100644 --- a/src/Orc.ProjectManagement/Managers/ProjectManager.cs +++ b/src/Orc.ProjectManagement/Managers/ProjectManager.cs @@ -109,8 +109,221 @@ void INeedCustomInitialization.Initialize() public event AsyncEventHandler? ProjectActivationCanceledAsync; public event AsyncEventHandler? ProjectActivationFailedAsync; - [Time] - public async Task InitializeAsync() + protected virtual async Task RaiseEventAsync(IProjectEventType projectEventType) + { + switch (projectEventType) + { + case ProjectLoadEvent projectLoadEvent: + switch (projectLoadEvent.Stage) + { + case ProjectEventTypeStage.Before: + await SafeInvokeWithTimeoutAsync(ProjectLoadingAsync, nameof(ProjectLoadingAsync), this, (ProjectCancelEventArgs)projectLoadEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.After: + await SafeInvokeWithTimeoutAsync(ProjectLoadedAsync, nameof(ProjectLoadedAsync), this, (ProjectEventArgs)projectLoadEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Cancelled: + await SafeInvokeWithTimeoutAsync(ProjectLoadingCanceledAsync, nameof(ProjectLoadingCanceledAsync), this, (ProjectLocationEventArgs)projectLoadEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Failed: + await SafeInvokeWithTimeoutAsync(ProjectLoadingFailedAsync, nameof(ProjectLoadingFailedAsync), this, (ProjectErrorEventArgs)projectLoadEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + break; + case ProjectSaveEvent projectSaveEvent: + switch (projectSaveEvent.Stage) + { + case ProjectEventTypeStage.Before: + await SafeInvokeWithTimeoutAsync(ProjectSavingAsync, nameof(ProjectSavingAsync), this, (ProjectCancelEventArgs)projectSaveEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.After: + await SafeInvokeWithTimeoutAsync(ProjectSavedAsync, nameof(ProjectSavedAsync), this, (ProjectEventArgs)projectSaveEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Cancelled: + await SafeInvokeWithTimeoutAsync(ProjectSavingCanceledAsync, nameof(ProjectSavingCanceledAsync), this, (ProjectEventArgs)projectSaveEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Failed: + await SafeInvokeWithTimeoutAsync(ProjectSavingFailedAsync, nameof(ProjectSavingFailedAsync), this, (ProjectErrorEventArgs)projectSaveEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + break; + case ProjectRefreshEvent projectRefreshEvent: + switch (projectRefreshEvent.Stage) + { + case ProjectEventTypeStage.Required: + await SafeInvokeWithTimeoutAsync(ProjectRefreshRequiredAsync, nameof(ProjectRefreshRequiredAsync), this, (ProjectEventArgs)projectRefreshEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Before: + await SafeInvokeWithTimeoutAsync(ProjectRefreshingAsync, nameof(ProjectRefreshingAsync), this, (ProjectCancelEventArgs)projectRefreshEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.After: + await SafeInvokeWithTimeoutAsync(ProjectRefreshedAsync, nameof(ProjectRefreshedAsync), this, (ProjectEventArgs)projectRefreshEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Cancelled: + await SafeInvokeWithTimeoutAsync(ProjectRefreshingCanceledAsync, nameof(ProjectRefreshingCanceledAsync), this, (ProjectRefreshErrorEventArgs)projectRefreshEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Failed: + await SafeInvokeWithTimeoutAsync(ProjectRefreshingFailedAsync, nameof(ProjectRefreshingFailedAsync), this, (ProjectErrorEventArgs)projectRefreshEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + break; + case ProjectCloseEvent projectCloseEvent: + switch (projectCloseEvent.Stage) + { + case ProjectEventTypeStage.Before: + await SafeInvokeWithTimeoutAsync(ProjectClosingAsync, nameof(ProjectClosingAsync), this, (ProjectCancelEventArgs)projectCloseEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.After: + await SafeInvokeWithTimeoutAsync(ProjectClosedAsync, nameof(ProjectClosedAsync), this, (ProjectEventArgs)projectCloseEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Cancelled: + await SafeInvokeWithTimeoutAsync(ProjectClosingCanceledAsync, nameof(ProjectClosingCanceledAsync), this, (ProjectEventArgs)projectCloseEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + break; + case ProjectActivationEvent projectActivationEvent: + switch (projectActivationEvent.Stage) + { + case ProjectEventTypeStage.Before: + await SafeInvokeWithTimeoutAsync(ProjectActivationAsync, nameof(ProjectActivationAsync), this, (ProjectUpdatingCancelEventArgs)projectActivationEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.After: + await SafeInvokeWithTimeoutAsync(ProjectActivatedAsync, nameof(ProjectActivatedAsync), this, (ProjectUpdatedEventArgs)projectActivationEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Cancelled: + await SafeInvokeWithTimeoutAsync(ProjectActivationCanceledAsync,nameof(ProjectActivationCanceledAsync), this, (ProjectActivationEventArgs)projectActivationEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + case ProjectEventTypeStage.Failed: + await SafeInvokeWithTimeoutAsync(ProjectActivationFailedAsync, nameof(ProjectActivationFailedAsync), this, (ProjectErrorEventArgs)projectActivationEvent.EventArgs, DefaultTimeout) + .ConfigureAwait(false); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private async Task SafeInvokeWithTimeoutAsync(AsyncEventHandler? handler, string eventName, object sender, TEventArgs e, int timeout) + where TEventArgs : EventArgs + { + if (handler is null) + { + return false; + } + + try + { + Log.DebugIfAttached($"Handling project management event '{eventName}'"); + + var task = SafeInvokeAsync(eventName, handler, sender, e); + var completedTask = await Task.WhenAny(task, Task.Delay(timeout)); + + if (completedTask != task) + { + Log.Warning($"Handling project management event '{eventName}' has timed out"); + } + else + { + Log.DebugIfAttached($"Handled project management event '{eventName}'"); + } + + return await task.ConfigureAwait(false); + } + catch (Exception ex) + { + Log.Error(ex, "Failed to handle project management event '{eventName}'"); + throw; + } + } + + private async Task SafeInvokeAsync(string eventName, AsyncEventHandler? handler, object sender, TEventArgs e) + where TEventArgs : EventArgs + { + if (handler is null) + { + return false; + } + + var eventListeners = handler.GetInvocationList().Cast>().ToArray(); + + foreach (var eventListener in eventListeners) + { + try + { + Log.DebugIfAttached($"Executing event handler: target '{eventListener.Target}', method '{eventListener.Method.Name}'"); + + await InvokeEventListenerAsync(eventName, sender, e, eventListener); + + Log.DebugIfAttached($"Event handler successfully executed: target '{eventListener.Target}', method '{eventListener.Method.Name}'"); + } + catch (Exception ex) + { + + Log.Error(ex, $"Failed to invoke event handler handler: target '{eventListener.Target}', method '{eventListener.Method.Name}'"); + throw; + } + } + + return true; + } + + protected virtual async Task InvokeEventListenerAsync(string eventName, object sender, TEventArgs args, AsyncEventHandler eventListener) + where TEventArgs : EventArgs + { + await eventListener(sender, args).ConfigureAwait(false); + } + + public virtual async Task InitializeAsync() { var locations = (from location in await _projectInitializer.GetInitialLocationsAsync() where !string.IsNullOrWhiteSpace(location) @@ -123,6 +336,7 @@ public async Task InitializeAsync() } } + [ObsoleteEx(Message = "Use extension method", ReplacementTypeOrMember = "RefreshActiveProjectAsync", RemoveInVersion = "6.0.0")] public Task RefreshAsync() { var project = ActiveProject; @@ -132,16 +346,14 @@ public Task RefreshAsync() : RefreshAsync(project); } - [Time] - public Task RefreshAsync(IProject project) + public virtual Task RefreshAsync(IProject project) { ArgumentNullException.ThrowIfNull(project); return SynchronizeProjectOperationAsync(project.Location, () => SyncedRefreshAsync(project)); } - [Time] - public Task LoadAsync(string location) + public virtual Task LoadAsync(string location) { Argument.IsNotNullOrWhitespace("location", location); @@ -158,8 +370,7 @@ public Task LoadAsync(string location) }); } - [Time] - public Task LoadInactiveAsync(string location) + public virtual Task LoadInactiveAsync(string location) { Argument.IsNotNullOrWhitespace("location", location); @@ -171,6 +382,7 @@ public Task LoadInactiveAsync(string location) }); } + [ObsoleteEx(Message = "Use extension method", ReplacementTypeOrMember = "SaveActiveProjectAsync", RemoveInVersion = "6.0.0")] public Task SaveAsync(string? location = null) { var project = ActiveProject; @@ -183,8 +395,7 @@ public Task SaveAsync(string? location = null) return SaveAsync(project, location); } - [Time] - public async Task SaveAsync(IProject project, string? location = null) + public virtual async Task SaveAsync(IProject project, string? location = null) { if (string.IsNullOrWhiteSpace(location)) { @@ -200,6 +411,7 @@ public async Task SaveAsync(IProject project, string? location = null) return await SynchronizeProjectOperationAsync(location, () => SyncedSaveAsync(project, location)); } + [ObsoleteEx(Message = "Use extension method", ReplacementTypeOrMember = "CloseActiveProjectAsync", RemoveInVersion = "6.0.0")] public Task CloseAsync() { var project = ActiveProject; @@ -209,8 +421,7 @@ public Task CloseAsync() : CloseAsync(project); } - [Time] - public Task CloseAsync(IProject project) + public virtual Task CloseAsync(IProject project) { ArgumentNullException.ThrowIfNull(project); @@ -219,8 +430,7 @@ public Task CloseAsync(IProject project) return SynchronizeProjectOperationAsync(location, () => SyncedCloseAsync(project)); } - [Time] - public async Task SetActiveProjectAsync(IProject? project) + public virtual async Task SetActiveProjectAsync(IProject? project) { using (await _commonAsyncLock.LockAsync()) { @@ -248,9 +458,7 @@ public async Task SetActiveProjectAsync(IProject? project) _projectStateSetter.SetProjectDeactivating(activeProject?.Location, true); _projectStateSetter.SetProjectActivating(project?.Location, true); - await ProjectActivationAsync - .SafeInvokeWithTimeoutAsync(nameof(ProjectActivationAsync), this, eventArgs, DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectActivationEvent(ProjectEventTypeStage.Before, eventArgs)).ConfigureAwait(false); if (eventArgs.Cancel) { @@ -260,9 +468,7 @@ await ProjectActivationAsync _projectStateSetter.SetProjectActivating(project?.Location, false); - await ProjectActivationCanceledAsync - .SafeInvokeWithTimeoutAsync(nameof(ProjectActivationCanceledAsync), this, new ProjectActivationEventArgs(project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectActivationEvent(ProjectEventTypeStage.Cancelled, new ProjectActivationEventArgs(project))).ConfigureAwait(false); return false; } @@ -285,9 +491,7 @@ await ProjectActivationCanceledAsync : "Failed to deactivate currently active project"); _projectStateSetter.SetProjectActivating(project?.Location ?? string.Empty, false); - await ProjectActivationFailedAsync - .SafeInvokeWithTimeoutAsync(nameof(ProjectActivationFailedAsync), this, new ProjectErrorEventArgs(project, exception), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectActivationEvent(ProjectEventTypeStage.Failed, new ProjectErrorEventArgs(project, exception))).ConfigureAwait(false); return false; } @@ -295,9 +499,7 @@ await ProjectActivationFailedAsync _projectStateSetter.SetProjectDeactivating(activeProject?.Location, false); _projectStateSetter.SetProjectActivating(project?.Location, false); - await ProjectActivatedAsync - .SafeInvokeWithTimeoutAsync(nameof(ProjectActivatedAsync), this, new ProjectUpdatedEventArgs(activeProject, project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectActivationEvent(ProjectEventTypeStage.After, new ProjectUpdatedEventArgs(activeProject, project))).ConfigureAwait(false); Log.Debug(project is not null ? $"Activating project '{project.Location}' was canceled" @@ -436,8 +638,7 @@ private async Task SyncedRefreshAsync(IProject project) _projectStateSetter.SetProjectRefreshing(projectLocation, true, isRefreshingActiveProject); var cancelEventArgs = new ProjectCancelEventArgs(project); - await ProjectRefreshingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectRefreshingAsync), this, cancelEventArgs, DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectRefreshEvent(ProjectEventTypeStage.Before, cancelEventArgs)).ConfigureAwait(false); Exception? error = null; IValidationContext? validationContext = null; @@ -448,8 +649,7 @@ await ProjectRefreshingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectRefreshing { _projectStateSetter.SetProjectRefreshing(projectLocation, false); - await ProjectRefreshingCanceledAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectRefreshingCanceledAsync), this, new ProjectRefreshErrorEventArgs(project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectRefreshEvent(ProjectEventTypeStage.Cancelled, new ProjectLocationEventArgs(projectLocation))).ConfigureAwait(false); return false; } @@ -485,8 +685,7 @@ await ProjectRefreshingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectRefreshing // Note: we disable IsRefreshingActiveProject at Activated event, that is why isActiveProject == false _projectStateSetter.SetProjectRefreshing(projectLocation, true, false); - await ProjectRefreshedAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectRefreshedAsync), this, new ProjectEventArgs(loadedProject), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectRefreshEvent(ProjectEventTypeStage.After, new ProjectEventArgs(loadedProject))).ConfigureAwait(false); ; if (isRefreshingActiveProject) { @@ -513,8 +712,7 @@ await ProjectRefreshingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectRefreshing new ProjectException(project, $"Failed to load project from location '{projectLocation}' while refreshing.", error), validationContext); - await ProjectRefreshingFailedAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectRefreshingFailedAsync), this, eventArgs, DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectRefreshEvent(ProjectEventTypeStage.Failed, eventArgs)).ConfigureAwait(false); return false; } @@ -549,8 +747,7 @@ await ProjectRefreshingFailedAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectRefr var cancelEventArgs = new ProjectCancelEventArgs(location); - await ProjectLoadingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectLoadingAsync), this, cancelEventArgs, DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectLoadEvent(ProjectEventTypeStage.Before, cancelEventArgs)).ConfigureAwait(false); if (cancelEventArgs.Cancel) { @@ -558,8 +755,7 @@ await ProjectLoadingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectLoadingAsync) _projectStateSetter.SetProjectLoading(location, false); - await ProjectLoadingCanceledAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectLoadingCanceledAsync), this, new ProjectLocationEventArgs(location), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectLoadEvent(ProjectEventTypeStage.Cancelled, new ProjectLocationEventArgs(location))).ConfigureAwait(false); return null; } @@ -608,16 +804,14 @@ await ProjectLoadingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectLoadingAsync) { _projectStateSetter.SetProjectLoading(location, false); - await ProjectLoadingFailedAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectLoadingFailedAsync), this, new ProjectErrorEventArgs(location, error, validationContext), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectLoadEvent(ProjectEventTypeStage.Failed, new ProjectErrorEventArgs(location, error, validationContext))).ConfigureAwait(false); return null; } _projectStateSetter.SetProjectLoading(location, false); - await ProjectLoadedAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectLoadedAsync), this, new ProjectEventArgs(project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectLoadEvent(ProjectEventTypeStage.After, new ProjectEventArgs(project))).ConfigureAwait(false); Log.Info("Loaded project from '{0}'", location); } @@ -643,16 +837,14 @@ private async Task SyncedSaveAsync(IProject project, string location) _projectStateSetter.SetProjectSaving(location, true); var cancelEventArgs = new ProjectCancelEventArgs(project); - await ProjectSavingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectSavingAsync), this, cancelEventArgs, DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectSaveEvent(ProjectEventTypeStage.Before, cancelEventArgs)).ConfigureAwait(false); if (cancelEventArgs.Cancel) { _projectStateSetter.SetProjectSaving(location, false); Log.Debug("Canceled saving of project to '{0}'", location); - await ProjectSavingCanceledAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectSavingCanceledAsync), this, new ProjectEventArgs(project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectSaveEvent(ProjectEventTypeStage.Cancelled, new ProjectEventArgs(project))).ConfigureAwait(false); return false; } @@ -674,8 +866,7 @@ await ProjectSavingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectSavingAsync), Log.Error(error, "Failed to save project '{0}' to '{1}'", project, location); - await ProjectSavingFailedAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectSavingFailedAsync), this, new ProjectErrorEventArgs(project, error), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectSaveEvent(ProjectEventTypeStage.Failed, new ProjectErrorEventArgs(project, error))).ConfigureAwait(false); return false; } @@ -686,16 +877,14 @@ await ProjectSavingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectSavingAsync), Log.Warning("Not saved project '{0}' to '{1}'", project, location); - await ProjectSavingFailedAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectSavingFailedAsync), this, new ProjectErrorEventArgs(project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectSaveEvent(ProjectEventTypeStage.Failed, new ProjectErrorEventArgs(project))).ConfigureAwait(false); return false; } _projectStateSetter.SetProjectSaving(location, false); - await ProjectSavedAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectSavedAsync), this, new ProjectEventArgs(project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectSaveEvent(ProjectEventTypeStage.After, new ProjectEventArgs(project))).ConfigureAwait(false); var projectString = project.ToString(); Log.Info("Saved project '{0}' to '{1}'", projectString, location); @@ -713,16 +902,14 @@ private async Task SyncedCloseAsync(IProject project) _projectStateSetter.SetProjectClosing(project.Location, true); var cancelEventArgs = new ProjectCancelEventArgs(project); - await ProjectClosingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectClosingAsync), this, cancelEventArgs, DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectCloseEvent(ProjectEventTypeStage.Before, cancelEventArgs)).ConfigureAwait(false); if (cancelEventArgs.Cancel) { _projectStateSetter.SetProjectClosing(project.Location, false); Log.Debug("Canceled closing project '{0}'", project); - await ProjectClosingCanceledAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectClosingCanceledAsync), this, new ProjectEventArgs(project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectCloseEvent(ProjectEventTypeStage.Cancelled, new ProjectEventArgs(project))).ConfigureAwait(false); return false; } @@ -735,8 +922,7 @@ await ProjectClosingAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectClosingAsync) UnregisterProject(project); _projectStateSetter.SetProjectClosing(project.Location, false); - await ProjectClosedAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectClosedAsync), this, new ProjectEventArgs(project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectCloseEvent(ProjectEventTypeStage.After, new ProjectEventArgs(project))).ConfigureAwait(false); Log.Info("Closed project '{0}'", project); @@ -855,8 +1041,7 @@ private async void OnProjectRefresherUpdated(object? sender, ProjectLocationEven if (_projects.TryGetValue(projectLocation, out var project)) { // Note: not sure why we still need this - await ProjectRefreshRequiredAsync.SafeInvokeWithTimeoutAsync(nameof(ProjectRefreshRequiredAsync), this, new ProjectEventArgs(project), DefaultTimeout) - .ConfigureAwait(false); + await RaiseEventAsync(new ProjectRefreshEvent(ProjectEventTypeStage.Required, new ProjectEventArgs(project))).ConfigureAwait(false); } else { diff --git a/src/Orc.ProjectManagement/Managers/ProjectRefreshEvent.cs b/src/Orc.ProjectManagement/Managers/ProjectRefreshEvent.cs new file mode 100644 index 00000000..b94306df --- /dev/null +++ b/src/Orc.ProjectManagement/Managers/ProjectRefreshEvent.cs @@ -0,0 +1,15 @@ +namespace Orc.ProjectManagement; + +using System; + +public class ProjectRefreshEvent : IProjectEventType +{ + public ProjectRefreshEvent(ProjectEventTypeStage stage, EventArgs eventArgs) + { + Stage = stage; + EventArgs = eventArgs; + } + + public ProjectEventTypeStage Stage { get; } + public EventArgs EventArgs { get; } +} diff --git a/src/Orc.ProjectManagement/Managers/ProjectSaveEvent.cs b/src/Orc.ProjectManagement/Managers/ProjectSaveEvent.cs new file mode 100644 index 00000000..616fbc1a --- /dev/null +++ b/src/Orc.ProjectManagement/Managers/ProjectSaveEvent.cs @@ -0,0 +1,15 @@ +namespace Orc.ProjectManagement; + +using System; + +public class ProjectSaveEvent : IProjectEventType +{ + public ProjectSaveEvent(ProjectEventTypeStage stage, EventArgs eventArgs) + { + Stage = stage; + EventArgs = eventArgs; + } + + public ProjectEventTypeStage Stage { get; } + public EventArgs EventArgs { get; } +} diff --git a/src/SolutionAssemblyInfo.cs b/src/SolutionAssemblyInfo.cs index 9011ac8a..5eaaeb40 100644 --- a/src/SolutionAssemblyInfo.cs +++ b/src/SolutionAssemblyInfo.cs @@ -8,6 +8,6 @@ [assembly: AssemblyCompany("WildGums")] [assembly: AssemblyVersion("5.0.0")] [assembly: AssemblyFileVersion("5.0.0")] -[assembly: AssemblyInformationalVersion("3.1.0-alpha.1")] -[assembly: AssemblyCopyright("Copyright © WildGums 2010 - 2018")] +[assembly: AssemblyInformationalVersion("5.1.0-alpha.1")] +[assembly: AssemblyCopyright("Copyright © WildGums 2010 - 2024")]