diff --git a/src/DotNet.ReproducibleBuilds/DotNet.ReproducibleBuilds.props b/src/DotNet.ReproducibleBuilds/DotNet.ReproducibleBuilds.props index a0d6a24..6897c05 100644 --- a/src/DotNet.ReproducibleBuilds/DotNet.ReproducibleBuilds.props +++ b/src/DotNet.ReproducibleBuilds/DotNet.ReproducibleBuilds.props @@ -12,17 +12,23 @@ - + true - + true - + true @@ -32,17 +38,23 @@ true - + true - + true - + true diff --git a/tests/DotNet.ReproducibleBuilds.Tests/ContinuousIntegrationTests.cs b/tests/DotNet.ReproducibleBuilds.Tests/ContinuousIntegrationTests.cs new file mode 100644 index 0000000..b9873a0 --- /dev/null +++ b/tests/DotNet.ReproducibleBuilds.Tests/ContinuousIntegrationTests.cs @@ -0,0 +1,65 @@ +using FluentAssertions; +using Microsoft.Build.Utilities.ProjectCreation; + +namespace DotNet.ReproducibleBuilds.Tests; + +public class ContinuousIntegrationTests : TestBase +{ + private const string ContinuousIntegrationBuild = "ContinuousIntegrationBuild"; + + [Theory] + [InlineData(null, "")] + [InlineData(true, "true")] + [InlineData(false, "false")] + public void RespectsSetValue(bool? value, string expected) + { + using EnvironmentVariableSuppressor hostSuppressor = new("TF_BUILD"); // Suppress our own CI provider variables (i.e. Azure DevOps) + + ProjectCreator.Templates + .ReproducibleBuildProject(GetRandomFile(".csproj")) + .PropertyGroup() + .Property(ContinuousIntegrationBuild, value?.ToLowerInvariant()) + .Project + .GetPropertyValue(ContinuousIntegrationBuild) + .Should().Be(expected); + } + + [Theory] + [MemberData(nameof(MemberData))] + public void RespectsGlobalProperites(Dictionary envVars) + { + using EnvironmentVariableSuppressor hostSuppressor = new("TF_BUILD"); // Suppress our own CI provider variables (i.e. Azure DevOps) + + // If ContinuousIntegrationBuild is not set, it should be set from the CI provider property + ProjectCreator.Templates + .ReproducibleBuildProject(GetRandomFile(".csproj")) + .ProjectWithGlobalProperties(envVars) + .GetPropertyValue(ContinuousIntegrationBuild) + .Should().Be(true.ToLowerInvariant()); + + // If ContinuousIntegrationBuild is set, it should take precedence over the CI provider variables + ProjectCreator.Templates + .ReproducibleBuildProject(GetRandomFile(".csproj")) + .ProjectWithGlobalProperties(envVars.With(ContinuousIntegrationBuild, false.ToLowerInvariant())) + .GetPropertyValue(ContinuousIntegrationBuild) + .Should().Be(false.ToLowerInvariant(), "because explicitly setting `ContinuousIntegrationBuild` should always win."); + } + + public static TheoryData> MemberData() + { + return new TheoryData> + { + { new() { ["TF_BUILD"] = "True" } }, + { new() { ["GITHUB_ACTIONS"] = "true" } }, + { new() { ["APPVEYOR"] = "True" } }, + { new() { ["CI"] = "true" } }, + { new() { ["TRAVIS"] = "true" } }, + { new() { ["CIRCLECI"] = "true" } }, + { new() { ["CODEBUILD_BUILD_ID"] = "abc:123", ["AWS_REGION"] = "us-east-1" } }, + { new() { ["BUILD_ID"] = "123", ["BUILD_URL"] = "https://buildserver.invalid/jenkins/job/MyJobName/123/" } }, + { new() { ["BUILD_ID"] = "123", ["PROJECT_ID"] = "234" } }, + { new() { ["TEAMCITY_VERSION"] = "10" } }, + { new() { ["JB_SPACE_API_URL"] = "https://api.invalid/url" } }, + }; + } +} diff --git a/tests/DotNet.ReproducibleBuilds.Tests/DictionaryExtensions.cs b/tests/DotNet.ReproducibleBuilds.Tests/DictionaryExtensions.cs new file mode 100644 index 0000000..d5b9919 --- /dev/null +++ b/tests/DotNet.ReproducibleBuilds.Tests/DictionaryExtensions.cs @@ -0,0 +1,7 @@ +namespace DotNet.ReproducibleBuilds.Tests; + +internal static class DictionaryExtensions +{ + public static IDictionary With(this IDictionary dictionary, TKey key, TValue value) where TKey : notnull + => new Dictionary(dictionary) { [key] = value }; +} diff --git a/tests/DotNet.ReproducibleBuilds.Tests/ProjectCreatorExtensions.cs b/tests/DotNet.ReproducibleBuilds.Tests/ProjectCreatorExtensions.cs new file mode 100644 index 0000000..d486a60 --- /dev/null +++ b/tests/DotNet.ReproducibleBuilds.Tests/ProjectCreatorExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.Build.Evaluation; +using Microsoft.Build.Utilities.ProjectCreation; + +namespace DotNet.ReproducibleBuilds.Tests; + +internal static class ProjectCreatorExtensions +{ + public static Project ProjectWithGlobalProperties(this ProjectCreator creator, IDictionary properties) + { + creator.TryGetProject(out Project project, properties); + + return project; + } +} diff --git a/tests/DotNet.ReproducibleBuilds.Tests/ProjectTemplates.cs b/tests/DotNet.ReproducibleBuilds.Tests/ProjectTemplates.cs index d2ebecb..beb9091 100644 --- a/tests/DotNet.ReproducibleBuilds.Tests/ProjectTemplates.cs +++ b/tests/DotNet.ReproducibleBuilds.Tests/ProjectTemplates.cs @@ -1,4 +1,5 @@ -using Microsoft.Build.Utilities.ProjectCreation; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Utilities.ProjectCreation; namespace DotNet.ReproducibleBuilds.Tests; @@ -20,7 +21,9 @@ public static ProjectCreator ReproducibleBuildProject(this ProjectCreatorTemplat .Import(Path.Combine(ThisAssemblyDirectory, "DotNet.ReproducibleBuilds.targets")) .Save(); + ProjectCollection projectCollection = new(); // Create a new collection for each project to ensure environment variables aren't shared between tests + return templates - .SdkCsproj(path: project.FullName, targetFramework: "net8.0"); + .SdkCsproj(path: project.FullName, targetFramework: "net8.0", projectCollection: projectCollection); } } diff --git a/tests/DotNet.ReproducibleBuilds.Tests/SourceLinkTests.cs b/tests/DotNet.ReproducibleBuilds.Tests/SourceLinkTests.cs index d46c7f9..75554f7 100644 --- a/tests/DotNet.ReproducibleBuilds.Tests/SourceLinkTests.cs +++ b/tests/DotNet.ReproducibleBuilds.Tests/SourceLinkTests.cs @@ -81,25 +81,22 @@ public void EmbedUntrackedSourcesIsSet(bool? embedUntrackedSources, bool expecte public void RepositoryBranchIsSet(string ci, string original, string expected) { using EnvironmentVariableSuppressor hostSuppressor = new("BUILD_SOURCEBRANCH"); // Suppress our own CI provider variables (i.e. Azure DevOps) - using EnvironmentVariableSuppressor ciSuppressor = new(ci); // Suppress the mock CI provider (just in case). + using EnvironmentVariableSuppressor ciSuppressor = new(ci); // Suppress the mock CI provider (just in case) - // If RepositoryBranch is set, it should take precedence over the CI provider variables - ProjectCreator.Templates + ProjectCreator project = ProjectCreator.Templates .ReproducibleBuildProject(GetRandomFile(".csproj")) .PropertyGroup() - .Property("RepositoryBranch", "explicitly-set") - .Property(ci, original) - .Project - .GetPropertyValue("RepositoryBranch") - .Should().Be("explicitly-set", "because explicitly setting `RepositoryBranch` should always win."); + .Property(ci, original); // If RepositoryBranch is not set, it should be set from the CI provider property - ProjectCreator.Templates - .ReproducibleBuildProject(GetRandomFile(".csproj")) - .PropertyGroup() - .Property(ci, original) - .Project + project.Project .GetPropertyValue("RepositoryBranch") .Should().Be(expected); + + // If RepositoryBranch is set, it should take precedence over the CI provider variables + project.Property("RepositoryBranch", "explicitly-set") + .Project + .GetPropertyValue("RepositoryBranch") + .Should().Be("explicitly-set", "because explicitly setting `RepositoryBranch` should always win."); } }