diff --git a/app/server/appsmith-git/src/main/java/com/appsmith/git/handler/ce/FSGitHandlerCEImpl.java b/app/server/appsmith-git/src/main/java/com/appsmith/git/handler/ce/FSGitHandlerCEImpl.java index bbaff5c949b5..c488bd6c9b44 100644 --- a/app/server/appsmith-git/src/main/java/com/appsmith/git/handler/ce/FSGitHandlerCEImpl.java +++ b/app/server/appsmith-git/src/main/java/com/appsmith/git/handler/ce/FSGitHandlerCEImpl.java @@ -5,9 +5,11 @@ import com.appsmith.external.constants.ErrorReferenceDocUrl; import com.appsmith.external.dtos.GitBranchDTO; import com.appsmith.external.dtos.GitLogDTO; +import com.appsmith.external.dtos.GitRefDTO; import com.appsmith.external.dtos.GitStatusDTO; import com.appsmith.external.dtos.MergeStatusDTO; import com.appsmith.external.git.constants.GitSpan; +import com.appsmith.external.git.constants.ce.RefType; import com.appsmith.external.git.handler.FSGitHandler; import com.appsmith.external.helpers.Stopwatch; import com.appsmith.git.configurations.GitServiceConfig; @@ -344,6 +346,52 @@ public Mono createAndCheckoutToBranch(Path repoSuffix, String branchName .subscribeOn(scheduler); } + private String createAndCheckoutBranch(Git git, GitRefDTO gitRefDTO) throws GitAPIException, IOException { + String branchName = gitRefDTO.getRefName(); + git.checkout() + .setCreateBranch(TRUE) + .setName(branchName) + .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK) + .call(); + + repositoryHelper.updateRemoteBranchTrackingConfig(branchName, git); + return git.getRepository().getBranch(); + } + + private String createTag(Git git, GitRefDTO gitRefDTO) throws GitAPIException { + String tagName = gitRefDTO.getRefName(); + String message = gitRefDTO.getMessage(); + return git.tag().setName(tagName).setMessage(message).call().getName(); + } + + @Override + public Mono createAndCheckoutReference(Path repoSuffix, GitRefDTO gitRefDTO) { + RefType refType = gitRefDTO.getRefType(); + String refName = gitRefDTO.getRefName(); + + return Mono.using( + () -> Git.open(createRepoPath(repoSuffix).toFile()), + git -> Mono.fromCallable(() -> { + log.info( + "{} : Creating reference of type {} and name {} for the repo {}", + Thread.currentThread().getName(), + refType.name(), + refName, + repoSuffix); + + if (RefType.TAG.equals(refType)) { + return createTag(git, gitRefDTO); + } + + return createAndCheckoutBranch(git, gitRefDTO); + }) + .timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS)) + .name(GitSpan.FS_CREATE_BRANCH) + .tap(Micrometer.observation(observationRegistry)), + Git::close) + .subscribeOn(scheduler); + } + @Override public Mono deleteBranch(Path repoSuffix, String branchName) { // We can safely assume that repo has been already initialised either in commit or clone flow and can directly diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/GitRefDTO.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/GitRefDTO.java index 7b9a75efe063..0f0e4c7def77 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/GitRefDTO.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/GitRefDTO.java @@ -14,6 +14,11 @@ public class GitRefDTO { RefType refType; + /** + * for tags, while tagging we require messages. + */ + String message; + boolean isDefault; boolean createdFromLocal; diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/git/handler/FSGitHandler.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/git/handler/FSGitHandler.java index 47d3f5883498..b1eb6d68a303 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/git/handler/FSGitHandler.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/git/handler/FSGitHandler.java @@ -2,6 +2,7 @@ import com.appsmith.external.dtos.GitBranchDTO; import com.appsmith.external.dtos.GitLogDTO; +import com.appsmith.external.dtos.GitRefDTO; import com.appsmith.external.dtos.GitStatusDTO; import com.appsmith.external.dtos.MergeStatusDTO; import org.eclipse.jgit.api.errors.GitAPIException; @@ -80,6 +81,8 @@ Mono pushApplication( */ Mono createAndCheckoutToBranch(Path repoSuffix, String branchName); + Mono createAndCheckoutReference(Path repoSuffix, GitRefDTO gitRefDTO); + /** * Delete a branch in the local repo * diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/git/GitApplicationHelperCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/git/GitApplicationHelperCEImpl.java index c299137a10f7..8b15d289ee26 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/git/GitApplicationHelperCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/git/GitApplicationHelperCEImpl.java @@ -1,5 +1,6 @@ package com.appsmith.server.applications.git; +import com.appsmith.external.git.constants.ce.RefType; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.actioncollections.base.ActionCollectionService; import com.appsmith.server.applications.base.ApplicationService; @@ -327,4 +328,16 @@ public Mono publishArtifactPostCommit(Artifact committedArtifact) { public Mono validateAndPublishArtifact(Artifact artifact, boolean publish) { return publishArtifact(artifact, publish); } + + @Override + public Mono publishArtifactPostRefCreation( + Artifact artifact, RefType refType, Boolean isPublishedManually) { + // TODO: create publish for ref type creation. + Application application = (Application) artifact; + if (RefType.TAG.equals(refType)) { + return Mono.just(application); + } + + return Mono.just(application); + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java index 207310d4e043..96b0b2747f24 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java @@ -419,21 +419,30 @@ protected Mono checkoutReference( }); } else { // TODO refactor method to account for RefName as well - checkedOutArtifactMono = Mono.defer(() -> gitArtifactHelper - .getArtifactByBaseIdAndBranchName( - baseArtifactId, finalRefName, gitArtifactHelper.getArtifactReadPermission()) + checkedOutArtifactMono = gitHandlingService + .checkoutArtifact(jsonTransformationDTO) + .flatMap(isCheckedOut -> gitArtifactHelper.getArtifactByBaseIdAndBranchName( + baseArtifactId, finalRefName, gitArtifactHelper.getArtifactReadPermission())) .flatMap(artifact -> gitAnalyticsUtils.addAnalyticsForGitOperation( AnalyticsEvents.GIT_CHECKOUT_BRANCH, artifact, - artifact.getGitArtifactMetadata().getIsRepoPrivate()))); + artifact.getGitArtifactMetadata().getIsRepoPrivate())); } - return checkedOutArtifactMono - .doFinally(signalType -> gitRedisUtils.releaseFileLock(baseArtifactId, addFileLock)) + return acquireFileLock + .then(checkedOutArtifactMono) + .flatMap(checkedOutArtifact -> gitRedisUtils + .releaseFileLock(baseArtifactId, addFileLock) + .thenReturn(checkedOutArtifact)) + .onErrorResume(error -> { + log.error("An error occurred while checking out the reference. error {}", error.getMessage()); + return gitRedisUtils + .releaseFileLock(baseArtifactId, addFileLock) + .then(Mono.error(error)); + }) .tag(GitConstants.GitMetricConstants.CHECKOUT_REMOTE, FALSE.toString()) .name(GitSpan.OPS_CHECKOUT_BRANCH) - .tap(Micrometer.observation(observationRegistry)) - .onErrorResume(Mono::error); + .tap(Micrometer.observation(observationRegistry)); } // TODO @Manish: add checkout Remote Branch @@ -451,8 +460,6 @@ public Mono createReference( 3. Rehydrate the artifact from source artifact reference */ - RefType refType = refDTO.getRefType(); - if (refDTO.getRefType() == null) { return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, REF_TYPE)); } @@ -462,116 +469,152 @@ public Mono createReference( } GitArtifactHelper gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType); - GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType); AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission(); Mono> baseAndBranchedArtifactMono = getBaseAndBranchedArtifacts(referencedArtifactId, artifactType, artifactEditPermission); - Mono createBranchMono = baseAndBranchedArtifactMono - .flatMap(artifactTuples -> { - Artifact baseArtifact = artifactTuples.getT1(); - Artifact parentArtifact = artifactTuples.getT2(); + return baseAndBranchedArtifactMono.flatMap(artifactTuples -> { + Artifact baseArtifact = artifactTuples.getT1(); + Artifact parentArtifact = artifactTuples.getT2(); - GitArtifactMetadata baseGitMetadata = baseArtifact.getGitArtifactMetadata(); - GitAuth baseGitAuth = baseGitMetadata.getGitAuth(); - GitArtifactMetadata parentGitMetadata = parentArtifact.getGitArtifactMetadata(); + return createReference(baseArtifact, parentArtifact, refDTO, gitType); + }); + } - ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO(); - jsonTransformationDTO.setWorkspaceId(baseArtifact.getWorkspaceId()); - jsonTransformationDTO.setBaseArtifactId(baseGitMetadata.getDefaultArtifactId()); - jsonTransformationDTO.setRepoName(baseGitMetadata.getRepoName()); - jsonTransformationDTO.setArtifactType(baseArtifact.getArtifactType()); - jsonTransformationDTO.setRefType(refType); - jsonTransformationDTO.setRefName(refDTO.getRefName()); - - if (parentGitMetadata == null - || !hasText(parentGitMetadata.getDefaultArtifactId()) - || !hasText(parentGitMetadata.getRepoName())) { - // TODO: add refType in error - return Mono.error( - new AppsmithException( - AppsmithError.INVALID_GIT_CONFIGURATION, - "Unable to find the parent reference. Please create a reference from other available references")); - } + protected Mono createReference( + Artifact baseArtifact, Artifact sourceArtifact, GitRefDTO refDTO, GitType gitType) { - Mono acquireGitLockMono = gitRedisUtils.acquireGitLock( - baseGitMetadata.getDefaultArtifactId(), - GitConstants.GitCommandConstants.CREATE_BRANCH, - FALSE); - Mono fetchRemoteMono = - gitHandlingService.fetchRemoteChanges(jsonTransformationDTO, baseGitAuth, TRUE); - - return acquireGitLockMono - .flatMap(ignoreLockAcquisition -> fetchRemoteMono.onErrorResume(error -> - Mono.error(new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "fetch", error)))) - .flatMap(ignoreFetchString -> gitHandlingService - .listReferences(jsonTransformationDTO, TRUE, refType) - .flatMap(refList -> { - boolean isDuplicateName = refList.stream() - // We are only supporting origin as the remote name so this is safe - // but needs to be altered if we start supporting user defined remote - // names - .anyMatch(ref -> ref.replaceFirst(ORIGIN, REMOTE_NAME_REPLACEMENT) - .equals(refDTO.getRefName())); - - if (isDuplicateName) { - return Mono.error(new AppsmithException( - AppsmithError.DUPLICATE_KEY_USER_ERROR, - "remotes/origin/" + refDTO.getRefName(), - FieldName.BRANCH_NAME)); - } + RefType refType = refDTO.getRefType(); + GitArtifactMetadata baseGitMetadata = baseArtifact.getGitArtifactMetadata(); + GitAuth baseGitAuth = baseGitMetadata.getGitAuth(); + GitArtifactMetadata sourceGitMetadata = sourceArtifact.getGitArtifactMetadata(); - Mono artifactExchangeJsonMono = - exportService.exportByArtifactId( - parentArtifact.getId(), VERSION_CONTROL, artifactType); + GitArtifactHelper gitArtifactHelper = + gitArtifactHelperResolver.getArtifactHelper(baseArtifact.getArtifactType()); + GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType); - Mono newArtifactFromSourceMono = - // TODO: add refType support over here - gitArtifactHelper.createNewArtifactForCheckout( - parentArtifact, refDTO.getRefName()); + ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO(); + jsonTransformationDTO.setWorkspaceId(baseArtifact.getWorkspaceId()); + jsonTransformationDTO.setBaseArtifactId(baseGitMetadata.getDefaultArtifactId()); + jsonTransformationDTO.setRepoName(baseGitMetadata.getRepoName()); + jsonTransformationDTO.setArtifactType(baseArtifact.getArtifactType()); + jsonTransformationDTO.setRefType(refType); + jsonTransformationDTO.setRefName(refDTO.getRefName()); - return Mono.zip(newArtifactFromSourceMono, artifactExchangeJsonMono); - })) - .flatMap(tuple -> { - ArtifactExchangeJson exportedJson = tuple.getT2(); - Artifact newRefArtifact = tuple.getT1(); + if (sourceGitMetadata == null + || !hasText(sourceGitMetadata.getDefaultArtifactId()) + || !hasText(sourceGitMetadata.getRepoName())) { + // TODO: add refType in error + return Mono.error(new AppsmithException( + AppsmithError.INVALID_GIT_CONFIGURATION, + "Unable to find the parent reference. Please create a reference from other available references")); + } - Mono refCreationMono = gitHandlingService - .createGitReference(jsonTransformationDTO) - // TODO: ths error could be shipped to handling layer as well? - .onErrorResume(error -> Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "ref creation preparation", - error.getMessage()))); + Mono acquireGitLockMono = gitRedisUtils.acquireGitLock( + baseGitMetadata.getDefaultArtifactId(), GitConstants.GitCommandConstants.CREATE_BRANCH, FALSE); + Mono fetchRemoteMono = gitHandlingService.fetchRemoteChanges(jsonTransformationDTO, baseGitAuth, TRUE); + + Mono createBranchMono = acquireGitLockMono + .flatMap(ignoreLockAcquisition -> fetchRemoteMono.onErrorResume( + error -> Mono.error(new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "fetch", error)))) + .flatMap(ignoreFetchString -> gitHandlingService + .listReferences(jsonTransformationDTO, TRUE, refType) + .flatMap(refList -> { + boolean isDuplicateName = refList.stream() + // We are only supporting origin as the remote name so this is safe + // but needs to be altered if we start supporting user defined remote + // names + .anyMatch(ref -> ref.replaceFirst(ORIGIN, REMOTE_NAME_REPLACEMENT) + .equals(refDTO.getRefName())); + + if (isDuplicateName) { + return Mono.error(new AppsmithException( + AppsmithError.DUPLICATE_KEY_USER_ERROR, + "remotes/origin/" + refDTO.getRefName(), + FieldName.BRANCH_NAME)); + } + + Mono refCreationValidationMono = + isValidationForRefCreationComplete(baseArtifact, sourceArtifact, gitType, refType); + + Mono artifactExchangeJsonMono = + exportService.exportByArtifactId( + sourceArtifact.getId(), VERSION_CONTROL, baseArtifact.getArtifactType()); + + Mono newArtifactFromSourceMono = + // TODO: add refType support over here + gitArtifactHelper.createNewArtifactForCheckout(sourceArtifact, refDTO.getRefName()); + + return refCreationValidationMono.flatMap(isOkayToProceed -> { + if (!TRUE.equals(isOkayToProceed)) { + return Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, "ref creation", "status unclean")); + } - return refCreationMono - .flatMap(ignoredString -> { - return importService.importArtifactInWorkspaceFromGit( - newRefArtifact.getWorkspaceId(), - newRefArtifact.getId(), - exportedJson, - refDTO.getRefName()); - }) - // after the branch is created, we need to reset the older branch to initial - // commit - .doOnSuccess(newImportedArtifact -> - discardChanges(parentArtifact.getId(), artifactType, gitType)); + return Mono.zip(newArtifactFromSourceMono, artifactExchangeJsonMono); }); + })) + .flatMap(tuple -> { + ArtifactExchangeJson exportedJson = tuple.getT2(); + Artifact newRefArtifact = tuple.getT1(); + + Mono refCreationMono = gitHandlingService + .createGitReference(jsonTransformationDTO, refDTO) + // TODO: this error could be shipped to handling layer as well? + .onErrorResume(error -> Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, "ref creation preparation", error.getMessage()))); + + return refCreationMono + .flatMap(ignoredString -> { + return importService.importArtifactInWorkspaceFromGit( + newRefArtifact.getWorkspaceId(), + newRefArtifact.getId(), + exportedJson, + refDTO.getRefName()); + }) + .flatMap(importedArtifact -> { + return gitArtifactHelper.publishArtifactPostRefCreation( + importedArtifact, refType, TRUE); + }) + // after the branch is created, we need to reset the older branch to the + // clean status, i.e. last commit + .doOnSuccess(newImportedArtifact -> discardChanges(sourceArtifact, gitType)); }) - .flatMap(newImportedArtifact -> gitAnalyticsUtils - .addAnalyticsForGitOperation( + .flatMap(newImportedArtifact -> gitRedisUtils + .releaseFileLock( + newImportedArtifact.getGitArtifactMetadata().getDefaultArtifactId(), TRUE) + .then(gitAnalyticsUtils.addAnalyticsForGitOperation( AnalyticsEvents.GIT_CREATE_BRANCH, newImportedArtifact, - newImportedArtifact.getGitArtifactMetadata().getIsRepoPrivate()) - .doFinally(signalType -> gitRedisUtils.releaseFileLock( - newImportedArtifact.getGitArtifactMetadata().getDefaultArtifactId(), TRUE))) + newImportedArtifact.getGitArtifactMetadata().getIsRepoPrivate()))) + .onErrorResume(error -> { + log.error("An error occurred while creating reference. error {}", error.getMessage()); + return gitRedisUtils + .releaseFileLock(baseGitMetadata.getDefaultArtifactId(), TRUE) + .then(Mono.error(new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "checkout"))); + }) .name(GitSpan.OPS_CREATE_BRANCH) .tap(Micrometer.observation(observationRegistry)); return Mono.create(sink -> createBranchMono.subscribe(sink::success, sink::error, null, sink.currentContext())); } + protected Mono isValidationForRefCreationComplete( + Artifact baseArtifact, Artifact parentArtifact, GitType gitType, RefType refType) { + if (RefType.BRANCH.equals(refType)) { + return Mono.just(TRUE); + } + + return getStatus(baseArtifact, parentArtifact, false, true, gitType).map(gitStatusDTO -> { + if (!Boolean.TRUE.equals(gitStatusDTO.getIsClean())) { + return FALSE; + } + + return TRUE; + }); + } + @Override public Mono deleteGitReference( String baseArtifactId, GitRefDTO gitRefDTO, ArtifactType artifactType, GitType gitType) { @@ -650,7 +693,9 @@ protected Mono deleteGitReference( return gitHandlingService .deleteGitReference(jsonTransformationDTO) - .doFinally(signalType -> gitRedisUtils.releaseFileLock(baseArtifactId, TRUE)) + .flatMap(isReferenceDeleted -> gitRedisUtils + .releaseFileLock(baseArtifactId, TRUE) + .thenReturn(isReferenceDeleted)) .flatMap(isReferenceDeleted -> { if (FALSE.equals(isReferenceDeleted)) { return Mono.error(new AppsmithException( @@ -686,6 +731,14 @@ protected Mono deleteGitReference( AnalyticsEvents.GIT_DELETE_BRANCH, deletedArtifact, deletedArtifact.getGitArtifactMetadata().getIsRepoPrivate())) + .onErrorResume(error -> { + log.error( + "An error occurred while deleting the git reference : {}, with base id {}", + referenceArtifactMetadata.getRefName(), + baseArtifactId); + + return gitRedisUtils.releaseFileLock(baseArtifactId, TRUE).then(Mono.error(error)); + }) .name(GitSpan.OPS_DELETE_BRANCH) .tap(Micrometer.observation(observationRegistry)); @@ -1163,13 +1216,14 @@ private Mono commitArtifact( return gitHandlingService .commitArtifact(updatedBranchedArtifact, commitDTO, jsonTransformationDTO) .onErrorResume(error -> { - return gitAnalyticsUtils - .addAnalyticsForGitOperation( + return gitRedisUtils + .releaseFileLock(baseArtifact.getId(), TRUE) + .then(gitAnalyticsUtils.addAnalyticsForGitOperation( AnalyticsEvents.GIT_COMMIT, updatedBranchedArtifact, error.getClass().getName(), error.getMessage(), - gitArtifactMetadata.getIsRepoPrivate()) + gitArtifactMetadata.getIsRepoPrivate())) .then(Mono.error(new AppsmithException( AppsmithError.GIT_ACTION_FAILED, "commit", error.getMessage()))); }); @@ -1198,6 +1252,16 @@ private Mono commitArtifact( .thenReturn(status) .name(OPS_COMMIT) .tap(Micrometer.observation(observationRegistry)); + }) + .onErrorResume(error -> { + log.error( + "An error occurred while committing changes to artifact with base id: {} and branch: {}", + branchedGitMetadata.getDefaultArtifactId(), + branchedGitMetadata.getBranchName()); + + return gitRedisUtils + .releaseFileLock(branchedGitMetadata.getDefaultArtifactId(), TRUE) + .then(Mono.error(error)); }); return Mono.create(sink -> { @@ -1384,10 +1448,16 @@ protected Mono getStatus( fetchRemoteMono = Mono.just("ignored"); } - return Mono.zip(prepareForStatus, fetchRemoteMono) - .then(gitHandlingService.getStatus(jsonTransformationDTO)); + return Mono.zip(prepareForStatus, fetchRemoteMono).flatMap(tuple2 -> { + return gitHandlingService + .getStatus(jsonTransformationDTO) + .flatMap(gitStatusDTO -> { + return gitRedisUtils + .releaseFileLock(baseArtifactId, isFileLock) + .thenReturn(gitStatusDTO); + }); + }); }) - .doFinally(signalType -> gitRedisUtils.releaseFileLock(baseArtifactId, isFileLock)) .onErrorResume(throwable -> { /* in case of any error, the global exception handler will release the lock @@ -1626,61 +1696,69 @@ public Mono discardChanges( GitArtifactHelper gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType); AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission(); - Mono branchedArtifactMonoCached = - gitArtifactHelper.getArtifactById(branchedArtifactId, artifactEditPermission); - - Mono recreatedArtifactFromLastCommit; + return gitArtifactHelper + .getArtifactById(branchedArtifactId, artifactEditPermission) + .flatMap(branchedArtifact -> discardChanges(branchedArtifact, gitType)); + } - // Rehydrate the artifact from local file system - recreatedArtifactFromLastCommit = branchedArtifactMonoCached - .flatMap(branchedArtifact -> { - GitArtifactMetadata branchedGitData = branchedArtifact.getGitArtifactMetadata(); - if (branchedGitData == null || !hasText(branchedGitData.getDefaultArtifactId())) { - return Mono.error( - new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); - } + protected Mono discardChanges(Artifact branchedArtifact, GitType gitType) { - return Mono.just(branchedArtifact) - .doFinally(signalType -> gitRedisUtils.acquireGitLock( - branchedGitData.getDefaultArtifactId(), - GitConstants.GitCommandConstants.DISCARD, - TRUE)); - }) - .flatMap(branchedArtifact -> { - GitArtifactMetadata branchedGitData = branchedArtifact.getGitArtifactMetadata(); - ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO(); - // Because this operation is only valid for branches - jsonTransformationDTO.setArtifactType(artifactType); - jsonTransformationDTO.setRefType(RefType.BRANCH); - jsonTransformationDTO.setWorkspaceId(branchedArtifact.getWorkspaceId()); - jsonTransformationDTO.setBaseArtifactId(branchedGitData.getDefaultArtifactId()); - jsonTransformationDTO.setRefName(branchedGitData.getRefName()); - jsonTransformationDTO.setRepoName(branchedGitData.getRepoName()); + GitArtifactHelper gitArtifactHelper = + gitArtifactHelperResolver.getArtifactHelper(branchedArtifact.getArtifactType()); + GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType); - GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType); + GitArtifactMetadata branchedGitData = branchedArtifact.getGitArtifactMetadata(); + if (branchedGitData == null || !hasText(branchedGitData.getDefaultArtifactId())) { + return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); + } - return gitHandlingService - .recreateArtifactJsonFromLastCommit(jsonTransformationDTO) - .onErrorResume(throwable -> { - log.error("Git recreate ArtifactJsonFailed : {}", throwable.getMessage()); - return Mono.error( - new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "discard changes", - "Please create a new branch and resolve conflicts in the remote repository before proceeding.")); - }) - .flatMap(artifactExchangeJson -> importService.importArtifactInWorkspaceFromGit( - branchedArtifact.getWorkspaceId(), - branchedArtifact.getId(), - artifactExchangeJson, - branchedGitData.getBranchName())) - // Update the last deployed status after the rebase - .flatMap(importedArtifact -> gitArtifactHelper.publishArtifact(importedArtifact, true)); + ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO(); + jsonTransformationDTO.setArtifactType(branchedArtifact.getArtifactType()); + // Because this operation is only valid for branches + jsonTransformationDTO.setRefType(RefType.BRANCH); + jsonTransformationDTO.setWorkspaceId(branchedArtifact.getWorkspaceId()); + jsonTransformationDTO.setBaseArtifactId(branchedGitData.getDefaultArtifactId()); + jsonTransformationDTO.setRefName(branchedGitData.getRefName()); + jsonTransformationDTO.setRepoName(branchedGitData.getRepoName()); + + Mono recreatedArtifactFromLastCommit = gitRedisUtils + .acquireGitLock(branchedGitData.getDefaultArtifactId(), GitConstants.GitCommandConstants.DISCARD, TRUE) + .then(gitHandlingService + .recreateArtifactJsonFromLastCommit(jsonTransformationDTO) + .onErrorResume(throwable -> { + log.error("Git recreate ArtifactJsonFailed : {}", throwable.getMessage()); + return gitRedisUtils + .releaseFileLock(branchedGitData.getDefaultArtifactId(), TRUE) + .then( + Mono.error( + new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, + "discard changes", + "Please create a new branch and resolve conflicts in the remote repository before proceeding."))); + })) + .flatMap(artifactExchangeJson -> importService.importArtifactInWorkspaceFromGit( + branchedArtifact.getWorkspaceId(), + branchedArtifact.getId(), + artifactExchangeJson, + branchedGitData.getBranchName())) + // Update the last deployed status after the rebase + .flatMap(importedArtifact -> gitArtifactHelper.publishArtifact(importedArtifact, true)) + .flatMap(publishedArtifact -> { + return gitRedisUtils + .releaseFileLock( + publishedArtifact.getGitArtifactMetadata().getDefaultArtifactId(), TRUE) + .then(gitAnalyticsUtils.addAnalyticsForGitOperation( + AnalyticsEvents.GIT_DISCARD_CHANGES, publishedArtifact, null)); + }) + .onErrorResume(error -> { + log.error( + "An error occurred while discarding branch with artifact id {}. error {}", + branchedGitData.getDefaultArtifactId(), + error.getMessage()); + return gitRedisUtils + .releaseFileLock(branchedGitData.getDefaultArtifactId(), TRUE) + .then(Mono.error(new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "checkout"))); }) - .flatMap(branchedArtifact -> gitAnalyticsUtils - .addAnalyticsForGitOperation(AnalyticsEvents.GIT_DISCARD_CHANGES, branchedArtifact, null) - .doFinally(signalType -> gitRedisUtils.releaseFileLock( - branchedArtifact.getGitArtifactMetadata().getDefaultArtifactId(), TRUE))) .name(GitSpan.OPS_DISCARD_CHANGES) .tap(Micrometer.observation(observationRegistry)); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java index 3159627aeb77..ddbb91643550 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java @@ -1,5 +1,6 @@ package com.appsmith.server.git.central; +import com.appsmith.external.dtos.GitRefDTO; import com.appsmith.external.dtos.GitStatusDTO; import com.appsmith.external.git.constants.ce.RefType; import com.appsmith.git.dto.CommitDTO; @@ -76,7 +77,9 @@ Mono recreateArtifactJsonFromLastCommit( Mono getStatus(ArtifactJsonTransformationDTO jsonTransformationDTO); - Mono createGitReference(ArtifactJsonTransformationDTO artifactJsonTransformationDTO); + Mono createGitReference(ArtifactJsonTransformationDTO artifactJsonTransformationDTO, GitRefDTO gitRefDTO); Mono deleteGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO); + + Mono checkoutArtifact(ArtifactJsonTransformationDTO jsonTransformationDTO); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java index a642383ce230..4ba699bc0575 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java @@ -2,6 +2,7 @@ import com.appsmith.external.constants.AnalyticsEvents; import com.appsmith.external.dtos.GitBranchDTO; +import com.appsmith.external.dtos.GitRefDTO; import com.appsmith.external.dtos.GitStatusDTO; import com.appsmith.external.git.constants.GitConstants; import com.appsmith.external.git.constants.GitSpan; @@ -662,7 +663,7 @@ public Mono getStatus(ArtifactJsonTransformationDTO jsonTransforma } @Override - public Mono createGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO) { + public Mono createGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO, GitRefDTO gitRefDTO) { GitArtifactHelper gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType()); @@ -671,7 +672,7 @@ public Mono createGitReference(ArtifactJsonTransformationDTO jsonTransfo jsonTransformationDTO.getBaseArtifactId(), jsonTransformationDTO.getRepoName()); - return fsGitHandler.createAndCheckoutToBranch(repoSuffix, jsonTransformationDTO.getRefName()); + return fsGitHandler.createAndCheckoutReference(repoSuffix, gitRefDTO); } @Override @@ -688,14 +689,34 @@ public Mono deleteGitReference(ArtifactJsonTransformationDTO jsonTransf .deleteBranch(repoSuffix, jsonTransformationDTO.getRefName()) .onErrorResume(throwable -> { log.error("Delete branch failed {}", throwable.getMessage()); + + Mono releaseLockMono = + gitRedisUtils.releaseFileLock(jsonTransformationDTO.getBaseArtifactId(), Boolean.TRUE); + if (throwable instanceof CannotDeleteCurrentBranchException) { - return Mono.error(new AppsmithException( + return releaseLockMono.then(Mono.error(new AppsmithException( AppsmithError.GIT_ACTION_FAILED, "delete branch", - "Cannot delete current checked out branch")); + "Cannot delete current checked out branch"))); } - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "delete branch", throwable.getMessage())); + + return releaseLockMono.then(Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, "delete branch", throwable.getMessage()))); }); } + + @Override + public Mono checkoutArtifact(ArtifactJsonTransformationDTO jsonTransformationDTO) { + + GitArtifactHelper gitArtifactHelper = + gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType()); + + Path repoSuffix = gitArtifactHelper.getRepoSuffixPath( + jsonTransformationDTO.getWorkspaceId(), + jsonTransformationDTO.getBaseArtifactId(), + jsonTransformationDTO.getRepoName()); + + // Tags and branch checkout with the same mechanism. + return fsGitHandler.checkoutToBranch(repoSuffix, jsonTransformationDTO.getRefName()); + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitArtifactHelperCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitArtifactHelperCE.java index 8f436fc18d28..88bcde32c5db 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitArtifactHelperCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitArtifactHelperCE.java @@ -1,5 +1,6 @@ package com.appsmith.server.services.ce; +import com.appsmith.external.git.constants.ce.RefType; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.Artifact; import com.appsmith.server.dtos.ArtifactExchangeJson; @@ -70,4 +71,6 @@ public interface GitArtifactHelperCE { Mono publishArtifactPostCommit(Artifact committedArtifact); Mono validateAndPublishArtifact(Artifact artifact, boolean publish); + + Mono publishArtifactPostRefCreation(Artifact artifact, RefType refType, Boolean isPublishedManually); }