From b7adeba95955d097ae0fd75a7ba8c1a572089d98 Mon Sep 17 00:00:00 2001 From: lingenj Date: Fri, 20 Dec 2024 17:01:45 +0100 Subject: [PATCH 01/11] Start implementation --- build.gradle.kts | 1 + .../docker/DockerImageVersion.java | 7 +- .../docker/search/FindDockerImageUses.java | 69 +++++- .../docker/table/DockerBaseImages.java | 8 + .../openrewrite/docker/trait/Dockerfile.java | 22 +- .../trait/DockerfileImageReference.java | 63 +++++ .../docker/trait/ImageMatcher.java | 16 ++ .../docker/trait/YamlImageReference.java | 49 ++++ .../org.openrewrite.trait.Reference$Provider | 2 + .../docker/FindDockerImagesUsedTest.java | 223 +++++++++++++++++- 10 files changed, 423 insertions(+), 37 deletions(-) create mode 100644 src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java create mode 100644 src/main/java/org/openrewrite/docker/trait/ImageMatcher.java create mode 100644 src/main/java/org/openrewrite/docker/trait/YamlImageReference.java create mode 100644 src/main/resources/services/org.openrewrite.trait.Reference$Provider diff --git a/build.gradle.kts b/build.gradle.kts index 65ed7c0..d88add7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,5 +10,6 @@ val rewriteVersion = rewriteRecipe.rewriteVersion.get() dependencies { implementation(platform("org.openrewrite:rewrite-bom:$rewriteVersion")) implementation("org.openrewrite:rewrite-core") + implementation("org.openrewrite:rewrite-yaml") testImplementation("org.openrewrite:rewrite-test") } diff --git a/src/main/java/org/openrewrite/docker/DockerImageVersion.java b/src/main/java/org/openrewrite/docker/DockerImageVersion.java index 9c250e0..852f096 100644 --- a/src/main/java/org/openrewrite/docker/DockerImageVersion.java +++ b/src/main/java/org/openrewrite/docker/DockerImageVersion.java @@ -22,11 +22,10 @@ public class DockerImageVersion { String imageName; - @Nullable String version; - @Override - public String toString() { - return imageName + (version != null ? ":" + version : ""); + public static DockerImageVersion of(String value) { + String[] imageVersionStr = value.split(":"); + return new DockerImageVersion(imageVersionStr[0], imageVersionStr.length > 1 ? imageVersionStr[1].split(" ")[0] : ""); } } diff --git a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java index 3513be3..ef06c33 100644 --- a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java +++ b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java @@ -15,17 +15,22 @@ */ package org.openrewrite.docker.search; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.*; import org.openrewrite.docker.DockerImageVersion; import org.openrewrite.docker.table.DockerBaseImages; +import org.openrewrite.docker.trait.ImageMatcher; import org.openrewrite.marker.SearchResult; +import org.openrewrite.text.Find; +import org.openrewrite.text.PlainText; +import org.openrewrite.trait.Reference; -import java.util.List; -import java.util.stream.Collectors; +import java.nio.file.Path; +import java.util.*; -import static org.openrewrite.docker.trait.Traits.dockerfile; +import static java.util.stream.Collectors.joining; public class FindDockerImageUses extends Recipe { transient DockerBaseImages dockerBaseImages = new DockerBaseImages(this); @@ -37,10 +42,58 @@ public String getDisplayName() { @Override public String getDescription() { - return "Produce an impact analysis of base images used in Dockerfiles."; + return "Produce an impact analysis of base images used in Dockerfiles, .gitlab-ci files, etc."; } @Override + public TreeVisitor getVisitor() { + return new TreeVisitor() { + + @Override + public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { + if (tree instanceof SourceFileWithReferences) { + SourceFileWithReferences sourceFile = (SourceFileWithReferences) tree; + Path sourcePath = sourceFile.getSourcePath(); + Collection references = sourceFile.getReferences().findMatches(new ImageMatcher()); + Map> matches = new HashMap<>(); + for (Reference ref : references) { + DockerImageVersion from = DockerImageVersion.of(ref.getValue()); + dockerBaseImages.insertRow(ctx, + new DockerBaseImages.Row(sourcePath.toString(), tree.getClass().getSimpleName(), from.getImageName(), from.getVersion()) + ); + matches.computeIfAbsent(ref.getTree(), t -> new ArrayList<>()).add(ref); + } + return new ReferenceFindSearchResultVisitor(matches).visit(tree, ctx, getCursor()); + } + return tree; + } + }; + } + + @Value + @EqualsAndHashCode(callSuper = false) + private static class ReferenceFindSearchResultVisitor extends TreeVisitor { + Map> matches; + + @Override + public Tree postVisit(Tree tree, ExecutionContext ctx) { + List references = matches.get(tree); + if (references != null) { + if (tree instanceof PlainText) { + String find = references.stream().map(Reference::getValue).sorted().collect(joining("|")); + return new Find(find, true, null, null, null, null, true) + .getVisitor() + .visitNonNull(tree, ctx); + } + return SearchResult.found(tree, references.get(0).getValue()); + } + return tree; + } + } + + ; + + /*@Override public TreeVisitor getVisitor() { return dockerfile().asVisitor((docker, ctx) -> { List froms = docker.getFroms(); @@ -57,5 +110,5 @@ public TreeVisitor getVisitor() { } return docker.getTree(); }); - } + }*/ } diff --git a/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java b/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java index c226836..ae65b97 100644 --- a/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java +++ b/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java @@ -30,6 +30,14 @@ public DockerBaseImages(Recipe recipe) { @Value public static class Row { + @Column(displayName = "Source path before the run", + description = "The source path of the file before the run.") + String sourcePath; + + @Column(displayName = "LST type", + description = "The LST model type that the file is parsed as.") + String type; + @Column(displayName = "Image name", description = "The full name of the image.") String imageName; diff --git a/src/main/java/org/openrewrite/docker/trait/Dockerfile.java b/src/main/java/org/openrewrite/docker/trait/Dockerfile.java index dc13348..77742a5 100644 --- a/src/main/java/org/openrewrite/docker/trait/Dockerfile.java +++ b/src/main/java/org/openrewrite/docker/trait/Dockerfile.java @@ -16,11 +16,10 @@ package org.openrewrite.docker.trait; import lombok.Value; -import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; import org.openrewrite.docker.DockerImageVersion; import org.openrewrite.text.PlainText; -import org.openrewrite.trait.SimpleTraitMatcher; +import org.openrewrite.trait.Reference; import org.openrewrite.trait.Trait; import java.util.ArrayList; @@ -44,19 +43,16 @@ public List getFroms() { return froms; } - public static class Matcher extends SimpleTraitMatcher { + public static class Matcher implements Reference.Matcher { @Override - protected @Nullable Dockerfile test(Cursor cursor) { - Object value = cursor.getValue(); - if (value instanceof PlainText) { - PlainText text = (PlainText) value; - String fileName = text.getSourcePath().toFile().getName(); - if (fileName.equals("Dockerfile") || fileName.equals("Containerfile")) { - return new Dockerfile(cursor); - } - } - return null; + public boolean matchesReference(Reference reference) { + return reference.getKind().equals(Reference.Kind.IMAGE); + } + + @Override + public Reference.Renamer createRenamer(String newName) { + return reference -> newName; } } } diff --git a/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java b/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java new file mode 100644 index 0000000..b9c7452 --- /dev/null +++ b/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java @@ -0,0 +1,63 @@ +package org.openrewrite.docker.trait; + +import lombok.Value; +import org.openrewrite.Cursor; +import org.openrewrite.SourceFile; +import org.openrewrite.internal.StringUtils; +import org.openrewrite.text.PlainText; +import org.openrewrite.trait.Reference; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +@Value +public class DockerfileImageReference implements Reference { + Cursor cursor; + String value; + + @Override + public Kind getKind() { + return Kind.IMAGE; + } + + public static class Provider implements Reference.Provider { + @Override + public boolean isAcceptable(SourceFile sourceFile) { + if (sourceFile instanceof PlainText) { + PlainText text = (PlainText) sourceFile; + String fileName = text.getSourcePath().toFile().getName(); + return (fileName.endsWith("Dockerfile") || fileName.equals("Containerfile")) + && (text.getText().contains("FROM") || text.getText().contains("from")); + } + return false; + } + + @Override + public Set getReferences(SourceFile sourceFile) { + Cursor c = new Cursor(new Cursor(null, Cursor.ROOT_VALUE), sourceFile); + String[] words = ((PlainText) sourceFile).getText() + .replaceAll("\\s*#.*?\\n", "") // remove comments + .replaceAll("\".*?\"", "") // remove string literals + .split("\\s+"); + + Set references = new HashSet<>(); + ArrayList imageVariables = new ArrayList<>(); + for (int i = 0, wordsLength = words.length; i < wordsLength; i++) { + if ("from".equalsIgnoreCase(words[i])) { + String image = words[i + 1].startsWith("--platform") ? words[i + 2] : words[i + 1]; + references.add(new org.openrewrite.text.DockerImageReference(c, image)); + } else if ("as".equalsIgnoreCase(words[i])) { + imageVariables.add(words[i + 1]); + } else if (words[i].startsWith("--from") && words[i].split("=").length == 2) { + String image = words[i].split("=")[1]; + if (!imageVariables.contains(image) && !StringUtils.isNumeric(image)) { + references.add(new org.openrewrite.text.DockerImageReference(c, image)); + } + } + } + + return references; + } + } +} diff --git a/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java b/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java new file mode 100644 index 0000000..e9082cc --- /dev/null +++ b/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java @@ -0,0 +1,16 @@ +package org.openrewrite.docker.trait; + +import org.openrewrite.trait.Reference; + +public class ImageMatcher implements Reference.Matcher { + + @Override + public boolean matchesReference(Reference reference) { + return reference.getKind().equals(Reference.Kind.IMAGE); + } + + @Override + public Reference.Renamer createRenamer(String newName) { + return reference -> newName; + } +} diff --git a/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java new file mode 100644 index 0000000..6602851 --- /dev/null +++ b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java @@ -0,0 +1,49 @@ +package org.openrewrite.docker.trait; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.Cursor; +import org.openrewrite.trait.Reference; +import org.openrewrite.trait.SimpleTraitMatcher; +import org.openrewrite.yaml.trait.YamlReference; +import org.openrewrite.yaml.tree.Yaml; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; +import java.util.regex.Pattern; + +@Value +@EqualsAndHashCode(callSuper = false) +public class YamlImageReference extends YamlReference { + Cursor cursor; + + @Override + public Reference.Kind getKind() { + return Reference.Kind.IMAGE; + } + + public static class Provider extends YamlProvider { + @Override + public SimpleTraitMatcher getMatcher() { + return new SimpleTraitMatcher() { + private final Predicate image = Pattern.compile("image").asPredicate(); + private final AtomicBoolean found = new AtomicBoolean(false); + + @Override + protected @Nullable YamlReference test(Cursor cursor) { + Object value = cursor.getValue(); + if (value instanceof Yaml.Scalar) { + if (image.test(((Yaml.Scalar) value).getValue())) { + found.set(true); + } else if (found.get()) { + found.set(false); + return new YamlImageReference(cursor); + } + } + return null; + } + }; + } + } +} diff --git a/src/main/resources/services/org.openrewrite.trait.Reference$Provider b/src/main/resources/services/org.openrewrite.trait.Reference$Provider new file mode 100644 index 0000000..94c0c37 --- /dev/null +++ b/src/main/resources/services/org.openrewrite.trait.Reference$Provider @@ -0,0 +1,2 @@ +org.openrewrite.docker.trait.YamlImageReference$Provider +org.openrewrite.docker.trait.DockerfileImageReference$Provider diff --git a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java index 39d2578..9ca4628 100644 --- a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java +++ b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java @@ -24,8 +24,11 @@ import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import java.util.function.Consumer; + import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.test.SourceSpecs.text; +import static org.openrewrite.yaml.Assertions.yaml; class FindDockerImagesUsedTest implements RewriteTest { @@ -48,7 +51,7 @@ void dockerfile(String path) { SHELL ["sh", "-lc"] """, """ - ~~(nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04)~~>FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04 + FROM ~~(nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04)~~>nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04 LABEL maintainer="Hugging Face" ARG DEBIAN_FRONTEND=noninteractive SHELL ["sh", "-lc"] @@ -59,26 +62,222 @@ void dockerfile(String path) { } @Test - void multistageDockerfile() { + void yamlFileWithMultipleImages() { rewriteRun( - spec -> spec.dataTable(DockerBaseImages.Row.class, rows -> assertThat(rows) - .containsOnly(new DockerBaseImages.Row("nvidia/cuda", "11.8.0-cudnn8-devel-ubuntu20.04"))), + assertImages("golang:1.7.0", "golang:1.7.0", "golang:1.7.3"), + yaml( + """ + test: + image: golang:1.7.3 + + accp: + image: golang:1.7.0 + + prod: + image: golang:1.7.0 + """, + """ + test: + image: ~~(golang:1.7.3)~~>golang:1.7.3 + + accp: + image: ~~(golang:1.7.0)~~>golang:1.7.0 + + prod: + image: ~~(golang:1.7.0)~~>golang:1.7.0 + """ + ) + ); + } + + @Test + void dockerFile() { + rewriteRun( + assertImages("golang:1.7.3"), text( //language=Dockerfile """ - FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04 AS base - LABEL maintainer="Hugging Face" - ARG DEBIAN_FRONTEND=noninteractive - SHELL ["sh", "-lc"] + FROM golang:1.7.3 as builder + WORKDIR /go/src/github.com/alexellis/href-counter/ + RUN go get -d -v golang.org/x/net/html + COPY app.go . + RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . """, """ - ~~(nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04)~~>FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04 AS base - LABEL maintainer="Hugging Face" - ARG DEBIAN_FRONTEND=noninteractive - SHELL ["sh", "-lc"] + FROM ~~(golang:1.7.3)~~>golang:1.7.3 as builder + WORKDIR /go/src/github.com/alexellis/href-counter/ + RUN go get -d -v golang.org/x/net/html + COPY app.go . + RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . + """, + spec -> spec.path("Dockerfile") + ) + ); + } + + @Test + void dockerMultipleStageFileWithLowerCaseText() { + rewriteRun( + assertImages("alpine:latest", "golang:1.7.3"), + text( + //language=Dockerfile + """ + FROM golang:1.7.3 as builder + WORKDIR /go/src/github.com/alexellis/href-counter/ + RUN go get -d -v golang.org/x/net/html + COPY app.go . + RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . + + from alpine:latest + run apk --no-cache add ca-certificates + workdir /root/ + copy --from=builder /go/src/github.com/alexellis/href-counter/app . + cmd ["./app"] + """, + """ + FROM ~~(golang:1.7.3)~~>golang:1.7.3 as builder + WORKDIR /go/src/github.com/alexellis/href-counter/ + RUN go get -d -v golang.org/x/net/html + COPY app.go . + RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . + + from ~~(alpine:latest)~~>alpine:latest + run apk --no-cache add ca-certificates + workdir /root/ + copy --from=builder /go/src/github.com/alexellis/href-counter/app . + cmd ["./app"] + """, + spec -> spec.path("Dockerfile") + ) + ); + } + + @Test + void dockerMultipleStageFileWithImageInFromOption() { + rewriteRun( + assertImages("alpine:latest", "nginx:latest"), + text( + //language=Dockerfile + """ + FROM alpine:latest + COPY --from=nginx:latest /etc/nginx/nginx.conf /etc/nginx/ + COPY --from=nginx:latest /usr/share/nginx/html /usr/share/nginx/html + RUN apk add --no-cache bash + WORKDIR /usr/share/nginx/html + CMD ["ls", "-la", "/usr/share/nginx/html"] + """, + """ + FROM ~~(alpine:latest)~~>alpine:latest + COPY --from=~~(nginx:latest)~~>nginx:latest /etc/nginx/nginx.conf /etc/nginx/ + COPY --from=~~(nginx:latest)~~>nginx:latest /usr/share/nginx/html /usr/share/nginx/html + RUN apk add --no-cache bash + WORKDIR /usr/share/nginx/html + CMD ["ls", "-la", "/usr/share/nginx/html"] + """, + spec -> spec.path("Dockerfile") + ) + ); + } + + @Test + void dockerMultipleStageFileWithFromOptionAsStageNumber() { + rewriteRun( + assertImages("golang:1.23", "scratch"), + text( + //language=Dockerfile + """ + # syntax=docker/dockerfile:1 + FROM golang:1.23 + WORKDIR /src + COPY <golang:1.23 + WORKDIR /src + COPY <scratch + COPY --from=0 /bin/hello /bin/hello + CMD ["/bin/hello"] """, spec -> spec.path("Dockerfile") ) ); } + + + @Test + void platformDockerfile() { + rewriteRun( + assertImages("alpine:latest"), + text( + //language=Dockerfile + """ + FROM --platform=linux/arm64 alpine:latest + RUN echo "Hello from ARM64!" > /message.txt + CMD ["cat", "/message.txt"] + """, + """ + FROM --platform=linux/arm64 ~~(alpine:latest)~~>alpine:latest + RUN echo "Hello from ARM64!" > /message.txt + CMD ["cat", "/message.txt"] + """, + spec -> spec.path("Dockerfile") + ) + ); + } + + @Test + void dockerFileIgnoreComment() { + rewriteRun( + assertImages("alpine:latest"), + text( + //language=Dockerfile + """ + # FROM alpine + FROM alpine:latest + """, + """ + # FROM alpine + FROM ~~(alpine:latest)~~>alpine:latest + """, + spec -> spec.path("Dockerfile") + ) + ); + } + + private static Consumer assertImages(String... expected) { + return spec -> spec.recipe(new FindDockerImageUses()) + .dataTable(DockerBaseImages.Row.class,rows -> + assertThat(rows) + .hasSize(expected.length) + .extracting(it -> it.getImageName() + ":" + it.getTag()) + .containsExactlyInAnyOrder(expected) + ); + } } From ee296fd4ecce8b83c29d25c87a3125d4f48baa85 Mon Sep 17 00:00:00 2001 From: lingenj Date: Mon, 23 Dec 2024 08:56:22 +0100 Subject: [PATCH 02/11] Implementation --- .../docker/search/FindDockerImageUses.java | 21 ------ .../trait/DockerfileImageReference.java | 4 +- .../org.openrewrite.trait.Reference$Provider | 0 .../docker/FindDockerImagesUsedTest.java | 71 ++++++++++++++++++- 4 files changed, 72 insertions(+), 24 deletions(-) rename src/main/resources/{ => META-INF}/services/org.openrewrite.trait.Reference$Provider (100%) diff --git a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java index ef06c33..dea8ee6 100644 --- a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java +++ b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java @@ -90,25 +90,4 @@ public Tree postVisit(Tree tree, ExecutionContext ctx) { return tree; } } - - ; - - /*@Override - public TreeVisitor getVisitor() { - return dockerfile().asVisitor((docker, ctx) -> { - List froms = docker.getFroms(); - if (!froms.isEmpty()) { - for (DockerImageVersion from : froms) { - dockerBaseImages.insertRow(ctx, new DockerBaseImages.Row( - from.getImageName(), - from.getVersion() == null ? "" : from.getVersion() - )); - } - return SearchResult.found(docker.getTree(), - froms.stream().map(DockerImageVersion::toString) - .collect(Collectors.joining(", "))); - } - return docker.getTree(); - }); - }*/ } diff --git a/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java b/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java index b9c7452..7fc5059 100644 --- a/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java +++ b/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java @@ -46,13 +46,13 @@ public Set getReferences(SourceFile sourceFile) { for (int i = 0, wordsLength = words.length; i < wordsLength; i++) { if ("from".equalsIgnoreCase(words[i])) { String image = words[i + 1].startsWith("--platform") ? words[i + 2] : words[i + 1]; - references.add(new org.openrewrite.text.DockerImageReference(c, image)); + references.add(new DockerfileImageReference(c, image)); } else if ("as".equalsIgnoreCase(words[i])) { imageVariables.add(words[i + 1]); } else if (words[i].startsWith("--from") && words[i].split("=").length == 2) { String image = words[i].split("=")[1]; if (!imageVariables.contains(image) && !StringUtils.isNumeric(image)) { - references.add(new org.openrewrite.text.DockerImageReference(c, image)); + references.add(new DockerfileImageReference(c, image)); } } } diff --git a/src/main/resources/services/org.openrewrite.trait.Reference$Provider b/src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider similarity index 100% rename from src/main/resources/services/org.openrewrite.trait.Reference$Provider rename to src/main/resources/META-INF/services/org.openrewrite.trait.Reference$Provider diff --git a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java index 9ca4628..23ae9f9 100644 --- a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java +++ b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java @@ -271,12 +271,81 @@ void dockerFileIgnoreComment() { ); } + @Test + void gitlabCIFile() { + rewriteRun( + assertImages("maven:latest"), + //language=yaml + yaml( + """ + image: maven:latest + + variables: + MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode" + MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" + + cache: + paths: + - .m2/repository/ + - target/ + + build: + stage: build + script: + - mvn $MAVEN_CLI_OPTS compile + + test: + stage: test + script: + - mvn $MAVEN_CLI_OPTS test + + deploy: + stage: deploy + script: + - mvn $MAVEN_CLI_OPTS deploy + only: + - master + """, + """ + image: ~~(maven:latest)~~>maven:latest + + variables: + MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode" + MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" + + cache: + paths: + - .m2/repository/ + - target/ + + build: + stage: build + script: + - mvn $MAVEN_CLI_OPTS compile + + test: + stage: test + script: + - mvn $MAVEN_CLI_OPTS test + + deploy: + stage: deploy + script: + - mvn $MAVEN_CLI_OPTS deploy + only: + - master + """, + spec -> spec.path(".gitlab-ci") + ) + ); + } + private static Consumer assertImages(String... expected) { return spec -> spec.recipe(new FindDockerImageUses()) .dataTable(DockerBaseImages.Row.class,rows -> assertThat(rows) .hasSize(expected.length) - .extracting(it -> it.getImageName() + ":" + it.getTag()) + .extracting(it -> it.getImageName() + (it.getTag().isEmpty() ? "" : ":" + it.getTag())) .containsExactlyInAnyOrder(expected) ); } From 5681c09ac17fd348c9e6242696723dc31f140b22 Mon Sep 17 00:00:00 2001 From: lingenj Date: Tue, 24 Dec 2024 15:38:23 +0100 Subject: [PATCH 03/11] Remove old code --- .../openrewrite/docker/trait/Dockerfile.java | 58 ------------------- .../org/openrewrite/docker/trait/Traits.java | 26 --------- 2 files changed, 84 deletions(-) delete mode 100644 src/main/java/org/openrewrite/docker/trait/Dockerfile.java delete mode 100644 src/main/java/org/openrewrite/docker/trait/Traits.java diff --git a/src/main/java/org/openrewrite/docker/trait/Dockerfile.java b/src/main/java/org/openrewrite/docker/trait/Dockerfile.java deleted file mode 100644 index 77742a5..0000000 --- a/src/main/java/org/openrewrite/docker/trait/Dockerfile.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 the original author or authors. - *

- * Licensed under the Moderne Source Available License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * https://docs.moderne.io/licensing/moderne-source-available-license - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openrewrite.docker.trait; - -import lombok.Value; -import org.openrewrite.Cursor; -import org.openrewrite.docker.DockerImageVersion; -import org.openrewrite.text.PlainText; -import org.openrewrite.trait.Reference; -import org.openrewrite.trait.Trait; - -import java.util.ArrayList; -import java.util.List; - -@Value -public class Dockerfile implements Trait

{ - Cursor cursor; - - public List<DockerImageVersion> getFroms() { - List<DockerImageVersion> froms = new ArrayList<>(); - for (String line : getTree().getText().split("\\R")) { - if (line.startsWith("FROM")) { - String[] imageVersionStr = line.substring("FROM".length()).trim().split(":"); - froms.add(new DockerImageVersion( - imageVersionStr[0], - imageVersionStr.length > 1 ? imageVersionStr[1].split(" ")[0] : null - )); - } - } - return froms; - } - - public static class Matcher implements Reference.Matcher { - - @Override - public boolean matchesReference(Reference reference) { - return reference.getKind().equals(Reference.Kind.IMAGE); - } - - @Override - public Reference.Renamer createRenamer(String newName) { - return reference -> newName; - } - } -} diff --git a/src/main/java/org/openrewrite/docker/trait/Traits.java b/src/main/java/org/openrewrite/docker/trait/Traits.java deleted file mode 100644 index 2753f6f..0000000 --- a/src/main/java/org/openrewrite/docker/trait/Traits.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 the original author or authors. - * <p> - * Licensed under the Moderne Source Available License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p> - * https://docs.moderne.io/licensing/moderne-source-available-license - * <p> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openrewrite.docker.trait; - -public class Traits { - - private Traits() { - } - - public static Dockerfile.Matcher dockerfile() { - return new Dockerfile.Matcher(); - } -} From dae3c0edd2512e32bbd9a363fd84a6ae98281af1 Mon Sep 17 00:00:00 2001 From: lingenj <jacob.van.lingen@moderne.io> Date: Tue, 24 Dec 2024 15:38:55 +0100 Subject: [PATCH 04/11] Add kubernetes-file test --- .../docker/search/FindDockerImageUses.java | 2 +- .../docker/trait/YamlImageReference.java | 7 ++- .../docker/FindDockerImagesUsedTest.java | 53 +++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java index dea8ee6..5d49a31 100644 --- a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java +++ b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java @@ -76,7 +76,7 @@ private static class ReferenceFindSearchResultVisitor extends TreeVisitor<Tree, Map<Tree, List<Reference>> matches; @Override - public Tree postVisit(Tree tree, ExecutionContext ctx) { + public @Nullable Tree postVisit(Tree tree, ExecutionContext ctx) { List<Reference> references = matches.get(tree); if (references != null) { if (tree instanceof PlainText) { diff --git a/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java index 6602851..cdf4864 100644 --- a/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java +++ b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java @@ -27,18 +27,17 @@ public static class Provider extends YamlProvider { @Override public SimpleTraitMatcher<YamlReference> getMatcher() { return new SimpleTraitMatcher<YamlReference>() { - private final Predicate<String> image = Pattern.compile("image").asPredicate(); private final AtomicBoolean found = new AtomicBoolean(false); @Override protected @Nullable YamlReference test(Cursor cursor) { Object value = cursor.getValue(); if (value instanceof Yaml.Scalar) { - if (image.test(((Yaml.Scalar) value).getValue())) { - found.set(true); - } else if (found.get()) { + if (found.get()) { found.set(false); return new YamlImageReference(cursor); + } else if ("image".equals(((Yaml.Scalar) value).getValue())) { + found.set(true); } } return null; diff --git a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java index 23ae9f9..2683caf 100644 --- a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java +++ b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java @@ -340,6 +340,59 @@ void gitlabCIFile() { ); } + @Test + void kubernetesFile() { + rewriteRun( + assertImages("image", "app:v1.2.3", "account/image:latest", "repo.id/account/bucket/image:v1.2.3@digest"), + //language=yaml + yaml( + """ + apiVersion: v1 + kind: Pod + spec: + containers: + - image: image + --- + apiVersion: v1 + kind: Pod + spec: + containers: + - image: app:v1.2.3 + initContainers: + - image: account/image:latest + --- + apiVersion: v1 + kind: Pod + spec: + containers: + - image: repo.id/account/bucket/image:v1.2.3@digest + """, + """ + apiVersion: v1 + kind: Pod + spec: + containers: + - image: ~~(image)~~>image + --- + apiVersion: v1 + kind: Pod + spec: + containers: + - image: ~~(app:v1.2.3)~~>app:v1.2.3 + initContainers: + - image: ~~(account/image:latest)~~>account/image:latest + --- + apiVersion: v1 + kind: Pod + spec: + containers: + - image: ~~(repo.id/account/bucket/image:v1.2.3@digest)~~>repo.id/account/bucket/image:v1.2.3@digest + """, + spec -> spec.path(".gitlab-ci") + ) + ); + } + private static Consumer<RecipeSpec> assertImages(String... expected) { return spec -> spec.recipe(new FindDockerImageUses()) .dataTable(DockerBaseImages.Row.class,rows -> From c76b783e758f3408533c5bdf870425cb10f1b5f6 Mon Sep 17 00:00:00 2001 From: lingenj <jacob.van.lingen@moderne.io> Date: Tue, 24 Dec 2024 15:40:01 +0100 Subject: [PATCH 05/11] cleanup --- .../java/org/openrewrite/docker/trait/YamlImageReference.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java index cdf4864..6671013 100644 --- a/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java +++ b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java @@ -10,8 +10,6 @@ import org.openrewrite.yaml.tree.Yaml; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Predicate; -import java.util.regex.Pattern; @Value @EqualsAndHashCode(callSuper = false) From 27ffeb5140d02c1e492ddb67e0a22dd16e4902d0 Mon Sep 17 00:00:00 2001 From: lingenj <jacob.van.lingen@moderne.io> Date: Tue, 24 Dec 2024 15:45:20 +0100 Subject: [PATCH 06/11] Mention kubernetes in the description --- .../java/org/openrewrite/docker/search/FindDockerImageUses.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java index 5d49a31..3908365 100644 --- a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java +++ b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java @@ -42,7 +42,7 @@ public String getDisplayName() { @Override public String getDescription() { - return "Produce an impact analysis of base images used in Dockerfiles, .gitlab-ci files, etc."; + return "Produce an impact analysis of base images used in Dockerfiles, .gitlab-ci files, Kubernetes Deployment file, etc."; } @Override From 8766668e4369528664c30984b999916f6d2d144a Mon Sep 17 00:00:00 2001 From: lingenj <jacob.van.lingen@moderne.io> Date: Tue, 24 Dec 2024 16:15:48 +0100 Subject: [PATCH 07/11] Small improvements --- .../docker/trait/YamlImageReference.java | 34 ++++++++++--------- .../docker/FindDockerImagesUsedTest.java | 12 +++++-- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java index 6671013..fb4fa80 100644 --- a/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java +++ b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java @@ -22,25 +22,27 @@ public Reference.Kind getKind() { } public static class Provider extends YamlProvider { - @Override - public SimpleTraitMatcher<YamlReference> getMatcher() { - return new SimpleTraitMatcher<YamlReference>() { - private final AtomicBoolean found = new AtomicBoolean(false); + private static final SimpleTraitMatcher<YamlReference> matcher = new SimpleTraitMatcher<YamlReference>() { + private final AtomicBoolean found = new AtomicBoolean(false); - @Override - protected @Nullable YamlReference test(Cursor cursor) { - Object value = cursor.getValue(); - if (value instanceof Yaml.Scalar) { - if (found.get()) { - found.set(false); - return new YamlImageReference(cursor); - } else if ("image".equals(((Yaml.Scalar) value).getValue())) { - found.set(true); - } + @Override + protected @Nullable YamlReference test(Cursor cursor) { + Object value = cursor.getValue(); + if (value instanceof Yaml.Scalar) { + if (found.get()) { + found.set(false); + return new YamlImageReference(cursor); + } else if ("image".equals(((Yaml.Scalar) value).getValue())) { + found.set(true); } - return null; } - }; + return null; + } + }; + + @Override + public SimpleTraitMatcher<YamlReference> getMatcher() { + return matcher; } } } diff --git a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java index 2683caf..ca3b94d 100644 --- a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java +++ b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java @@ -352,20 +352,24 @@ void kubernetesFile() { spec: containers: - image: image + name: image-container --- apiVersion: v1 kind: Pod spec: containers: - - image: app:v1.2.3 + - name: my-container + image: app:v1.2.3 initContainers: - image: account/image:latest + name: my-init-container --- apiVersion: v1 kind: Pod spec: containers: - image: repo.id/account/bucket/image:v1.2.3@digest + name: my-container """, """ apiVersion: v1 @@ -373,20 +377,24 @@ void kubernetesFile() { spec: containers: - image: ~~(image)~~>image + name: image-container --- apiVersion: v1 kind: Pod spec: containers: - - image: ~~(app:v1.2.3)~~>app:v1.2.3 + - name: my-container + image: ~~(app:v1.2.3)~~>app:v1.2.3 initContainers: - image: ~~(account/image:latest)~~>account/image:latest + name: my-init-container --- apiVersion: v1 kind: Pod spec: containers: - image: ~~(repo.id/account/bucket/image:v1.2.3@digest)~~>repo.id/account/bucket/image:v1.2.3@digest + name: my-container """, spec -> spec.path(".gitlab-ci") ) From 908bc7448197787db249ef4409357662db97e3cf Mon Sep 17 00:00:00 2001 From: Tim te Beek <tim@moderne.io> Date: Mon, 30 Dec 2024 14:52:25 +0100 Subject: [PATCH 08/11] Add missing license headers --- .../docker/trait/DockerfileImageReference.java | 15 +++++++++++++++ .../openrewrite/docker/trait/ImageMatcher.java | 15 +++++++++++++++ .../docker/trait/YamlImageReference.java | 15 +++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java b/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java index 7fc5059..2e689bc 100644 --- a/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java +++ b/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java @@ -1,3 +1,18 @@ +/* + * Copyright 2024 the original author or authors. + * <p> + * Licensed under the Moderne Source Available License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * https://docs.moderne.io/licensing/moderne-source-available-license + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.openrewrite.docker.trait; import lombok.Value; diff --git a/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java b/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java index e9082cc..1e71016 100644 --- a/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java +++ b/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java @@ -1,3 +1,18 @@ +/* + * Copyright 2024 the original author or authors. + * <p> + * Licensed under the Moderne Source Available License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * https://docs.moderne.io/licensing/moderne-source-available-license + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.openrewrite.docker.trait; import org.openrewrite.trait.Reference; diff --git a/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java index fb4fa80..3a90252 100644 --- a/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java +++ b/src/main/java/org/openrewrite/docker/trait/YamlImageReference.java @@ -1,3 +1,18 @@ +/* + * Copyright 2024 the original author or authors. + * <p> + * Licensed under the Moderne Source Available License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * https://docs.moderne.io/licensing/moderne-source-available-license + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.openrewrite.docker.trait; import lombok.EqualsAndHashCode; From d8532da009e20839dc9866941cc2f7de91d60acc Mon Sep 17 00:00:00 2001 From: Tim te Beek <tim@moderne.io> Date: Mon, 30 Dec 2024 15:07:10 +0100 Subject: [PATCH 09/11] Show that GitHub Actions ci image references are also picked --- .../docker/FindDockerImagesUsedTest.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java index ca3b94d..cb2dcbe 100644 --- a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java +++ b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java @@ -340,6 +340,25 @@ void gitlabCIFile() { ); } + @Test + void githubActions() { + rewriteRun( + assertImages("ghcr.io/owner/image"), + //language=yaml + yaml( + """ + container: + image: ghcr.io/owner/image + """, + """ + container: + image: ~~(ghcr.io/owner/image)~~>ghcr.io/owner/image + """, + spec -> spec.path(".github/workflows/ci.yml") + ) + ); + } + @Test void kubernetesFile() { rewriteRun( @@ -403,7 +422,7 @@ void kubernetesFile() { private static Consumer<RecipeSpec> assertImages(String... expected) { return spec -> spec.recipe(new FindDockerImageUses()) - .dataTable(DockerBaseImages.Row.class,rows -> + .dataTable(DockerBaseImages.Row.class, rows -> assertThat(rows) .hasSize(expected.length) .extracting(it -> it.getImageName() + (it.getTag().isEmpty() ? "" : ":" + it.getTag())) From 41f99c8102eb19d5e962705e7bae6c81f0b19afe Mon Sep 17 00:00:00 2001 From: Tim te Beek <tim@moderne.io> Date: Mon, 30 Dec 2024 15:12:21 +0100 Subject: [PATCH 10/11] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../openrewrite/docker/search/FindDockerImageUses.java | 2 +- .../org/openrewrite/docker/table/DockerBaseImages.java | 8 ++------ .../docker/trait/DockerfileImageReference.java | 4 ++-- .../java/org/openrewrite/docker/trait/ImageMatcher.java | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java index 3908365..0510fbc 100644 --- a/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java +++ b/src/main/java/org/openrewrite/docker/search/FindDockerImageUses.java @@ -59,7 +59,7 @@ public TreeVisitor<?, ExecutionContext> getVisitor() { for (Reference ref : references) { DockerImageVersion from = DockerImageVersion.of(ref.getValue()); dockerBaseImages.insertRow(ctx, - new DockerBaseImages.Row(sourcePath.toString(), tree.getClass().getSimpleName(), from.getImageName(), from.getVersion()) + new DockerBaseImages.Row(sourcePath.toString(), from.getImageName(), from.getVersion()) ); matches.computeIfAbsent(ref.getTree(), t -> new ArrayList<>()).add(ref); } diff --git a/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java b/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java index ae65b97..ac168d6 100644 --- a/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java +++ b/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java @@ -30,14 +30,10 @@ public DockerBaseImages(Recipe recipe) { @Value public static class Row { - @Column(displayName = "Source path before the run", - description = "The source path of the file before the run.") + @Column(displayName = "Source path", + description = "The source file containing the image reference.") String sourcePath; - @Column(displayName = "LST type", - description = "The LST model type that the file is parsed as.") - String type; - @Column(displayName = "Image name", description = "The full name of the image.") String imageName; diff --git a/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java b/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java index 2e689bc..7fc025f 100644 --- a/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java +++ b/src/main/java/org/openrewrite/docker/trait/DockerfileImageReference.java @@ -42,8 +42,8 @@ public boolean isAcceptable(SourceFile sourceFile) { if (sourceFile instanceof PlainText) { PlainText text = (PlainText) sourceFile; String fileName = text.getSourcePath().toFile().getName(); - return (fileName.endsWith("Dockerfile") || fileName.equals("Containerfile")) - && (text.getText().contains("FROM") || text.getText().contains("from")); + return (fileName.endsWith("Dockerfile") || fileName.equals("Containerfile")) && + (text.getText().contains("FROM") || text.getText().contains("from")); } return false; } diff --git a/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java b/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java index 1e71016..2c767da 100644 --- a/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java +++ b/src/main/java/org/openrewrite/docker/trait/ImageMatcher.java @@ -21,7 +21,7 @@ public class ImageMatcher implements Reference.Matcher { @Override public boolean matchesReference(Reference reference) { - return reference.getKind().equals(Reference.Kind.IMAGE); + return reference.getKind() == Reference.Kind.IMAGE; } @Override From 45568cd3a3dca201bc1f19076193a8ff20ce3341 Mon Sep 17 00:00:00 2001 From: lingenj <jacob.van.lingen@moderne.io> Date: Tue, 31 Dec 2024 13:31:38 +0100 Subject: [PATCH 11/11] Make `version` @Nullable again --- src/main/java/org/openrewrite/docker/DockerImageVersion.java | 5 +++-- .../java/org/openrewrite/docker/table/DockerBaseImages.java | 2 ++ .../org/openrewrite/docker/FindDockerImagesUsedTest.java | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/docker/DockerImageVersion.java b/src/main/java/org/openrewrite/docker/DockerImageVersion.java index 852f096..e38e2ea 100644 --- a/src/main/java/org/openrewrite/docker/DockerImageVersion.java +++ b/src/main/java/org/openrewrite/docker/DockerImageVersion.java @@ -22,10 +22,11 @@ public class DockerImageVersion { String imageName; + @Nullable String version; public static DockerImageVersion of(String value) { - String[] imageVersionStr = value.split(":"); - return new DockerImageVersion(imageVersionStr[0], imageVersionStr.length > 1 ? imageVersionStr[1].split(" ")[0] : ""); + String[] imageVersionStr = value.trim().split(":"); + return new DockerImageVersion(imageVersionStr[0], imageVersionStr.length > 1 ? imageVersionStr[1].split(" ")[0] : null); } } diff --git a/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java b/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java index ac168d6..9f8fda9 100644 --- a/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java +++ b/src/main/java/org/openrewrite/docker/table/DockerBaseImages.java @@ -16,6 +16,7 @@ package org.openrewrite.docker.table; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.Column; import org.openrewrite.DataTable; import org.openrewrite.Recipe; @@ -40,6 +41,7 @@ public static class Row { @Column(displayName = "Tag", description = "The tag, if any. If no tag is specified, this will be empty.") + @Nullable String tag; } } diff --git a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java index cb2dcbe..53ff6f7 100644 --- a/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java +++ b/src/test/java/org/openrewrite/docker/FindDockerImagesUsedTest.java @@ -425,7 +425,7 @@ private static Consumer<RecipeSpec> assertImages(String... expected) { .dataTable(DockerBaseImages.Row.class, rows -> assertThat(rows) .hasSize(expected.length) - .extracting(it -> it.getImageName() + (it.getTag().isEmpty() ? "" : ":" + it.getTag())) + .extracting(it -> it.getImageName() + (it.getTag() == null ? "" : ":" + it.getTag())) .containsExactlyInAnyOrder(expected) ); }