chore: added interface changes to the server (#38258)

## Description
- interface layer changes for Git executor


Fixes #
> [!WARNING]  
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._

## Automation

/ok-to-test tags="@tag.Git"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/12481281011>
> Commit: e510ca6c5f34e5dd201d040d4fc866ca4c0628d8
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12481281011&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Git`
> Spec:
> <hr>Tue, 24 Dec 2024 16:05:39 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **New Features**
- Introduced methods for creating and checking out branches and tags in
Git.
- Added functionality for validating reference creation based on
artifact status.
- Enhanced artifact publishing with new methods related to reference
creation.
  - Added a method for checking out artifacts based on provided details.

- **Bug Fixes**
- Improved error handling for Git operations, providing more specific
error messages.

- **Documentation**
- Updated method signatures to reflect new parameters and
functionalities.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Manish Kumar 2024-12-25 01:11:47 +05:30 committed by GitHub
parent c0d393a8f7
commit a8cb8aac44
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 332 additions and 158 deletions

View File

@ -5,9 +5,11 @@ import com.appsmith.external.constants.AnalyticsEvents;
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 class FSGitHandlerCEImpl implements FSGitHandler {
.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<String> 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<Boolean> deleteBranch(Path repoSuffix, String branchName) {
// We can safely assume that repo has been already initialised either in commit or clone flow and can directly

View File

@ -14,6 +14,11 @@ public class GitRefDTO {
RefType refType;
/**
* for tags, while tagging we require messages.
*/
String message;
boolean isDefault;
boolean createdFromLocal;

View File

@ -2,6 +2,7 @@ package com.appsmith.external.git.handler;
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 @@ public interface FSGitHandler {
*/
Mono<String> createAndCheckoutToBranch(Path repoSuffix, String branchName);
Mono<String> createAndCheckoutReference(Path repoSuffix, GitRefDTO gitRefDTO);
/**
* Delete a branch in the local repo
*

View File

@ -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 class GitApplicationHelperCEImpl implements GitArtifactHelperCE<Applicati
public Mono<? extends Artifact> validateAndPublishArtifact(Artifact artifact, boolean publish) {
return publishArtifact(artifact, publish);
}
@Override
public Mono<Application> 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);
}
}

View File

@ -419,21 +419,30 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
});
} 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 class CentralGitServiceCEImpl implements CentralGitServiceCE {
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 class CentralGitServiceCEImpl implements CentralGitServiceCE {
}
GitArtifactHelper<?> gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType);
AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission();
Mono<Tuple2<? extends Artifact, ? extends Artifact>> baseAndBranchedArtifactMono =
getBaseAndBranchedArtifacts(referencedArtifactId, artifactType, artifactEditPermission);
Mono<? extends Artifact> 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());
protected Mono<? extends Artifact> createReference(
Artifact baseArtifact, Artifact sourceArtifact, GitRefDTO refDTO, GitType gitType) {
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"));
}
RefType refType = refDTO.getRefType();
GitArtifactMetadata baseGitMetadata = baseArtifact.getGitArtifactMetadata();
GitAuth baseGitAuth = baseGitMetadata.getGitAuth();
GitArtifactMetadata sourceGitMetadata = sourceArtifact.getGitArtifactMetadata();
Mono<Boolean> acquireGitLockMono = gitRedisUtils.acquireGitLock(
baseGitMetadata.getDefaultArtifactId(),
GitConstants.GitCommandConstants.CREATE_BRANCH,
FALSE);
Mono<String> fetchRemoteMono =
gitHandlingService.fetchRemoteChanges(jsonTransformationDTO, baseGitAuth, TRUE);
GitArtifactHelper<?> gitArtifactHelper =
gitArtifactHelperResolver.getArtifactHelper(baseArtifact.getArtifactType());
GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType);
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()));
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 (isDuplicateName) {
return Mono.error(new AppsmithException(
AppsmithError.DUPLICATE_KEY_USER_ERROR,
"remotes/origin/" + refDTO.getRefName(),
FieldName.BRANCH_NAME));
}
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<? extends ArtifactExchangeJson> artifactExchangeJsonMono =
exportService.exportByArtifactId(
parentArtifact.getId(), VERSION_CONTROL, artifactType);
Mono<Boolean> acquireGitLockMono = gitRedisUtils.acquireGitLock(
baseGitMetadata.getDefaultArtifactId(), GitConstants.GitCommandConstants.CREATE_BRANCH, FALSE);
Mono<String> fetchRemoteMono = gitHandlingService.fetchRemoteChanges(jsonTransformationDTO, baseGitAuth, TRUE);
Mono<? extends Artifact> newArtifactFromSourceMono =
// TODO: add refType support over here
gitArtifactHelper.createNewArtifactForCheckout(
parentArtifact, refDTO.getRefName());
Mono<? extends Artifact> 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()));
return Mono.zip(newArtifactFromSourceMono, artifactExchangeJsonMono);
}))
.flatMap(tuple -> {
ArtifactExchangeJson exportedJson = tuple.getT2();
Artifact newRefArtifact = tuple.getT1();
if (isDuplicateName) {
return Mono.error(new AppsmithException(
AppsmithError.DUPLICATE_KEY_USER_ERROR,
"remotes/origin/" + refDTO.getRefName(),
FieldName.BRANCH_NAME));
}
Mono<String> 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<Boolean> refCreationValidationMono =
isValidationForRefCreationComplete(baseArtifact, sourceArtifact, gitType, refType);
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));
Mono<? extends ArtifactExchangeJson> artifactExchangeJsonMono =
exportService.exportByArtifactId(
sourceArtifact.getId(), VERSION_CONTROL, baseArtifact.getArtifactType());
Mono<? extends Artifact> 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 Mono.zip(newArtifactFromSourceMono, artifactExchangeJsonMono);
});
}))
.flatMap(tuple -> {
ArtifactExchangeJson exportedJson = tuple.getT2();
Artifact newRefArtifact = tuple.getT1();
Mono<String> 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<Boolean> 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<? extends Artifact> deleteGitReference(
String baseArtifactId, GitRefDTO gitRefDTO, ArtifactType artifactType, GitType gitType) {
@ -650,7 +693,9 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
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 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
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 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
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 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
.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 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
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 class CentralGitServiceCEImpl implements CentralGitServiceCE {
GitArtifactHelper<?> gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission();
Mono<? extends Artifact> branchedArtifactMonoCached =
gitArtifactHelper.getArtifactById(branchedArtifactId, artifactEditPermission);
return gitArtifactHelper
.getArtifactById(branchedArtifactId, artifactEditPermission)
.flatMap(branchedArtifact -> discardChanges(branchedArtifact, gitType));
}
Mono<? extends Artifact> recreatedArtifactFromLastCommit;
protected Mono<? extends Artifact> discardChanges(Artifact branchedArtifact, GitType 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));
}
GitArtifactHelper<?> gitArtifactHelper =
gitArtifactHelperResolver.getArtifactHelper(branchedArtifact.getArtifactType());
GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType);
return Mono.just(branchedArtifact)
.doFinally(signalType -> gitRedisUtils.acquireGitLock(
branchedGitData.getDefaultArtifactId(),
GitConstants.GitCommandConstants.DISCARD,
TRUE));
GitArtifactMetadata branchedGitData = branchedArtifact.getGitArtifactMetadata();
if (branchedGitData == null || !hasText(branchedGitData.getDefaultArtifactId())) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR));
}
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<? extends Artifact> 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));
})
.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());
GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType);
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));
.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));

View File

@ -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 @@ public interface GitHandlingServiceCE {
Mono<GitStatusDTO> getStatus(ArtifactJsonTransformationDTO jsonTransformationDTO);
Mono<String> createGitReference(ArtifactJsonTransformationDTO artifactJsonTransformationDTO);
Mono<String> createGitReference(ArtifactJsonTransformationDTO artifactJsonTransformationDTO, GitRefDTO gitRefDTO);
Mono<Boolean> deleteGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO);
Mono<Boolean> checkoutArtifact(ArtifactJsonTransformationDTO jsonTransformationDTO);
}

View File

@ -2,6 +2,7 @@ package com.appsmith.server.git.fs;
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 class GitFSServiceCEImpl implements GitHandlingServiceCE {
}
@Override
public Mono<String> createGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO) {
public Mono<String> createGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO, GitRefDTO gitRefDTO) {
GitArtifactHelper<?> gitArtifactHelper =
gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType());
@ -671,7 +672,7 @@ public class GitFSServiceCEImpl implements GitHandlingServiceCE {
jsonTransformationDTO.getBaseArtifactId(),
jsonTransformationDTO.getRepoName());
return fsGitHandler.createAndCheckoutToBranch(repoSuffix, jsonTransformationDTO.getRefName());
return fsGitHandler.createAndCheckoutReference(repoSuffix, gitRefDTO);
}
@Override
@ -688,14 +689,34 @@ public class GitFSServiceCEImpl implements GitHandlingServiceCE {
.deleteBranch(repoSuffix, jsonTransformationDTO.getRefName())
.onErrorResume(throwable -> {
log.error("Delete branch failed {}", throwable.getMessage());
Mono<Boolean> 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<Boolean> 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());
}
}

View File

@ -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<T extends Artifact> {
Mono<T> publishArtifactPostCommit(Artifact committedArtifact);
Mono<? extends Artifact> validateAndPublishArtifact(Artifact artifact, boolean publish);
Mono<T> publishArtifactPostRefCreation(Artifact artifact, RefType refType, Boolean isPublishedManually);
}