diff --git a/Makefile b/Makefile index 466fafd1..f1fe2cbe 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ mvn-docker-build: #TODO: add --abort-on-container-exit to docker-compose once itests can be made not to flap see issue #21 integration-test: @-echo git_api_token=${ITEST_GH_TOKEN} > $(CURDIR)/itest.env - user_itest_secrets_file_secret=$(CURDIR)/itest.env docker-compose up + user_itest_secrets_file_secret=$(CURDIR)/itest.env docker compose up rm itest.env get-main-project-dirs: diff --git a/dockerfile-image-update/pom.xml b/dockerfile-image-update/pom.xml index 3809295a..79af83f3 100644 --- a/dockerfile-image-update/pom.xml +++ b/dockerfile-image-update/pom.xml @@ -159,6 +159,16 @@ logging-interceptor 4.9.1 + + com.auth0 + java-jwt + 3.18.1 + + + org.bouncycastle + bcprov-jdk15on + 1.68 + diff --git a/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/CommandLine.java b/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/CommandLine.java index 745cecf2..0f42e19c 100644 --- a/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/CommandLine.java +++ b/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/CommandLine.java @@ -112,6 +112,14 @@ static ArgumentParser getArgumentParser() { .setDefault(false) //To prevent null from being returned by the argument .required(false) .help("Enable debug logging, including git wire logs."); + parser.addArgument("--" + SKIP_GITHUB_APP_ID) + .type(String.class) + .required(false) + .help("Github app ID of the Github App upon whose presence we skip sending the DFIU PR."); + parser.addArgument("--" + SKIP_GITHUB_APP_KEY) + .type(String.class) + .required(false) + .help("Path to the Github app key of the Github App upon whose presence we skip sending the DFIU PR."); return parser; } @@ -253,7 +261,7 @@ public static GitHubBuilder shouldAddWireLogger(final GitHubBuilder builder, fin logger.setLevel(HttpLoggingInterceptor.Level.HEADERS); logger.redactHeader("Authorization"); - builder.withConnector(new OkHttpGitHubConnector(new OkHttpClient.Builder() + builder.withConnector(new OkHttpGitHubConnector(new OkHttpClient.Builder() .addInterceptor(logger) .build())); } diff --git a/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/Constants.java b/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/Constants.java index 194f7112..cde999a8 100644 --- a/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/Constants.java +++ b/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/Constants.java @@ -42,6 +42,8 @@ private Constants() { public static final String IGNORE_IMAGE_STRING = "x"; public static final String FILE_NAMES_TO_SEARCH = "filenamestosearch"; public static final String RATE_LIMIT_PR_CREATION = "rate_limit_pr_creations"; + public static final String SKIP_GITHUB_APP_ID = "skipAppId"; + public static final String SKIP_GITHUB_APP_KEY = "skipAppKey"; public static final String DEBUG = "debug"; //max number of PRs to be sent (or tokens to be added) per DEFAULT_RATE_LIMIT_DURATION(per hour in this case) public static final long DEFAULT_RATE_LIMIT = 60; diff --git a/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/GithubAppCheck.java b/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/GithubAppCheck.java new file mode 100644 index 00000000..ddf8dc48 --- /dev/null +++ b/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/GithubAppCheck.java @@ -0,0 +1,134 @@ +package com.salesforce.dockerfileimageupdate.utils; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.salesforce.dockerfileimageupdate.CommandLine; +import net.sourceforge.argparse4j.inf.Namespace; +import org.bouncycastle.util.io.pem.PemReader; +import org.kohsuke.github.GitHub; +import org.kohsuke.github.GitHubBuilder; +import org.kohsuke.github.HttpException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.Security; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.time.Instant; +import java.util.Date; + +public class GithubAppCheck { + private static final Logger log = LoggerFactory.getLogger(GithubAppCheck.class); + + private final String appId; + private final String privateKeyPath; + private String jwt; + private Instant jwtExpiry; + private GitHub gitHub; + + public GithubAppCheck(final Namespace ns){ + this.appId = ns.get(Constants.SKIP_GITHUB_APP_ID); + this.privateKeyPath = ns.get(Constants.SKIP_GITHUB_APP_KEY); + this.jwt = null; + this.jwtExpiry = null; + this.gitHub = null; + if (this.appId != null && this.privateKeyPath != null) { + try { + generateJWT(this.appId, this.privateKeyPath); + } catch (GeneralSecurityException | IOException exception) { + log.warn("Could not initialise JWT due to exception: {}", exception.getMessage()); + } + try { + this.gitHub = new GitHubBuilder() + .withEndpoint(CommandLine.gitApiUrl(ns)) + .withJwtToken(jwt) + .build(); + } catch (IOException exception) { + log.warn("Could not initialise github due to exception: {}", exception.getMessage()); + } + } + else { + log.warn("Could not find any Github app ID and Github app Key in the declared list. Hence assuming this class is no longer needed"); + } + } + + /** + * Method to verify whether the github app is installed on a repository or not. + * @param fullRepoName = The repository full name, i.e, of the format "owner/repoName". Eg: "Salesforce/dockerfile-image-update" + * @return True if github app is installed, false otherwise. + */ + protected boolean isGithubAppEnabledOnRepository(String fullRepoName) { + refreshJwtIfNeeded(appId, privateKeyPath); + try { + gitHub.getApp().getInstallationByRepository(fullRepoName.split("/")[0], fullRepoName.split("/")[1]); + return true; + } catch (HttpException exception) { + if (exception.getResponseCode() != 404) { + // Log for any HTTP status code other than 404 Not found. + log.warn("Caught a HTTPException {} while trying to get app installation. Defaulting to False", exception.getMessage()); + } + return false; + } catch (IOException exception) { + // Most often happens on timeout scenarios. + log.warn("Caught a IOException {} while trying to get app installation. Defaulting to False", exception.getMessage()); + return false; + } + } + + /** + * Method to refresh the JWT token if needed. Checks the JWT expiry time, and if it is 60s away from expiring, refreshes it. + * @param appId = The id of the Github App to generate the JWT for + * @param privateKeyPath = The path to the private key of the Github App to generate the JWT for + */ + private void refreshJwtIfNeeded(String appId, String privateKeyPath) { + if (jwt == null || jwtExpiry.isBefore(Instant.now().minusSeconds(60))) { // Adding a buffer to ensure token validity + try { + generateJWT(appId, privateKeyPath); + } catch (IOException | GeneralSecurityException exception) { + log.warn("Could not refresh the JWT due to exception: {}", exception.getMessage()); + } + } + } + + /** + * Method to generate the JWT used to access the Github App APIs. We generate the JWT to be valid for 600 seconds. + * Along with the JWT value, the jwtExpiry value is set to the time of 600 sec from now. + * @param appId = The id of the Github App to generate the JWT for + * @param privateKeyPath = The path to the private key of the Github App to generate the JWT for + * @throws IOException + * @throws GeneralSecurityException + */ + private void generateJWT(String appId, String privateKeyPath) throws IOException, GeneralSecurityException { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + RSAPrivateKey privateKey = getRSAPrivateKey(privateKeyPath); + + Algorithm algorithm = Algorithm.RSA256(null, privateKey); + Instant now = Instant.now(); + jwt = JWT.create() + .withIssuer(appId) + .withIssuedAt(Date.from(now)) + .withExpiresAt(Date.from(now.plusSeconds(600))) // 10 minutes expiration + .sign(algorithm); + jwtExpiry = now.plusSeconds(600); + } + + /** + * The method to get the private key in an RSA Encoded format. Makes use of org.bouncycastle.util + * @param privateKeyPath + * @return + * @throws IOException + * @throws GeneralSecurityException + */ + private RSAPrivateKey getRSAPrivateKey(String privateKeyPath) throws IOException, GeneralSecurityException { + try (PemReader pemReader = new PemReader(new FileReader(new File(privateKeyPath)))) { + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pemReader.readPemObject().getContent()); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return (RSAPrivateKey) keyFactory.generatePrivate(spec); + } + } +} diff --git a/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/PullRequests.java b/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/PullRequests.java index b99987c7..c07c8659 100644 --- a/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/PullRequests.java +++ b/dockerfile-image-update/src/main/java/com/salesforce/dockerfileimageupdate/utils/PullRequests.java @@ -25,6 +25,7 @@ public void prepareToCreate(final Namespace ns, pullRequestSender.forkRepositoriesFoundAndGetPathToDockerfiles(contentsFoundWithImage, gitForkBranch); List exceptions = new ArrayList<>(); List skippedRepos = new ArrayList<>(); + GithubAppCheck githubAppCheck = new GithubAppCheck(ns); for (String currUserRepo : pathToDockerfilesInParentRepo.keySet()) { Optional forkWithContentPaths = pathToDockerfilesInParentRepo.get(currUserRepo).stream().findFirst(); @@ -32,8 +33,8 @@ public void prepareToCreate(final Namespace ns, try { //If the repository has been onboarded to renovate enterprise, skip sending the DFIU PR if(ns.getBoolean(Constants.CHECK_FOR_RENOVATE) - && (isRenovateEnabled(Constants.RENOVATE_CONFIG_FILEPATHS, forkWithContentPaths.get()))) { - log.info("Found a renovate configuration file in the repo {}. Skip sending DFIU PRs to this repository.", forkWithContentPaths.get().getParent().getFullName()); + && (githubAppCheck.isGithubAppEnabledOnRepository(forkWithContentPaths.get().getParent().getFullName()))) { + log.info("The repo {} is onboarded onto Renovate. Hence, skip sending DFIU PRs to this repository.", forkWithContentPaths.get().getParent().getFullName()); } else { dockerfileGitHubUtil.changeDockerfiles(ns, pathToDockerfilesInParentRepo, diff --git a/dockerfile-image-update/src/test/java/com/salesforce/dockerfileimageupdate/utils/PullRequestsTest.java b/dockerfile-image-update/src/test/java/com/salesforce/dockerfileimageupdate/utils/PullRequestsTest.java index d1aaf477..b746ff1e 100644 --- a/dockerfile-image-update/src/test/java/com/salesforce/dockerfileimageupdate/utils/PullRequestsTest.java +++ b/dockerfile-image-update/src/test/java/com/salesforce/dockerfileimageupdate/utils/PullRequestsTest.java @@ -17,144 +17,135 @@ import static org.testng.Assert.assertThrows; public class PullRequestsTest { - @Test - public void testPullRequestsPrepareToCreateSuccessful() throws Exception { - Map nsMap = ImmutableMap.of(Constants.IMG, - "image", Constants.TAG, - "tag", Constants.STORE, - "store", Constants.SKIP_PR_CREATION, - false, Constants.CHECK_FOR_RENOVATE, false); - Namespace ns = new Namespace(nsMap); - PullRequests pullRequests = new PullRequests(); - GitHubPullRequestSender pullRequestSender = mock(GitHubPullRequestSender.class); - PagedSearchIterable contentsFoundWithImage = mock(PagedSearchIterable.class); - GitForkBranch gitForkBranch = mock(GitForkBranch.class); - DockerfileGitHubUtil dockerfileGitHubUtil = mock(DockerfileGitHubUtil.class); - RateLimiter rateLimiter = Mockito.spy(new RateLimiter()); - Multimap pathToDockerfilesInParentRepo = ArrayListMultimap.create(); - GitHubContentToProcess gitHubContentToProcess = mock(GitHubContentToProcess.class); - pathToDockerfilesInParentRepo.put("repo1", gitHubContentToProcess); - pathToDockerfilesInParentRepo.put("repo2", gitHubContentToProcess); - when(pullRequestSender.forkRepositoriesFoundAndGetPathToDockerfiles(contentsFoundWithImage, gitForkBranch)).thenReturn(pathToDockerfilesInParentRepo); - - - pullRequests.prepareToCreate(ns, pullRequestSender, contentsFoundWithImage, - gitForkBranch, dockerfileGitHubUtil, rateLimiter); - - verify(dockerfileGitHubUtil, times(2)).changeDockerfiles(eq(ns), - eq(pathToDockerfilesInParentRepo), - eq(gitHubContentToProcess), anyList(), eq(gitForkBranch), - eq(rateLimiter)); - } - - @Test(expectedExceptions = IOException.class) - public void testPullRequestsPrepareThrowsException() throws Exception { - Map nsMap = ImmutableMap.of(Constants.IMG, - "image", Constants.TAG, - "tag", Constants.STORE, - "store", Constants.SKIP_PR_CREATION, - false, Constants.CHECK_FOR_RENOVATE, false); - Namespace ns = new Namespace(nsMap); - PullRequests pullRequests = new PullRequests(); - GitHubPullRequestSender pullRequestSender = mock(GitHubPullRequestSender.class); - PagedSearchIterable contentsFoundWithImage = mock(PagedSearchIterable.class); - GitForkBranch gitForkBranch = mock(GitForkBranch.class); - RateLimiter rateLimiter = Mockito.spy(new RateLimiter()); - DockerfileGitHubUtil dockerfileGitHubUtil = mock(DockerfileGitHubUtil.class); - Multimap pathToDockerfilesInParentRepo = ArrayListMultimap.create(); - GitHubContentToProcess gitHubContentToProcess = mock(GitHubContentToProcess.class); - pathToDockerfilesInParentRepo.put("repo1", gitHubContentToProcess); - GHRepository ghRepository = mock(GHRepository.class); - - when(pullRequestSender.forkRepositoriesFoundAndGetPathToDockerfiles(contentsFoundWithImage, gitForkBranch)).thenReturn(pathToDockerfilesInParentRepo); - ArgumentCaptor valueCapture = ArgumentCaptor.forClass(String.class); - when(gitHubContentToProcess.getParent()).thenReturn(ghRepository); - when(ghRepository.getFullName()).thenReturn("repo"); - doThrow(new IOException("Exception")).when(dockerfileGitHubUtil).changeDockerfiles( - eq(ns), - eq(pathToDockerfilesInParentRepo), - eq(gitHubContentToProcess), - anyList(), - eq(gitForkBranch), - eq(rateLimiter)); - - pullRequests.prepareToCreate(ns, pullRequestSender, contentsFoundWithImage, - gitForkBranch, dockerfileGitHubUtil, rateLimiter); - - assertThrows(IOException.class, () -> pullRequests.prepareToCreate(ns, pullRequestSender, contentsFoundWithImage, - gitForkBranch, dockerfileGitHubUtil, rateLimiter)); - } + @Test + public void testPullRequestsPrepareToCreateSuccessful() throws Exception { + Map nsMap = ImmutableMap.of(Constants.IMG, + "image", Constants.TAG, + "tag", Constants.STORE, + "store", Constants.SKIP_PR_CREATION, + false, Constants.CHECK_FOR_RENOVATE, false); + Namespace ns = new Namespace(nsMap); + PullRequests pullRequests = new PullRequests(); + GitHubPullRequestSender pullRequestSender = mock(GitHubPullRequestSender.class); + PagedSearchIterable contentsFoundWithImage = mock(PagedSearchIterable.class); + GitForkBranch gitForkBranch = mock(GitForkBranch.class); + DockerfileGitHubUtil dockerfileGitHubUtil = mock(DockerfileGitHubUtil.class); + GithubAppCheck githubAppCheck = mock(GithubAppCheck.class); + RateLimiter rateLimiter = Mockito.spy(new RateLimiter()); + Multimap pathToDockerfilesInParentRepo = ArrayListMultimap.create(); + GitHubContentToProcess gitHubContentToProcess = mock(GitHubContentToProcess.class); + pathToDockerfilesInParentRepo.put("repo1", gitHubContentToProcess); + pathToDockerfilesInParentRepo.put("repo2", gitHubContentToProcess); + when(pullRequestSender.forkRepositoriesFoundAndGetPathToDockerfiles(contentsFoundWithImage, gitForkBranch)).thenReturn(pathToDockerfilesInParentRepo); + + pullRequests.prepareToCreate(ns, pullRequestSender, contentsFoundWithImage, + gitForkBranch, dockerfileGitHubUtil, rateLimiter); + + verify(dockerfileGitHubUtil, times(2)).changeDockerfiles(eq(ns), + eq(pathToDockerfilesInParentRepo), + eq(gitHubContentToProcess), anyList(), eq(gitForkBranch), + eq(rateLimiter)); + } + + @Test(expectedExceptions = IOException.class) + public void testPullRequestsPrepareThrowsException() throws Exception { + Map nsMap = ImmutableMap.of(Constants.IMG, + "image", Constants.TAG, + "tag", Constants.STORE, + "store", Constants.SKIP_PR_CREATION, + false, Constants.CHECK_FOR_RENOVATE, false); + Namespace ns = new Namespace(nsMap); + PullRequests pullRequests = new PullRequests(); + GitHubPullRequestSender pullRequestSender = mock(GitHubPullRequestSender.class); + PagedSearchIterable contentsFoundWithImage = mock(PagedSearchIterable.class); + GitForkBranch gitForkBranch = mock(GitForkBranch.class); + RateLimiter rateLimiter = Mockito.spy(new RateLimiter()); + DockerfileGitHubUtil dockerfileGitHubUtil = mock(DockerfileGitHubUtil.class); + GithubAppCheck githubAppCheck = mock(GithubAppCheck.class); + Multimap pathToDockerfilesInParentRepo = ArrayListMultimap.create(); + GitHubContentToProcess gitHubContentToProcess = mock(GitHubContentToProcess.class); + pathToDockerfilesInParentRepo.put("repo1", gitHubContentToProcess); + GHRepository ghRepository = mock(GHRepository.class); + + when(pullRequestSender.forkRepositoriesFoundAndGetPathToDockerfiles(contentsFoundWithImage, gitForkBranch)).thenReturn(pathToDockerfilesInParentRepo); + ArgumentCaptor valueCapture = ArgumentCaptor.forClass(String.class); + when(gitHubContentToProcess.getParent()).thenReturn(ghRepository); + when(ghRepository.getFullName()).thenReturn("repo"); + doThrow(new IOException("Exception")).when(dockerfileGitHubUtil).changeDockerfiles( + eq(ns), + eq(pathToDockerfilesInParentRepo), + eq(gitHubContentToProcess), + anyList(), + eq(gitForkBranch), + eq(rateLimiter)); + + pullRequests.prepareToCreate(ns, pullRequestSender, contentsFoundWithImage, + gitForkBranch, dockerfileGitHubUtil, rateLimiter); + + assertThrows(IOException.class, () -> pullRequests.prepareToCreate(ns, pullRequestSender, contentsFoundWithImage, + gitForkBranch, dockerfileGitHubUtil, rateLimiter)); + } + + @Test + public void testPullRequestsPrepareToCreateWhenNoDockerfileFound() throws Exception { + Map nsMap = ImmutableMap.of(Constants.IMG, + "image", Constants.TAG, + "tag", Constants.STORE, + "store", Constants.SKIP_PR_CREATION, + false, Constants.CHECK_FOR_RENOVATE, false); + Namespace ns = new Namespace(nsMap); + PullRequests pullRequests = new PullRequests(); + GitHubPullRequestSender pullRequestSender = mock(GitHubPullRequestSender.class); + PagedSearchIterable contentsFoundWithImage = mock(PagedSearchIterable.class); + GitForkBranch gitForkBranch = mock(GitForkBranch.class); + GithubAppCheck githubAppCheck = mock(GithubAppCheck.class); + RateLimiter rateLimiter = Mockito.spy(new RateLimiter()); + DockerfileGitHubUtil dockerfileGitHubUtil = mock(DockerfileGitHubUtil.class); + Multimap pathToDockerfilesInParentRepo = mock(Multimap.class); + GitHubContentToProcess gitHubContentToProcess = mock(GitHubContentToProcess.class); + when(pullRequestSender.forkRepositoriesFoundAndGetPathToDockerfiles(contentsFoundWithImage, gitForkBranch)).thenReturn(pathToDockerfilesInParentRepo); + Set currUsers = new HashSet<>(); + currUsers.add("repo1"); + when(pathToDockerfilesInParentRepo.keySet()).thenReturn(currUsers); + pullRequests.prepareToCreate(ns, pullRequestSender, contentsFoundWithImage, + gitForkBranch, dockerfileGitHubUtil, rateLimiter); + + verify(dockerfileGitHubUtil, times(0)).changeDockerfiles(eq(ns), + eq(pathToDockerfilesInParentRepo), + eq(gitHubContentToProcess), anyList(), eq(gitForkBranch),eq(rateLimiter)); + } @Test - public void testPullRequestsPrepareToCreateWhenNoDockerfileFound() throws Exception { + public void testPullRequestsPrepareSkipsSendingPRIfRepoOnboardedToRenovate() throws Exception { Map nsMap = ImmutableMap.of(Constants.IMG, - "image", Constants.TAG, - "tag", Constants.STORE, - "store", Constants.SKIP_PR_CREATION, - false, Constants.CHECK_FOR_RENOVATE, false); + "image", Constants.TAG, + "tag", Constants.STORE, + "store", Constants.SKIP_PR_CREATION, + false, Constants.CHECK_FOR_RENOVATE, true); Namespace ns = new Namespace(nsMap); PullRequests pullRequests = new PullRequests(); GitHubPullRequestSender pullRequestSender = mock(GitHubPullRequestSender.class); PagedSearchIterable contentsFoundWithImage = mock(PagedSearchIterable.class); GitForkBranch gitForkBranch = mock(GitForkBranch.class); + GithubAppCheck githubAppCheck = mock(GithubAppCheck.class); RateLimiter rateLimiter = Mockito.spy(new RateLimiter()); DockerfileGitHubUtil dockerfileGitHubUtil = mock(DockerfileGitHubUtil.class); Multimap pathToDockerfilesInParentRepo = mock(Multimap.class); GitHubContentToProcess gitHubContentToProcess = mock(GitHubContentToProcess.class); + GHRepository ghRepository = mock(GHRepository.class); + when(pullRequestSender.forkRepositoriesFoundAndGetPathToDockerfiles(contentsFoundWithImage, gitForkBranch)).thenReturn(pathToDockerfilesInParentRepo); Set currUsers = new HashSet<>(); currUsers.add("repo1"); when(pathToDockerfilesInParentRepo.keySet()).thenReturn(currUsers); - pullRequests.prepareToCreate(ns, pullRequestSender, contentsFoundWithImage, - gitForkBranch, dockerfileGitHubUtil, rateLimiter); - - verify(dockerfileGitHubUtil, times(0)).changeDockerfiles(eq(ns), - eq(pathToDockerfilesInParentRepo), - eq(gitHubContentToProcess), anyList(), eq(gitForkBranch),eq(rateLimiter)); - } - - @Test - public void testPullRequestsPrepareSkipsSendingPRIfRepoOnboardedToRenovate() throws Exception { - Map nsMap = ImmutableMap.of( - Constants.IMG, "image", - Constants.TAG, "tag", - Constants.STORE,"store", - Constants.SKIP_PR_CREATION,false, - Constants.CHECK_FOR_RENOVATE, true); - - - Namespace ns = new Namespace(nsMap); - PullRequests pullRequests = new PullRequests(); - GitHubPullRequestSender pullRequestSender = mock(GitHubPullRequestSender.class); - PagedSearchIterable contentsFoundWithImage = mock(PagedSearchIterable.class); - GitForkBranch gitForkBranch = mock(GitForkBranch.class); - DockerfileGitHubUtil dockerfileGitHubUtil = mock(DockerfileGitHubUtil.class); - RateLimiter rateLimiter = Mockito.spy(new RateLimiter()); - Multimap pathToDockerfilesInParentRepo = ArrayListMultimap.create(); - GitHubContentToProcess gitHubContentToProcess = mock(GitHubContentToProcess.class); - pathToDockerfilesInParentRepo.put("repo1", gitHubContentToProcess); - pathToDockerfilesInParentRepo.put("repo2", gitHubContentToProcess); - pathToDockerfilesInParentRepo.put("repo3", gitHubContentToProcess); - GHContent content = mock(GHContent.class); - InputStream inputStream1 = new ByteArrayInputStream("{someKey:someValue}".getBytes()); - InputStream inputStream2 = new ByteArrayInputStream("{enabled:false}".getBytes()); - GHRepository ghRepository = mock(GHRepository.class); - - when(pullRequestSender.forkRepositoriesFoundAndGetPathToDockerfiles(contentsFoundWithImage, gitForkBranch)).thenReturn(pathToDockerfilesInParentRepo); when(gitHubContentToProcess.getParent()).thenReturn(ghRepository); - //Fetch the content of the renovate.json file for the 3 repos. - // The first one returns a file with regular json content. - // The second one returns a file with the key 'enabled' set to 'false' to replicate a repo that has been onboarded to renovate but has it disabled - // The third repo does not have the renovate.json file - when(ghRepository.getFileContent(anyString())).thenReturn(content).thenReturn(content).thenThrow(new FileNotFoundException()); - when(ghRepository.getFullName()).thenReturn("org/repo"); - when(content.read()).thenReturn(inputStream1).thenReturn(inputStream2); + when(ghRepository.getFullName()).thenReturn("repoParent"); + when(githubAppCheck.isGithubAppEnabledOnRepository(anyString())).thenReturn(true); pullRequests.prepareToCreate(ns, pullRequestSender, contentsFoundWithImage, gitForkBranch, dockerfileGitHubUtil, rateLimiter); - //Verify that the DFIU PR is skipped for the first repo, but is sent to the other two repos - verify(dockerfileGitHubUtil, times(2)).changeDockerfiles(eq(ns), + verify(dockerfileGitHubUtil, times(0)).changeDockerfiles(eq(ns), eq(pathToDockerfilesInParentRepo), eq(gitHubContentToProcess), anyList(), eq(gitForkBranch), eq(rateLimiter));