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

Add test cases for detecting CI environments and setting ContinuousIntegrationBuild #49

Merged
merged 4 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 18 additions & 6 deletions src/DotNet.ReproducibleBuilds/DotNet.ReproducibleBuilds.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,23 @@

<!-- Enable ContinuousIntegrationBuild when running on CI -->

<!-- Azure Pipelines / DevOpsServer -->
<!-- Azure Pipelines / DevOpsServer
https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#system-variables-devops-services
-->
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<!-- GitHub Actions -->
<!-- GitHub Actions
https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
-->
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<!-- AppVeyor -->
<!-- AppVeyor
https://www.appveyor.com/docs/environment-variables/
-->
<PropertyGroup Condition="'$(APPVEYOR)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
Expand All @@ -32,17 +38,23 @@
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<!-- Travis CI -->
<!-- Travis CI
https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
-->
<PropertyGroup Condition="'$(TRAVIS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<!-- Circle CI -->
<!-- Circle CI
https://circleci.com/docs/variables/#built-in-environment-variables
-->
<PropertyGroup Condition="'$(CIRCLECI)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<!-- AWS CodeBuild -->
<!-- AWS CodeBuild
https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
-->
<PropertyGroup Condition="'$(CODEBUILD_BUILD_ID)' != '' and '$(AWS_REGION)' != '' ">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<string, string> 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<Dictionary<string, string>> MemberData()
{
return new TheoryData<Dictionary<string, string>>
{
{ 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" } },
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace DotNet.ReproducibleBuilds.Tests;

internal static class DictionaryExtensions
{
public static IDictionary<TKey, TValue> With<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value) where TKey : notnull
=> new Dictionary<TKey, TValue>(dictionary) { [key] = value };
}
Original file line number Diff line number Diff line change
@@ -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<string, string> properties)
{
creator.TryGetProject(out Project project, properties);

return project;
}
}
7 changes: 5 additions & 2 deletions tests/DotNet.ReproducibleBuilds.Tests/ProjectTemplates.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Build.Utilities.ProjectCreation;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Tests;

Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

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

Subtle detail, good catch


return templates
.SdkCsproj(path: project.FullName, targetFramework: "net8.0");
.SdkCsproj(path: project.FullName, targetFramework: "net8.0", projectCollection: projectCollection);
}
}
23 changes: 10 additions & 13 deletions tests/DotNet.ReproducibleBuilds.Tests/SourceLinkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
}