-
-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add FilteringRule to Husky tasks (#115)
* test: add EchoWithInclude test * test: improve integration test speed * test: enable remote debugging inside the container * feat: add FilteringRules * test: test skip when no match files found * fix: update sln file * test: add integration tests
1 parent
91383d1
commit 5bc3b51
Showing
21 changed files
with
615 additions
and
249 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Husky.TaskRunner; | ||
|
||
public enum FilteringRules | ||
{ | ||
Variable, | ||
Staged, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,279 @@ | ||
using System.Runtime.CompilerServices; | ||
using DotNet.Testcontainers.Containers; | ||
using FluentAssertions; | ||
|
||
namespace HuskyIntegrationTests; | ||
public class Issue106Tests (ITestOutputHelper output) | ||
{ | ||
[Fact] | ||
public async Task FilteringRuleNotDefined_WithInclude_ShouldNotSkip() | ||
{ | ||
// arrange | ||
const string taskRunner = | ||
""" | ||
{ | ||
"tasks": [ | ||
{ | ||
"name": "Echo", | ||
"command": "echo", | ||
"args": [ | ||
"Husky.Net is awesome!" | ||
], | ||
"include": [ | ||
"client/**/*" | ||
] | ||
} | ||
] | ||
} | ||
"""; | ||
await using var c = await ArrangeContainer(taskRunner); | ||
await c.BashAsync("git add ."); | ||
|
||
// act | ||
var result = await c.BashAsync(output, "git commit -m 'add task-runner.json'"); | ||
|
||
// assert | ||
result.ExitCode.Should().Be(0); | ||
result.Stderr.Should().NotContain(DockerHelper.Skipped); | ||
} | ||
|
||
[Fact] | ||
public async Task FilteringRuleNotDefined_WithStagedVariable_WithExcludeCommitedFile_ShouldSkip() | ||
{ | ||
// arrange | ||
const string taskRunner = | ||
""" | ||
{ | ||
"tasks": [ | ||
{ | ||
"name": "Echo", | ||
"command": "echo", | ||
"args": [ | ||
"${staged}" | ||
], | ||
"exclude": [ | ||
"**/task-runner.json" | ||
] | ||
} | ||
] | ||
} | ||
"""; | ||
await using var c = await ArrangeContainer(taskRunner); | ||
await c.BashAsync("git add ."); | ||
|
||
// act | ||
var result = await c.BashAsync(output, "git commit -m 'add task-runner.json'"); | ||
|
||
// assert | ||
result.ExitCode.Should().Be(0); | ||
result.Stderr.Should().Contain(DockerHelper.Skipped); | ||
} | ||
|
||
[Fact] | ||
public async Task FilteringRuleVariable_WithStagedVariable_WithExcludeCommitedFile_ShouldSkip() | ||
{ | ||
// arrange | ||
const string taskRunner = | ||
""" | ||
{ | ||
"tasks": [ | ||
{ | ||
"name": "Echo", | ||
"command": "echo", | ||
"filteringRule": "variable", | ||
"args": [ | ||
"${staged}" | ||
], | ||
"exclude": [ | ||
"**/task-runner.json" | ||
] | ||
} | ||
] | ||
} | ||
"""; | ||
await using var c = await ArrangeContainer(taskRunner); | ||
await c.BashAsync("git add ."); | ||
|
||
// act | ||
var result = await c.BashAsync(output, "git commit -m 'add task-runner.json'"); | ||
|
||
// assert | ||
result.ExitCode.Should().Be(0); | ||
result.Stderr.Should().Contain(DockerHelper.Skipped); | ||
} | ||
|
||
[Fact] | ||
public async Task FilteringRuleVariable_WithStagedVariable_WithIncludeCommitedFile_ShouldNotSkip() | ||
{ | ||
// arrange | ||
const string taskRunner = | ||
""" | ||
{ | ||
"tasks": [ | ||
{ | ||
"name": "Echo", | ||
"command": "echo", | ||
"pathMode": "absolute", | ||
"filteringRule": "variable", | ||
"args": [ | ||
"${staged}" | ||
], | ||
"include": [ | ||
"**/task-runner.json" | ||
] | ||
} | ||
] | ||
} | ||
"""; | ||
await using var c = await ArrangeContainer(taskRunner); | ||
await c.BashAsync("git add ."); | ||
|
||
// act | ||
var result = await c.BashAsync(output, "git commit -m 'add task-runner.json'"); | ||
|
||
// assert | ||
result.ExitCode.Should().Be(0); | ||
result.Stderr.Should().NotContain(DockerHelper.Skipped); | ||
result.Stderr.Should().Contain(".husky/task-runner.json"); | ||
} | ||
|
||
[Fact] | ||
public async Task FilteringRuleStaged_WithoutAnyMatchedFile_ShouldSkip() | ||
{ | ||
// arrange | ||
const string taskRunner = | ||
""" | ||
{ | ||
"tasks": [ | ||
{ | ||
"name": "Echo", | ||
"command": "echo", | ||
"filteringRule": "staged", | ||
"args": [ | ||
"Husky.Net is awesome!" | ||
], | ||
"include": [ | ||
"client/**/*" | ||
] | ||
} | ||
] | ||
} | ||
"""; | ||
await using var c = await ArrangeContainer(taskRunner); | ||
await c.BashAsync("git add ."); | ||
|
||
// act | ||
var result = await c.BashAsync(output, "git commit -m 'add task-runner.json'"); | ||
|
||
// assert | ||
result.ExitCode.Should().Be(0); | ||
result.Stderr.Should().Contain(DockerHelper.Skipped); | ||
} | ||
|
||
[Fact] | ||
public async Task FilteringRuleStaged_WithoutIncludeAndExclude_ShouldNotSkip() | ||
{ | ||
// arrange | ||
const string taskRunner = | ||
""" | ||
{ | ||
"tasks": [ | ||
{ | ||
"name": "Echo", | ||
"command": "echo", | ||
"filteringRule": "staged", | ||
"args": [ | ||
"Husky.Net is awesome!" | ||
] | ||
} | ||
] | ||
} | ||
"""; | ||
await using var c = await ArrangeContainer(taskRunner); | ||
await c.BashAsync("git add ."); | ||
|
||
// act | ||
var result = await c.BashAsync(output, "git commit -m 'add task-runner.json'"); | ||
|
||
// assert | ||
result.ExitCode.Should().Be(0); | ||
result.Stderr.Should().NotContain(DockerHelper.Skipped); | ||
} | ||
|
||
[Fact] | ||
public async Task FilteringRuleStaged_WithExcludeCommitedFile_ShouldSkip() | ||
{ | ||
// arrange | ||
const string taskRunner = | ||
""" | ||
{ | ||
"tasks": [ | ||
{ | ||
"name": "Echo", | ||
"command": "echo", | ||
"filteringRule": "staged", | ||
"args": [ | ||
"Husky.Net is awesome!" | ||
], | ||
"exclude": [ | ||
"**/task-runner.json" | ||
] | ||
} | ||
] | ||
} | ||
"""; | ||
await using var c = await ArrangeContainer(taskRunner); | ||
await c.BashAsync("git add ."); | ||
|
||
// act | ||
var result = await c.BashAsync(output, "git commit -m 'add task-runner.json'"); | ||
|
||
// assert | ||
result.ExitCode.Should().Be(0); | ||
result.Stderr.Should().Contain(DockerHelper.Skipped); | ||
} | ||
|
||
[Fact] | ||
public async Task FilteringRuleStaged_WithIncludeCommitedFile_ShouldNotSkip() | ||
{ | ||
// arrange | ||
const string taskRunner = | ||
""" | ||
{ | ||
"tasks": [ | ||
{ | ||
"name": "Echo", | ||
"command": "echo", | ||
"filteringRule": "staged", | ||
"args": [ | ||
"Husky.Net is awesome!" | ||
], | ||
"include": [ | ||
"**/task-runner.json" | ||
] | ||
} | ||
] | ||
} | ||
"""; | ||
await using var c = await ArrangeContainer(taskRunner); | ||
await c.BashAsync("git add ."); | ||
|
||
// act | ||
var result = await c.BashAsync(output, "git commit -m 'add task-runner.json'"); | ||
|
||
// assert | ||
result.ExitCode.Should().Be(0); | ||
result.Stderr.Should().NotContain(DockerHelper.Skipped); | ||
} | ||
|
||
|
||
private async Task<IContainer> ArrangeContainer(string taskRunner, [CallerMemberName] string name = null!) | ||
{ | ||
var c = await DockerHelper.StartWithInstalledHusky(name); | ||
await c.BashAsync("dotnet tool restore"); | ||
await c.BashAsync("git add ."); | ||
await c.UpdateTaskRunner(taskRunner); | ||
await c.BashAsync("dotnet husky add pre-commit -c 'dotnet husky run'"); | ||
return c; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
To debug the tests remotely: | ||
|
||
1. Run the test in debug mode. | ||
2. The test will freeze when Husky processes run. | ||
3. In Rider, go to "Attach to Remote Process." | ||
4. Select Docker and locate the test container. | ||
5. Install remote debugging tools and select the running .NET process. | ||
6. It should hit the breakpoint in the code |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
7 changes: 0 additions & 7 deletions
7
tests/HuskyIntegrationTests/Utilities/DockerFixtureCollection.cs
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
using System.Diagnostics; | ||
using System.Runtime.CompilerServices; | ||
using DotNet.Testcontainers.Builders; | ||
using DotNet.Testcontainers.Containers; | ||
using HuskyIntegrationTests.Utilities; | ||
|
||
namespace HuskyIntegrationTests; | ||
|
||
public static class DockerHelper | ||
{ | ||
public const string SuccessfullyExecuted = "✔ Successfully executed"; | ||
public const string Skipped = "💤 Skipped,"; | ||
|
||
public static async Task<ExecResult> BashAsync(this IContainer container, params string[] command) | ||
{ | ||
var result = await container.ExecAsync(["/bin/bash", "-c", ..command]); | ||
if (result.ExitCode != 0) | ||
throw new Exception(result.Stderr + result.Stdout); | ||
return result; | ||
} | ||
|
||
public static async Task<ExecResult> BashAsync(this IContainer container, ITestOutputHelper output, params string[] command) | ||
{ | ||
var result = await container.ExecAsync(["/bin/bash", "-c", ..command]); | ||
output.WriteLine($"{string.Join(" ", command)}:"); | ||
|
||
if (!string.IsNullOrEmpty(result.Stdout)) | ||
output.WriteLine(result.Stdout); | ||
|
||
if (!string.IsNullOrEmpty(result.Stderr)) | ||
output.WriteLine(result.Stderr); | ||
|
||
return result; | ||
} | ||
|
||
public static Task<ExecResult> UpdateTaskRunner(this IContainer container, string content) | ||
{ | ||
return container.BashAsync($"echo -e '{content}' > /test/.husky/task-runner.json"); | ||
} | ||
|
||
public static Task<ExecResult> AddCsharpClass(this IContainer container, string content, string fileName = "Class2.cs") | ||
{ | ||
return container.BashAsync($"echo -e '{content}' > /test/{fileName}"); | ||
} | ||
|
||
public static async Task<IContainer> StartContainerAsync(string? folderNameToCopy = null, [CallerMemberName] string name = null!) | ||
{ | ||
await GlobalImageBuilder.BuildImageAsync(); | ||
var builder = new ContainerBuilder() | ||
.WithName(GenerateContainerName(name)) | ||
.WithImage("husky") | ||
.WithWorkingDirectory("/test/") | ||
.WithEntrypoint("/bin/bash", "-c") | ||
.WithCleanUp(true) | ||
.WithCommand("tail -f /dev/null"); | ||
|
||
if (Debugger.IsAttached) | ||
{ | ||
builder = builder.WithEnvironment("HUSKY_INTEGRATION_TEST", "1"); | ||
} | ||
|
||
if (!string.IsNullOrEmpty(folderNameToCopy)) | ||
{ | ||
builder = builder.WithResourceMapping(GetTestFolderPath(folderNameToCopy), "/test/"); | ||
} | ||
|
||
var container = builder.Build(); | ||
await container.StartAsync(); | ||
return container; | ||
} | ||
|
||
public static async Task<IContainer> StartWithInstalledHusky([CallerMemberName] string name = null!) | ||
{ | ||
await GlobalImageBuilder.BuildImageAsync(); | ||
var c = await StartContainerAsync(nameof(TestProjectBase), name); | ||
await c.BashAsync("git init"); | ||
await c.BashAsync("dotnet new tool-manifest"); | ||
await c.BashAsync("dotnet tool install --no-cache --add-source /app/nupkg/ husky --version 99.1.1-test"); | ||
await c.BashAsync("dotnet tool restore"); | ||
await c.BashAsync("dotnet husky install"); | ||
await c.BashAsync("git config --global user.email \"you@example.com\""); | ||
await c.BashAsync("git config --global user.name \"Your Name\""); | ||
await c.BashAsync("git add ."); | ||
await c.BashAsync("git commit -m 'initial commit'"); | ||
return c; | ||
} | ||
|
||
private static string GenerateContainerName(string name) | ||
{ | ||
return $"{name}-{Guid.NewGuid().ToString("N")[..4]}"; | ||
} | ||
|
||
private static string GetTestFolderPath(string folderName) | ||
{ | ||
var baseDirectory = CommonDirectoryPath.GetProjectDirectory().DirectoryPath; | ||
return Path.Combine(baseDirectory, folderName); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
63 changes: 63 additions & 0 deletions
63
tests/HuskyIntegrationTests/Utilities/GlobalImageBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
using Docker.DotNet.Models; | ||
using DotNet.Testcontainers.Builders; | ||
using DotNet.Testcontainers.Configurations; | ||
using DotNet.Testcontainers.Containers; | ||
|
||
namespace HuskyIntegrationTests.Utilities; | ||
|
||
public static class GlobalImageBuilder | ||
{ | ||
/// <summary> | ||
/// Set this value to false if you don't want the image to be removed after the test | ||
/// This is useful if you want to debug the image, or fix tests issues | ||
/// </summary> | ||
private const bool RemoveHuskyImageAfterTest = true; | ||
|
||
|
||
private static bool _imageBuilt; | ||
private static readonly SemaphoreSlim SemaphoreSlim = new(1, 1); | ||
|
||
public static async ValueTask BuildImageAsync() | ||
{ | ||
if (_imageBuilt) | ||
{ | ||
return; | ||
} | ||
|
||
await SemaphoreSlim.WaitAsync(); | ||
try | ||
{ | ||
if (!_imageBuilt && !await ImageExistsAsync("husky")) | ||
{ | ||
var image = new ImageFromDockerfileBuilder() | ||
.WithBuildArgument("RESOURCE_REAPER_SESSION_ID", ResourceReaper.DefaultSessionId.ToString("D")) | ||
.WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), string.Empty) | ||
.WithDockerfile("Dockerfile") | ||
.WithName("husky") | ||
.WithCleanUp(RemoveHuskyImageAfterTest) | ||
.Build(); | ||
|
||
await image.CreateAsync(); | ||
_imageBuilt = true; | ||
} | ||
} | ||
finally | ||
{ | ||
SemaphoreSlim.Release(); | ||
} | ||
} | ||
|
||
private static async Task<bool> ImageExistsAsync(string imageName) | ||
{ | ||
var clientConfiguration = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration(); | ||
using var dockerClient = clientConfiguration.CreateClient(); | ||
var images = await dockerClient.Images.ListImagesAsync(new ImagesListParameters | ||
{ | ||
Filters = new Dictionary<string, IDictionary<string, bool>> | ||
{ | ||
["reference"] = new Dictionary<string, bool> { [imageName] = true } | ||
} | ||
}); | ||
return _imageBuilt = images.Count > 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", | ||
"showLiveOutput": true | ||
} |