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

fix: support skipping tags #1787

Merged
merged 7 commits into from
May 25, 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
2 changes: 1 addition & 1 deletion src/main/java/io/fabric8/maven/docker/BuildMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private void proceedWithDockerBuild(ServiceHub hub, BuildService.BuildContext bu
hub.getBuildXService().build(createProjectPaths(), imageConfig, null, createCompleteAuthConfigList(false, imageConfig, getRegistryConfig(pullRegistry), createMojoParameters()), buildArchiveFile);
} else {
buildService.buildImage(imageConfig, pullManager, buildContext, buildArchiveFile);
if (!skipTag) {
if (!skipTag && !imageConfig.getBuildConfiguration().skipTag()) {
buildService.tagImage(imageConfig);
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/io/fabric8/maven/docker/TagMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public void executeInternal(ServiceHub hub) throws DockerAccessException, MojoEx
List<ImageConfiguration> imageConfigs = getResolvedImages();
for (ImageConfiguration imageConfig : imageConfigs) {
BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();
if (buildConfig.skipTag() || buildConfig.isBuildX()) {
// Tag happens at the building stage.
continue;
}

hub.getBuildService().tagImage(imageConfig.getName(), tagName, repo, buildConfig.cleanupMode());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ public class BuildImageConfiguration implements Serializable {
@Parameter
private Boolean skipPush;

@Parameter
private Boolean skipTag;
nodece marked this conversation as resolved.
Show resolved Hide resolved

@Parameter
private ArchiveCompression compression = ArchiveCompression.none;

Expand Down Expand Up @@ -411,6 +414,10 @@ public boolean skipPush() {
return skipPush != null ? skipPush : false;
}

public boolean skipTag() {
return skipTag != null && skipTag;
}

public boolean useDefaultExcludes() {

return useDefaultExcludes != null ? useDefaultExcludes : true;
Expand Down Expand Up @@ -733,6 +740,11 @@ public Builder skipPush(Boolean skipPush) {
return this;
}

public Builder skipTag(Boolean skipTag) {
config.skipTag = skipTag;
return this;
}

public Builder buildOptions(Map<String,String> buildOptions) {
config.buildOptions = buildOptions;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public enum ConfigKey {
SHMSIZE,
SKIP_BUILD("skip.build"),
SKIP_PUSH,
SKIP_TAG("skip.tag"),
SKIP_RUN("skip.run"),
STOP_NAME_PATTERN,
COPY_NAME_PATTERN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ private BuildImageConfiguration extractBuildConfiguration(ImageConfiguration fro
.workdir(valueProvider.getString(WORKDIR, config.getWorkdir()))
.skip(valueProvider.getBoolean(SKIP_BUILD, config.getSkip()))
.skipPush(valueProvider.getBoolean(SKIP_PUSH, config.getSkipPush()))
.skipTag(valueProvider.getBoolean(SKIP_TAG, config.skipTag()))
.imagePullPolicy(valueProvider.getString(IMAGE_PULL_POLICY_BUILD, config.getImagePullPolicy()))
.contextDir(valueProvider.getString(CONTEXT_DIR, config.getContextDirRaw()))
.dockerArchive(valueProvider.getString(DOCKER_ARCHIVE, config.getDockerArchiveRaw()))
Expand Down
10 changes: 6 additions & 4 deletions src/main/java/io/fabric8/maven/docker/service/BuildXService.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,12 @@ protected void buildX(List<String> buildX, String builderName, BuildDirs buildDi
append(cmdLine, "build", "--progress=plain", "--builder", builderName, "--platform",
String.join(",", platforms), "--tag",
new ImageName(imageConfig.getName()).getFullName(configuredRegistry));
buildConfiguration.getTags().forEach(t -> {
cmdLine.add("--tag");
cmdLine.add(new ImageName(imageConfig.getName(), t).getFullName(configuredRegistry));
});
if (!buildConfiguration.skipTag()) {
buildConfiguration.getTags().forEach(t -> {
cmdLine.add("--tag");
cmdLine.add(new ImageName(imageConfig.getName(), t).getFullName(configuredRegistry));
});
}
nodece marked this conversation as resolved.
Show resolved Hide resolved

Map<String, String> args = buildConfiguration.getArgs();
if (args != null) {
Expand Down
87 changes: 80 additions & 7 deletions src/test/java/io/fabric8/maven/docker/BuildMojoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
import io.fabric8.maven.docker.service.BuildService;
import io.fabric8.maven.docker.service.BuildXService;
import io.fabric8.maven.docker.service.ImagePullManager;
import io.fabric8.maven.docker.util.ImageName;

import org.apache.maven.plugin.MojoExecutionException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
Expand All @@ -31,6 +35,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@ExtendWith(MockitoExtension.class)
class BuildMojoTest extends MojoTestBase {
Expand Down Expand Up @@ -241,6 +246,59 @@ void buildUsingBuildxWithMultipleAuth() throws IOException, MojoExecutionExcepti
thenAuthContainsRegistry("custom-registry.org");
}

@ParameterizedTest
@ValueSource(booleans = {true, false})
void buildWithTagByBuildx(boolean skipTag) throws IOException, MojoExecutionException {
givenBuildXService();

List<String> tags = new ArrayList<>();
tags.add("tag-" + System.currentTimeMillis());
tags.add("tag-" + System.currentTimeMillis());

givenMavenProject(buildMojo);
ImageConfiguration imageConfiguration = singleImageConfiguration(builder -> {
builder.buildx(getBuildXPlatforms(TWO_BUILDX_PLATFORMS).build());
builder.tags(tags);
builder.skipTag(skipTag);
});
givenResolvedImages(buildMojo, Collections.singletonList(imageConfiguration));
givenPackaging("jar");

whenMojoExecutes();

List<String> fullTags = skipTag ? Collections.emptyList() : tags.stream()
.map(tag -> new ImageName(imageConfiguration.getName(), tag).getFullName())
.collect(Collectors.toList());
thenBuildxRun(null, null, true, null, fullTags);
}

@ParameterizedTest
@ValueSource(booleans = {true, false})
void buildWithTagByNormalBuild(boolean skipTag) throws IOException, MojoExecutionException {
givenMavenProject(buildMojo);

List<String> tags = new ArrayList<>();
tags.add("tag-" + System.currentTimeMillis());
tags.add("tag-" + System.currentTimeMillis());

ImageConfiguration imageConfiguration = singleImageConfiguration(builder -> {
builder.skipTag(skipTag);
builder.tags(tags);
});
givenResolvedImages(buildMojo, Collections.singletonList(imageConfiguration));
givenPackaging("jar");
givenSkipPom(true);

whenMojoExecutes();

thenBuildRun();

if (!skipTag) {
Mockito.verify(buildService, Mockito.times(1))
.tagImage(Mockito.any(ImageConfiguration.class));
}
}

private void givenBuildXService() {
BuildXService buildXService = new BuildXService(dockerAccess, dockerAssemblyManager, log, exec);

Expand Down Expand Up @@ -288,24 +346,39 @@ private void verifyBuild(int wantedNumberOfInvocations) throws DockerAccessExcep
.buildImage(Mockito.any(ImageConfiguration.class), Mockito.any(ImagePullManager.class), Mockito.any(BuildService.BuildContext.class), Mockito.any());
}

private void thenBuildxRun(String relativeConfigFile, String contextDir, boolean nativePlatformIncluded,
String attestation) throws MojoExecutionException {
thenBuildxRun(relativeConfigFile, contextDir, nativePlatformIncluded, attestation, Collections.emptyList());
}

private void thenBuildxRun(String relativeConfigFile, String contextDir,
boolean nativePlatformIncluded, String attestation) throws MojoExecutionException {
boolean nativePlatformIncluded, String attestation, List<String> tags)
throws MojoExecutionException {
Path buildPath = projectBaseDirectory.toPath().resolve("target/docker/example/latest");
String config = getOsDependentBuild(buildPath, "docker");
String configFile = relativeConfigFile != null ? getOsDependentBuild(projectBaseDirectory.toPath(), relativeConfigFile) : null;
String configFile =
relativeConfigFile != null ? getOsDependentBuild(projectBaseDirectory.toPath(), relativeConfigFile) :
null;

List<String> cmds =
BuildXService.append(new ArrayList<>(), "docker", "--config", config, "buildx",
"create", "--driver", "docker-container", "--name", "maven" , "--node", "maven0");
BuildXService.append(new ArrayList<>(), "docker", "--config", config, "buildx",
"create", "--driver", "docker-container", "--name", "maven", "--node", "maven0");
if (configFile != null) {
BuildXService.append(cmds, "--config", configFile.replace('/', File.separatorChar));
}
Mockito.verify(exec).process(cmds);

if (nativePlatformIncluded) {
List<String> buildXLine = BuildXService.append(new ArrayList<>(), "docker", "--config", config, "buildx",
List<String> buildXLine = BuildXService.append(new ArrayList<>(), "docker", "--config", config, "buildx",
"build", "--progress=plain", "--builder", "maven",
"--platform", NATIVE_PLATFORM, "--tag", "example:latest", "--build-arg", "foo=bar");
"--platform", NATIVE_PLATFORM, "--tag", "example:latest");

tags.forEach(tag -> {
buildXLine.add("--tag");
buildXLine.add(tag);
});
buildXLine.add("--build-arg");
buildXLine.add("foo=bar");

if (attestation != null) {
buildXLine.add(attestation);
Expand All @@ -315,7 +388,7 @@ private void thenBuildxRun(String relativeConfigFile, String contextDir,
buildXLine.add(getOsDependentBuild(buildPath, "build"));
} else {
Path contextPath = tmpDir.resolve("docker-build");
BuildXService.append(buildXLine, "--file=" + contextPath.resolve("Dockerfile"), contextPath.toString() );
BuildXService.append(buildXLine, "--file=" + contextPath.resolve("Dockerfile"), contextPath.toString());
}

buildXLine.add("--load");
Expand Down
26 changes: 17 additions & 9 deletions src/test/java/io/fabric8/maven/docker/MojoTestBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.fabric8.maven.docker.util.AnsiLogger;
import io.fabric8.maven.docker.util.AuthConfigFactory;
import io.fabric8.maven.docker.util.GavLabel;
import java.util.function.Consumer;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Build;
import org.apache.maven.monitor.logging.DefaultLog;
Expand Down Expand Up @@ -113,18 +114,25 @@ protected ImageConfiguration singleImageWithBuild() {
}

ImageConfiguration singleImageConfiguration(BuildXConfiguration buildx, String contextDir) {
BuildImageConfiguration buildImageConfiguration = new BuildImageConfiguration.Builder()
.from("scratch")
.buildx(buildx)
.args(Collections.singletonMap("foo", "bar"))
.contextDir(contextDir)
.build();
return singleImageConfiguration(builder -> {
builder.buildx(buildx);
builder.contextDir(contextDir);
});
}

ImageConfiguration singleImageConfiguration(
Consumer<BuildImageConfiguration.Builder> buildImageConfigurationConsumer) {
BuildImageConfiguration.Builder builder = new BuildImageConfiguration.Builder()
.from("scratch")
.args(Collections.singletonMap("foo", "bar"));
buildImageConfigurationConsumer.accept(builder);
BuildImageConfiguration buildImageConfiguration = builder.build();
buildImageConfiguration.initAndValidate(log);

return new Builder()
.name("example:latest")
.buildConfig(buildImageConfiguration)
.build();
.name("example:latest")
.buildConfig(buildImageConfiguration)
.build();
}

ImageConfiguration singleImageConfigurationWithBuildWithSquash(BuildXConfiguration buildx, String contextDir) {
Expand Down
72 changes: 72 additions & 0 deletions src/test/java/io/fabric8/maven/docker/TagMojoTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package io.fabric8.maven.docker;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import io.fabric8.maven.docker.config.BuildXConfiguration;
import io.fabric8.maven.docker.config.ImageConfiguration;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import org.apache.maven.plugin.MojoExecutionException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class TagMojoTest extends MojoTestBase {

private static final String NON_NATIVE_PLATFORM = "linux/amd64";
private static final String NATIVE_PLATFORM = "linux/arm64";

@InjectMocks
private TagMojo tagMojo;

@Nested
class NormalBuild {
@ParameterizedTest
@ValueSource(booleans = {true, false})
@DisplayName("If skipTag is true, call tag; otherwise, do not call")
void tag(boolean skipTag) throws IOException, MojoExecutionException {
givenMavenProject(tagMojo);

ImageConfiguration imageConfiguration = singleImageConfiguration(builder -> {
builder.skipTag(skipTag);
});
givenResolvedImages(tagMojo, Collections.singletonList(imageConfiguration));

tagMojo.executeInternal(serviceHub);

verify(buildService, times(skipTag ? 0 : 1)).tagImage(any(), any(), any(), any());
}
}

@Nested
class BuildX {
@ParameterizedTest
@ValueSource(booleans = {true, false})
@DisplayName("Always skip tag")
void tag(boolean skipTag) throws IOException, MojoExecutionException {
givenMavenProject(tagMojo);

ImageConfiguration imageConfiguration = singleImageConfiguration(builder -> {
builder.skipTag(skipTag);
ArrayList<String> platforms = new ArrayList<>();
platforms.add(NON_NATIVE_PLATFORM);
platforms.add(NATIVE_PLATFORM);
BuildXConfiguration buildXConfiguration =
new BuildXConfiguration.Builder().platforms(platforms).build();
builder.buildx(buildXConfiguration);
});
givenResolvedImages(tagMojo, Collections.singletonList(imageConfiguration));

tagMojo.executeInternal(serviceHub);

verify(buildService, times(0)).tagImage(any(), any(), any(), any());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,13 @@ void multipleAssembliesUniqueNames() {
assemblies(Arrays.asList(assemblyConfigurationOne, assemblyConfigurationTwo)).build();
Assertions.assertThrows(IllegalArgumentException.class, () -> config.initAndValidate(logger));
}

@Test
void skipTag() {
BuildImageConfiguration config =
new BuildImageConfiguration.Builder().skipTag(true)
.build();
config.initAndValidate(logger);
Assertions.assertTrue(config.skipTag());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,20 @@ void testSkipPush() {
Assertions.assertFalse(resolveExternalImageConfig(new String[] { k(ConfigKey.NAME), "image", k(ConfigKey.FROM), "busybox" }).getBuildConfiguration().skipPush());
}

@Test
void testSkipTag() {
Assertions.assertFalse(
resolveExternalImageConfig(getSkipTestData(ConfigKey.SKIP_TAG, false)).getBuildConfiguration()
.skipTag());
Assertions.assertTrue(
resolveExternalImageConfig(getSkipTestData(ConfigKey.SKIP_TAG, true)).getBuildConfiguration()
.skipTag());

Assertions.assertFalse(resolveExternalImageConfig(
new String[]{k(ConfigKey.NAME), "image", k(ConfigKey.FROM), "busybox"}).getBuildConfiguration()
.skipTag());
}

@Test
void testSkipRun() {
Assertions.assertFalse(resolveExternalImageConfig(getSkipTestData(ConfigKey.SKIP_RUN, false)).getRunConfiguration().skip());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,14 @@ void testGetMap() {
Assertions.assertEquals("notignored1", m.get("ckey"));
}

@Test
void testSkipTag() {
configure(PropertyMode.Override);
Assertions.assertNull(provider.getBoolean(ConfigKey.SKIP_TAG, null));

props.put("docker.skip.tag", "true");
Assertions.assertTrue(provider.getBoolean(ConfigKey.SKIP_TAG, null));
props.put("docker.skip.tag", "false");
Assertions.assertFalse(provider.getBoolean(ConfigKey.SKIP_TAG, null));
}
}
Loading
Loading