Skip to content

Commit 890e120

Browse files
authored
Merge pull request #687 from pchugh17/issue_236
PR to fix #236, to bring in support for sending PRs to docker-compose files
2 parents 0744b1f + 696e7e8 commit 890e120

File tree

25 files changed

+719
-118
lines changed

25 files changed

+719
-118
lines changed

dockerfile-image-update/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@
9797
</dependency>
9898
<dependency>
9999
<groupId>org.mockito</groupId>
100-
<artifactId>mockito-all</artifactId>
101-
<version>1.10.19</version>
100+
<artifactId>mockito-core</artifactId>
101+
<version>4.11.0</version>
102102
<scope>test</scope>
103103
</dependency>
104104
<dependency>

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/CommandLine.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static void main(String[] args)
4949
DockerfileGitHubUtil dockerfileGitHubUtil = initializeDockerfileGithubUtil(ns.get(Constants.GIT_API));
5050

5151
/* Execute given command. */
52-
((ExecutableWithNamespace)runClass.newInstance()).execute(ns, dockerfileGitHubUtil);
52+
((ExecutableWithNamespace)runClass.getDeclaredConstructor().newInstance()).execute(ns, dockerfileGitHubUtil);
5353
}
5454

5555
static ArgumentParser getArgumentParser() {
@@ -83,14 +83,19 @@ static ArgumentParser getArgumentParser() {
8383
.setDefault(false)
8484
.help("Only update image tag store. Skip creating PRs");
8585
parser.addArgument("-x")
86-
.help("comment snippet mentioned in line just before FROM instruction for ignoring a child image. " +
86+
.help("comment snippet mentioned in line just before 'FROM' instruction(Dockerfile)" +
87+
"or 'image' instruction(docker-compose) for ignoring a child image. " +
8788
"Defaults to 'no-dfiu'");
89+
parser.addArgument("-t", "--" + FILE_NAMES_TO_SEARCH)
90+
.type(String.class)
91+
.setDefault("Dockerfile,docker-compose")
92+
.help("Comma seperated list of filenames to search & update for PR creation" +
93+
"(default: Dockerfile,docker-compose)");
8894
parser.addArgument("-r", "--" + RATE_LIMIT_PR_CREATION)
8995
.type(String.class)
9096
.setDefault("")
9197
.required(false)
9298
.help("Use RateLimiting when sending PRs. RateLimiting is enabled only if this value is set it's disabled by default.");
93-
9499
return parser;
95100
}
96101

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/model/GitForkBranch.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.salesforce.dockerfileimageupdate.model;
22

3+
import com.salesforce.dockerfileimageupdate.utils.Constants;
4+
import org.apache.commons.lang3.StringUtils;
5+
36
/**
47
* Converts an imageName to a branchName. Primary conversion necessary is : to -
58
* Also support backward compatible method of specifying a branch
@@ -9,11 +12,12 @@
912
*/
1013
public class GitForkBranch {
1114
private final String branchPrefix;
15+
private final String branchSuffix;
1216
private final String imageName;
1317
private final String imageTag;
1418
private final boolean specifiedBranchOverride;
1519

16-
public GitForkBranch(String imageName, String imageTag, String specifiedBranch) {
20+
public GitForkBranch(String imageName, String imageTag, String specifiedBranch, String filenamesSearchedFor) {
1721
this.imageTag = (imageTag == null || imageTag.trim().isEmpty()) ? "" : imageTag.trim();
1822
this.imageName = (imageName == null || imageName.trim().isEmpty()) ? "" : imageName.trim();
1923
if (specifiedBranch == null || specifiedBranch.trim().isEmpty()) {
@@ -22,9 +26,11 @@ public GitForkBranch(String imageName, String imageTag, String specifiedBranch)
2226
}
2327
this.branchPrefix = this.imageName.replace(":", "-").toLowerCase();
2428
this.specifiedBranchOverride = false;
29+
this.branchSuffix = getBranchSuffix(StringUtils.isNotBlank(filenamesSearchedFor) ? filenamesSearchedFor.toLowerCase():filenamesSearchedFor);
2530
} else {
2631
this.branchPrefix = specifiedBranch;
2732
this.specifiedBranchOverride = true;
33+
this.branchSuffix = "";
2834
}
2935
}
3036

@@ -71,9 +77,9 @@ public boolean useSpecifiedBranchOverride() {
7177
*/
7278
public String getBranchName() {
7379
if (this.imageTag == null || this.imageTag.trim().isEmpty()) {
74-
return this.branchPrefix;
80+
return this.branchPrefix + this.branchSuffix;
7581
} else {
76-
return this.branchPrefix + "-" + this.imageTag;
82+
return this.branchPrefix + "-" + this.imageTag + this.branchSuffix;
7783
}
7884
}
7985

@@ -84,4 +90,18 @@ public String getImageName() {
8490
public String getImageTag() {
8591
return imageTag;
8692
}
93+
94+
private String getBranchSuffix(String filenamesSearchedFor) {
95+
// To avoid branch with same imageName-tag getting overwritten in case multiple runs(with different file types)
96+
// on same image tag store, adding suffix to branch name
97+
if (StringUtils.isBlank(filenamesSearchedFor)) {
98+
return "";
99+
} else if (filenamesSearchedFor.contains(Constants.FILENAME_DOCKERFILE) && !filenamesSearchedFor.contains(Constants.FILENAME_DOCKER_COMPOSE)) {
100+
return "_dockerfile";
101+
} else if (filenamesSearchedFor.contains(Constants.FILENAME_DOCKER_COMPOSE) && !filenamesSearchedFor.contains(Constants.FILENAME_DOCKERFILE)) {
102+
return "_dockercompose";
103+
} else {
104+
return "";
105+
}
106+
}
87107
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package com.salesforce.dockerfileimageupdate.model;
2+
3+
import com.salesforce.dockerfileimageupdate.utils.DockerfileGitHubUtil;
4+
import org.apache.commons.lang3.StringUtils;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
import java.util.regex.Pattern;
9+
10+
public class ImageKeyValuePair {
11+
private static final Logger log = LoggerFactory.getLogger(ImageKeyValuePair.class);
12+
private static final String IMAGE = "image";
13+
private static final String INVALID_IMAGE_VALUE = "It is not a valid value for image key.";
14+
15+
/**
16+
* The name of the base image
17+
*/
18+
private final String baseImageName;
19+
/**
20+
* The tag of the base image
21+
*/
22+
private final String tag;
23+
/**
24+
* Comment starting with #
25+
*/
26+
private final String comments;
27+
/**
28+
* Yaml Spacing #
29+
*/
30+
private final String spaces;
31+
32+
/**
33+
* Accepts an image key value pair line from a docker-compose file
34+
* See {@code isImageKeyValuePair} to ensure you're passing a valid line in.
35+
*
36+
* @param imageKeyValuePair an Image Key value pair from a docker-compose file e.g: image: imageName:imageTag
37+
*/
38+
public ImageKeyValuePair(String imageKeyValuePair) {
39+
if (!isImageKeyValuePair(imageKeyValuePair)) {
40+
throw new IllegalArgumentException(INVALID_IMAGE_VALUE);
41+
}
42+
String lineWithoutComment = imageKeyValuePair;
43+
int commentIndex = imageKeyValuePair.indexOf("#");
44+
if (commentIndex >= 0) {
45+
comments = imageKeyValuePair.substring(commentIndex);
46+
lineWithoutComment = imageKeyValuePair.substring(0, commentIndex);
47+
} else {
48+
comments = null;
49+
}
50+
// Get Yaml spacing in variable
51+
if (lineWithoutComment.startsWith(" ")) {
52+
spaces = lineWithoutComment.substring(0, lineWithoutComment.indexOf(IMAGE));
53+
} else {
54+
spaces = "";
55+
}
56+
57+
// Remove "image:" from remaining string
58+
String lineWithoutImageKey = lineWithoutComment.trim().
59+
replaceFirst(IMAGE, "").replaceFirst(":", "").
60+
trim();
61+
62+
String[] imageAndTag = lineWithoutImageKey.split(":");
63+
if (StringUtils.isNotEmpty(lineWithoutImageKey) && imageAndTag.length > 0) {
64+
baseImageName = imageAndTag[0];
65+
if (imageAndTag.length > 1) {
66+
tag = imageAndTag[1];
67+
} else {
68+
tag = null;
69+
}
70+
} else {
71+
baseImageName = null;
72+
tag = null;
73+
}
74+
}
75+
76+
/**
77+
* Internal API to get a new ComposeImageValuePair from an existing object
78+
* @param baseImageName baseImageName to add
79+
* @param tag tag to add
80+
* @param comments comments to add
81+
*/
82+
private ImageKeyValuePair(String baseImageName, String tag, String comments, String spaces) {
83+
this.baseImageName = baseImageName;
84+
this.tag = tag;
85+
this.comments = comments;
86+
this.spaces = spaces;
87+
}
88+
89+
/**
90+
* Check if this {@code lineInFile} is a image instruction,
91+
* it is referencing {@code imageName} as a base image,
92+
* and the tag is not the same as {@code imageTag} (or there is no tag)
93+
* @param lineInFile Line a code file
94+
* @param imageName images name
95+
* @param imageTag tag for imageName
96+
* @return {@link Boolean} value isImageKeyValuePairWithThisImageAndOlderTag
97+
*/
98+
public static boolean isImageKeyValuePairWithThisImageAndOlderTag(String lineInFile, String imageName, String imageTag) {
99+
if (ImageKeyValuePair.isImageKeyValuePair(lineInFile)) {
100+
ImageKeyValuePair imageKeyValuePair = new ImageKeyValuePair(lineInFile);
101+
return imageKeyValuePair.hasBaseImage(imageName)
102+
&& imageKeyValuePair.hasADifferentTag(imageTag)
103+
&& DockerfileGitHubUtil.isValidImageTag(imageKeyValuePair.getTag());
104+
}
105+
return false;
106+
}
107+
108+
/**
109+
* Get a new {@code ComposeImageValuePair} the same as this but with the {@code tag} set as {@code newTag}
110+
* @param newTag the new image tag
111+
* @return a new image instruction with the new image tag
112+
*/
113+
public ImageKeyValuePair getImageKeyValuePairWithNewTag(String newTag) {
114+
return new ImageKeyValuePair(baseImageName, newTag, comments, spaces);
115+
}
116+
117+
/**
118+
* Determines whether the line is a image instruction line in a docker-compose.yaml
119+
* @param composeImageKeyValueLine a single line(key:value) from a docker-compose.yaml
120+
* @return the line is a image instruction line or not
121+
*/
122+
public static boolean isImageKeyValuePair(String composeImageKeyValueLine) {
123+
if (StringUtils.isNotBlank(composeImageKeyValueLine)) {
124+
return composeImageKeyValueLine.trim().startsWith(ImageKeyValuePair.IMAGE);
125+
}
126+
return false;
127+
}
128+
129+
/**
130+
* @return a String representation of a image instruction line in docker-compose.yaml file. No new line at the end
131+
*/
132+
@Override
133+
public String toString() {
134+
StringBuilder stringBuilder = new StringBuilder(spaces + IMAGE);
135+
stringBuilder.append(": ");
136+
stringBuilder.append(baseImageName);
137+
if (hasTag()) {
138+
stringBuilder.append(String.format(":%s", tag.trim()));
139+
}
140+
141+
if (hasComments()) {
142+
stringBuilder.append(String.format(" %s", comments));
143+
}
144+
145+
return stringBuilder.toString();
146+
}
147+
148+
public String getBaseImageName() {
149+
return baseImageName;
150+
}
151+
152+
/**
153+
* Check to see if the {@code baseImageName} in this object is the {@code imageToFind} without
154+
* the other details (e.g. registry)
155+
* @param imageToFind the image name to search for
156+
* @return is {@code baseImageName} the same as {@code imageToFind} without extra things like registry
157+
*/
158+
public boolean hasBaseImage(String imageToFind) {
159+
return baseImageName != null &&
160+
imageToFind != null &&
161+
baseImageName.endsWith(imageToFind);
162+
}
163+
164+
/**
165+
* @return whether the {@code ComposeImageValuePair} has a {@code tag}
166+
*/
167+
public boolean hasTag() {
168+
return tag != null;
169+
}
170+
171+
/**
172+
* Determines whether the {@code tag} and {@code expectedTag} are the same
173+
* @param expectedTag the tag to compare against ComposeImageValuePair's {@code tag}
174+
* @return {@code true} if the 2 tags are different
175+
*/
176+
public boolean hasADifferentTag(String expectedTag) {
177+
if (tag == null && expectedTag == null) {
178+
return false;
179+
}
180+
if (tag == null || expectedTag == null) {
181+
return true;
182+
}
183+
return !tag.trim().equals(expectedTag.trim());
184+
}
185+
186+
public String getTag() {
187+
return tag;
188+
}
189+
190+
public boolean hasComments() {
191+
return comments != null;
192+
}
193+
194+
public String getComments() {
195+
return comments;
196+
}
197+
}

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/process/ForkableRepoValidator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.salesforce.dockerfileimageupdate.model.FromInstruction;
44
import com.salesforce.dockerfileimageupdate.model.GitForkBranch;
5+
import com.salesforce.dockerfileimageupdate.model.ImageKeyValuePair;
56
import com.salesforce.dockerfileimageupdate.model.ShouldForkResult;
67
import com.salesforce.dockerfileimageupdate.utils.DockerfileGitHubUtil;
78
import org.kohsuke.github.GHContent;
@@ -101,6 +102,8 @@ protected boolean hasNoChanges(GHContent content, GitForkBranch gitForkBranch) {
101102
String line;
102103
while ( (line = reader.readLine()) != null ) {
103104
if (FromInstruction.isFromInstructionWithThisImageAndOlderTag(line,
105+
gitForkBranch.getImageName(), gitForkBranch.getImageTag()) ||
106+
ImageKeyValuePair.isImageKeyValuePairWithThisImageAndOlderTag(line,
104107
gitForkBranch.getImageName(), gitForkBranch.getImageTag())) {
105108
return false;
106109
}

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/process/GitHubPullRequestSender.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public Multimap<String, GitHubContentToProcess> forkRepositoriesFoundAndGetPathT
7979

8080
log.info("Out of {} content search results processed, {} were deemed eligible for forking to yield {} repositories to fork.",
8181
totalContentsFound, contentsShouldFork, pathToDockerfilesInParentRepo.keys().size());
82-
log.info("Path to Dockerfiles in repos: {}", pathToDockerfilesInParentRepo);
82+
log.info("Path to files in repos: {}", pathToDockerfilesInParentRepo);
8383

8484
return pathToDockerfilesInParentRepo;
8585
}

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/search/GitHubImageSearchTermList.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.salesforce.dockerfileimageupdate.search;
22

33
import com.google.common.collect.ImmutableList;
4+
import com.salesforce.dockerfileimageupdate.utils.Constants;
45

56
import java.util.ArrayList;
67
import java.util.List;
@@ -20,12 +21,12 @@ public class GitHubImageSearchTermList {
2021
* @param image the name of the image including registry
2122
* @return list of GitHub code search terms
2223
*/
23-
public static List<String> getSearchTerms(String image) {
24+
public static List<String> getSearchTerms(String image, String filenames) {
2425
if (image == null || image.trim().isEmpty()) {
2526
return ImmutableList.of();
2627
}
2728
String[] imageParts = image.split("/");
28-
ProcessingState state = processDomainPartOfImage(imageParts[0]);
29+
ProcessingState state = processDomainPartOfImage(imageParts[0], filenames);
2930
if (imageParts.length > 1) {
3031
for (int i = 1; i < imageParts.length - 1; i++) {
3132
String imagePart = imageParts[i];
@@ -90,12 +91,14 @@ private static void processFinalUrlSegment(ProcessingState state, String finalIm
9091
* @param domain the domain part of the image (which may or may not be the full registry name)
9192
* @return processing state for the search terms
9293
*/
93-
static ProcessingState processDomainPartOfImage(String domain) {
94+
static ProcessingState processDomainPartOfImage(String domain, String filenames) {
9495
ProcessingState state = new ProcessingState();
9596
if (domain == null || domain.trim().isEmpty()) {
9697
return state;
9798
} else {
98-
state.addToCurrentTerm("FROM ");
99+
if (filenames.equalsIgnoreCase(Constants.FILENAME_DOCKERFILE)) {
100+
state.addToCurrentTerm("FROM ");
101+
}
99102
if (domain.contains("-")) {
100103
processDashedDomainParts(state, domain);
101104
} else {

dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/subcommands/impl/All.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,11 @@ protected Optional<Exception> processImageWithTag(String image, String tag, Name
8181
PullRequests pullRequests = getPullRequests();
8282
GitHubPullRequestSender pullRequestSender = getPullRequestSender(dockerfileGitHubUtil, ns);
8383
GitForkBranch gitForkBranch = getGitForkBranch(image, tag, ns);
84+
String filenamesToSearch = ns.get(Constants.FILE_NAMES_TO_SEARCH);
8485

85-
log.info("Finding Dockerfiles with the image name {}...", image);
86+
log.info("Finding files:{} with the image name {}...", filenamesToSearch, image);
8687
Optional<List<PagedSearchIterable<GHContent>>> contentsWithImage =
87-
this.dockerfileGitHubUtil.findFilesWithImage(image, orgsToIncludeInSearch, gitApiSearchLimit);
88+
this.dockerfileGitHubUtil.findFilesWithImage(image, orgsToIncludeInSearch, gitApiSearchLimit, filenamesToSearch);
8889
if (contentsWithImage.isPresent()) {
8990
Iterator<PagedSearchIterable<GHContent>> it = contentsWithImage.get().iterator();
9091
while (it.hasNext()){
@@ -137,7 +138,7 @@ protected GitHubPullRequestSender getPullRequestSender(DockerfileGitHubUtil dock
137138
}
138139

139140
protected GitForkBranch getGitForkBranch(String image, String tag, Namespace ns){
140-
return new GitForkBranch(image, tag, ns.get(Constants.GIT_BRANCH));
141+
return new GitForkBranch(image, tag, ns.get(Constants.GIT_BRANCH), ns.get(Constants.FILE_NAMES_TO_SEARCH));
141142
}
142143

143144
protected PullRequests getPullRequests(){

0 commit comments

Comments
 (0)