Search in sources :

Example 6 with CommitsApi

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

the class GitLabWorkspaceApi method isWorkspaceOutdatedByAccessType.

private boolean isWorkspaceOutdatedByAccessType(String projectId, String workspaceId, WorkspaceType workspaceType, ProjectFileAccessProvider.WorkspaceAccessType workspaceAccessType) {
    LegendSDLCServerException.validateNonNull(projectId, "projectId may not be null");
    LegendSDLCServerException.validateNonNull(workspaceId, "workspaceId may not be null");
    LegendSDLCServerException.validateNonNull(workspaceType, "workspaceType may not be null");
    LegendSDLCServerException.validateNonNull(workspaceAccessType, "workspaceAccessType may not be null");
    GitLabProjectId gitLabProjectId = parseProjectId(projectId);
    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 workspaceRevisionId = workspaceBranch.getCommit().getId();
    // Get HEAD of master
    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();
    CommitsApi commitsApi = gitLabApi.getCommitsApi();
    // Check if the workspace does not have the latest revision of the project, i.e. it is outdated
    try {
        if (masterRevisionId.equals(workspaceRevisionId)) {
            return false;
        }
        Pager<CommitRef> masterHeadCommitRefsPager = withRetries(() -> commitsApi.getCommitRefs(gitLabProjectId.getGitLabId(), masterRevisionId, RefType.BRANCH, ITEMS_PER_PAGE));
        Stream<CommitRef> masterHeadCommitRefs = PagerTools.stream(masterHeadCommitRefsPager);
        // This will check if the branch contains the master HEAD commit by looking up the list of references the commit is pushed to
        return masterHeadCommitRefs.noneMatch(cr -> workspaceBranchName.equals(cr.getName()));
    } catch (Exception e) {
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to check if " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId + " is outdated", () -> "Unknown revision (" + masterRevisionId + "), or " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " (" + workspaceId + ") or project (" + projectId + ")", () -> "Error checking if " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " of project " + projectId + " is outdated");
    }
}
Also used : GitLabApi(org.gitlab4j.api.GitLabApi) GitLabProjectId(org.finos.legend.sdlc.server.gitlab.GitLabProjectId) Branch(org.gitlab4j.api.models.Branch) RepositoryApi(org.gitlab4j.api.RepositoryApi) CommitRef(org.gitlab4j.api.models.CommitRef) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabApiException(org.gitlab4j.api.GitLabApiException) CommitsApi(org.gitlab4j.api.CommitsApi)

Example 7 with CommitsApi

use of org.gitlab4j.api.CommitsApi 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)

Example 8 with CommitsApi

use of org.gitlab4j.api.CommitsApi in project catma by forTEXT.

the class JGitRepoManagerTest method pushToGitLabRepoWithAuthentication.

@Test
public void pushToGitLabRepoWithAuthentication() throws Exception {
    GitLabServerManager gitLabServerManager = new GitLabServerManager(UserIdentification.userToMap(this.catmaUser.getIdentifier()));
    // create a repository
    IRemoteGitServerManager.CreateRepositoryResponse createRepositoryResponse = gitLabServerManager.createRepository(Randomizer.getRepoName(), null);
    try (ILocalGitRepositoryManager jGitRepoManager = new JGitRepoManager(this.catmaProperties.getProperty(RepositoryPropertyKey.GitBasedRepositoryBasePath.name()), this.catmaUser)) {
        this.directoriesToDeleteOnTearDown.add(jGitRepoManager.getRepositoryBasePath());
        // clone it
        String repoName = jGitRepoManager.clone("", createRepositoryResponse.repositoryHttpUrl, null, gitLabServerManager.getUsername(), gitLabServerManager.getPassword());
        File testRepoPath = new File(jGitRepoManager.getRepositoryBasePath(), repoName);
        assert testRepoPath.exists();
        assert testRepoPath.isDirectory();
        // can't call open on an attached instance
        jGitRepoManager.detach();
        // open the cloned repo, add and commit a file, then push
        jGitRepoManager.open("", testRepoPath.getName());
        File originalSourceDocument = new File("testdocs/rose_for_emily.pdf");
        byte[] originalSourceDocumentBytes = Files.readAllBytes(originalSourceDocument.toPath());
        File targetFile = new File(testRepoPath, originalSourceDocument.getName());
        jGitRepoManager.addAndCommit(targetFile, originalSourceDocumentBytes, "Test Committer", "testcommitter@catma.de");
        jGitRepoManager.push(gitLabServerManager.getUsername(), gitLabServerManager.getPassword());
        // assert that the push worked by looking at the commits on the server
        CommitsApi commitsApi = gitLabServerManager.getUserGitLabApi().getCommitsApi();
        List<Commit> commits = commitsApi.getCommits(createRepositoryResponse.repositoryId);
        assertEquals(1, commits.size());
        assertEquals("Adding rose_for_emily.pdf", commits.get(0).getMessage());
        // cleanup (these are not handled by tearDown)
        gitLabServerManager.deleteRepository(createRepositoryResponse.repositoryId);
        await().until(() -> gitLabServerManager.getAdminGitLabApi().getProjectApi().getProjects().isEmpty());
        // see GitLabServerManagerTest tearDown() for more info
        User user = gitLabServerManager.getGitLabUser();
        gitLabServerManager.getAdminGitLabApi().getUserApi().deleteUser(user.getId());
        GitLabServerManagerTest.awaitUserDeleted(gitLabServerManager.getAdminGitLabApi().getUserApi(), user.getId());
    }
}
Also used : RevCommit(org.eclipse.jgit.revwalk.RevCommit) Commit(org.gitlab4j.api.models.Commit) User(org.gitlab4j.api.models.User) ILocalGitRepositoryManager(de.catma.repository.git.interfaces.ILocalGitRepositoryManager) File(java.io.File) CommitsApi(org.gitlab4j.api.CommitsApi) IRemoteGitServerManager(de.catma.repository.git.interfaces.IRemoteGitServerManager) Test(org.junit.Test)

Aggregations

CommitsApi (org.gitlab4j.api.CommitsApi)8 LegendSDLCServerException (org.finos.legend.sdlc.server.error.LegendSDLCServerException)7 GitLabProjectId (org.finos.legend.sdlc.server.gitlab.GitLabProjectId)6 GitLabApiException (org.gitlab4j.api.GitLabApiException)6 ProjectFileAccessProvider (org.finos.legend.sdlc.server.project.ProjectFileAccessProvider)5 GitLabApi (org.gitlab4j.api.GitLabApi)5 RepositoryApi (org.gitlab4j.api.RepositoryApi)5 Branch (org.gitlab4j.api.models.Branch)5 Commit (org.gitlab4j.api.models.Commit)4 CommitRef (org.gitlab4j.api.models.CommitRef)4 Instant (java.time.Instant)2 Collections (java.util.Collections)2 List (java.util.List)2 Function (java.util.function.Function)2 Predicate (java.util.function.Predicate)2 Collectors (java.util.stream.Collectors)2 Inject (javax.inject.Inject)2 Status (javax.ws.rs.core.Response.Status)2 WorkspaceType (org.finos.legend.sdlc.domain.model.project.workspace.WorkspaceType)2 GitLabUserContext (org.finos.legend.sdlc.server.gitlab.auth.GitLabUserContext)2