Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce MSBuild tasks programability #12364

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions OrchardCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Media.AmazonS3"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.ArchiveLater", "src\OrchardCore.Modules\OrchardCore.ArchiveLater\OrchardCore.ArchiveLater.csproj", "{190C4BEB-C506-4F7F-BDCA-93F3C1C221BC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Build.Tasks", "src\OrchardCore.Build.Tasks\OrchardCore.Build.Tasks.csproj", "{FE8B2FE3-A35D-486B-99D4-9592455CF12C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Features.Core", "src\OrchardCore\OrchardCore.Features.Core\OrchardCore.Features.Core.csproj", "{122EC0DA-A593-4038-BD21-2D4A7061F348}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Search", "src\OrchardCore.Modules\OrchardCore.Search\OrchardCore.Search.csproj", "{7BDF280B-70B7-4AFC-A6F7-B5759DCA2A2C}"
Expand Down Expand Up @@ -522,6 +524,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Email.Smtp", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Rules.Core", "src\OrchardCore\OrchardCore.Rules.Core\OrchardCore.Rules.Core.csproj", "{4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Tests.BuildTasks", "test\OrchardCore.Tests.BuildTasks\OrchardCore.Tests.BuildTasks.csproj", "{94D5435A-2A42-484A-9B89-0E48A0430DB3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1314,6 +1318,10 @@ Global
{190C4BEB-C506-4F7F-BDCA-93F3C1C221BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{190C4BEB-C506-4F7F-BDCA-93F3C1C221BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{190C4BEB-C506-4F7F-BDCA-93F3C1C221BC}.Release|Any CPU.Build.0 = Release|Any CPU
{FE8B2FE3-A35D-486B-99D4-9592455CF12C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FE8B2FE3-A35D-486B-99D4-9592455CF12C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE8B2FE3-A35D-486B-99D4-9592455CF12C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE8B2FE3-A35D-486B-99D4-9592455CF12C}.Release|Any CPU.Build.0 = Release|Any CPU
{122EC0DA-A593-4038-BD21-2D4A7061F348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{122EC0DA-A593-4038-BD21-2D4A7061F348}.Debug|Any CPU.Build.0 = Debug|Any CPU
{122EC0DA-A593-4038-BD21-2D4A7061F348}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -1600,6 +1608,7 @@ Global
{38F43FA0-5BA8-4D6B-8F66-C708D590EF76} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{FF1C550C-6D30-499A-AF11-68DE7C8B6869} = {90030E85-0C4F-456F-B879-443E8A3F220D}
{190C4BEB-C506-4F7F-BDCA-93F3C1C221BC} = {90030E85-0C4F-456F-B879-443E8A3F220D}
{FE8B2FE3-A35D-486B-99D4-9592455CF12C} = {184139CF-C4AB-4FBE-AE19-54C8B3FE5C5E}
{122EC0DA-A593-4038-BD21-2D4A7061F348} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{7BDF280B-70B7-4AFC-A6F7-B5759DCA2A2C} = {90030E85-0C4F-456F-B879-443E8A3F220D}
{307AA9CB-D62E-4E30-B715-53E3BF535D94} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
Expand Down
53 changes: 53 additions & 0 deletions src/OrchardCore.Build.Tasks/CopyTranslationFilesTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Security.Cryptography;
using Microsoft.Build.Framework;
using MSBuildTask = Microsoft.Build.Utilities.Task;

namespace OrchardCore.Build.Tasks;

public class CopyTranslationFilesTask : MSBuildTask
{
public string SourceFilePath { get; set; }

[Required]
public string DestinationFolderPath { get; set; }

public override bool Execute()
{
if (string.IsNullOrEmpty(SourceFilePath))
{
return true;
}

if (!Directory.Exists(DestinationFolderPath))
{
Directory.CreateDirectory(DestinationFolderPath);
}

var fileInfo = new FileInfo(SourceFilePath);
var destinationFilePath = Path.Combine(DestinationFolderPath, fileInfo.Name);

if (File.Exists(destinationFilePath))
{
// Skip unchanged files
var sourceFileHash = GetFileHashAsync(fileInfo.FullName);
var destinationFileHash = GetFileHashAsync(destinationFilePath);

if (sourceFileHash.Equals(destinationFileHash, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}

fileInfo.CopyTo(destinationFilePath, overwrite: true);

return true;
}

private static string GetFileHashAsync(string fileName)
{
using var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
var hash = SHA256.HashData(stream);

return Convert.ToBase64String(hash);
}
}
25 changes: 25 additions & 0 deletions src/OrchardCore.Build.Tasks/CreateFolderTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using MSBuildTask = Microsoft.Build.Utilities.Task;

namespace OrchardCore.Build.Tasks;

public abstract class CreateWebFolderTask : MSBuildTask
{
public abstract string FolderName { get; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public abstract string FolderName { get; }
public abstract string FolderPath { get; }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a folder name, not a path, such as wwwroot

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How it's used is a path. Passing wwwroot/some/sub/folder to it would work too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, it could be


public override bool Execute()
{
if (!Directory.Exists(FolderName))
{
Directory.CreateDirectory(FolderName);

var dummyFilePath = Path.Combine(FolderName, ".placeholder");

if (!File.Exists(dummyFilePath))
{
File.WriteAllText(dummyFilePath, string.Empty);
}
}

return true;
}
}
6 changes: 6 additions & 0 deletions src/OrchardCore.Build.Tasks/CreateLocalizationFolderTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace OrchardCore.Build.Tasks;

public class CreateLocalizationFolderTask : CreateWebFolderTask
{
public override string FolderName => "Localization";
}
6 changes: 6 additions & 0 deletions src/OrchardCore.Build.Tasks/CreateWebRootFolderTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace OrchardCore.Build.Tasks;

public class CreateWebRootFolderTask : CreateWebFolderTask
{
public override string FolderName => "wwwroot";
}
15 changes: 15 additions & 0 deletions src/OrchardCore.Build.Tasks/OrchardCore.Build.Tasks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="..\OrchardCore.Build\OrchardCore.Commons.targets" />
<Import Project="..\OrchardCore.Build\Dependencies.props" />

<PropertyGroup>
<TargetFramework>$(CommonTargetFrameworks)</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions src/OrchardCore.Build/Dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<PackageManagement Include="MailKit" Version="4.5.0" />
<PackageManagement Include="Markdig" Version="0.36.2" />
<PackageManagement Include="MessagePack" Version="2.2.60" />
<PackageManagement Include="Microsoft.Build.Utilities.Core" Version="17.3.1" />
<PackageManagement Include="Microsoft.Extensions.Azure" Version="1.7.3" />
<PackageManagement Include="Microsoft.Extensions.Http.Resilience" Version="8.4.0" />
<PackageManagement Include="Microsoft.Identity.Web" Version="2.17.5" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<_OrchardCoreBuildTasksAssembly>$(ProjectDir)..\..\OrchardCore.Build.Tasks\bin\$(Configuration)\$(CommonTargetFrameworks)\OrchardCore.Build.Tasks.dll</_OrchardCoreBuildTasksAssembly>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only work if the DLL already exists but if you build the solution for the first time, it won't. Won't this cause issues?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be there during the build, if it's not there the GitHub Workflow will not pass

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but this line will be executed even when one just opens the solution and anything else that MSBuild does, not just during a build.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only work if the DLL already exists but if you build the solution for the first time, it won't. Won't this cause issues?>

This is weird, could you please try the following:

  1. Add this reference <ProjectReference Include="..\OrchardCore.Build.Tasks\OrchardCore.Build.Tasks.csproj" /> to OC.Cms.Web
  2. Open CMD and locate the web project
  3. Run dotnet clean
  4. Run dotnet build

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please make sure you test all of such scenarios? And in VS, the solution should still work with F5.

</PropertyGroup>

<UsingTask TaskName="CopyTranslationFilesTask" AssemblyFile="$(_OrchardCoreBuildTasksAssembly)" />
<UsingTask TaskName="CreateLocalizationFolderTask" AssemblyFile="$(_OrchardCoreBuildTasksAssembly)" />
<UsingTask TaskName="CreateWebRootFolderTask" AssemblyFile="$(_OrchardCoreBuildTasksAssembly)" />

<ItemGroup>
<Compile Remove="App_Data\**;Localization\**" />
Expand All @@ -12,25 +20,19 @@
<RelativePath>App_Data\%(RecursiveDir)%(Filename)%(Extension)</RelativePath>
</ResolvedFileToPublish>
</ItemGroup>


<Target Name="MakeWebRootFolder" BeforeTargets="MakeLocalizationFolder" Condition="!Exists('wwwroot/.placeholder')">
<WriteLinesToFile Lines="" Encoding="Unicode" File="wwwroot/.placeholder" />
<Target Name="MakeWebRootFolder" BeforeTargets="MakeLocalizationFolder">
<CreateWebRootFolderTask />
</Target>

<Target Name="MakeLocalizationFolder" BeforeTargets="Build" Condition="!Exists('Localization/.placeholder')">
<WriteLinesToFile Lines="" Encoding="Unicode" File="Localization/.placeholder" />
<Target Name="MakeLocalizationFolder" BeforeTargets="Build">
<CreateLocalizationFolderTask />
</Target>

<Target
Name="CopyPackageTranslationFiles"
AfterTargets="Build">
<Target Name="CopyPackageTranslationFiles" AfterTargets="Build">
<Message Text="Copying translation files: $(MSBuildProjectName)" Importance="high" />
<Copy
SourceFiles="%(PackageTranslationFiles.FullPath)"
DestinationFolder="Localization\%(RecursiveDir)"
Condition="'@(PackageTranslationFiles)' != ''"
SkipUnchangedFiles="true"
OverwriteReadOnlyFiles="false" />
<CopyTranslationFilesTask Condition="'@(PackageTranslationFiles)' != ''" SourceFilePath="%(PackageTranslationFiles.FullPath)" DestinationFolderPath="Localization\%(RecursiveDir)" />
</Target>

<Target Name="PublishTranslationFiles" BeforeTargets="GetCopyToPublishDirectoryItems">
Expand Down
51 changes: 51 additions & 0 deletions test/OrchardCore.Tests.BuildTasks/CopyTranslationFilesTaskTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace OrchardCore.Build.Tasks.Tests;

public class CopyTranslationFilesTaskTests
{
private const string LocalizationFolder = "Localization";

private readonly Mock<IBuildEngine> _buildEngine;
private readonly List<BuildErrorEventArgs> _errors = [];

public CopyTranslationFilesTaskTests()
{
_buildEngine = new Mock<IBuildEngine>();
_buildEngine.Setup(be => be.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
.Callback<BuildErrorEventArgs>(e => _errors.Add(e));
}

[Theory]
[InlineData("ar.po")]
[InlineData("fr.po")]
public void ShouldCopyTranslationFilesToLocalizationFolder(string file)
{
// Arrange
var filePath = Path.Combine(LocalizationFolder, file);
var task = new CopyTranslationFilesTask
{
BuildEngine = _buildEngine.Object,
SourceFilePath = file,
DestinationFolderPath = LocalizationFolder,
};

TryCreateLocalizationFolder();

// Act
var result = task.Execute();

// Assert
Assert.True(result);
Assert.Empty(_errors);
Assert.True(File.Exists(filePath));
}

private static void TryCreateLocalizationFolder()
{
if (Directory.Exists(LocalizationFolder))
{
Directory.Delete(LocalizationFolder, recursive: true);
}

Directory.CreateDirectory(LocalizationFolder);
}
}
86 changes: 86 additions & 0 deletions test/OrchardCore.Tests.BuildTasks/CreateWebRootFolderTaskTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
namespace OrchardCore.Build.Tasks.Tests;

public class CreateWebRootFolderTaskTests
{
private const string WebRootFolder = "wwwroot";

private static readonly string _placeholderFile = $"{WebRootFolder}\\.placeholder";

private readonly Mock<IBuildEngine> _buildEngine;
private readonly List<BuildErrorEventArgs> _errors = [];

public CreateWebRootFolderTaskTests()
{
_buildEngine = new Mock<IBuildEngine>();
_buildEngine.Setup(be => be.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
.Callback<BuildErrorEventArgs>(e => _errors.Add(e));

TryDeleteLocalizationFolder();
}

[Fact]
public void ShouldCreateLocalizationFolder()
{
// Arrange
var task = new CreateWebRootFolderTask
{
BuildEngine = _buildEngine.Object
};

// Act
var result = task.Execute();

// Assert
Assert.True(result);
Assert.Empty(_errors);
Assert.True(Directory.Exists(WebRootFolder));
Assert.True(File.Exists(_placeholderFile));
}

[Fact]
public void ShouldNotCreateLocalizationFolderIfExists()
{
// Arrange
var task = new CreateWebRootFolderTask
{
BuildEngine = _buildEngine.Object
};
Directory.CreateDirectory(WebRootFolder);

var createdTime1 = new DirectoryInfo(_placeholderFile).CreationTimeUtc;

// Act & Assert
task.Execute();
Assert.True(Directory.Exists(WebRootFolder));

var createdTime2 = new DirectoryInfo(_placeholderFile).CreationTimeUtc;
Assert.Equal(createdTime1, createdTime2);
}

[Fact]
public void IgnorePlaceholderFileIfExists()
{
// Arrange
var task = new CreateWebRootFolderTask
{
BuildEngine = _buildEngine.Object
};

// Act & Assert
task.Execute();

var createdTime1 = new FileInfo(_placeholderFile).CreationTimeUtc;
task.Execute();

var createdTime2 = new FileInfo(_placeholderFile).CreationTimeUtc;
Assert.Equal(createdTime1, createdTime2);
}

private static void TryDeleteLocalizationFolder()
{
if (Directory.Exists(WebRootFolder))
{
Directory.Delete(WebRootFolder, true);
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this under the test solution folder.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was there, but I'd like to separate them into another project to make it clear that's not related to the actual OC tests project

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All tests and even benchmarks are there, so this is suitable (and it's already in the same file system folder).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarks in a separated project, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand, sorry.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benchmarks in a separate project OrchardCore.Benchmarks too, so we don't want not mixed things

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm talking about moving this project to the test solution folder. Not move it into a different project.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed that while I was preparing a demo

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="..\..\src\OrchardCore.Build\Dependencies.props" />

<PropertyGroup>
<TargetFrameworks>$(CommonTargetFrameworks)</TargetFrameworks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't be needed if #15764 is merged first.

</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\OrchardCore.Build.Tasks\OrchardCore.Build.Tasks.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.analyzers" />
<PackageReference Include="xunit.runner.visualstudio" />
<DotNetCliToolReference Include="dotnet-xunit" Version="$(DotNetXunitVersion)" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="ar.po">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="fr.po">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>

<Import Project="..\..\src\OrchardCore.Build\OrchardCore.Commons.targets" />

</Project>
6 changes: 6 additions & 0 deletions test/OrchardCore.Tests.BuildTasks/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
global using System;
global using System.Collections.Generic;
global using System.IO;
global using Microsoft.Build.Framework;
global using Moq;
global using Xunit;
3 changes: 3 additions & 0 deletions test/OrchardCore.Tests.BuildTasks/ar.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
msgctxt "OrchardCore.Tests.Localization.PortableObjectStringLocalizerFactoryTests.Model"
msgid "Hello"
msgstr "مرحبا"
3 changes: 3 additions & 0 deletions test/OrchardCore.Tests.BuildTasks/fr.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
msgctxt "OrchardCore.Tests.Localization.PortableObjectStringLocalizerFactoryTests.Model"
msgid "Hello"
msgstr "Bonjour"