use of org.gitlab4j.api.GitLabApi in project legend-sdlc by finos.
the class GitLabReviewApi method getReviewUpdateStatus.
@Override
public ReviewUpdateStatus getReviewUpdateStatus(String projectId, String reviewId) {
LegendSDLCServerException.validateNonNull(projectId, "projectId may not be null");
LegendSDLCServerException.validateNonNull(reviewId, "reviewId may not be null");
GitLabProjectId gitLabProjectId = parseProjectId(projectId);
GitLabApi gitLabApi = getGitLabApi(gitLabProjectId.getGitLabMode());
MergeRequest mergeRequest = getReviewMergeRequest(gitLabApi.getMergeRequestApi(), gitLabProjectId, reviewId);
if (!(isOpen(mergeRequest) || isLocked(mergeRequest))) {
throw new LegendSDLCServerException("Cannot get update status for review " + mergeRequest.getIid() + " in project " + projectId + ": state is " + getReviewState(mergeRequest), Status.CONFLICT);
}
return getReviewUpdateStatus(gitLabProjectId, gitLabApi, mergeRequest);
}
use of org.gitlab4j.api.GitLabApi in project legend-sdlc by finos.
the class GitLabReviewApi method updateReview.
@Override
public ReviewUpdateStatus updateReview(String projectId, String reviewId) {
LegendSDLCServerException.validateNonNull(projectId, "projectId may not be null");
LegendSDLCServerException.validateNonNull(reviewId, "reviewId may not be null");
GitLabProjectId gitLabProjectId = parseProjectId(projectId);
GitLabApi gitLabApi = getGitLabApi(gitLabProjectId.getGitLabMode());
MergeRequestApi mergeRequestApi = gitLabApi.getMergeRequestApi();
// Check the current status of the review
MergeRequest initialMergeRequest = getReviewMergeRequest(mergeRequestApi, gitLabProjectId, reviewId);
if (!isOpen(initialMergeRequest)) {
throw new LegendSDLCServerException("Only open reviews can be updated: state of review " + initialMergeRequest.getIid() + " in project " + projectId + " is " + getReviewState(initialMergeRequest), Status.CONFLICT);
}
ReviewUpdateStatus updateStatus = getReviewUpdateStatus(gitLabProjectId, gitLabApi, initialMergeRequest);
if (updateStatus.isUpdateInProgress() || ((updateStatus.getBaseRevisionId() != null) && updateStatus.getBaseRevisionId().equals(updateStatus.getTargetRevisionId()))) {
// Update in progress or already up to date: no need to update
return updateStatus;
}
// Start update attempt
MergeRequest rebaseMergeRequest;
try {
CallUntil<MergeRequest, GitLabApiException> callUntil = CallUntil.callUntil(() -> withRetries(() -> mergeRequestApi.rebaseMergeRequest(gitLabProjectId.getGitLabId(), initialMergeRequest.getIid())), MergeRequest::getRebaseInProgress, 3, 500L);
if (!callUntil.succeeded()) {
throw new LegendSDLCServerException("Failed to start update for review " + reviewId + " in project " + projectId);
}
rebaseMergeRequest = callUntil.getResult();
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to update review " + reviewId + " in project " + projectId, () -> "Unknown review in project " + projectId + ": " + reviewId, () -> "Error updating review " + reviewId + " in project " + projectId);
}
return getReviewUpdateStatus(gitLabProjectId, gitLabApi, rebaseMergeRequest);
}
use of org.gitlab4j.api.GitLabApi in project legend-sdlc by finos.
the class GitLabVersionApi method newVersion.
private Version newVersion(GitLabProjectId projectId, String revisionId, VersionId versionId, String notes) {
String tagName = buildVersionTagName(versionId);
String message = "Release tag for version " + versionId.toVersionIdString();
try {
GitLabApi gitLabApi = getGitLabApi(projectId.getGitLabMode());
CommitsApi commitsApi = gitLabApi.getCommitsApi();
Commit referenceCommit;
if (revisionId == null) {
referenceCommit = commitsApi.getCommit(projectId.getGitLabId(), MASTER_BRANCH);
if (referenceCommit == null) {
throw new LegendSDLCServerException("Cannot create version " + versionId.toVersionIdString() + " of project " + projectId + ": cannot find current revision (project may be corrupt)", Status.INTERNAL_SERVER_ERROR);
}
} else {
try {
referenceCommit = commitsApi.getCommit(projectId.getGitLabId(), revisionId);
} catch (GitLabApiException e) {
if (GitLabApiTools.isNotFoundGitLabApiException(e)) {
throw new LegendSDLCServerException("Revision " + revisionId + " is unknown in project " + projectId, Status.BAD_REQUEST);
}
throw e;
}
Pager<CommitRef> referenceCommitBranchPager = withRetries(() -> commitsApi.getCommitRefs(projectId.getGitLabId(), referenceCommit.getId(), RefType.BRANCH, ITEMS_PER_PAGE));
Stream<CommitRef> referenceCommitBranches = PagerTools.stream(referenceCommitBranchPager);
if (referenceCommitBranches.noneMatch(ref -> MASTER_BRANCH.equals(ref.getName()))) {
throw new LegendSDLCServerException("Revision " + revisionId + " is unknown in project " + projectId, Status.BAD_REQUEST);
}
}
String referenceRevisionId = referenceCommit.getId();
Pager<CommitRef> referenceCommitTagPager = withRetries(() -> commitsApi.getCommitRefs(projectId.getGitLabId(), referenceRevisionId, RefType.TAG, ITEMS_PER_PAGE));
List<CommitRef> referenceCommitTags = PagerTools.stream(referenceCommitTagPager).collect(Collectors.toList());
if (referenceCommitTags.stream().map(CommitRef::getName).anyMatch(GitLabVersionApi::isVersionTagName)) {
StringBuilder builder = new StringBuilder("Revision ").append(referenceRevisionId).append(" has already been released in ");
List<VersionId> revisionVersionIds = referenceCommitTags.stream().map(CommitRef::getName).filter(GitLabVersionApi::isVersionTagName).map(GitLabVersionApi::parseVersionTagName).collect(Collectors.toList());
if (revisionVersionIds.size() == 1) {
builder.append("version ");
revisionVersionIds.get(0).appendVersionIdString(builder);
} else {
builder.append("versions ");
revisionVersionIds.sort(Comparator.naturalOrder());
boolean first = true;
for (VersionId revisionVersionId : revisionVersionIds) {
if (first) {
first = false;
} else {
builder.append(", ");
}
revisionVersionId.appendVersionIdString(builder);
}
}
throw new LegendSDLCServerException(builder.toString());
}
Tag tag = getGitLabApi(projectId.getGitLabMode()).getTagsApi().createTag(projectId.getGitLabId(), tagName, referenceRevisionId, message, notes);
return fromGitLabTag(projectId.toString(), tag);
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to create version " + versionId.toVersionIdString() + " of project " + projectId, () -> "Unknown project: " + projectId, () -> "Error creating version " + versionId.toVersionIdString() + " of project " + projectId);
}
}
use of org.gitlab4j.api.GitLabApi in project legend-sdlc by finos.
the class GitLabWorkspaceApi method attemptToRebaseWorkspaceUsingTemporaryBranch.
/**
* This method attempts to rebase the workspace branch on top of master by using a temp branch. Detailed procedure outlined below:
* 1. Create a new merge request (MR) that merges temp branch into master branch so that we can use gitlab rebase functionality
* 2. Call rebase.
* 3. Continuously check the rebase status of the merge request:
* - If failed -> return `false`
* - If succeeded, proceed
* 4. Re-create workspace branch on top of the rebased temp branch.
* 5. Cleanup: remove the temp branch and the MR
* 6. Return `true`
*
* @return a boolean flag indicating if the attempted rebase succeeded.
*/
private boolean attemptToRebaseWorkspaceUsingTemporaryBranch(String projectId, String workspaceId, WorkspaceType workspaceType, String tempBranchName, String masterRevisionId) {
GitLabProjectId gitLabProjectId = parseProjectId(projectId);
GitLabApi gitLabApi = getGitLabApi(gitLabProjectId.getGitLabMode());
RepositoryApi repositoryApi = gitLabApi.getRepositoryApi();
// Create merge request to rebase
MergeRequestApi mergeRequestApi = getGitLabApi(gitLabProjectId.getGitLabMode()).getMergeRequestApi();
String title = "Update workspace " + workspaceId;
String message = "Update workspace " + workspaceId + " up to revision " + masterRevisionId;
MergeRequest mergeRequest;
try {
mergeRequest = mergeRequestApi.createMergeRequest(gitLabProjectId.getGitLabId(), tempBranchName, MASTER_BRANCH, title, message, null, null, null, null, false, false);
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to create merge request in project " + projectId, () -> "Unknown branch in project " + projectId + ": " + tempBranchName, () -> "Error creating merge request in project " + projectId);
}
// Attempt to rebase the merge request
try {
mergeRequestApi.rebaseMergeRequest(gitLabProjectId.getGitLabId(), mergeRequest.getIid());
// Check rebase status
// This only throws when we have 403, so we need to keep polling till we know the result
// See https://docs.gitlab.com/ee/api/merge_requests.html#rebase-a-merge-request
CallUntil<MergeRequest, GitLabApiException> rebaseStatusCallUntil = CallUntil.callUntil(() -> withRetries(() -> mergeRequestApi.getRebaseStatus(gitLabProjectId.getGitLabId(), mergeRequest.getIid())), mr -> !mr.getRebaseInProgress(), 600, 1000L);
if (!rebaseStatusCallUntil.succeeded()) {
LOGGER.warn("Timeout waiting for merge request " + mergeRequest.getIid() + " in project " + projectId + " to finish rebasing");
return false;
}
// Check if there is merge conflict
if (rebaseStatusCallUntil.getResult().getMergeError() != null) {
return false;
} else // if there are no merge conflicts, proceed with the update
{
// Create backup branch
Branch backupBranch;
ProjectFileAccessProvider.WorkspaceAccessType backupWorkspaceAccessType = ProjectFileAccessProvider.WorkspaceAccessType.BACKUP;
ProjectFileAccessProvider.WorkspaceAccessType workspaceAccessType = ProjectFileAccessProvider.WorkspaceAccessType.WORKSPACE;
try {
backupBranch = GitLabApiTools.createBranchFromSourceBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, backupWorkspaceAccessType), getWorkspaceBranchName(workspaceId, workspaceType, workspaceAccessType), 30, 1_000);
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to create " + workspaceType.getLabel() + " " + backupWorkspaceAccessType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown project: " + projectId, () -> "Error creating " + workspaceType.getLabel() + " " + backupWorkspaceAccessType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId);
}
if (backupBranch == null) {
throw new LegendSDLCServerException("Failed to create " + workspaceType.getLabel() + " " + backupWorkspaceAccessType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " from " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId);
}
// Delete original branch
boolean originalBranchDeleted;
try {
originalBranchDeleted = GitLabApiTools.deleteBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, workspaceAccessType), 20, 1_000);
} catch (Exception e) {
throw buildException(e, () -> "Error while attempting to update the workspace " + workspaceId + " in project " + projectId + ": user " + getCurrentUser() + " is not allowed to delete workspace", () -> "Error while attempting to update the workspace " + workspaceId + " in project " + projectId + ": unknown workspace or project", () -> "Error while attempting to update the workspace " + workspaceId + " in project " + projectId + ": error deleting workspace");
}
if (!originalBranchDeleted) {
throw new LegendSDLCServerException("Failed to delete " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId);
}
// Create new workspace branch off the temp branch head
Branch newWorkspaceBranch;
try {
newWorkspaceBranch = GitLabApiTools.createBranchFromSourceBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, workspaceAccessType), tempBranchName, 30, 1_000);
} catch (Exception e) {
throw buildException(e, () -> "Error while attempting to update the workspace " + workspaceId + " in project " + projectId + ": user " + getCurrentUser() + " is not allowed to create workspace", () -> "Error while attempting to update the workspace " + workspaceId + " in project " + projectId + ": unknown project: " + projectId, () -> "Error while attempting to update the workspace " + workspaceId + " in project " + projectId + ": error creating workspace");
}
if (newWorkspaceBranch == null) {
throw new LegendSDLCServerException("Failed to create " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " from temporary workspace " + tempBranchName + " in project " + projectId);
}
// Delete backup branch
try {
boolean deleted = GitLabApiTools.deleteBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, backupWorkspaceAccessType), 20, 1_000);
if (!deleted) {
LOGGER.error("Failed to delete {} {} in project {}", workspaceType.getLabel() + " " + backupWorkspaceAccessType.getLabel() + " " + workspaceAccessType.getLabel(), workspaceId, projectId);
}
} catch (Exception e) {
// unfortunate, but this should not throw error
LOGGER.error("Error deleting {} {} in project {}", workspaceType.getLabel() + " " + backupWorkspaceAccessType.getLabel() + " " + workspaceAccessType.getLabel(), workspaceId, projectId);
}
}
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to rebase merge request " + mergeRequest.getIid() + " in project " + projectId, () -> "Unknown merge request ( " + mergeRequest.getIid() + " ) or project ( " + projectId + " )", () -> "Error rebasing merge request " + mergeRequest.getIid() + " in project " + projectId);
} finally {
// Try to close merge request
try {
mergeRequestApi.updateMergeRequest(gitLabProjectId.getGitLabId(), mergeRequest.getIid(), null, title, null, null, StateEvent.CLOSE, null, null, null, null, null, null);
} catch (Exception closeEx) {
// if we fail, log the error but we don't throw it
LOGGER.error("Could not close merge request {} for project {}: {}", mergeRequest.getIid(), projectId, mergeRequest.getWebUrl(), closeEx);
}
// Delete temporary branch in the background
submitBackgroundRetryableTask(() -> waitForPipelinesDeleteBranchAndVerify(gitLabApi, gitLabProjectId, tempBranchName), 5000L, "delete " + tempBranchName);
}
return true;
}
use of org.gitlab4j.api.GitLabApi in project legend-sdlc by finos.
the class GitLabWorkspaceApi method updateWorkspace.
/**
* There are 4 possible outcome for this method:
* 1. NO_OP: If the workspace is already branching from the HEAD of master, nothing is needed.
* 2. UPDATED: If the workspace is not branching from the HEAD of master, and we successfully rebase the branch to master HEAD.
* 3. CONFLICT: If the workspace is not branching from the HEAD of master, and we failed to rebase the branch to master HEAD due to merge conflicts.
* 4. ERROR
* <p>
* The procedure goes like the followings:
* 1. Check if the current workspace is already up to date:
* - If yes, return NO_OP
* - If no, proceed
* 2. Create a temp branch to attempt to rebase:
* - If rebase succeeded, return UPDATED
* - If rebase failed, further check if we need to enter conflict resolution mode.
* This check makes sure the conflict that causes rebase to fail does not come from intermediate
* commits by squashing these commits and attempt to do another rebase there. If this still fails
* it means the workspace in overall truly has merge conflicts while updating, so entering conflict resolution mode
*/
@Override
public WorkspaceUpdateReport updateWorkspace(String projectId, String workspaceId, WorkspaceType workspaceType) {
LegendSDLCServerException.validateNonNull(projectId, "projectId may not be null");
LegendSDLCServerException.validateNonNull(workspaceId, "workspaceId may not be null");
LOGGER.info("Updating workspace {} in project {} to latest revision", workspaceId, projectId);
GitLabProjectId gitLabProjectId = parseProjectId(projectId);
ProjectFileAccessProvider.WorkspaceAccessType workspaceAccessType = ProjectFileAccessProvider.WorkspaceAccessType.WORKSPACE;
String workspaceBranchName = getBranchName(workspaceId, workspaceType, workspaceAccessType);
GitLabApi gitLabApi = getGitLabApi(gitLabProjectId.getGitLabMode());
RepositoryApi repositoryApi = gitLabApi.getRepositoryApi();
// Get the workspace branch
Branch workspaceBranch;
try {
workspaceBranch = withRetries(() -> repositoryApi.getBranch(gitLabProjectId.getGitLabId(), workspaceBranchName));
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to access " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " in project " + projectId + ": " + workspaceId, () -> "Error accessing " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId);
}
String currentWorkspaceRevisionId = workspaceBranch.getCommit().getId();
LOGGER.info("Found latest revision of {} {} in project {}: {}", workspaceType.getLabel() + " " + workspaceAccessType.getLabel(), workspaceId, projectId, currentWorkspaceRevisionId);
// Determine the revision to update to
Branch masterBranch;
try {
masterBranch = withRetries(() -> repositoryApi.getBranch(gitLabProjectId.getGitLabId(), MASTER_BRANCH));
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to access the latest revision in project " + projectId, () -> "Unknown project: " + projectId, () -> "Error accessing latest revision for project " + projectId);
}
String masterRevisionId = masterBranch.getCommit().getId();
LOGGER.info("Found latest revision of project {}: {}", projectId, masterRevisionId);
CommitsApi commitsApi = gitLabApi.getCommitsApi();
// Check if the workspace already has the latest revision
try {
boolean isAlreadyLatest = false;
// This will check if the branch contains the master HEAD commit by looking up the list of references the commit is pushed to
if (masterRevisionId.equals(currentWorkspaceRevisionId)) {
isAlreadyLatest = true;
} else {
Pager<CommitRef> masterHeadCommitRefPager = withRetries(() -> commitsApi.getCommitRefs(gitLabProjectId.getGitLabId(), masterRevisionId, RefType.BRANCH, ITEMS_PER_PAGE));
Stream<CommitRef> masterHeadCommitRefs = PagerTools.stream(masterHeadCommitRefPager);
if (masterHeadCommitRefs.anyMatch(cr -> workspaceBranchName.equals(cr.getName()))) {
isAlreadyLatest = true;
}
}
if (isAlreadyLatest) {
// revision is already in the workspace, no update necessary, hence NO_OP
LOGGER.info("Workspace {} in project {} already has revision {}, no update necessary", workspaceId, projectId, masterRevisionId);
return createWorkspaceUpdateReport(WorkspaceUpdateReportStatus.NO_OP, masterRevisionId, currentWorkspaceRevisionId);
}
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to access revision " + masterRevisionId + " in project " + projectId, () -> "Unknown revision in project " + projectId + ": " + masterRevisionId, () -> "Error accessing revision " + masterRevisionId + " of project " + projectId);
}
// Temp branch for checking for merge conflicts
String tempBranchName = newUserTemporaryBranchName();
Branch tempBranch;
try {
tempBranch = GitLabApiTools.createBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), tempBranchName, currentWorkspaceRevisionId, 30, 1_000);
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to create temporary workspace " + tempBranchName + " in project " + projectId, () -> "Unknown project: " + projectId, () -> "Error creating temporary workspace " + tempBranchName + " in project " + projectId);
}
if (tempBranch == null) {
throw new LegendSDLCServerException("Failed to create temporary workspace " + tempBranchName + " in project " + projectId + " from revision " + currentWorkspaceRevisionId);
}
// Attempt to rebase the temporary branch on top of master
boolean rebaseSucceeded = this.attemptToRebaseWorkspaceUsingTemporaryBranch(projectId, workspaceId, workspaceType, tempBranchName, masterRevisionId);
// 2. There are merge conflicts, so we enter conflict resolution route
if (!rebaseSucceeded) {
String workspaceCreationRevisionId;
try {
workspaceCreationRevisionId = withRetries(() -> repositoryApi.getMergeBase(gitLabProjectId.getGitLabId(), Arrays.asList(MASTER_BRANCH, currentWorkspaceRevisionId)).getId());
} catch (Exception e) {
throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to get merged base revision for workspace " + workspaceId + " from project " + projectId, () -> "Could not find revision " + currentWorkspaceRevisionId + " from project " + projectId, () -> "Failed to fetch merged base revision for workspace " + workspaceId + " from project " + projectId);
}
// Small optimization step to make sure we need squashing.
// If there are less than 2 commits (not including the base commit), there is no point in squashing
List<Revision> latestTwoRevisionsOnWorkspaceBranch = this.revisionApi.getWorkspaceRevisionContext(projectId, workspaceId, workspaceType).getRevisions(null, null, null, 2);
Set<String> latestTwoRevisionOnWorkspaceBranchIds = latestTwoRevisionsOnWorkspaceBranch.stream().map(Revision::getId).collect(Collectors.toSet());
if (latestTwoRevisionOnWorkspaceBranchIds.contains(workspaceCreationRevisionId)) {
LOGGER.debug("Failed to rebase branch {}, but the branch does not have enough commits to perform squashing. Proceeding to conflict resolution...", workspaceBranchName);
return this.createConflictResolution(projectId, workspaceId, workspaceType, masterRevisionId);
} else {
LOGGER.debug("Failed to rebase branch {}. Performing squashing commits and re-attempting rebase...", workspaceBranchName);
}
WorkspaceUpdateReport rebaseUpdateAttemptReport = this.attemptToSquashAndRebaseWorkspace(projectId, workspaceId, workspaceType, masterRevisionId, currentWorkspaceRevisionId, workspaceCreationRevisionId);
return WorkspaceUpdateReportStatus.UPDATED.equals(rebaseUpdateAttemptReport.getStatus()) ? rebaseUpdateAttemptReport : this.createConflictResolution(projectId, workspaceId, workspaceType, masterRevisionId);
}
String updatedCurrentWorkspaceRevisionId = this.revisionApi.getWorkspaceRevisionContext(projectId, workspaceId, workspaceType).getCurrentRevision().getId();
return createWorkspaceUpdateReport(WorkspaceUpdateReportStatus.UPDATED, masterRevisionId, updatedCurrentWorkspaceRevisionId);
}
Aggregations