com.salesforce.dockerfileimageupdate.model.FromInstruction Maven / Gradle / Ivy
package com.salesforce.dockerfileimageupdate.model;
import com.google.common.collect.ImmutableList;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.List;
/**
* This class represents a FROM instruction line in a Dockerfile and contains methods to determine whether a line
* is a FROM instruction. For more information: https://docs.docker.com/engine/reference/builder/#from
*/
public class FromInstruction {
private static final String NAME = "FROM";
private static final String INVALID_INSTRUCTION_LINE = "You have not provided a valid FROM instruction line.";
/**
* The name of the base image
*/
private final String baseImageName;
/**
* The tag of the base image
*/
private final String tag;
/**
* As of writing, this could include {@code AS name} to run a multi-stage build
*/
private final List additionalParts;
/**
* Comment starting with #
*/
private final String comments;
/**
* Accepts a FROM instruction line from a Dockerfile
* See {@code isFromInstruction} to ensure you're passing a valid line in.
*
* @param fromInstructionLine a FROM instruction line from a Dockerfile
*/
public FromInstruction(String fromInstructionLine) {
if (!isFromInstruction(fromInstructionLine)) {
throw new IllegalArgumentException(INVALID_INSTRUCTION_LINE);
}
String lineWithoutComment = fromInstructionLine;
int commentIndex = fromInstructionLine.indexOf("#");
if (commentIndex >= 0) {
comments = fromInstructionLine.substring(commentIndex);
lineWithoutComment = fromInstructionLine.substring(0, commentIndex);
} else {
comments = null;
}
// Trim the space now that we don't have comments to worry about
lineWithoutComment = lineWithoutComment.trim();
String[] lineParts = lineWithoutComment.split("\\s+");
if (lineParts.length > 1) {
// The image will be after the FROM part
String dockerFileImage = lineParts[1];
String[] imageAndTag = dockerFileImage.split(":");
baseImageName = imageAndTag[0];
if (imageAndTag.length > 1) {
tag = imageAndTag[1];
} else {
tag = null;
}
if (lineParts.length > 2) {
additionalParts = ImmutableList.copyOf(Arrays.asList(lineParts).subList(2, lineParts.length));
} else {
additionalParts = ImmutableList.of();
}
} else {
baseImageName = null;
tag = null;
additionalParts = ImmutableList.of();
}
}
/**
* Internal API to get a new FromInstruction from an existing object
* @param baseImageName baseImageName to add
* @param tag tag to add
* @param additionalParts additionalParts to add
* @param comments comments to add
*/
private FromInstruction(String baseImageName, String tag, List additionalParts, String comments) {
this.baseImageName = baseImageName;
this.tag = tag;
this.additionalParts = ImmutableList.copyOf(additionalParts);
this.comments = comments;
}
/**
* Check if this {@code lineInFile} is a FROM instruction,
* it is referencing {@code imageName} as a base image,
* and the tag is not the same as {@code imageTag} (or there is no tag)
* @param lineInFile Line a code file
* @param imageName images name
* @param imageTag tag for imageName
* @return {@link Boolean} value isFromInstructionWithThisImageAndOlderTag
*/
public static boolean isFromInstructionWithThisImageAndOlderTag(String lineInFile, String imageName, String imageTag) {
if (FromInstruction.isFromInstruction(lineInFile)) {
FromInstruction fromInstruction = new FromInstruction(lineInFile);
return fromInstruction.hasBaseImage(imageName) && fromInstruction.hasADifferentTag(imageTag);
}
return false;
}
/**
* Get a new {@code FromInstruction} the same as this but with the {@code tag} set as {@code newTag}
* @param newTag the new image tag
* @return a new FROM with the new image tag
*/
public FromInstruction getFromInstructionWithNewTag(String newTag) {
return new FromInstruction(baseImageName, newTag, additionalParts, comments);
}
/**
* Determines whether the line is a FROM instruction line in a Dockerfile
* @param dockerFileLine a single line from a Dockerfile
* @return the line is a FROM instruction line or not
*/
public static boolean isFromInstruction(String dockerFileLine) {
if (StringUtils.isNotBlank(dockerFileLine)) {
return dockerFileLine.trim().startsWith(FromInstruction.NAME);
}
return false;
}
/**
* @return a String representation of a FROM instruction line in Dockerfile. No new line at the end
*/
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder(NAME);
stringBuilder.append(" ");
stringBuilder.append(baseImageName);
if (hasTag()) {
stringBuilder.append(String.format(":%s", tag.trim()));
}
for (String part : additionalParts) {
if (StringUtils.isNotBlank(part)) {
stringBuilder.append(String.format(" %s", part.trim()));
}
}
if (hasComments()) {
stringBuilder.append(String.format(" %s", comments));
}
return stringBuilder.toString();
}
public String getBaseImageName() {
return baseImageName;
}
/**
* Check to see if the {@code baseImageName} in this object is the {@code imageToFind} without
* the other details (e.g. registry)
* @param imageToFind the image name to search for
* @return is {@code baseImageName} the same as {@code imageToFind} without extra things like registry
*/
public boolean hasBaseImage(String imageToFind) {
return baseImageName != null &&
imageToFind != null &&
baseImageName.endsWith(imageToFind);
}
/**
* @return whether the {@code FromInstruction} has a {@code tag}
*/
public boolean hasTag() {
return tag != null;
}
/**
* Determines whether the {@code tag} and {@code expectedTag} are the same
* @param expectedTag the tag to compare against FromInstruction's {@code tag}
* @return {@code true} if the 2 tags are different
*/
public boolean hasADifferentTag(String expectedTag) {
if (tag == null && expectedTag == null) {
return false;
}
if (tag == null || expectedTag == null) {
return true;
}
return !tag.trim().equals(expectedTag.trim());
}
public String getTag() {
return tag;
}
public List getAdditionalParts() {
return additionalParts;
}
public boolean hasComments() {
return comments != null;
}
public String getComments() {
return comments;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy