com.liferay.jenkins.results.parser.MergePortalSubrepositoryUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.jenkins.results.parser
Show all versions of com.liferay.jenkins.results.parser
Liferay Jenkins Results Parser
/**
* SPDX-FileCopyrightText: (c) 2024 Liferay, Inc. https://liferay.com
* SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
*/
package com.liferay.jenkins.results.parser;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Michael Hashimoto
*/
public class MergePortalSubrepositoryUtil {
public static void mergePortalSubrepository(
URL jenkinsBuildURL, PullRequest portalPullRequest,
URL subrepositoryGitHubURL, String subrepositoryUpstreamBranchName,
String targetGitRepoCommitSHA)
throws IOException {
_onMergeStarted(jenkinsBuildURL, portalPullRequest);
_checkPassingTestSuites(jenkinsBuildURL, portalPullRequest);
GitWorkingDirectory portalGitWorkingDirectory = _getGitWorkingDirectory(
jenkinsBuildURL, portalPullRequest, portalPullRequest.getBaseURL(),
portalPullRequest.getUpstreamRemoteGitBranchName());
String startingPortalCommitSHA =
portalGitWorkingDirectory.getLatestCommitSHA();
GitWorkingDirectory subrepositoryGitWorkingDirectory =
_getGitWorkingDirectory(
jenkinsBuildURL, portalPullRequest, subrepositoryGitHubURL,
subrepositoryUpstreamBranchName);
String currentGitRepoCommitSHA = _getCurrentGitRepoCommitSHA(
jenkinsBuildURL, portalPullRequest, portalGitWorkingDirectory,
subrepositoryGitWorkingDirectory, targetGitRepoCommitSHA);
_fetchSubrepositoryBranchToPortalRepository(
jenkinsBuildURL, portalPullRequest, portalGitWorkingDirectory,
subrepositoryGitWorkingDirectory);
_checkMergeCommitSHA(
jenkinsBuildURL, portalPullRequest, currentGitRepoCommitSHA,
targetGitRepoCommitSHA, portalGitWorkingDirectory,
subrepositoryGitWorkingDirectory);
_createAndApplyPatch(
jenkinsBuildURL, portalPullRequest, portalGitWorkingDirectory,
subrepositoryGitWorkingDirectory, currentGitRepoCommitSHA,
targetGitRepoCommitSHA);
_commitGitRepoUpdates(
jenkinsBuildURL, portalPullRequest, portalGitWorkingDirectory,
subrepositoryGitWorkingDirectory, startingPortalCommitSHA,
targetGitRepoCommitSHA);
_pushUpdatesToRemoteBranch(
jenkinsBuildURL, portalPullRequest, portalGitWorkingDirectory);
String endingPortalCommitSHA =
portalGitWorkingDirectory.getLatestCommitSHA();
_onMergeCompleted(
jenkinsBuildURL, portalPullRequest, subrepositoryGitHubURL,
targetGitRepoCommitSHA, startingPortalCommitSHA,
endingPortalCommitSHA);
}
private static void _checkMergeCommitSHA(
URL jenkinsBuildURL, PullRequest portalPullRequest,
String currentGitRepoCommitSHA, String targetGitRepoCommitSHA,
GitWorkingDirectory portalGitWorkingDirectory,
GitWorkingDirectory subrepositoryGitWorkingDirectory) {
if (Objects.equals(
subrepositoryGitWorkingDirectory.getMergeBaseCommitSHA(
currentGitRepoCommitSHA, targetGitRepoCommitSHA),
currentGitRepoCommitSHA)) {
return;
}
StringBuilder sb = new StringBuilder();
sb.append(
_getGitHubURLString(
portalPullRequest.getBaseURL(), targetGitRepoCommitSHA));
sb.append(" is incompatible with sha found in '");
sb.append(
JenkinsResultsParserUtil.getPathRelativeTo(
_getGitRepoFile(
portalGitWorkingDirectory,
subrepositoryGitWorkingDirectory),
portalGitWorkingDirectory.getWorkingDirectory()));
sb.append("'");
_reportError(sb.toString(), jenkinsBuildURL, portalPullRequest);
}
private static void _checkPassingTestSuites(
URL jenkinsBuildURL, PullRequest portalPullRequest) {
List requiredPassingTestSuiteNames = new ArrayList<>();
Collections.addAll(requiredPassingTestSuiteNames, "relevant", "sf");
requiredPassingTestSuiteNames.removeAll(
portalPullRequest.getPassingTestSuiteNames());
if (requiredPassingTestSuiteNames.isEmpty()) {
return;
}
StringBuilder sb = new StringBuilder();
sb.append("Skip merge subrepo because tests have not passed.\n");
sb.append("\n");
for (String requiredPassingTestSuiteName :
requiredPassingTestSuiteNames) {
sb.append("- ci:test:");
sb.append(requiredPassingTestSuiteName);
sb.append("
\n");
}
sb.append("
\n");
_reportError(sb.toString(), jenkinsBuildURL, portalPullRequest);
}
private static void _commitGitRepoUpdates(
URL jenkinsBuildURL, PullRequest portalPullRequest,
GitWorkingDirectory portalGitWorkingDirectory,
GitWorkingDirectory subrepositoryGitWorkingDirectory,
String startingPortalCommitSHA, String targetGitRepoCommitSHA) {
File gitRepoFile = _getGitRepoFile(
portalGitWorkingDirectory, subrepositoryGitWorkingDirectory);
String gitRepoFilePath = JenkinsResultsParserUtil.getPathRelativeTo(
gitRepoFile, portalGitWorkingDirectory.getWorkingDirectory());
try {
String gitRepoFileContent = JenkinsResultsParserUtil.read(
gitRepoFile);
gitRepoFileContent = gitRepoFileContent.replaceAll(
"commit = [0-9a-f]{40}", "commit = " + targetGitRepoCommitSHA);
gitRepoFileContent = gitRepoFileContent.replaceAll(
"parent = [0-9a-f]{40}", "parent = " + startingPortalCommitSHA);
JenkinsResultsParserUtil.write(gitRepoFile, gitRepoFileContent);
portalGitWorkingDirectory.stageFileInCurrentLocalGitBranch(
JenkinsResultsParserUtil.getPathRelativeTo(
gitRepoFile,
portalGitWorkingDirectory.getWorkingDirectory()));
portalGitWorkingDirectory.commitFileToCurrentBranch(
gitRepoFilePath,
"subrepo:ignore Update '" + gitRepoFilePath + "'.");
}
catch (IOException ioException) {
_reportError(
"Unable to update " + gitRepoFilePath, jenkinsBuildURL,
portalPullRequest, ioException);
}
}
private static void _createAndApplyPatch(
URL jenkinsBuildURL, PullRequest portalPullRequest,
GitWorkingDirectory portalGitWorkingDirectory,
GitWorkingDirectory subrepositoryGitWorkingDirectory, String gitRepoSHA,
String targetGitRepoCommitSHA) {
Set modifiedFilesInCommitRange =
subrepositoryGitWorkingDirectory.getModifiedFilesInCommitRange(
gitRepoSHA, targetGitRepoCommitSHA);
StringBuilder sb = new StringBuilder();
sb.append("git format-patch --root \"");
sb.append(gitRepoSHA);
sb.append("..");
sb.append(targetGitRepoCommitSHA);
sb.append("\" -- ");
boolean foundModifiedFiles = false;
for (File modifiedFile : modifiedFilesInCommitRange) {
String modifiedFilePath = JenkinsResultsParserUtil.getCanonicalPath(
modifiedFile);
if (modifiedFilePath.endsWith("gradle.properties") ||
modifiedFilePath.endsWith("gradlew") ||
modifiedFilePath.endsWith("gradlew.bat") ||
modifiedFilePath.contains("gradle/")) {
continue;
}
sb.append(" ");
sb.append(
JenkinsResultsParserUtil.getPathRelativeTo(
modifiedFile,
subrepositoryGitWorkingDirectory.getWorkingDirectory()));
foundModifiedFiles = true;
}
if (!foundModifiedFiles) {
_reportError(
"No found modified files", jenkinsBuildURL, portalPullRequest);
return;
}
GitUtil.ExecutionResult executionResult =
portalGitWorkingDirectory.executeBashCommands(
3, GitUtil.MILLIS_RETRY_DELAY, 1000 * 60 * 10, "rm -f *.patch",
sb.toString(), "(git am --abort || true)",
JenkinsResultsParserUtil.combine(
"git am --directory=\"",
_getSubrepositoryModuleDirPath(
portalGitWorkingDirectory,
subrepositoryGitWorkingDirectory),
"\" --keep-cr --whitespace=nowarn *.patch"));
if (executionResult.getExitValue() != 0) {
throw new RuntimeException(
"Unable to create & apply the patch \n" +
executionResult.getStandardError());
}
System.out.println(executionResult.getStandardOut());
}
private static void _fetchSubrepositoryBranchToPortalRepository(
URL jenkinsBuildURL, PullRequest portalPullRequest,
GitWorkingDirectory portalGitWorkingDirectory,
GitWorkingDirectory subrepositoryGitWorkingDirectory) {
LocalGitBranch portalCurrentLocalGitBranch =
portalGitWorkingDirectory.getCurrentLocalGitBranch();
String portalCurrentBranchSHA = portalCurrentLocalGitBranch.getSHA();
try {
portalGitWorkingDirectory.fetch(
null,
subrepositoryGitWorkingDirectory.getCurrentLocalGitBranch());
}
catch (Exception exception) {
File subrepositoryWorkingDirectory =
subrepositoryGitWorkingDirectory.getWorkingDirectory();
_reportError(
"Unable to fetch from " + subrepositoryWorkingDirectory,
jenkinsBuildURL, portalPullRequest, exception);
}
finally {
portalGitWorkingDirectory.reset("--hard " + portalCurrentBranchSHA);
}
}
private static String _getCurrentGitRepoCommitSHA(
URL jenkinsBuildURL, PullRequest portalPullRequest,
GitWorkingDirectory portalGitWorkingDirectory,
GitWorkingDirectory subrepositoryGitWorkingDirectory,
String targetGitRepoCommitSHA) {
File gitRepoFile = _getGitRepoFile(
portalGitWorkingDirectory, subrepositoryGitWorkingDirectory);
Properties gitRepoProperties = JenkinsResultsParserUtil.getProperties(
gitRepoFile);
String currentGitRepoCommitSHA = gitRepoProperties.getProperty(
"commit");
if (Objects.equals(currentGitRepoCommitSHA, targetGitRepoCommitSHA)) {
StringBuilder sb = new StringBuilder();
sb.append(
_getGitHubURLString(
portalPullRequest.getBaseURL(), targetGitRepoCommitSHA));
sb.append(" already found in '");
sb.append(
JenkinsResultsParserUtil.getPathRelativeTo(
gitRepoFile,
portalGitWorkingDirectory.getWorkingDirectory()));
sb.append("'");
_reportError(sb.toString(), jenkinsBuildURL, portalPullRequest);
return null;
}
return currentGitRepoCommitSHA;
}
private static String _getGitHubURLString(
URL gitHubURL, String gitBranchSHA) {
StringBuilder sb = new StringBuilder();
Matcher matcher = _gitHubURLPattern.matcher(String.valueOf(gitHubURL));
if (matcher.find()) {
sb.append(matcher.group("userName"));
sb.append("/");
sb.append(matcher.group("repositoryName"));
sb.append("/");
sb.append(matcher.group("branchName"));
}
else {
sb.append(gitHubURL);
}
if (!JenkinsResultsParserUtil.isNullOrEmpty(gitBranchSHA)) {
sb.append(" (");
sb.append(gitBranchSHA.substring(0, 7));
sb.append(")");
}
return sb.toString();
}
private static File _getGitRepoFile(
GitWorkingDirectory portalGitWorkingDirectory,
GitWorkingDirectory subrepositoryGitWorkingDirectory) {
GitRemote subrepositoryUpstreamGitRemote =
subrepositoryGitWorkingDirectory.getUpstreamGitRemote();
Set gitRepoFiles = portalGitWorkingDirectory.findFiles(
".gitrepo", subrepositoryUpstreamGitRemote.getRemoteURL());
for (File gitRepoFile : gitRepoFiles) {
return gitRepoFile;
}
return null;
}
private static GitWorkingDirectory _getGitWorkingDirectory(
URL jenkinsBuildURL, PullRequest portalPullRequest, URL gitHubURL,
String upstreamBranchName)
throws IOException {
Matcher gitHubURLMatcher = _gitHubURLPattern.matcher(
String.valueOf(gitHubURL));
if (!gitHubURLMatcher.find()) {
_reportError(
"Invalid GitHub URL " + gitHubURL, jenkinsBuildURL,
portalPullRequest);
return null;
}
String baseRepositoryDirPath =
JenkinsResultsParserUtil.getBuildProperty("base.repository.dir");
String repositoryName = gitHubURLMatcher.group("repositoryName");
String repositoryDirPath = JenkinsResultsParserUtil.combine(
baseRepositoryDirPath, "/", repositoryName);
if (repositoryName.equals("liferay-portal-ee") &&
upstreamBranchName.matches("\\d+\\.\\d+\\.x")) {
repositoryDirPath = JenkinsResultsParserUtil.combine(
baseRepositoryDirPath, "/liferay-portal-", upstreamBranchName);
}
return GitWorkingDirectoryFactory.newGitWorkingDirectory(
upstreamBranchName, repositoryDirPath, repositoryName);
}
private static String _getPullRequestLink(PullRequest portalPullRequest) {
StringBuilder sb = new StringBuilder();
sb.append("");
sb.append(portalPullRequest.getReceiverUsername());
sb.append("#");
sb.append(portalPullRequest.getNumber());
sb.append("");
return sb.toString();
}
private static String _getSubrepositoryModuleDirPath(
GitWorkingDirectory portalGitWorkingDirectory,
GitWorkingDirectory subrepositoryGitWorkingDirectory) {
String relativeFilePath = JenkinsResultsParserUtil.getPathRelativeTo(
_getGitRepoFile(
portalGitWorkingDirectory, subrepositoryGitWorkingDirectory),
portalGitWorkingDirectory.getWorkingDirectory());
return relativeFilePath.replaceAll("/\\.gitrepo", "");
}
private static void _onMergeCompleted(
URL jenkinsBuildURL, PullRequest portalPullRequest,
URL subrepositoryGitHubURL, String targetGitRepoCommitSHA,
String startingPortalCommitSHA, String endingPortalCommitSHA) {
Matcher matcher = _gitHubURLPattern.matcher(
String.valueOf(subrepositoryGitHubURL));
if (!matcher.find()) {
_reportError(
"Invalid subrepository github url " + subrepositoryGitHubURL,
jenkinsBuildURL, portalPullRequest);
return;
}
String message = JenkinsResultsParserUtil.combine(
"Completed subrepo merge process at ",
JenkinsResultsParserUtil.toDateString(new Date()), ".\n",
"All commits have been successfully pulled.\n",
"Diff URL: ",
startingPortalCommitSHA.substring(0, 7), "...",
endingPortalCommitSHA.substring(0, 7), "");
JenkinsResultsParserUtil.updateBuildDescription(
message, jenkinsBuildURL);
PullRequest.Comment pullRequestComment = portalPullRequest.addComment(
JenkinsResultsParserUtil.combine(
message, "\n\nFor more details click here."));
GitHubRemoteGitCommit gitHubRemoteGitCommit =
GitCommitFactory.newGitHubRemoteGitCommit(
matcher.group("userName"), matcher.group("repositoryName"),
targetGitRepoCommitSHA);
gitHubRemoteGitCommit.setStatus(
GitHubRemoteGitCommit.Status.SUCCESS, "liferay/merged-into-central",
JenkinsResultsParserUtil.combine(
"Merged into ", portalPullRequest.getReceiverUsername(), "/",
portalPullRequest.getGitRepositoryName()),
String.valueOf(pullRequestComment.getURL()));
portalPullRequest.close();
}
private static void _onMergeStarted(
URL jenkinsBuildURL, PullRequest portalPullRequest) {
portalPullRequest.addComment(
JenkinsResultsParserUtil.combine(
"Started subrepo merge process here at ",
JenkinsResultsParserUtil.toDateString(new Date()), "."));
JenkinsResultsParserUtil.updateBuildDescription(
_getPullRequestLink(portalPullRequest), jenkinsBuildURL);
}
private static void _pushUpdatesToRemoteBranch(
URL jenkinsBuildURL, PullRequest portalPullRequest,
GitWorkingDirectory portalGitWorkingDirectory) {
URL portalBaseURL = portalPullRequest.getBaseURL();
Matcher gitHubURLMatcher = _gitHubURLPattern.matcher(
String.valueOf(portalBaseURL));
if (!gitHubURLMatcher.find()) {
_reportError(
"Invalid GitHub URL " + portalBaseURL, jenkinsBuildURL,
portalPullRequest);
return;
}
String remoteURL = JenkinsResultsParserUtil.combine(
"[email protected]:", gitHubURLMatcher.group("userName"), "/",
gitHubURLMatcher.group("repositoryName"), ".git");
RemoteGitBranch remoteGitBranch =
portalGitWorkingDirectory.pushToRemoteGitRepository(
false, portalGitWorkingDirectory.getCurrentLocalGitBranch(),
gitHubURLMatcher.group("branchName"), remoteURL);
if (remoteGitBranch == null) {
_reportError(
"Unable to push updates to " + remoteURL, jenkinsBuildURL,
portalPullRequest);
}
}
private static void _reportError(
String errorMessage, URL jenkinsBuildURL,
PullRequest portalPullRequest) {
_reportError(errorMessage, jenkinsBuildURL, portalPullRequest, null);
}
private static void _reportError(
String errorMessage, URL jenkinsBuildURL, PullRequest portalPullRequest,
Exception exception) {
System.out.println(errorMessage);
JenkinsResultsParserUtil.updateBuildDescription(
errorMessage, jenkinsBuildURL);
portalPullRequest.addComment(
JenkinsResultsParserUtil.combine(
errorMessage, "\n\nFor more details click here."));
if (exception != null) {
throw new RuntimeException(errorMessage, exception);
}
throw new RuntimeException(errorMessage);
}
private static final Pattern _gitHubURLPattern = Pattern.compile(
"https://github.com/(?[^/]+)/(?[^/]+)/tree/" +
"(?[^/]+)");
}