Search in sources :

Example 1 with Commit

use of org.gitlab4j.api.models.Commit in project legend-sdlc by finos.

the class GitLabReviewApi method getMergeRequestTargetRevision.

private String getMergeRequestTargetRevision(GitLabProjectId projectId, GitLabApi gitLabApi, MergeRequest mergeRequest) {
    RepositoryApi repositoryApi = gitLabApi.getRepositoryApi();
    Branch targetBranch;
    try {
        targetBranch = withRetries(() -> repositoryApi.getBranch(projectId.getGitLabId(), mergeRequest.getTargetBranch()));
    } catch (Exception e) {
        LOGGER.error("Error getting target branch head for merge request {} in project {} (target branch: {})", mergeRequest.getIid(), projectId, mergeRequest.getTargetBranch(), e);
        StringBuilder builder = new StringBuilder("Error getting target revision for review ").append(mergeRequest.getIid()).append(" for project ").append(projectId);
        StringTools.appendThrowableMessageIfPresent(builder, e);
        throw new LegendSDLCServerException(builder.toString(), e);
    }
    Commit targetHead = targetBranch.getCommit();
    if ((targetHead == null) || (targetHead.getId() == null)) {
        LOGGER.error("Error getting target branch head for merge request {} in project {} (target branch: {}): {}", mergeRequest.getIid(), projectId, mergeRequest.getTargetBranch(), targetHead);
        throw new LegendSDLCServerException("Error getting target revision for review " + mergeRequest.getIid() + " for project");
    }
    return targetHead.getId();
}
Also used : LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) Commit(org.gitlab4j.api.models.Commit) Branch(org.gitlab4j.api.models.Branch) RepositoryApi(org.gitlab4j.api.RepositoryApi) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabApiException(org.gitlab4j.api.GitLabApiException)

Example 2 with Commit

use of org.gitlab4j.api.models.Commit 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);
    }
}
Also used : VersionId(org.finos.legend.sdlc.domain.model.version.VersionId) GitLabApi(org.gitlab4j.api.GitLabApi) GitLabApiException(org.gitlab4j.api.GitLabApiException) CommitRef(org.gitlab4j.api.models.CommitRef) CommitsApi(org.gitlab4j.api.CommitsApi) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabApiException(org.gitlab4j.api.GitLabApiException) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) Commit(org.gitlab4j.api.models.Commit) Tag(org.gitlab4j.api.models.Tag)

Example 3 with Commit

use of org.gitlab4j.api.models.Commit in project choerodon-starters by open-hand.

the class MergeRequestApi method getCommits.

/**
 * Get a list of merge request commits.
 * <p>
 * GET /projects/:id/merge_requests/:merge_request_iid/commits
 *
 * @param projectId      the project ID for the merge request
 * @param mergeRequestId the ID of the merge request
 * @param page           the page to get
 * @param perPage        the number of commits per page
 * @return a list containing the commits for the specified merge request
 * @throws GitLabApiException GitLabApiException if any exception occurs during execution
 */
public List<Commit> getCommits(int projectId, int mergeRequestId, int page, int perPage) throws GitLabApiException {
    Form formData = new GitLabApiForm().withParam("owned", true).withParam(PAGE_PARAM, page).withParam(PER_PAGE_PARAM, perPage);
    Response response = get(Response.Status.OK, formData.asMap(), "projects", projectId, "merge_requests", mergeRequestId, "commits");
    return (response.readEntity(new GenericType<List<Commit>>() {
    }));
}
Also used : Response(javax.ws.rs.core.Response) GenericType(javax.ws.rs.core.GenericType) Commit(org.gitlab4j.api.models.Commit) Form(javax.ws.rs.core.Form)

Example 4 with Commit

use of org.gitlab4j.api.models.Commit in project legend-sdlc by finos.

the class GitLabReviewApi method getMergeRequestBaseRevision.

private String getMergeRequestBaseRevision(GitLabProjectId projectId, GitLabApi gitLabApi, MergeRequest mergeRequest) {
    DiffRef diffRef = mergeRequest.getDiffRefs();
    if (diffRef != null) {
        String baseRevisionId = diffRef.getBaseSha();
        if (baseRevisionId != null) {
            return baseRevisionId;
        }
    }
    RepositoryApi repositoryApi = gitLabApi.getRepositoryApi();
    String sourceBranchName = mergeRequest.getSourceBranch();
    String targetBranchName = mergeRequest.getTargetBranch();
    Commit mergeBase;
    try {
        mergeBase = withRetries(() -> repositoryApi.getMergeBase(projectId.getGitLabId(), Arrays.asList(sourceBranchName, targetBranchName)));
    } catch (Exception e) {
        LOGGER.error("Error getting merge base for merge request {} in project {} (source branch: {}, target branch: {})", mergeRequest.getIid(), projectId, sourceBranchName, targetBranchName);
        StringBuilder builder = new StringBuilder("Error getting base revision for review ").append(mergeRequest.getIid()).append(" for project ").append(projectId);
        StringTools.appendThrowableMessageIfPresent(builder, e);
        throw new LegendSDLCServerException(builder.toString(), e);
    }
    if ((mergeBase == null) || (mergeBase.getId() == null)) {
        LOGGER.error("Error getting merge base for merge request {} in project {} (source branch: {}, target branch: {}): {}", mergeRequest.getIid(), projectId, sourceBranchName, targetBranchName, mergeBase);
        throw new LegendSDLCServerException("Error getting base revision for review " + mergeRequest.getIid() + " for project " + projectId);
    }
    return mergeBase.getId();
}
Also used : LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) Commit(org.gitlab4j.api.models.Commit) RepositoryApi(org.gitlab4j.api.RepositoryApi) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabApiException(org.gitlab4j.api.GitLabApiException) DiffRef(org.gitlab4j.api.models.DiffRef)

Example 5 with Commit

use of org.gitlab4j.api.models.Commit in project legend-sdlc by finos.

the class GitLabWorkspaceApi method attemptToSquashAndRebaseWorkspace.

/**
 * This method is called when we failed to rebase the workspace branch on top of master branch. This implies that either
 * the whole change is causing a merge conflict or one of the intermediate commits have conflicts with the change in master branch
 * <p>
 * To handle the latter case, this method will squash all commits on the workspace branch and attempt rebase again.
 * If this fails, it implies that rebase fails because of merge conflicts, which means we have to enter conflict resolution route.
 * <p>
 * NOTE: based on the nature of this method, we have an optimization here where we check for the number of commits on the current
 * branch, if it is less than 2 (not counting the base), it means no squashing is needed and we can just immediately tell that there
 * is merge conflict and the workspace update should enter conflict resolution route
 * <p>
 * So following is the summary of this method:
 * 1. Create a temp branch to do the squashing on that branch
 * 3. Attempt to rebase the temp branch on master:
 * - If succeeded: re-create current workspace branch on top of the temp branch
 * - If failed -> implies conflict resolution is needed
 *
 * @return a workspace update report that might have status as UPDATED or CONFLICT.
 */
private WorkspaceUpdateReport attemptToSquashAndRebaseWorkspace(String projectId, String workspaceId, WorkspaceType workspaceType, String masterRevisionId, String currentWorkspaceRevisionId, String workspaceCreationRevisionId) {
    GitLabProjectId gitLabProjectId = parseProjectId(projectId);
    RepositoryApi repositoryApi = getGitLabApi(gitLabProjectId.getGitLabMode()).getRepositoryApi();
    // Create temp branch for rebasing
    String tempBranchName = newUserTemporaryBranchName();
    Branch tempBranch;
    try {
        tempBranch = GitLabApiTools.createBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), tempBranchName, workspaceCreationRevisionId, 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 " + workspaceCreationRevisionId);
    }
    CompareResults comparisonResult;
    try {
        comparisonResult = withRetries(() -> repositoryApi.compare(gitLabProjectId.getGitLabId(), workspaceCreationRevisionId, currentWorkspaceRevisionId, true));
    } catch (Exception e) {
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to get comparison information from revision " + workspaceCreationRevisionId + "  to revision " + currentWorkspaceRevisionId + " on project" + projectId, () -> "Could not find revisions " + workspaceCreationRevisionId + " ," + currentWorkspaceRevisionId + " on project" + projectId, () -> "Failed to fetch comparison information from revision " + workspaceCreationRevisionId + "  to revision " + currentWorkspaceRevisionId + " on project" + projectId);
    }
    // Create a new commit on temp branch that squashes all changes on the concerned workspace branch
    CommitsApi commitsApi = getGitLabApi(gitLabProjectId.getGitLabMode()).getCommitsApi();
    ProjectFileAccessProvider.WorkspaceAccessType workspaceAccessType = ProjectFileAccessProvider.WorkspaceAccessType.WORKSPACE;
    ProjectFileAccessProvider.FileAccessContext workspaceFileAccessContext = getProjectFileAccessProvider().getWorkspaceFileAccessContext(projectId, workspaceId, workspaceType, workspaceAccessType);
    Commit squashedCommit;
    try {
        List<CommitAction> commitActions = Lists.mutable.empty();
        comparisonResult.getDiffs().forEach(diff -> {
            if (diff.getDeletedFile()) {
                // DELETE
                commitActions.add(new CommitAction().withAction(CommitAction.Action.DELETE).withFilePath(diff.getOldPath()));
            } else if (diff.getRenamedFile()) {
                // MOVE
                // 
                // tl;dr;
                // split this into a delete and a create to make sure the moved entity has the content of the entity
                // in workspace HEAD revision
                // 
                // Since we use comparison API to compute the diff, Git has a smart algorithm to calculate file move
                // as it is a heuristics such that if file content is only slightly different, Git can conclude that the
                // diff was of type RENAME.
                // 
                // The problem with this is when we compare the workspace BASE revision and workspace HEAD revision
                // Assume that we actually renamed an entity, we also want to update the entity path, not just its location
                // the location part is correctly identified by Git, and the change in content is captured as a diff string
                // in comparison result (which shows the change in the path)
                // but when we create CommitAction, there is no way for us to apply this patch on top of the entity content
                // so if we just create action of type MOVE for CommitAction, we will have the content of the old entity
                // which has the wrong path, and thus this entity if continue to exist in the workspace will throw off
                // our path and entity location validation check
                commitActions.add(new CommitAction().withAction(CommitAction.Action.DELETE).withFilePath(diff.getOldPath()));
                commitActions.add(new CommitAction().withAction(CommitAction.Action.CREATE).withFilePath(diff.getNewPath()).withEncoding(Constants.Encoding.BASE64).withContent(encodeBase64(workspaceFileAccessContext.getFile(diff.getNewPath()).getContentAsBytes())));
            } else if (diff.getNewFile()) {
                // CREATE
                commitActions.add(new CommitAction().withAction(CommitAction.Action.CREATE).withFilePath(diff.getNewPath()).withEncoding(Constants.Encoding.BASE64).withContent(encodeBase64(workspaceFileAccessContext.getFile(diff.getNewPath()).getContentAsBytes())));
            } else {
                // UPDATE
                commitActions.add(new CommitAction().withAction(CommitAction.Action.UPDATE).withFilePath(diff.getOldPath()).withEncoding(Constants.Encoding.BASE64).withContent(encodeBase64(workspaceFileAccessContext.getFile(diff.getOldPath()).getContentAsBytes())));
            }
        });
        squashedCommit = commitsApi.createCommit(gitLabProjectId.getGitLabId(), tempBranchName, "aggregated changes for workspace " + workspaceId, null, null, getCurrentUser(), commitActions);
    } catch (Exception e) {
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to create commit on temporary workspace " + tempBranchName + " of project " + projectId, () -> "Unknown project: " + projectId + " or temporary workspace " + tempBranchName, () -> "Failed to create commit in temporary workspace " + tempBranchName + " of project " + projectId);
    }
    // Attempt to rebase the temporary branch on top of master
    boolean attemptRebaseResult = this.attemptToRebaseWorkspaceUsingTemporaryBranch(projectId, workspaceId, workspaceType, tempBranchName, masterRevisionId);
    // If rebasing failed, this implies there are conflicts, otherwise, the workspace should be updated
    if (!attemptRebaseResult) {
        return createWorkspaceUpdateReport(WorkspaceUpdateReportStatus.CONFLICT, null, null);
    }
    return createWorkspaceUpdateReport(WorkspaceUpdateReportStatus.UPDATED, masterRevisionId, squashedCommit.getId());
}
Also used : CompareResults(org.gitlab4j.api.models.CompareResults) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabApiException(org.gitlab4j.api.GitLabApiException) CommitsApi(org.gitlab4j.api.CommitsApi) ProjectFileAccessProvider(org.finos.legend.sdlc.server.project.ProjectFileAccessProvider) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) Commit(org.gitlab4j.api.models.Commit) GitLabProjectId(org.finos.legend.sdlc.server.gitlab.GitLabProjectId) Branch(org.gitlab4j.api.models.Branch) RepositoryApi(org.gitlab4j.api.RepositoryApi) CommitAction(org.gitlab4j.api.models.CommitAction)

Aggregations

Commit (org.gitlab4j.api.models.Commit)7 LegendSDLCServerException (org.finos.legend.sdlc.server.error.LegendSDLCServerException)5 GitLabApiException (org.gitlab4j.api.GitLabApiException)4 RepositoryApi (org.gitlab4j.api.RepositoryApi)4 CommitsApi (org.gitlab4j.api.CommitsApi)3 GitLabProjectId (org.finos.legend.sdlc.server.gitlab.GitLabProjectId)2 ProjectFileAccessProvider (org.finos.legend.sdlc.server.project.ProjectFileAccessProvider)2 Branch (org.gitlab4j.api.models.Branch)2 ILocalGitRepositoryManager (de.catma.repository.git.interfaces.ILocalGitRepositoryManager)1 IRemoteGitServerManager (de.catma.repository.git.interfaces.IRemoteGitServerManager)1 File (java.io.File)1 Form (javax.ws.rs.core.Form)1 GenericType (javax.ws.rs.core.GenericType)1 Response (javax.ws.rs.core.Response)1 RevCommit (org.eclipse.jgit.revwalk.RevCommit)1 VersionId (org.finos.legend.sdlc.domain.model.version.VersionId)1 ProjectStructure (org.finos.legend.sdlc.server.project.ProjectStructure)1 GitLabApi (org.gitlab4j.api.GitLabApi)1 CommitAction (org.gitlab4j.api.models.CommitAction)1 CommitRef (org.gitlab4j.api.models.CommitRef)1