diff --git a/src/Runner.Worker/ActionManager.cs b/src/Runner.Worker/ActionManager.cs index 954d9a691f6..88af0347a74 100644 --- a/src/Runner.Worker/ActionManager.cs +++ b/src/Runner.Worker/ActionManager.cs @@ -1004,7 +1004,7 @@ private ActionSetupInfo PrepareRepositoryActionAsync(IExecutionContext execution if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Container) { var containerAction = actionDefinitionData.Execution as ContainerActionExecutionData; - if (containerAction.Image.EndsWith("Dockerfile") || containerAction.Image.EndsWith("dockerfile")) + if (DockerUtil.IsDockerfile(containerAction.Image)) { var dockerFileFullPath = Path.Combine(actionEntryDirectory, containerAction.Image); executionContext.Debug($"Dockerfile for action: '{dockerFileFullPath}'."); diff --git a/src/Runner.Worker/Container/DockerUtil.cs b/src/Runner.Worker/Container/DockerUtil.cs index 02c2ece5b79..af7de18df04 100644 --- a/src/Runner.Worker/Container/DockerUtil.cs +++ b/src/Runner.Worker/Container/DockerUtil.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.RegularExpressions; namespace GitHub.Runner.Worker.Container @@ -17,7 +18,7 @@ public static List ParseDockerPort(IList portMappingLines) string pattern = $"^(?<{targetPort}>\\d+)/(?<{proto}>\\w+) -> (?<{host}>.+):(?<{hostPort}>\\d+)$"; List portMappings = new List(); - foreach(var line in portMappingLines) + foreach (var line in portMappingLines) { Match m = Regex.Match(line, pattern, RegexOptions.None, TimeSpan.FromSeconds(1)); if (m.Success) @@ -61,5 +62,15 @@ public static string ParseRegistryHostnameFromImageName(string name) } return ""; } + + public static bool IsDockerfile(string image) + { + if (image.StartsWith("docker://", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + var imageWithoutPath = image.Split('/').Last(); + return imageWithoutPath.StartsWith("Dockerfile.") || imageWithoutPath.StartsWith("dockerfile.") || imageWithoutPath.EndsWith("Dockerfile") || imageWithoutPath.EndsWith("dockerfile"); + } } } diff --git a/src/Runner.Worker/Handlers/ContainerActionHandler.cs b/src/Runner.Worker/Handlers/ContainerActionHandler.cs index 3eaacb5a94c..d6494f63f4c 100644 --- a/src/Runner.Worker/Handlers/ContainerActionHandler.cs +++ b/src/Runner.Worker/Handlers/ContainerActionHandler.cs @@ -44,9 +44,8 @@ public async Task RunAsync(ActionRunStage stage) { Data.Image = Data.Image.Substring("docker://".Length); } - else if (Data.Image.EndsWith("Dockerfile") || Data.Image.EndsWith("dockerfile")) + else if (DockerUtil.IsDockerfile(Data.Image)) { - // ensure docker file exist var dockerFile = Path.Combine(ActionDirectory, Data.Image); ArgUtil.File(dockerFile, nameof(Data.Image)); @@ -68,6 +67,10 @@ public async Task RunAsync(ActionRunStage stage) Data.Image = imageName; } + else + { + throw new InvalidOperationException($"'{Data.Image}' should be either '[path]/Dockerfile' or 'docker://image[:tag]'."); + } string type = Action.Type == Pipelines.ActionSourceType.Repository ? "Dockerfile" : "DockerHub"; // Set extra telemetry base on the current context. diff --git a/src/Test/L0/Container/DockerUtilL0.cs b/src/Test/L0/Container/DockerUtilL0.cs index c069255a85c..c31e9aead81 100644 --- a/src/Test/L0/Container/DockerUtilL0.cs +++ b/src/Test/L0/Container/DockerUtilL0.cs @@ -144,5 +144,48 @@ public void ParseRegistryHostnameFromImageName(string input, string expected) var actual = DockerUtil.ParseRegistryHostnameFromImageName(input); Assert.Equal(expected, actual); } + + [Theory] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + [InlineData("dockerhub/repo", false)] + [InlineData("debian:latest", false)] + [InlineData("something/dockerfileimage", false)] + [InlineData("ghcr.io/docker/dockerfile", true)] // should be false but might break the current workflows + [InlineData("Dockerfile", true)] + [InlineData("Dockerfile.", true)] + [InlineData(".Dockerfile", true)] + [InlineData(".Dockerfile.", false)] + [InlineData("dockerfile", true)] + [InlineData("dockerfile.", true)] + [InlineData(".dockerfile", true)] + [InlineData(".dockerfile.", false)] + [InlineData("Dockerfile.test", true)] + [InlineData("test.Dockerfile", true)] + [InlineData("docker/dockerfile:latest", false)] + [InlineData("/some/path/dockerfile:latest", false)] + [InlineData("dockerfile:latest", false)] + [InlineData("Dockerfile:latest", false)] + [InlineData("dockerfile-latest", false)] + [InlineData("Dockerfile-latest", false)] + [InlineData("dockerfile.latest", true)] + [InlineData("Dockerfile.latest", true)] + [InlineData("../dockerfile/dockerfileone", false)] + [InlineData("../Dockerfile/dockerfileone", false)] + [InlineData("../dockerfile.test", true)] + [InlineData("../Dockerfile.test", true)] + [InlineData("./dockerfile/image", false)] + [InlineData("./Dockerfile/image", false)] + [InlineData("example/Dockerfile.test", true)] + [InlineData("./example/Dockerfile.test", true)] + [InlineData("example/test.dockerfile", true)] + [InlineData("./example/test.dockerfile", true)] + [InlineData("docker://Dockerfile", false)] + [InlineData("docker://ubuntu:latest", false)] + public void IsDockerfile(string input, bool expected) + { + var actual = DockerUtil.IsDockerfile(input); + Assert.Equal(expected, actual); + } } }