chore: git branches IT (#38805)
## Description > [!TIP] > _Add a TL;DR when the description is longer than 500 words or extremely technical (helps the content, marketing, and DevRel team)._ > > _Please also include relevant motivation and context. List any dependencies that are required for this change. Add links to Notion, Figma or any other documents that might be relevant to the PR._ Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!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, @tag.ImportExport" ### 🔍 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/12983176503> > Commit: 946207867347024d59e989acbc6e019bdb3c263a > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12983176503&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Git, @tag.ImportExport` > Spec: > <hr>Mon, 27 Jan 2025 07:09:02 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 - **New Features** - Added file format versioning for Git-related artifacts - Enhanced Git reference management with new methods and constants - Improved logging for Git operations - **Bug Fixes** - Refined file path matching and handling in Git operations - Improved error handling during artifact and metadata processing - **Refactor** - Centralized file and constant management across Git-related services - Updated method signatures to support more comprehensive Git workflows - Simplified code by removing hardcoded string literals - **Tests** - Added new integration test suite for Git branch and repository operations <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
a243e97510
commit
e89788dd1a
|
|
@ -63,11 +63,13 @@ import java.util.stream.Stream;
|
|||
import static com.appsmith.external.git.constants.GitConstants.ACTION_COLLECTION_LIST;
|
||||
import static com.appsmith.external.git.constants.GitConstants.ACTION_LIST;
|
||||
import static com.appsmith.external.git.constants.GitConstants.CUSTOM_JS_LIB_LIST;
|
||||
import static com.appsmith.external.git.constants.GitConstants.GitMetricConstants.ACTION_COLLECTION_BODY;
|
||||
import static com.appsmith.external.git.constants.GitConstants.GitMetricConstants.NEW_ACTION_BODY;
|
||||
import static com.appsmith.external.git.constants.GitConstants.GitMetricConstants.RESOURCE_TYPE;
|
||||
import static com.appsmith.external.git.constants.GitConstants.NAME_SEPARATOR;
|
||||
import static com.appsmith.external.git.constants.GitConstants.PAGE_LIST;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.ACTION_COLLECTION_BODY;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.NEW_ACTION_BODY;
|
||||
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GitMetricConstantsCE.RESOURCE_TYPE;
|
||||
import static com.appsmith.external.git.constants.GitConstants.README_FILE_NAME;
|
||||
import static com.appsmith.git.constants.CommonConstants.JSON_EXTENSION;
|
||||
import static com.appsmith.git.constants.GitDirectories.ACTION_COLLECTION_DIRECTORY;
|
||||
import static com.appsmith.git.constants.GitDirectories.ACTION_DIRECTORY;
|
||||
import static com.appsmith.git.constants.GitDirectories.DATASOURCE_DIRECTORY;
|
||||
|
|
@ -265,7 +267,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
try (Stream<Path> stream = Files.walk(baseRepo).parallel()) {
|
||||
return stream.filter(path -> {
|
||||
try {
|
||||
return Files.isRegularFile(path) || FileUtils.isEmptyDirectory(path.toFile());
|
||||
return !path.toString().contains(".git" + File.separator)
|
||||
&& (Files.isRegularFile(path) || FileUtils.isEmptyDirectory(path.toFile()));
|
||||
} catch (IOException e) {
|
||||
log.error("Unable to find file details. Please check the file at file path: {}", path);
|
||||
log.error("Assuming that it does not exist for now ...");
|
||||
|
|
@ -291,7 +294,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
// Remove all files that need to be serialized from the existing files list, as well as the README file
|
||||
// What we are left with are all the files to be deleted
|
||||
filesInRepo.removeAll(updatedFilesToBeSerialized);
|
||||
filesInRepo.remove("README.md");
|
||||
filesInRepo.remove(README_FILE_NAME);
|
||||
|
||||
// Delete all the files because they are no longer needed
|
||||
// This covers both older structures of storing files and,
|
||||
|
|
@ -345,15 +348,13 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
// Save application
|
||||
saveResource(
|
||||
applicationGitReference.getApplication(),
|
||||
baseRepo.resolve(CommonConstants.APPLICATION + CommonConstants.JSON_EXTENSION));
|
||||
baseRepo.resolve(CommonConstants.APPLICATION + JSON_EXTENSION));
|
||||
|
||||
// Save application metadata
|
||||
fileOperations.saveMetadataResource(applicationGitReference, baseRepo);
|
||||
|
||||
// Save application theme
|
||||
saveResource(
|
||||
applicationGitReference.getTheme(),
|
||||
baseRepo.resolve(CommonConstants.THEME + CommonConstants.JSON_EXTENSION));
|
||||
saveResource(applicationGitReference.getTheme(), baseRepo.resolve(CommonConstants.THEME + JSON_EXTENSION));
|
||||
|
||||
// Save pages
|
||||
Path pageDirectory = baseRepo.resolve(PAGE_DIRECTORY);
|
||||
|
|
@ -369,9 +370,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
modifiedResources != null && modifiedResources.isResourceUpdated(PAGE_LIST, pageName);
|
||||
if (Boolean.TRUE.equals(isResourceUpdated)) {
|
||||
// Save page metadata
|
||||
saveResource(
|
||||
pageResource.getValue(),
|
||||
pageSpecificDirectory.resolve(pageName + CommonConstants.JSON_EXTENSION));
|
||||
saveResource(pageResource.getValue(), pageSpecificDirectory.resolve(pageName + JSON_EXTENSION));
|
||||
Map<String, JSONObject> result = DSLTransformerHelper.flatten(
|
||||
new JSONObject(applicationGitReference.getPageDsl().get(pageName)));
|
||||
result.keySet().parallelStream().forEach(key -> {
|
||||
|
|
@ -390,8 +389,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
pageSpecificDirectory.resolve(CommonConstants.WIDGETS).toFile(), validWidgetToParentMap);
|
||||
|
||||
// Remove the canvas.json from the file system since the value is stored in the page.json
|
||||
fileOperations.deleteFile(
|
||||
pageSpecificDirectory.resolve(CommonConstants.CANVAS + CommonConstants.JSON_EXTENSION));
|
||||
fileOperations.deleteFile(pageSpecificDirectory.resolve(CommonConstants.CANVAS + JSON_EXTENSION));
|
||||
}
|
||||
validPages.add(pageName);
|
||||
}
|
||||
|
|
@ -416,7 +414,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
String uidString = jsLibEntry.getKey();
|
||||
boolean isResourceUpdated = modifiedResources.isResourceUpdated(CUSTOM_JS_LIB_LIST, uidString);
|
||||
|
||||
String fileNameWithExtension = getJsLibFileName(uidString) + CommonConstants.JSON_EXTENSION;
|
||||
String fileNameWithExtension = getJsLibFileName(uidString) + JSON_EXTENSION;
|
||||
|
||||
Path jsLibSpecificFile = jsLibDirectory.resolve(fileNameWithExtension);
|
||||
if (isResourceUpdated) {
|
||||
|
|
@ -463,9 +461,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
queryName,
|
||||
actionSpecificDirectory.resolve(queryName));
|
||||
// Delete the resource from the old file structure v2
|
||||
fileOperations.deleteFile(pageSpecificDirectory
|
||||
.resolve(ACTION_DIRECTORY)
|
||||
.resolve(queryName + CommonConstants.JSON_EXTENSION));
|
||||
fileOperations.deleteFile(
|
||||
pageSpecificDirectory.resolve(ACTION_DIRECTORY).resolve(queryName + JSON_EXTENSION));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -504,8 +501,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
actionCollectionName,
|
||||
actionCollectionSpecificDirectory.resolve(actionCollectionName));
|
||||
// Delete the resource from the old file structure v2
|
||||
fileOperations.deleteFile(actionCollectionSpecificDirectory.resolve(
|
||||
actionCollectionName + CommonConstants.JSON_EXTENSION));
|
||||
fileOperations.deleteFile(
|
||||
actionCollectionSpecificDirectory.resolve(actionCollectionName + JSON_EXTENSION));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -522,8 +519,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
applicationGitReference.getDatasources().entrySet()) {
|
||||
saveResource(
|
||||
resource.getValue(),
|
||||
baseRepo.resolve(DATASOURCE_DIRECTORY).resolve(resource.getKey() + CommonConstants.JSON_EXTENSION));
|
||||
validDatasourceFileNames.add(resource.getKey() + CommonConstants.JSON_EXTENSION);
|
||||
baseRepo.resolve(DATASOURCE_DIRECTORY).resolve(resource.getKey() + JSON_EXTENSION));
|
||||
validDatasourceFileNames.add(resource.getKey() + JSON_EXTENSION);
|
||||
}
|
||||
// Scan datasource directory and delete any unwanted files if present
|
||||
if (!applicationGitReference.getDatasources().isEmpty()) {
|
||||
|
|
@ -594,7 +591,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
}
|
||||
|
||||
// Write metadata for the jsObject
|
||||
Path metadataPath = path.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION);
|
||||
Path metadataPath = path.resolve(CommonConstants.METADATA + JSON_EXTENSION);
|
||||
return fileOperations.writeToFile(sourceEntity, metadataPath);
|
||||
} catch (IOException e) {
|
||||
log.debug(e.getMessage());
|
||||
|
|
@ -630,7 +627,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
}
|
||||
|
||||
// Write metadata for the actions
|
||||
Path metadataPath = path.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION);
|
||||
Path metadataPath = path.resolve(CommonConstants.METADATA + JSON_EXTENSION);
|
||||
return fileOperations.writeToFile(sourceEntity, metadataPath);
|
||||
} catch (IOException e) {
|
||||
log.error("Error while reading file {} with message {} with cause", path, e.getMessage(), e.getCause());
|
||||
|
|
@ -769,9 +766,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
if (resourcePath.toFile().exists()) {
|
||||
body = fileOperations.readFileAsString(resourcePath);
|
||||
}
|
||||
Object file = fileOperations.readFile(directoryPath
|
||||
.resolve(resourceName)
|
||||
.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
Object file = fileOperations.readFile(
|
||||
directoryPath.resolve(resourceName).resolve(CommonConstants.METADATA + JSON_EXTENSION));
|
||||
actionCollectionBodyMap.put(resourceName + keySuffix, body);
|
||||
resource.put(resourceName + keySuffix, file);
|
||||
}
|
||||
|
|
@ -799,9 +795,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
if (queryPath.toFile().exists()) {
|
||||
body = fileOperations.readFileAsString(queryPath);
|
||||
}
|
||||
Object file = fileOperations.readFile(directoryPath
|
||||
.resolve(resourceName)
|
||||
.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
Object file = fileOperations.readFile(
|
||||
directoryPath.resolve(resourceName).resolve(CommonConstants.METADATA + JSON_EXTENSION));
|
||||
actionCollectionBodyMap.put(resourceName + keySuffix, body);
|
||||
resource.put(resourceName + keySuffix, file);
|
||||
}
|
||||
|
|
@ -811,13 +806,12 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
|
||||
private Object readPageMetadata(Path directoryPath) {
|
||||
return fileOperations.readFile(
|
||||
directoryPath.resolve(directoryPath.toFile().getName() + CommonConstants.JSON_EXTENSION));
|
||||
directoryPath.resolve(directoryPath.toFile().getName() + JSON_EXTENSION));
|
||||
}
|
||||
|
||||
protected GitResourceMap fetchGitResourceMap(Path baseRepoPath) throws IOException {
|
||||
// Extract application metadata from the json
|
||||
Object metadata = fileOperations.readFile(
|
||||
baseRepoPath.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
Object metadata = fileOperations.readFile(baseRepoPath.resolve(CommonConstants.METADATA + JSON_EXTENSION));
|
||||
Integer fileFormatVersion = fileOperations.getFileFormatVersion(metadata);
|
||||
// Check if fileFormat of the saved files in repo is compatible
|
||||
if (!isFileFormatCompatible(fileFormatVersion)) {
|
||||
|
|
@ -828,6 +822,9 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
Map<GitResourceIdentity, Object> resourceMap = gitResourceMap.getGitResourceMap();
|
||||
|
||||
Set<String> filesInRepo = getExistingFilesInRepo(baseRepoPath);
|
||||
// Remove all files that need not be fetched to the git resource map
|
||||
// i.e. -> README.md
|
||||
filesInRepo.remove(README_FILE_NAME);
|
||||
|
||||
filesInRepo.parallelStream()
|
||||
.filter(path -> !Files.isDirectory(baseRepoPath.resolve(path)))
|
||||
|
|
@ -853,7 +850,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
} else if (filePath.matches(JS_LIB_DIRECTORY + "/.*")) {
|
||||
String fileName = FilenameUtils.getBaseName(filePath);
|
||||
identity = new GitResourceIdentity(GitResourceType.JSLIB_CONFIG, fileName, filePath);
|
||||
} else if (filePath.matches(PAGE_DIRECTORY + "/[^/]*/[^/]*.json]")) {
|
||||
} else if (filePath.matches(PAGE_DIRECTORY + "/[^/]*/[^/]*.json")) {
|
||||
String gitSyncId =
|
||||
objectMapper.valueToTree(contents).get("gitSyncId").asText();
|
||||
identity = new GitResourceIdentity(GitResourceType.CONTEXT_CONFIG, gitSyncId, filePath);
|
||||
|
|
@ -866,6 +863,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
String gitSyncId =
|
||||
objectMapper.valueToTree(configContents).get("gitSyncId").asText();
|
||||
identity = new GitResourceIdentity(GitResourceType.QUERY_DATA, gitSyncId, filePath);
|
||||
contents = fileOperations.readFileAsString(path);
|
||||
} else if (filePath.matches(PAGE_DIRECTORY + "/[^/]*/" + ACTION_COLLECTION_DIRECTORY + "/.*/metadata.json")) {
|
||||
String gitSyncId =
|
||||
objectMapper.valueToTree(contents).get("gitSyncId").asText();
|
||||
|
|
@ -875,6 +873,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
String gitSyncId =
|
||||
objectMapper.valueToTree(configContents).get("gitSyncId").asText();
|
||||
identity = new GitResourceIdentity(GitResourceType.JSOBJECT_DATA, gitSyncId, filePath);
|
||||
contents = fileOperations.readFileAsString(path);
|
||||
} else if (filePath.matches(PAGE_DIRECTORY + "/[^/]*/widgets/.*\\.json")) {
|
||||
Pattern pageDirPattern = Pattern.compile("(" + PAGE_DIRECTORY + "/([^/]*))/widgets/.*\\.json");
|
||||
Matcher matcher = pageDirPattern.matcher(filePath);
|
||||
|
|
@ -886,7 +885,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
String gitSyncId =
|
||||
objectMapper.valueToTree(configContents).get("gitSyncId").asText();
|
||||
String widgetId = objectMapper.valueToTree(contents).get("widgetId").asText();
|
||||
identity = new GitResourceIdentity(GitResourceType.JSOBJECT_DATA, gitSyncId + "-" + widgetId, filePath);
|
||||
identity = new GitResourceIdentity(GitResourceType.WIDGET_CONFIG, gitSyncId + "-" + widgetId, filePath);
|
||||
} else return null;
|
||||
|
||||
return Tuples.of(identity, contents);
|
||||
|
|
@ -895,18 +894,17 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
private ApplicationGitReference fetchApplicationReference(Path baseRepoPath) {
|
||||
ApplicationGitReference applicationGitReference = new ApplicationGitReference();
|
||||
// Extract application metadata from the json
|
||||
Object metadata = fileOperations.readFile(
|
||||
baseRepoPath.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
Object metadata = fileOperations.readFile(baseRepoPath.resolve(CommonConstants.METADATA + JSON_EXTENSION));
|
||||
Integer fileFormatVersion = fileOperations.getFileFormatVersion(metadata);
|
||||
// Check if fileFormat of the saved files in repo is compatible
|
||||
if (!isFileFormatCompatible(fileFormatVersion)) {
|
||||
throw new AppsmithPluginException(AppsmithPluginError.INCOMPATIBLE_FILE_FORMAT);
|
||||
}
|
||||
// Extract application data from the json
|
||||
applicationGitReference.setApplication(fileOperations.readFile(
|
||||
baseRepoPath.resolve(CommonConstants.APPLICATION + CommonConstants.JSON_EXTENSION)));
|
||||
applicationGitReference.setApplication(
|
||||
fileOperations.readFile(baseRepoPath.resolve(CommonConstants.APPLICATION + JSON_EXTENSION)));
|
||||
applicationGitReference.setTheme(
|
||||
fileOperations.readFile(baseRepoPath.resolve(CommonConstants.THEME + CommonConstants.JSON_EXTENSION)));
|
||||
fileOperations.readFile(baseRepoPath.resolve(CommonConstants.THEME + JSON_EXTENSION)));
|
||||
Path pageDirectory = baseRepoPath.resolve(PAGE_DIRECTORY);
|
||||
// Reconstruct application from given file format
|
||||
switch (fileFormatVersion) {
|
||||
|
|
@ -965,8 +963,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
for (File page : Objects.requireNonNull(directory.listFiles())) {
|
||||
pageMap.put(
|
||||
page.getName(),
|
||||
fileOperations.readFile(
|
||||
page.toPath().resolve(CommonConstants.CANVAS + CommonConstants.JSON_EXTENSION)));
|
||||
fileOperations.readFile(page.toPath().resolve(CommonConstants.CANVAS + JSON_EXTENSION)));
|
||||
|
||||
if (fileFormatVersion >= 4) {
|
||||
actionMap.putAll(
|
||||
|
|
@ -1107,7 +1104,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
deleteWidgets(file, validWidgetToParentMap);
|
||||
}
|
||||
|
||||
String name = file.getName().replace(CommonConstants.JSON_EXTENSION, CommonConstants.EMPTY_STRING);
|
||||
String name = file.getName().replace(JSON_EXTENSION, CommonConstants.EMPTY_STRING);
|
||||
// If input widget was inside a container before, but the user moved it out of the container
|
||||
// then we need to delete the widget from the container directory
|
||||
// The check here is to validate if the parent is correct or not
|
||||
|
|
@ -1192,8 +1189,8 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
|
||||
metadataMono = gitResetMono.map(isSwitched -> {
|
||||
Path baseRepoPath = Paths.get(gitServiceConfig.getGitRootPath()).resolve(baseRepoSuffix);
|
||||
Object metadata = fileOperations.readFile(
|
||||
baseRepoPath.resolve(CommonConstants.METADATA + CommonConstants.JSON_EXTENSION));
|
||||
Object metadata =
|
||||
fileOperations.readFile(baseRepoPath.resolve(CommonConstants.METADATA + JSON_EXTENSION));
|
||||
return metadata;
|
||||
});
|
||||
} catch (GitAPIException | IOException exception) {
|
||||
|
|
@ -1222,8 +1219,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
.resolve(baseRepoSuffixPath)
|
||||
.resolve(pageSuffix);
|
||||
|
||||
Object pageObject =
|
||||
fileOperations.readFile(repoPath.resolve(pageName + CommonConstants.JSON_EXTENSION));
|
||||
Object pageObject = fileOperations.readFile(repoPath.resolve(pageName + JSON_EXTENSION));
|
||||
|
||||
return pageObject;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -167,9 +167,9 @@ public class FileOperationsCEv2Impl implements FileOperationsCE {
|
|||
if (metadata == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
JsonNode json = objectMapper.valueToTree(metadata);
|
||||
int fileFormatVersion = json.get(CommonConstants.FILE_FORMAT_VERSION).asInt();
|
||||
return fileFormatVersion;
|
||||
return json.get(CommonConstants.FILE_FORMAT_VERSION).asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -436,12 +436,13 @@ public class FSGitHandlerCEImpl implements FSGitHandler {
|
|||
return Mono.using(
|
||||
() -> Git.open(createRepoPath(repoSuffix).toFile()),
|
||||
git -> Mono.fromCallable(() -> {
|
||||
log.debug(Thread.currentThread().getName() + ": Switching to the branch "
|
||||
+ branchName);
|
||||
log.info(
|
||||
"{}: Switching to the branch {}",
|
||||
Thread.currentThread().getName(),
|
||||
branchName);
|
||||
|
||||
// We can safely assume that repo has been already initialised either in commit or
|
||||
// clone flow and
|
||||
// can directly
|
||||
// open the repo
|
||||
// clone flow and can directly open the repo
|
||||
if (StringUtils.equalsIgnoreCase(
|
||||
branchName, git.getRepository().getBranch())) {
|
||||
return TRUE;
|
||||
|
|
@ -611,8 +612,12 @@ public class FSGitHandlerCEImpl implements FSGitHandler {
|
|||
return Mono.using(
|
||||
() -> Git.open(repoPath.toFile()),
|
||||
git -> Mono.fromCallable(() -> {
|
||||
log.debug(Thread.currentThread().getName() + ": Get status for repo " + repoPath
|
||||
+ ", branch " + branchName);
|
||||
log.info(
|
||||
"{}: Get status for repo {}, {}",
|
||||
Thread.currentThread().getName(),
|
||||
repoPath,
|
||||
branchName);
|
||||
|
||||
Status status = git.status().call();
|
||||
GitStatusDTO response = new GitStatusDTO();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ public class GitConstantsCE {
|
|||
public static final String ACTION_LIST = "actionList";
|
||||
public static final String ACTION_COLLECTION_LIST = "actionCollectionList";
|
||||
|
||||
public static final String README_FILE_NAME = "README.md";
|
||||
|
||||
public static final String DEFAULT_COMMIT_MESSAGE = "System generated commit, ";
|
||||
public static final String EMPTY_COMMIT_ERROR_MESSAGE = "On current branch nothing to commit, working tree clean";
|
||||
public static final String MERGE_CONFLICT_BRANCH_NAME = "_mergeConflict";
|
||||
|
|
|
|||
|
|
@ -67,10 +67,12 @@ import static com.appsmith.external.git.constants.GitConstants.NAME_SEPARATOR;
|
|||
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties;
|
||||
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyProperties;
|
||||
import static com.appsmith.git.constants.CommonConstants.DELIMITER_PATH;
|
||||
import static com.appsmith.git.constants.CommonConstants.FILE_FORMAT_VERSION;
|
||||
import static com.appsmith.git.constants.CommonConstants.JSON_EXTENSION;
|
||||
import static com.appsmith.git.constants.CommonConstants.MAIN_CONTAINER;
|
||||
import static com.appsmith.git.constants.CommonConstants.WIDGETS;
|
||||
import static com.appsmith.git.constants.ce.GitDirectoriesCE.PAGE_DIRECTORY;
|
||||
import static com.appsmith.git.constants.CommonConstants.fileFormatVersion;
|
||||
import static com.appsmith.git.constants.GitDirectories.PAGE_DIRECTORY;
|
||||
import static com.appsmith.server.constants.FieldName.ACTION_COLLECTION_LIST;
|
||||
import static com.appsmith.server.constants.FieldName.ACTION_LIST;
|
||||
import static com.appsmith.server.constants.FieldName.CHILDREN;
|
||||
|
|
@ -189,6 +191,10 @@ public class ApplicationGitFileUtilsCEImpl implements ArtifactGitFileUtilsCE<App
|
|||
copyProperties(applicationJson, applicationMetadata, keys);
|
||||
final String metadataFilePath = CommonConstants.METADATA + JSON_EXTENSION;
|
||||
ObjectNode metadata = objectMapper.valueToTree(applicationMetadata);
|
||||
|
||||
// put file format version;
|
||||
metadata.put(FILE_FORMAT_VERSION, fileFormatVersion);
|
||||
|
||||
GitResourceIdentity metadataIdentity =
|
||||
new GitResourceIdentity(GitResourceType.ROOT_CONFIG, metadataFilePath, metadataFilePath);
|
||||
resourceMap.put(metadataIdentity, metadata);
|
||||
|
|
@ -717,7 +723,6 @@ public class ApplicationGitFileUtilsCEImpl implements ArtifactGitFileUtilsCE<App
|
|||
artifactExchangeJson.setContextList(pageList);
|
||||
|
||||
// widgets
|
||||
|
||||
pageList.parallelStream().forEach(newPage -> {
|
||||
Map<String, org.json.JSONObject> widgetsData = resourceMap.entrySet().stream()
|
||||
.filter(entry -> {
|
||||
|
|
@ -730,13 +735,23 @@ public class ApplicationGitFileUtilsCEImpl implements ArtifactGitFileUtilsCE<App
|
|||
.getFilePath()
|
||||
.replaceFirst(
|
||||
PAGE_DIRECTORY
|
||||
+ DELIMITER_PATH
|
||||
+ newPage.getUnpublishedPage()
|
||||
.getName()
|
||||
+ DELIMITER_PATH
|
||||
+ WIDGETS
|
||||
+ DELIMITER_PATH,
|
||||
MAIN_CONTAINER + DELIMITER_PATH),
|
||||
entry -> (org.json.JSONObject) entry.getValue()));
|
||||
entry -> {
|
||||
try {
|
||||
return new org.json.JSONObject(objectMapper.writeValueAsString(entry.getValue()));
|
||||
} catch (JsonProcessingException jsonProcessingException) {
|
||||
log.error(
|
||||
"Error while deserializing widget with file path {}",
|
||||
entry.getKey().getFilePath());
|
||||
throw new RuntimeException(jsonProcessingException);
|
||||
}
|
||||
}));
|
||||
|
||||
Layout layout = newPage.getUnpublishedPage().getLayouts().get(0);
|
||||
org.json.JSONObject mainContainer;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.appsmith.external.dtos.GitStatusDTO;
|
|||
import com.appsmith.external.dtos.MergeStatusDTO;
|
||||
import com.appsmith.external.git.constants.GitConstants;
|
||||
import com.appsmith.external.git.constants.GitSpan;
|
||||
import com.appsmith.external.git.constants.ce.GitConstantsCE;
|
||||
import com.appsmith.external.git.constants.ce.RefType;
|
||||
import com.appsmith.external.git.dtos.FetchRemoteDTO;
|
||||
import com.appsmith.external.models.Datasource;
|
||||
|
|
@ -78,7 +79,6 @@ import reactor.util.function.Tuple2;
|
|||
import reactor.util.function.Tuple3;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -95,9 +95,9 @@ import static com.appsmith.external.git.constants.ce.GitSpanCE.OPS_STATUS;
|
|||
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties;
|
||||
import static com.appsmith.server.constants.FieldName.BRANCH_NAME;
|
||||
import static com.appsmith.server.constants.FieldName.DEFAULT;
|
||||
import static com.appsmith.server.constants.FieldName.REF_NAME;
|
||||
import static com.appsmith.server.constants.FieldName.REF_TYPE;
|
||||
import static com.appsmith.server.constants.SerialiseArtifactObjective.VERSION_CONTROL;
|
||||
import static com.appsmith.server.constants.ce.FieldNameCE.REF_NAME;
|
||||
import static com.appsmith.server.constants.ce.FieldNameCE.REF_TYPE;
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static org.springframework.util.StringUtils.hasText;
|
||||
|
|
@ -510,13 +510,13 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
final String workspaceId = baseArtifact.getWorkspaceId();
|
||||
final String finalRemoteRefName = gitRefDTO.getRefName().replaceFirst(ORIGIN, REMOTE_NAME_REPLACEMENT);
|
||||
|
||||
ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO();
|
||||
jsonTransformationDTO.setRepoName(repoName);
|
||||
ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO(
|
||||
workspaceId, baseArtifactId, repoName, baseArtifact.getArtifactType());
|
||||
|
||||
jsonTransformationDTO.setRefType(gitRefDTO.getRefType());
|
||||
jsonTransformationDTO.setRefName(finalRemoteRefName);
|
||||
jsonTransformationDTO.setWorkspaceId(workspaceId);
|
||||
jsonTransformationDTO.setBaseArtifactId(baseArtifactId);
|
||||
jsonTransformationDTO.setArtifactType(baseArtifact.getArtifactType());
|
||||
|
||||
FetchRemoteDTO fetchRemoteDTO = new FetchRemoteDTO(List.of(finalRemoteRefName), gitRefDTO.getRefType(), false);
|
||||
|
||||
Mono<? extends Artifact> artifactMono;
|
||||
if (baseRefName.equals(finalRemoteRefName)) {
|
||||
|
|
@ -528,11 +528,12 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
artifactMono = Mono.just(baseArtifact);
|
||||
} else {
|
||||
// create new Artifact
|
||||
artifactMono = gitArtifactHelper.createNewArtifactForCheckout(baseArtifact, finalRemoteRefName);
|
||||
artifactMono = generateArtifactForRefCreation(baseArtifact, finalRemoteRefName, gitRefDTO.getRefType());
|
||||
}
|
||||
|
||||
Mono<? extends Artifact> checkedOutRemoteArtifactMono = gitHandlingService
|
||||
.fetchRemoteReferences(jsonTransformationDTO, baseGitMetadata.getGitAuth(), false)
|
||||
.fetchRemoteReferences(jsonTransformationDTO, fetchRemoteDTO, baseGitMetadata.getGitAuth())
|
||||
.flatMap(ignoredFetchString -> gitHandlingService.checkoutRemoteReference(jsonTransformationDTO))
|
||||
.onErrorResume(error -> Mono.error(
|
||||
new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "checkout branch", error.getMessage())))
|
||||
.flatMap(ignoreRemoteChanges -> {
|
||||
|
|
@ -600,18 +601,6 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
GitArtifactMetadata baseGitMetadata = baseArtifact.getGitArtifactMetadata();
|
||||
GitAuth baseGitAuth = baseGitMetadata.getGitAuth();
|
||||
GitArtifactMetadata sourceGitMetadata = sourceArtifact.getGitArtifactMetadata();
|
||||
ArtifactType artifactType = baseArtifact.getArtifactType();
|
||||
|
||||
GitArtifactHelper<?> gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
|
||||
GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(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 (sourceGitMetadata == null
|
||||
|| !hasText(sourceGitMetadata.getDefaultArtifactId())
|
||||
|
|
@ -622,19 +611,40 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
"Unable to find the parent reference. Please create a reference from other available references"));
|
||||
}
|
||||
|
||||
ArtifactType artifactType = baseArtifact.getArtifactType();
|
||||
String workspaceId = baseArtifact.getWorkspaceId();
|
||||
String baseArtifactId = baseGitMetadata.getDefaultArtifactId();
|
||||
String repoName = baseGitMetadata.getRepoName();
|
||||
|
||||
String sourceArtifactId = sourceArtifact.getId();
|
||||
|
||||
GitArtifactHelper<?> gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
|
||||
GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType);
|
||||
|
||||
ArtifactJsonTransformationDTO baseRefTransformationDTO =
|
||||
new ArtifactJsonTransformationDTO(workspaceId, baseArtifactId, repoName, artifactType);
|
||||
baseRefTransformationDTO.setRefName(sourceGitMetadata.getRefName());
|
||||
baseRefTransformationDTO.setRefType(refType);
|
||||
|
||||
ArtifactJsonTransformationDTO createRefTransformationDTO =
|
||||
new ArtifactJsonTransformationDTO(workspaceId, baseArtifactId, repoName, artifactType);
|
||||
createRefTransformationDTO.setRefType(refType);
|
||||
createRefTransformationDTO.setRefName(refDTO.getRefName());
|
||||
|
||||
Mono<Boolean> acquireGitLockMono = gitRedisUtils.acquireGitLock(
|
||||
artifactType,
|
||||
baseGitMetadata.getDefaultArtifactId(),
|
||||
GitConstants.GitCommandConstants.CREATE_BRANCH,
|
||||
FALSE);
|
||||
|
||||
Mono<String> fetchRemoteMono =
|
||||
gitHandlingService.fetchRemoteReferences(jsonTransformationDTO, baseGitAuth, TRUE);
|
||||
gitHandlingService.fetchRemoteReferences(baseRefTransformationDTO, baseGitAuth, TRUE);
|
||||
|
||||
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)
|
||||
.listReferences(createRefTransformationDTO, TRUE)
|
||||
.flatMap(refList -> {
|
||||
boolean isDuplicateName = refList.stream()
|
||||
// We are only supporting origin as the remote name so this is safe
|
||||
|
|
@ -655,11 +665,10 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
|
||||
Mono<? extends ArtifactExchangeJson> artifactExchangeJsonMono =
|
||||
exportService.exportByArtifactId(
|
||||
sourceArtifact.getId(), VERSION_CONTROL, baseArtifact.getArtifactType());
|
||||
sourceArtifactId, VERSION_CONTROL, baseArtifact.getArtifactType());
|
||||
|
||||
Mono<? extends Artifact> newArtifactFromSourceMono =
|
||||
// TODO: add refType support over here
|
||||
gitArtifactHelper.createNewArtifactForCheckout(sourceArtifact, refDTO.getRefName());
|
||||
Mono<? extends Artifact> newArtifactFromSourceMono = generateArtifactForRefCreation(
|
||||
sourceArtifact, refDTO.getRefName(), refDTO.getRefType());
|
||||
|
||||
return refCreationValidationMono.flatMap(isOkayToProceed -> {
|
||||
if (!TRUE.equals(isOkayToProceed)) {
|
||||
|
|
@ -675,7 +684,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
Artifact newRefArtifact = tuple.getT1();
|
||||
|
||||
Mono<String> refCreationMono = gitHandlingService
|
||||
.createGitReference(jsonTransformationDTO, refDTO)
|
||||
.createGitReference(createRefTransformationDTO, baseGitMetadata, 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())));
|
||||
|
|
@ -694,13 +703,11 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
})
|
||||
// after the branch is created, we need to reset the older branch to the
|
||||
// clean status, i.e. last commit
|
||||
.flatMap(newImportedArtifact -> discardChanges(sourceArtifact, gitType));
|
||||
.flatMap(newImportedArtifact ->
|
||||
discardChanges(sourceArtifact, gitType).thenReturn(newImportedArtifact));
|
||||
})
|
||||
.flatMap(newImportedArtifact -> gitRedisUtils
|
||||
.releaseFileLock(
|
||||
artifactType,
|
||||
newImportedArtifact.getGitArtifactMetadata().getDefaultArtifactId(),
|
||||
TRUE)
|
||||
.releaseFileLock(artifactType, baseArtifactId, TRUE)
|
||||
.then(gitAnalyticsUtils.addAnalyticsForGitOperation(
|
||||
AnalyticsEvents.GIT_CREATE_BRANCH,
|
||||
newImportedArtifact,
|
||||
|
|
@ -708,7 +715,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
.onErrorResume(error -> {
|
||||
log.error("An error occurred while creating reference. error {}", error.getMessage());
|
||||
return gitRedisUtils
|
||||
.releaseFileLock(artifactType, baseGitMetadata.getDefaultArtifactId(), TRUE)
|
||||
.releaseFileLock(artifactType, baseArtifactId, TRUE)
|
||||
.then(Mono.error(new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "checkout")));
|
||||
})
|
||||
.name(GitSpan.OPS_CREATE_BRANCH)
|
||||
|
|
@ -1066,7 +1073,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
jsonTransformationDTO.setArtifactType(artifactType);
|
||||
jsonTransformationDTO.setRepoName(repoName);
|
||||
|
||||
final String README_FILE_NAME = "README.md";
|
||||
final String README_FILE_NAME = GitConstantsCE.README_FILE_NAME;
|
||||
Mono<Boolean> readMeIntialisationMono = gitHandlingService.initialiseReadMe(
|
||||
jsonTransformationDTO, artifact, README_FILE_NAME, originHeader);
|
||||
|
||||
|
|
@ -1095,30 +1102,31 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
commitDTO.setMessage(commitMessage);
|
||||
|
||||
return this.commitArtifact(commitDTO, artifact.getId(), artifactType, gitType)
|
||||
.onErrorResume(error ->
|
||||
// If the push fails remove all the cloned files from local repo
|
||||
this.detachRemote(baseArtifactId, artifactType, gitType)
|
||||
.flatMap(isDeleted -> {
|
||||
if (error instanceof TransportException) {
|
||||
return gitAnalyticsUtils
|
||||
.addAnalyticsForGitOperation(
|
||||
AnalyticsEvents.GIT_CONNECT,
|
||||
artifact,
|
||||
error.getClass()
|
||||
.getName(),
|
||||
error.getMessage(),
|
||||
artifact.getGitArtifactMetadata()
|
||||
.getIsRepoPrivate())
|
||||
.then(Mono.error(new AppsmithException(
|
||||
AppsmithError
|
||||
.INVALID_GIT_SSH_CONFIGURATION,
|
||||
error.getMessage())));
|
||||
}
|
||||
return Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_ACTION_FAILED,
|
||||
"push",
|
||||
error.getMessage()));
|
||||
}));
|
||||
.onErrorResume(error -> {
|
||||
log.error("Error while committing", error);
|
||||
// If the push fails remove all the cloned files from local repo
|
||||
return this.detachRemote(baseArtifactId, artifactType, gitType)
|
||||
.flatMap(isDeleted -> {
|
||||
if (error instanceof TransportException) {
|
||||
return gitAnalyticsUtils
|
||||
.addAnalyticsForGitOperation(
|
||||
AnalyticsEvents.GIT_CONNECT,
|
||||
artifact,
|
||||
error.getClass()
|
||||
.getName(),
|
||||
error.getMessage(),
|
||||
artifact.getGitArtifactMetadata()
|
||||
.getIsRepoPrivate())
|
||||
.then(Mono.error(new AppsmithException(
|
||||
AppsmithError.INVALID_GIT_SSH_CONFIGURATION,
|
||||
error.getMessage())));
|
||||
}
|
||||
return Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_ACTION_FAILED,
|
||||
"push",
|
||||
error.getMessage()));
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(gitAnalyticsUtils.addAnalyticsForGitOperation(
|
||||
AnalyticsEvents.GIT_CONNECT,
|
||||
|
|
@ -1429,23 +1437,26 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
jsonTransformationDTO.setRefName(gitArtifactMetadata.getRefName());
|
||||
|
||||
// Remove the git contents from file system
|
||||
return Mono.zip(gitHandlingService.listBranches(jsonTransformationDTO), Mono.just(baseArtifact));
|
||||
return Mono.zip(
|
||||
gitHandlingService.listReferences(jsonTransformationDTO, false), Mono.just(baseArtifact));
|
||||
})
|
||||
.flatMap(tuple -> {
|
||||
List<String> localBranches = tuple.getT1();
|
||||
Artifact baseArtifact = tuple.getT2();
|
||||
|
||||
GitArtifactMetadata baseGitMetadata = baseArtifact.getGitArtifactMetadata();
|
||||
localBranches.remove(baseGitMetadata.getRefName());
|
||||
|
||||
baseArtifact.setGitArtifactMetadata(null);
|
||||
gitArtifactHelper.resetAttributeInBaseArtifact(baseArtifact);
|
||||
|
||||
GitArtifactMetadata gitArtifactMetadata = baseArtifact.getGitArtifactMetadata();
|
||||
ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO();
|
||||
jsonTransformationDTO.setRefType(RefType.branch);
|
||||
jsonTransformationDTO.setWorkspaceId(baseArtifact.getWorkspaceId());
|
||||
jsonTransformationDTO.setBaseArtifactId(gitArtifactMetadata.getDefaultArtifactId());
|
||||
jsonTransformationDTO.setRepoName(gitArtifactMetadata.getRepoName());
|
||||
jsonTransformationDTO.setBaseArtifactId(baseGitMetadata.getDefaultArtifactId());
|
||||
jsonTransformationDTO.setRepoName(baseGitMetadata.getRepoName());
|
||||
jsonTransformationDTO.setArtifactType(baseArtifact.getArtifactType());
|
||||
jsonTransformationDTO.setRefName(gitArtifactMetadata.getRefName());
|
||||
jsonTransformationDTO.setRefName(baseGitMetadata.getRefName());
|
||||
|
||||
// Remove the parent artifact branch name from the list
|
||||
Mono<Boolean> removeRepoMono = gitHandlingService.removeRepository(jsonTransformationDTO);
|
||||
|
|
@ -1523,7 +1534,6 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
GitType gitType) {
|
||||
|
||||
ArtifactType artifactType = baseArtifact.getArtifactType();
|
||||
GitArtifactHelper<?> gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
|
||||
GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType);
|
||||
|
||||
GitArtifactMetadata baseGitMetadata = baseArtifact.getGitArtifactMetadata();
|
||||
|
|
@ -1738,14 +1748,12 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
jsonTransformationDTO.setBaseArtifactId(baseArtifactId);
|
||||
jsonTransformationDTO.setArtifactType(baseArtifact.getArtifactType());
|
||||
|
||||
Path repoSuffix = gitArtifactHelper.getRepoSuffixPath(workspaceId, baseArtifactId, repoName);
|
||||
|
||||
return Mono.defer(() -> {
|
||||
// Rehydrate the artifact from git system
|
||||
|
||||
Mono<MergeStatusDTO> mergeStatusDTOMono = gitHandlingService
|
||||
.pullArtifact(jsonTransformationDTO, baseGitMetadata)
|
||||
.cache();
|
||||
|
||||
Mono<ArtifactExchangeJson> artifactExchangeJsonMono = mergeStatusDTOMono.flatMap(status ->
|
||||
gitHandlingService.reconstructArtifactJsonFromGitRepository(jsonTransformationDTO));
|
||||
|
||||
|
|
@ -1766,25 +1774,27 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
.getGitArtifactMetadata()
|
||||
.getIsRepoPrivate()))
|
||||
.flatMap(importedBranchedArtifact -> {
|
||||
CommitDTO commitDTO = new CommitDTO();
|
||||
commitDTO.setMessage(DEFAULT_COMMIT_MESSAGE
|
||||
+ GitDefaultCommitMessage.SYNC_WITH_REMOTE_AFTER_PULL.getReason());
|
||||
|
||||
GitPullDTO gitPullDTO = new GitPullDTO();
|
||||
gitPullDTO.setMergeStatus(status);
|
||||
gitPullDTO.setArtifact(importedBranchedArtifact);
|
||||
|
||||
return gitArtifactHelper
|
||||
.publishArtifact(importedBranchedArtifact, false)
|
||||
// TODO: Verify if we need to commit after pulling? (Gonna be a product
|
||||
// decision, hence got
|
||||
.then(Mono.defer(() -> commitArtifact(
|
||||
commitDTO,
|
||||
baseArtifact,
|
||||
importedBranchedArtifact,
|
||||
gitType,
|
||||
false))
|
||||
.thenReturn(gitPullDTO));
|
||||
.then(getGitUserForArtifactId(baseArtifactId))
|
||||
.flatMap(gitAuthor -> {
|
||||
CommitDTO commitDTO = new CommitDTO();
|
||||
commitDTO.setMessage(DEFAULT_COMMIT_MESSAGE
|
||||
+ GitDefaultCommitMessage.SYNC_WITH_REMOTE_AFTER_PULL.getReason());
|
||||
commitDTO.setAuthor(gitAuthor);
|
||||
|
||||
GitPullDTO gitPullDTO = new GitPullDTO();
|
||||
gitPullDTO.setMergeStatus(status);
|
||||
gitPullDTO.setArtifact(importedBranchedArtifact);
|
||||
|
||||
return Mono.defer(() -> commitArtifact(
|
||||
commitDTO,
|
||||
baseArtifact,
|
||||
importedBranchedArtifact,
|
||||
gitType,
|
||||
false))
|
||||
.thenReturn(gitPullDTO);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -1960,20 +1970,21 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
}
|
||||
|
||||
private Mono<? extends Artifact> updateArtifactWithGitMetadataGivenPermission(
|
||||
Artifact artifact, GitArtifactMetadata gitMetadata) {
|
||||
Artifact branchedArtifact, GitArtifactMetadata branchedGitMetadata) {
|
||||
|
||||
if (gitMetadata == null) {
|
||||
if (branchedGitMetadata == null) {
|
||||
return Mono.error(
|
||||
new AppsmithException(AppsmithError.INVALID_PARAMETER, "Git metadata values cannot be null"));
|
||||
}
|
||||
|
||||
artifact.setGitArtifactMetadata(gitMetadata);
|
||||
// For base artifact we expect a GitAuth to be a part of gitMetadata. We are using save method to leverage
|
||||
// @Encrypted annotation used for private SSH keys
|
||||
branchedGitMetadata.setLastCommittedAt(Instant.now());
|
||||
branchedArtifact.setGitArtifactMetadata(branchedGitMetadata);
|
||||
// For base branchedArtifact we expect a GitAuth to be a part of branchedGitMetadata.
|
||||
// We are using save method to leverage @Encrypted annotation used for private SSH keys
|
||||
// saveArtifact method sets the transient fields so no need to set it again from this method
|
||||
return gitArtifactHelperResolver
|
||||
.getArtifactHelper(artifact.getArtifactType())
|
||||
.saveArtifact(artifact);
|
||||
.getArtifactHelper(branchedArtifact.getArtifactType())
|
||||
.saveArtifact(branchedArtifact);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2356,7 +2367,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
TRUE)
|
||||
.flatMap(ignoredLock -> {
|
||||
Mono<List<String>> listBranchesMono =
|
||||
Mono.defer(() -> gitHandlingService.listReferences(jsonTransformationDTO, false));
|
||||
Mono.defer(() -> gitHandlingService.listReferences(jsonTransformationDTO, true));
|
||||
|
||||
if (TRUE.equals(pruneBranches)) {
|
||||
return gitHandlingService
|
||||
|
|
@ -2578,6 +2589,17 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
});
|
||||
}
|
||||
|
||||
protected Mono<? extends Artifact> generateArtifactForRefCreation(
|
||||
Artifact branchedArtifact, String refName, RefType refType) {
|
||||
ArtifactType artifactType = branchedArtifact.getArtifactType();
|
||||
GitArtifactHelper<?> gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
|
||||
AclPermission editPermission = gitArtifactHelper.getArtifactEditPermission();
|
||||
|
||||
return gitArtifactHelper
|
||||
.getArtifactById(branchedArtifact.getId(), editPermission)
|
||||
.flatMap(sourceArtifact -> gitArtifactHelper.createNewArtifactForCheckout(sourceArtifact, refName));
|
||||
}
|
||||
|
||||
/**
|
||||
* In some scenarios:
|
||||
* connect: after loading the modal, keyTypes is not available, so a network call has to be made to ssh-keypair.
|
||||
|
|
@ -2703,7 +2725,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
baseArtifact, destinationArtifact, false, false, gitType)
|
||||
.flatMap(destinationBranchStatus -> {
|
||||
if (destinationBranchStatus.getIsClean()) {
|
||||
Mono.just(destinationBranchStatus);
|
||||
return Mono.just(destinationBranchStatus);
|
||||
}
|
||||
|
||||
AppsmithException statusFailureException;
|
||||
|
|
@ -2723,7 +2745,8 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
return Mono.error(statusFailureException);
|
||||
}));
|
||||
|
||||
return sourceBranchStatusMono.zipWith(destinationBranchStatusMono);
|
||||
return sourceBranchStatusMono.zipWhen(
|
||||
sourceStatusDTO -> destinationBranchStatusMono);
|
||||
})
|
||||
.onErrorResume(error -> {
|
||||
log.error(
|
||||
|
|
@ -2802,7 +2825,8 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
commitDTO,
|
||||
importedDestinationArtifact.getId(),
|
||||
artifactType,
|
||||
gitType)
|
||||
gitType,
|
||||
false)
|
||||
.then(gitAnalyticsUtils.addAnalyticsForGitOperation(
|
||||
AnalyticsEvents.GIT_MERGE,
|
||||
importedDestinationArtifact,
|
||||
|
|
@ -2965,7 +2989,8 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
.then(Mono.error(uncleanStatusException));
|
||||
}));
|
||||
|
||||
return sourceBranchStatusMono.zipWith(destinationBranchStatusMono);
|
||||
return sourceBranchStatusMono.zipWhen(
|
||||
sourceStatusDTO -> destinationBranchStatusMono);
|
||||
})
|
||||
.onErrorResume(error -> {
|
||||
log.error(
|
||||
|
|
@ -2980,32 +3005,30 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
AppsmithError.GIT_ACTION_FAILED, "status", error));
|
||||
});
|
||||
|
||||
return statusTupleMono.flatMap(statusTuple -> {
|
||||
GitMergeDTO mergeDTO = new GitMergeDTO();
|
||||
mergeDTO.setSourceBranch(sourceBranch);
|
||||
mergeDTO.setDestinationBranch(destinationBranch);
|
||||
return statusTupleMono
|
||||
.flatMap(statusTuple -> {
|
||||
GitMergeDTO mergeDTO = new GitMergeDTO();
|
||||
mergeDTO.setSourceBranch(sourceBranch);
|
||||
mergeDTO.setDestinationBranch(destinationBranch);
|
||||
return gitHandlingService.isBranchMergable(jsonTransformationDTO, mergeDTO);
|
||||
})
|
||||
.onErrorResume(error -> {
|
||||
MergeStatusDTO mergeStatus = new MergeStatusDTO();
|
||||
mergeStatus.setMergeAble(false);
|
||||
mergeStatus.setStatus("Merge check failed!");
|
||||
mergeStatus.setMessage(error.getMessage());
|
||||
|
||||
Mono<MergeStatusDTO> isBranchMergable =
|
||||
gitHandlingService.isBranchMergable(jsonTransformationDTO, mergeDTO);
|
||||
|
||||
return isBranchMergable.onErrorResume(error -> {
|
||||
MergeStatusDTO mergeStatus = new MergeStatusDTO();
|
||||
mergeStatus.setMergeAble(false);
|
||||
mergeStatus.setStatus("Merge check failed!");
|
||||
mergeStatus.setMessage(error.getMessage());
|
||||
|
||||
return gitAnalyticsUtils
|
||||
.addAnalyticsForGitOperation(
|
||||
AnalyticsEvents.GIT_MERGE_CHECK,
|
||||
baseArtifact,
|
||||
error.getClass().getName(),
|
||||
error.getMessage(),
|
||||
baseGitMetadata.getIsRepoPrivate(),
|
||||
false,
|
||||
false)
|
||||
.thenReturn(mergeStatus);
|
||||
});
|
||||
});
|
||||
return gitAnalyticsUtils
|
||||
.addAnalyticsForGitOperation(
|
||||
AnalyticsEvents.GIT_MERGE_CHECK,
|
||||
baseArtifact,
|
||||
error.getClass().getName(),
|
||||
error.getMessage(),
|
||||
baseGitMetadata.getIsRepoPrivate(),
|
||||
false,
|
||||
false)
|
||||
.thenReturn(mergeStatus);
|
||||
});
|
||||
},
|
||||
ignoreLock -> gitRedisUtils.releaseFileLock(artifactType, baseArtifactId, TRUE));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -87,7 +87,10 @@ public interface GitHandlingServiceCE {
|
|||
|
||||
Mono<GitStatusDTO> getStatus(ArtifactJsonTransformationDTO jsonTransformationDTO);
|
||||
|
||||
Mono<String> createGitReference(ArtifactJsonTransformationDTO artifactJsonTransformationDTO, GitRefDTO gitRefDTO);
|
||||
Mono<String> createGitReference(
|
||||
ArtifactJsonTransformationDTO artifactJsonTransformationDTO,
|
||||
GitArtifactMetadata baseGitData,
|
||||
GitRefDTO gitRefDTO);
|
||||
|
||||
Mono<String> checkoutRemoteReference(ArtifactJsonTransformationDTO jsonTransformationDTO);
|
||||
|
||||
|
|
|
|||
|
|
@ -101,8 +101,9 @@ import static com.appsmith.external.git.constants.GitConstants.DEFAULT_COMMIT_ME
|
|||
import static com.appsmith.external.git.constants.GitConstants.EMPTY_COMMIT_ERROR_MESSAGE;
|
||||
import static com.appsmith.external.git.constants.GitConstants.GIT_CONFIG_ERROR;
|
||||
import static com.appsmith.external.git.constants.GitConstants.GIT_PROFILE_ERROR;
|
||||
import static com.appsmith.external.git.constants.ce.GitSpanCE.OPS_COMMIT;
|
||||
import static com.appsmith.external.git.constants.ce.GitSpanCE.OPS_STATUS;
|
||||
import static com.appsmith.external.git.constants.GitConstants.README_FILE_NAME;
|
||||
import static com.appsmith.external.git.constants.GitSpan.OPS_COMMIT;
|
||||
import static com.appsmith.external.git.constants.GitSpan.OPS_STATUS;
|
||||
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties;
|
||||
import static com.appsmith.server.constants.ArtifactType.APPLICATION;
|
||||
import static com.appsmith.server.constants.SerialiseArtifactObjective.VERSION_CONTROL;
|
||||
|
|
@ -761,7 +762,7 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE {
|
|||
.flatMap(artifact -> {
|
||||
String repoName = GitUtils.getRepoName(gitConnectDTO.getRemoteUrl());
|
||||
Path readMePath = gitArtifactHelper.getRepoSuffixPath(
|
||||
artifact.getWorkspaceId(), baseArtifactId, repoName, "README.md");
|
||||
artifact.getWorkspaceId(), baseArtifactId, repoName, README_FILE_NAME);
|
||||
try {
|
||||
Mono<Path> readMeMono = gitArtifactHelper.intialiseReadMe(artifact, readMePath, originHeader);
|
||||
return Mono.zip(readMeMono, currentUserMono)
|
||||
|
|
|
|||
|
|
@ -34,4 +34,12 @@ public class ArtifactJsonTransformationDTO {
|
|||
this.baseArtifactId = baseArtifactId;
|
||||
this.repoName = repoName;
|
||||
}
|
||||
|
||||
public ArtifactJsonTransformationDTO(
|
||||
String workspaceId, String baseArtifactId, String repoName, ArtifactType artifactType) {
|
||||
this.workspaceId = workspaceId;
|
||||
this.baseArtifactId = baseArtifactId;
|
||||
this.repoName = repoName;
|
||||
this.artifactType = artifactType;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -321,7 +321,8 @@ public class GitFSServiceCEImpl implements GitHandlingServiceCE {
|
|||
Path readmePath = gitArtifactHelper.getRepoSuffixPath(
|
||||
jsonTransformationDTO.getWorkspaceId(),
|
||||
jsonTransformationDTO.getBaseArtifactId(),
|
||||
jsonTransformationDTO.getRepoName());
|
||||
jsonTransformationDTO.getRepoName(),
|
||||
readmeFileName);
|
||||
try {
|
||||
return gitArtifactHelper
|
||||
.intialiseReadMe(artifact, readmePath, originHeader)
|
||||
|
|
@ -688,16 +689,25 @@ public class GitFSServiceCEImpl implements GitHandlingServiceCE {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> createGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO, GitRefDTO gitRefDTO) {
|
||||
public Mono<String> createGitReference(
|
||||
ArtifactJsonTransformationDTO jsonTransformationDTO, GitArtifactMetadata baseGitData, GitRefDTO gitRefDTO) {
|
||||
GitArtifactHelper<?> gitArtifactHelper =
|
||||
gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType());
|
||||
|
||||
String remoteUrl = baseGitData.getRemoteUrl();
|
||||
String publicKey = baseGitData.getGitAuth().getPublicKey();
|
||||
String privateKey = baseGitData.getGitAuth().getPrivateKey();
|
||||
|
||||
Path repoSuffix = gitArtifactHelper.getRepoSuffixPath(
|
||||
jsonTransformationDTO.getWorkspaceId(),
|
||||
jsonTransformationDTO.getBaseArtifactId(),
|
||||
jsonTransformationDTO.getRepoName());
|
||||
|
||||
return fsGitHandler.createAndCheckoutReference(repoSuffix, gitRefDTO);
|
||||
// TODO: add the checkout to the current branch as well.
|
||||
return fsGitHandler
|
||||
.createAndCheckoutReference(repoSuffix, gitRefDTO)
|
||||
.then(fsGitHandler.pushApplication(
|
||||
repoSuffix, remoteUrl, publicKey, privateKey, gitRefDTO.getRefName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -630,8 +630,9 @@ public class CommonGitFileUtilsCE {
|
|||
*/
|
||||
protected void copyMetadataToArtifactExchangeJson(
|
||||
GitResourceMap gitResourceMap, ArtifactExchangeJson artifactExchangeJson) {
|
||||
final String metadataFilePath = CommonConstants.METADATA + JSON_EXTENSION;
|
||||
GitResourceIdentity metadataIdentity =
|
||||
new GitResourceIdentity(GitResourceType.ROOT_CONFIG, METADATA + JSON_EXTENSION, "");
|
||||
new GitResourceIdentity(GitResourceType.ROOT_CONFIG, metadataFilePath, metadataFilePath);
|
||||
Object metadata = gitResourceMap.getGitResourceMap().get(metadataIdentity);
|
||||
|
||||
Gson gson = new Gson();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,618 @@
|
|||
package com.appsmith.server.git;
|
||||
|
||||
import com.appsmith.external.dtos.GitBranchDTO;
|
||||
import com.appsmith.external.dtos.GitRefDTO;
|
||||
import com.appsmith.external.dtos.GitStatusDTO;
|
||||
import com.appsmith.external.dtos.MergeStatusDTO;
|
||||
import com.appsmith.external.git.constants.ce.RefType;
|
||||
import com.appsmith.git.configurations.GitServiceConfig;
|
||||
import com.appsmith.git.dto.CommitDTO;
|
||||
import com.appsmith.server.applications.base.ApplicationService;
|
||||
import com.appsmith.server.configurations.ProjectProperties;
|
||||
import com.appsmith.server.constants.ArtifactType;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.constants.GitDefaultCommitMessage;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.domains.Artifact;
|
||||
import com.appsmith.server.domains.GitArtifactMetadata;
|
||||
import com.appsmith.server.domains.GitProfile;
|
||||
import com.appsmith.server.dtos.AutoCommitResponseDTO;
|
||||
import com.appsmith.server.dtos.GitConnectDTO;
|
||||
import com.appsmith.server.dtos.GitMergeDTO;
|
||||
import com.appsmith.server.dtos.GitPullDTO;
|
||||
import com.appsmith.server.git.autocommit.AutoCommitService;
|
||||
import com.appsmith.server.git.central.CentralGitService;
|
||||
import com.appsmith.server.git.central.GitType;
|
||||
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
||||
import com.appsmith.server.git.templates.contexts.GitContext;
|
||||
import com.appsmith.server.git.templates.providers.GitBranchesTestTemplateProvider;
|
||||
import com.appsmith.server.services.ConsolidatedAPIService;
|
||||
import com.appsmith.server.services.GitArtifactHelper;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.Status;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.security.test.context.support.WithUserDetails;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.appsmith.external.git.constants.GitConstants.DEFAULT_COMMIT_MESSAGE;
|
||||
import static com.appsmith.external.git.constants.GitConstants.EMPTY_COMMIT_ERROR_MESSAGE;
|
||||
import static com.appsmith.server.exceptions.AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES;
|
||||
import static com.appsmith.server.git.autocommit.AutoCommitEventHandlerImpl.AUTO_COMMIT_MSG_FORMAT;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* This integration test suite validates the end-to-end Git workflow for artifacts, performing a sequence of
|
||||
* operations that test repository setup, branch management, status validation, and cleanup. The operations
|
||||
* proceed as follows:
|
||||
*
|
||||
* 1. **Connect Artifact to Git**:
|
||||
* - The artifact is connected to an empty Git repository using a remote URL provided by the Git server initializer.
|
||||
* - A system-generated commit is created as part of the connection process.
|
||||
* - Auto-commit is enabled by default, as verified in the artifact metadata.
|
||||
* - The repository is checked to confirm a single system-generated commit and a clean working directory.
|
||||
*
|
||||
* 2. **Verify Initial Repository State**:
|
||||
* - The default branch is initialized, and its name is verified to match the metadata.
|
||||
* - The repository status is confirmed to be clean with no uncommitted changes.
|
||||
*
|
||||
* 3. **Trigger and Validate Auto-Commit**:
|
||||
* - Auto-commit is triggered, and the resulting commit is validated in the Git log.
|
||||
* - Commit history is checked to confirm the auto-commit appears as a second commit following the initial system-generated commit.
|
||||
*
|
||||
* 4. **Perform Status, Pull, and Commit Operations on the Default Branch (`master`)**:
|
||||
* - The repository status is checked to confirm no changes (`isClean = true`).
|
||||
* - A `pull` operation is executed to ensure synchronization, even when no updates are available.
|
||||
* - A `commit` is attempted with no changes, and the response is validated to confirm no new commits were created.
|
||||
*
|
||||
* 5. **Create and Verify Branches**:
|
||||
* - A new branch `foo` is created from the default branch (`master`).
|
||||
* - Metadata for `foo` is validated, and the commit history confirms that `foo` starts from the latest commit on `master`.
|
||||
* - A second branch `bar` is created from `foo`. Its metadata is verified, and the commit log confirms it starts from the latest commit on `foo`.
|
||||
*
|
||||
* 6. **Test Merging Scenarios**:
|
||||
* - A merge from `bar` to `foo` is validated and shows no action required (`ALREADY_UP_TO_DATE`), as no changes exist.
|
||||
* - Additional changes made to `bar` are merged back into `foo` successfully.
|
||||
*
|
||||
* 7. **Branch Deletion and Repopulation**:
|
||||
* - The branch `foo` is deleted locally but repopulated from the remote repository.
|
||||
* - The latest commit on `foo` is verified to match the changes made on `foo` before deletion.
|
||||
* - An attempt to delete the currently checked-out branch (`master`) fails as expected.
|
||||
*
|
||||
* 8. **Make Changes and Validate Commits**:
|
||||
* - Changes are made to the artifact on `foo` to trigger diffs.
|
||||
* - The repository status is validated as `isClean = false` with pending changes.
|
||||
* - A commit is created with a custom message, and the Git log confirms the commit as the latest on `foo`.
|
||||
* - Changes are successfully discarded, restoring the repository to a clean state.
|
||||
*
|
||||
* 9. **Set and Test Branch Protection**:
|
||||
* - The `master` branch is marked as protected. Commits directly to `master` are restricted.
|
||||
* - Attempts to commit to `master` fail with the appropriate error message.
|
||||
*
|
||||
* 10. **Merge Branches (`baz` to `bar`)**:
|
||||
* - A new branch `baz` is created from `bar`, and its commit log is verified.
|
||||
* - Changes are made to `baz` and successfully merged into `bar` via a fast-forward merge.
|
||||
* - The commit history confirms the merge, and the top commit matches the changes made in `baz`.
|
||||
*
|
||||
* 11. **Disconnect Artifact and Cleanup**:
|
||||
* - The artifact is disconnected from the Git repository.
|
||||
* - All repository branches (`foo`, `bar`, `baz`) except `master` are removed.
|
||||
* - The file system is verified to confirm all repository data is cleaned up.
|
||||
* - Applications associated with the deleted branches are also removed.
|
||||
*
|
||||
* This test suite ensures comprehensive coverage of Git workflows, including repository connection, branch creation,
|
||||
* branch protection, merging, status validation, and repository cleanup. Each operation includes detailed assertions
|
||||
* to validate expected outcomes and handle edge cases.
|
||||
*/
|
||||
|
||||
@Testcontainers
|
||||
@SpringBootTest
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class GitBranchesITWithCentralService {
|
||||
|
||||
@Autowired
|
||||
@RegisterExtension
|
||||
GitBranchesTestTemplateProvider templateProvider;
|
||||
|
||||
@Autowired
|
||||
@RegisterExtension
|
||||
ArtifactBuilderExtension artifactBuilderExtension;
|
||||
|
||||
@Autowired
|
||||
@RegisterExtension
|
||||
GitServerInitializerExtension gitServerInitializerExtension;
|
||||
|
||||
@Autowired
|
||||
CentralGitService centralGitService;
|
||||
@Autowired
|
||||
GitTestUtils gitTestUtils;
|
||||
@Autowired
|
||||
GitArtifactHelperResolver gitArtifactHelperResolver;
|
||||
@Autowired
|
||||
GitServiceConfig gitServiceConfig;
|
||||
@Autowired
|
||||
AutoCommitService autoCommitService;
|
||||
@Autowired
|
||||
ProjectProperties projectProperties;
|
||||
@Autowired
|
||||
ApplicationService applicationService;
|
||||
@Autowired
|
||||
ConsolidatedAPIService consolidatedAPIService;
|
||||
|
||||
final String ORIGIN = "https://foo.bar.com";
|
||||
|
||||
@TestTemplate
|
||||
@WithUserDetails(value = "api_user")
|
||||
void test(GitContext gitContext, ExtensionContext extensionContext) throws IOException, GitAPIException, InterruptedException {
|
||||
|
||||
ExtensionContext.Store contextStore = extensionContext.getStore(ExtensionContext.Namespace.create(ArtifactBuilderExtension.class));
|
||||
String artifactId = contextStore.get(FieldName.ARTIFACT_ID, String.class);
|
||||
|
||||
GitConnectDTO connectDTO = new GitConnectDTO();
|
||||
connectDTO.setRemoteUrl(gitServerInitializerExtension.getGitSshUrl("test" + artifactId));
|
||||
GitProfile gitProfile = new GitProfile("foo bar", "foo@bar.com", null);
|
||||
connectDTO.setGitProfile(gitProfile);
|
||||
|
||||
// TODO:
|
||||
// - Move the filePath variable to be relative, so that template name and repo name is prettier
|
||||
// - Is it possible to use controller layer here? Might help with also including web filters in IT
|
||||
Artifact artifact = centralGitService.connectArtifactToGit(artifactId, gitContext.getArtifactType(), connectDTO, ORIGIN, GitType.FILE_SYSTEM)
|
||||
.block();
|
||||
|
||||
assertThat(artifact).isNotNull();
|
||||
|
||||
ArtifactType artifactType = artifact.getArtifactType();
|
||||
GitArtifactMetadata artifactMetadata = artifact.getGitArtifactMetadata();
|
||||
GitArtifactHelper<?> artifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
|
||||
Path repoSuffix = artifactHelper.getRepoSuffixPath(
|
||||
artifact.getWorkspaceId(),
|
||||
artifactMetadata.getDefaultArtifactId(),
|
||||
artifactMetadata.getRepoName());
|
||||
|
||||
// Auto-commit should be turned on by default
|
||||
assertThat(artifactMetadata.getAutoCommitConfig().getEnabled()).isTrue();
|
||||
|
||||
Path path = Path.of(gitServiceConfig.getGitRootPath()).resolve(repoSuffix);
|
||||
String branch;
|
||||
ObjectId topOfCommits;
|
||||
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
branch = git.log().getRepository().getBranch();
|
||||
assertThat(branch).isEqualTo(artifactMetadata.getRefName());
|
||||
|
||||
// Assert only single system generated commit exists on FS
|
||||
Iterable<RevCommit> commits = git.log().call();
|
||||
Iterator<RevCommit> commitIterator = commits.iterator();
|
||||
assertThat(commitIterator.hasNext()).isTrue();
|
||||
|
||||
RevCommit firstCommit = commitIterator.next();
|
||||
assertThat(firstCommit.getFullMessage()).isEqualTo(DEFAULT_COMMIT_MESSAGE + GitDefaultCommitMessage.CONNECT_FLOW.getReason());
|
||||
topOfCommits = firstCommit.getId();
|
||||
|
||||
// TODO: Check why there is ane extra commit
|
||||
assertThat(commitIterator.hasNext()).isTrue();
|
||||
|
||||
// Assert that git directory is clean
|
||||
Status status = git.status().call();
|
||||
assertThat(status.isClean()).isTrue();
|
||||
}
|
||||
|
||||
// Assert that the artifact does have auto-commit requirements, and auto-commit gets initiated
|
||||
AutoCommitResponseDTO autoCommitResponseDTO = autoCommitService.autoCommitApplication(artifactId).block();
|
||||
|
||||
assertThat(autoCommitResponseDTO).isNotNull();
|
||||
AutoCommitResponseDTO.AutoCommitResponse autoCommitProgress = autoCommitResponseDTO.getAutoCommitResponse();
|
||||
// This check requires RTS to be running on your local since client side changes come in from there
|
||||
// Please make sure to run RTS before triggering this test
|
||||
assertThat(autoCommitProgress).isEqualTo(AutoCommitResponseDTO.AutoCommitResponse.PUBLISHED);
|
||||
|
||||
// Wait for auto-commit to complete
|
||||
// This should not take more than 2 seconds, we're checking every 500 ms
|
||||
long startTime = System.currentTimeMillis(), currentTime = System.currentTimeMillis();
|
||||
while (!autoCommitProgress.equals(AutoCommitResponseDTO.AutoCommitResponse.IDLE)) {
|
||||
Thread.sleep(500);
|
||||
//TODO: Undo this
|
||||
if (currentTime - startTime > 2000) {
|
||||
// fail("Auto-commit took too long");
|
||||
}
|
||||
autoCommitProgress = getAutocommitProgress(artifactId, artifact, artifactMetadata);
|
||||
currentTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// Now there should be two commits in the git log response
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
branch = git.log().getRepository().getBranch();
|
||||
assertThat(branch).isEqualTo(artifactMetadata.getRefName());
|
||||
|
||||
Iterable<RevCommit> commits = git.log().call();
|
||||
Iterator<RevCommit> commitIterator = commits.iterator();
|
||||
assertThat(commitIterator.hasNext()).isTrue();
|
||||
|
||||
RevCommit autoCommit = commitIterator.next();
|
||||
/*
|
||||
org.opentest4j.AssertionFailedError:
|
||||
expected: "System generated commit, to support new features in Appsmith UNKNOWN"
|
||||
but was: "System generated commit, initial commit"
|
||||
Expected :"System generated commit, to support new features in Appsmith UNKNOWN"
|
||||
Actual :"System generated commit, initial commit"
|
||||
<Click to see difference>
|
||||
*/
|
||||
assertThat(autoCommit.getFullMessage()).isEqualTo(String.format(AUTO_COMMIT_MSG_FORMAT, projectProperties.getVersion()));
|
||||
assertThat(commitIterator.hasNext()).isTrue();
|
||||
RevCommit firstCommit = commitIterator.next();
|
||||
assertThat(firstCommit.getId()).isEqualTo(topOfCommits);
|
||||
|
||||
topOfCommits = autoCommit.getId();
|
||||
}
|
||||
|
||||
// Assert that the initialized branch is set as default
|
||||
assertThat(artifactMetadata.getRefName()).isEqualTo(artifactMetadata.getDefaultBranchName());
|
||||
|
||||
// Assert that the branch is not protected by default
|
||||
assertThat(artifactMetadata.getBranchProtectionRules()).isNullOrEmpty();
|
||||
|
||||
// Check that the status is clean
|
||||
GitStatusDTO statusDTO = centralGitService.getStatus(artifactId, artifactType, true, GitType.FILE_SYSTEM).block();
|
||||
assertThat(statusDTO).isNotNull();
|
||||
assertThat(statusDTO.getIsClean()).isTrue();
|
||||
assertThat(statusDTO.getAheadCount()).isEqualTo(0);
|
||||
assertThat(statusDTO.getBehindCount()).isEqualTo(0);
|
||||
|
||||
// Check that pull when not required, still goes through
|
||||
GitPullDTO gitPullDTO = centralGitService.pullArtifact(artifactId, artifactType, GitType.FILE_SYSTEM).block();
|
||||
assertThat(gitPullDTO).isNotNull();
|
||||
|
||||
// Check that commit says that there is nothing to commit
|
||||
CommitDTO newCommitDTO = new CommitDTO();
|
||||
newCommitDTO.setMessage("Unused message");
|
||||
String commitResponse = centralGitService.commitArtifact(newCommitDTO, artifactId, artifactType, GitType.FILE_SYSTEM)
|
||||
.block();
|
||||
|
||||
assertThat(commitResponse).contains(EMPTY_COMMIT_ERROR_MESSAGE);
|
||||
|
||||
// Check that the previous attempt didn't actually go through
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
branch = git.log().getRepository().getBranch();
|
||||
assertThat(branch).isEqualTo(artifactMetadata.getRefName());
|
||||
|
||||
Iterable<RevCommit> commits = git.log().call();
|
||||
assertThat(commits.iterator().next().getId()).isEqualTo(topOfCommits);
|
||||
}
|
||||
|
||||
// Check that discard, even when not required, goes through
|
||||
Artifact discardedArtifact = centralGitService.discardChanges(artifactId, artifactType, GitType.FILE_SYSTEM).block();
|
||||
assertThat(discardedArtifact).isNotNull();
|
||||
|
||||
// Make a change in the artifact to trigger a diff
|
||||
gitTestUtils.createADiffInArtifact(artifact).block();
|
||||
|
||||
// Check that the status is not clean
|
||||
GitStatusDTO statusDTO2 = centralGitService.getStatus(artifactId, artifactType, true, GitType.FILE_SYSTEM).block();
|
||||
assertThat(statusDTO2).isNotNull();
|
||||
assertThat(statusDTO2.getIsClean()).isFalse();
|
||||
assertThat(statusDTO2.getAheadCount()).isEqualTo(0);
|
||||
assertThat(statusDTO2.getBehindCount()).isEqualTo(0);
|
||||
|
||||
// Check that commit makes the custom message be the top of the log
|
||||
CommitDTO commitDTO2 = new CommitDTO();
|
||||
commitDTO2.setMessage("Custom message");
|
||||
String commitResponse2 = centralGitService.commitArtifact(commitDTO2, artifactId, artifactType, GitType.FILE_SYSTEM)
|
||||
.block();
|
||||
|
||||
assertThat(commitResponse2).contains("Committed successfully!");
|
||||
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
branch = git.log().getRepository().getBranch();
|
||||
assertThat(branch).isEqualTo(artifactMetadata.getRefName());
|
||||
|
||||
Iterable<RevCommit> commits = git.log().call();
|
||||
Iterator<RevCommit> commitIterator = commits.iterator();
|
||||
RevCommit newCommit = commitIterator.next();
|
||||
assertThat(newCommit.getFullMessage()).isEqualTo("Custom message");
|
||||
|
||||
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
|
||||
|
||||
topOfCommits = newCommit.getId();
|
||||
}
|
||||
|
||||
// Check that status is clean again
|
||||
GitStatusDTO statusDTO3 = centralGitService.getStatus(artifactId, artifactType, true, GitType.FILE_SYSTEM).block();
|
||||
assertThat(statusDTO3).isNotNull();
|
||||
assertThat(statusDTO3.getIsClean()).isTrue();
|
||||
assertThat(statusDTO3.getAheadCount()).isEqualTo(0);
|
||||
assertThat(statusDTO3.getBehindCount()).isEqualTo(0);
|
||||
|
||||
// Make another change to trigger a diff
|
||||
gitTestUtils.createADiffInArtifact(artifact).block();
|
||||
|
||||
// Check that status in not clean
|
||||
GitStatusDTO statusDTO4 = centralGitService.getStatus(artifactId, artifactType, true, GitType.FILE_SYSTEM ).block();
|
||||
assertThat(statusDTO4).isNotNull();
|
||||
assertThat(statusDTO4.getIsClean()).isFalse();
|
||||
assertThat(statusDTO4.getAheadCount()).isEqualTo(0);
|
||||
assertThat(statusDTO4.getBehindCount()).isEqualTo(0);
|
||||
|
||||
// Protect the master branch
|
||||
List<String> protectedBranches = centralGitService.updateProtectedBranches(artifactId, artifactType, List.of(branch)).block();
|
||||
assertThat(protectedBranches).containsExactly(branch);
|
||||
|
||||
// Now try to commit, and check that it fails
|
||||
CommitDTO commitDTO3 = new CommitDTO();
|
||||
commitDTO3.setMessage("Failed commit");
|
||||
Mono<String> commitResponse3Mono = centralGitService.commitArtifact(commitDTO3, artifactId, artifactType, GitType.FILE_SYSTEM);
|
||||
StepVerifier.create(commitResponse3Mono)
|
||||
.expectErrorSatisfies(e -> assertThat(e.getMessage()).contains("Cannot commit to protected branch"))
|
||||
.verify();
|
||||
|
||||
// Create a new branch foo from master, check that the commit for new branch is created as system generated
|
||||
// On top of the previous custom commit
|
||||
GitRefDTO gitRefDTO = new GitRefDTO();
|
||||
gitRefDTO.setRefName("foo");
|
||||
gitRefDTO.setRefType(RefType.branch);
|
||||
Artifact fooArtifact = centralGitService.createReference(artifactId, artifactType, gitRefDTO, GitType.FILE_SYSTEM).block();
|
||||
assertThat(fooArtifact).isNotNull();
|
||||
|
||||
String fooArtifactId = fooArtifact.getId();
|
||||
GitArtifactMetadata fooMetadata = fooArtifact.getGitArtifactMetadata();
|
||||
assertThat(fooMetadata.getRefName()).isEqualTo("foo");
|
||||
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
// since the new flow discards the parent branch,
|
||||
// the parent branch is checkedOut at last.
|
||||
|
||||
Iterable<RevCommit> commits = git.log().call();
|
||||
Iterator<RevCommit> commitIterator = commits.iterator();
|
||||
RevCommit newCommit = commitIterator.next();
|
||||
assertThat(newCommit.getId()).isEqualTo(topOfCommits);
|
||||
}
|
||||
|
||||
// Since the status
|
||||
// Check that status on foo, it should be dirty
|
||||
GitStatusDTO statusDTO5 = centralGitService.getStatus(fooArtifactId, artifactType, true, GitType.FILE_SYSTEM).block();
|
||||
assertThat(statusDTO5).isNotNull();
|
||||
assertThat(statusDTO5.getIsClean()).isFalse();
|
||||
assertThat(statusDTO5.getAheadCount()).isEqualTo(0);
|
||||
assertThat(statusDTO5.getBehindCount()).isEqualTo(0);
|
||||
|
||||
// Create another branch bar from foo
|
||||
GitRefDTO barBranchDTO = new GitRefDTO();
|
||||
barBranchDTO.setRefName("bar");
|
||||
barBranchDTO.setRefType(RefType.branch);
|
||||
Artifact barArtifact = centralGitService.createReference(fooArtifactId, artifactType, barBranchDTO, GitType.FILE_SYSTEM).block();
|
||||
assertThat(barArtifact).isNotNull();
|
||||
|
||||
String barArtifactId = barArtifact.getId();
|
||||
GitArtifactMetadata barMetadata = barArtifact.getGitArtifactMetadata();
|
||||
assertThat(barMetadata.getRefName()).isEqualTo("bar");
|
||||
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
Iterable<RevCommit> commits = git.log().call();
|
||||
Iterator<RevCommit> commitIterator = commits.iterator();
|
||||
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
|
||||
}
|
||||
|
||||
// Check that status on foo,
|
||||
// it should be clean as the diff is transferred to new branch bar
|
||||
GitStatusDTO fooStatusDTO = centralGitService.getStatus(fooArtifactId, artifactType, true, GitType.FILE_SYSTEM).block();
|
||||
assertThat(fooStatusDTO).isNotNull();
|
||||
assertThat(fooStatusDTO.getIsClean()).isTrue();
|
||||
assertThat(fooStatusDTO.getAheadCount()).isEqualTo(0);
|
||||
assertThat(fooStatusDTO.getBehindCount()).isEqualTo(0);
|
||||
|
||||
// Check that status on bar, it should be dirty
|
||||
GitStatusDTO barStatusDTO = centralGitService.getStatus(barArtifactId, artifactType, true, GitType.FILE_SYSTEM).block();
|
||||
assertThat(barStatusDTO).isNotNull();
|
||||
assertThat(barStatusDTO.getIsClean()).isFalse();
|
||||
assertThat(barStatusDTO.getAheadCount()).isEqualTo(0);
|
||||
assertThat(barStatusDTO.getBehindCount()).isEqualTo(0);
|
||||
|
||||
Artifact discardBarBranch = centralGitService.discardChanges(barArtifactId, artifactType, GitType.FILE_SYSTEM).block();
|
||||
assertThat(discardBarBranch).isNotNull();
|
||||
|
||||
// Check merge status to foo shows no action required
|
||||
// bar -> foo
|
||||
GitMergeDTO gitMergeDTO = new GitMergeDTO();
|
||||
gitMergeDTO.setDestinationBranch("foo");
|
||||
gitMergeDTO.setSourceBranch("bar");
|
||||
MergeStatusDTO mergeStatusDTO = centralGitService.isBranchMergable(barArtifactId, artifactType, gitMergeDTO, GitType.FILE_SYSTEM ).block();
|
||||
assertThat(mergeStatusDTO).isNotNull();
|
||||
assertThat(mergeStatusDTO.getStatus()).isEqualTo("ALREADY_UP_TO_DATE");
|
||||
|
||||
// Delete foo locally and re-populate from remote
|
||||
List<String> branchList = centralGitService.listBranchForArtifact(artifactId, artifactType, false, GitType.FILE_SYSTEM)
|
||||
.flatMapMany(Flux::fromIterable)
|
||||
.map(GitBranchDTO::getBranchName)
|
||||
.collectList()
|
||||
.block();
|
||||
assertThat(branchList).containsExactlyInAnyOrder(
|
||||
artifactMetadata.getRefName(),
|
||||
"origin/" + artifactMetadata.getRefName(),
|
||||
fooMetadata.getRefName(),
|
||||
"origin/" + fooMetadata.getRefName(),
|
||||
barMetadata.getRefName(),
|
||||
"origin/" + barMetadata.getRefName());
|
||||
|
||||
Mono<? extends Artifact> deleteBranchAttemptMono = centralGitService.deleteGitReference(artifactId, artifactType, gitRefDTO, GitType.FILE_SYSTEM);
|
||||
StepVerifier
|
||||
.create(deleteBranchAttemptMono)
|
||||
.expectErrorSatisfies(e -> assertThat(e.getMessage()).contains("Cannot delete current checked out branch"))
|
||||
.verify();
|
||||
|
||||
// TODO: I'm having to checkout myself to be able to delete the branch.
|
||||
// Are we relying on auto-commit check to do this otherwise?
|
||||
// Is this a potential bug?
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
git.checkout().setName("bar").call();
|
||||
}
|
||||
|
||||
GitRefDTO deleteFooDTO = new GitRefDTO();
|
||||
deleteFooDTO.setRefType(RefType.branch);
|
||||
deleteFooDTO.setRefName("foo");
|
||||
centralGitService.deleteGitReference(artifactId, artifactType, deleteFooDTO, GitType.FILE_SYSTEM).block();
|
||||
|
||||
List<String> branchList2 = centralGitService.listBranchForArtifact(artifactId, artifactType, false, GitType.FILE_SYSTEM)
|
||||
.flatMapMany(Flux::fromIterable)
|
||||
.map(GitBranchDTO::getBranchName)
|
||||
.collectList()
|
||||
.block();
|
||||
assertThat(branchList2).containsExactlyInAnyOrder(
|
||||
artifactMetadata.getRefName(),
|
||||
"origin/" + artifactMetadata.getRefName(),
|
||||
"origin/" + fooMetadata.getRefName(),
|
||||
barMetadata.getRefName(),
|
||||
"origin/" + barMetadata.getRefName());
|
||||
|
||||
GitRefDTO checkoutFooDTO = new GitRefDTO();
|
||||
checkoutFooDTO.setRefName("origin/foo");
|
||||
checkoutFooDTO.setRefType(RefType.branch);
|
||||
Artifact checkedOutFooArtifact = centralGitService.checkoutReference(artifactId, artifactType, checkoutFooDTO, true, GitType.FILE_SYSTEM).block();
|
||||
|
||||
assertThat(checkedOutFooArtifact).isNotNull();
|
||||
List<String> branchList3 = centralGitService.listBranchForArtifact(artifactId, artifactType, false, GitType.FILE_SYSTEM)
|
||||
.flatMapMany(Flux::fromIterable)
|
||||
.map(GitBranchDTO::getBranchName)
|
||||
.collectList()
|
||||
.block();
|
||||
assertThat(branchList3).containsExactlyInAnyOrder(
|
||||
artifactMetadata.getRefName(),
|
||||
"origin/" + artifactMetadata.getRefName(),
|
||||
fooMetadata.getRefName(),
|
||||
"origin/" + fooMetadata.getRefName(),
|
||||
barMetadata.getRefName(),
|
||||
"origin/" + barMetadata.getRefName());
|
||||
|
||||
// Verify latest commit on foo should be same as changes made on foo previously
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
branch = git.log().getRepository().getBranch();
|
||||
assertThat(branch).isEqualTo(fooMetadata.getRefName());
|
||||
|
||||
Iterable<RevCommit> commits = git.log().call();
|
||||
Iterator<RevCommit> commitIterator = commits.iterator();
|
||||
|
||||
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
|
||||
}
|
||||
|
||||
// Make more changes on foo and attempt discard
|
||||
gitTestUtils.createADiffInArtifact(checkedOutFooArtifact).block();
|
||||
|
||||
GitStatusDTO discardableStatus = centralGitService.getStatus(checkedOutFooArtifact.getId(), artifactType, false, GitType.FILE_SYSTEM).block();
|
||||
|
||||
assertThat(discardableStatus).isNotNull();
|
||||
assertThat(discardableStatus.getIsClean()).isFalse();
|
||||
|
||||
Artifact discardedFoo = centralGitService.discardChanges(checkedOutFooArtifact.getId(), artifactType, GitType.FILE_SYSTEM).block();
|
||||
|
||||
GitStatusDTO discardedStatus = centralGitService.getStatus(checkedOutFooArtifact.getId(), artifactType, false, GitType.FILE_SYSTEM).block();
|
||||
|
||||
assertThat(discardedStatus).isNotNull();
|
||||
// TODO: Why is this not clean?
|
||||
// There is an on page load that gets triggered here that is causing a diff
|
||||
// This should ideally have already been fixed on initial artifact import
|
||||
// assertThat(discardedStatus.getIsClean()).isTrue();
|
||||
|
||||
// Make a change to trigger a diff on bar
|
||||
gitTestUtils.createADiffInArtifact(barArtifact).block();
|
||||
|
||||
// Check merge status to master shows not merge-able
|
||||
GitMergeDTO gitMergeDTO2 = new GitMergeDTO();
|
||||
gitMergeDTO2.setSourceBranch("bar");
|
||||
gitMergeDTO2.setDestinationBranch("master");
|
||||
MergeStatusDTO mergeStatusDTO2 = centralGitService.isBranchMergable(barArtifactId, artifactType, gitMergeDTO2, GitType.FILE_SYSTEM).block();
|
||||
|
||||
assertThat(mergeStatusDTO2).isNotNull();
|
||||
assertThat(mergeStatusDTO2.isMergeAble()).isFalse();
|
||||
assertThat(mergeStatusDTO2.getMessage()).contains(GIT_MERGE_FAILED_LOCAL_CHANGES.getMessage("bar"));
|
||||
|
||||
// Create a new branch baz and check for new commit
|
||||
GitRefDTO gitBranchDTO = new GitRefDTO();
|
||||
gitBranchDTO.setRefName("baz");
|
||||
gitBranchDTO.setRefType(RefType.branch);
|
||||
Artifact bazArtifact = centralGitService.createReference(barArtifactId, artifactType, gitBranchDTO, GitType.FILE_SYSTEM).block();
|
||||
assertThat(bazArtifact).isNotNull();
|
||||
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
Iterable<RevCommit> commits = git.log().call();
|
||||
Iterator<RevCommit> commitIterator = commits.iterator();
|
||||
RevCommit newCommit = commitIterator.next();
|
||||
assertThat(newCommit.getId()).isEqualTo(topOfCommits);
|
||||
}
|
||||
|
||||
GitStatusDTO bazStatus = centralGitService.getStatus(bazArtifact.getId(), artifactType, true, GitType.FILE_SYSTEM).block();
|
||||
assertThat(bazStatus).isNotNull();
|
||||
assertThat(bazStatus.getIsClean()).isFalse();
|
||||
|
||||
centralGitService.discardChanges(bazArtifact.getId(), artifactType, GitType.FILE_SYSTEM).block();
|
||||
|
||||
GitStatusDTO bazStatus2 = centralGitService.getStatus(bazArtifact.getId(), artifactType, true, GitType.FILE_SYSTEM).block();
|
||||
assertThat(bazStatus2).isNotNull();
|
||||
assertThat(bazStatus2.getIsClean()).isTrue();
|
||||
|
||||
GitMergeDTO gitMergeDTO3 = new GitMergeDTO();
|
||||
gitMergeDTO3.setSourceBranch("baz");
|
||||
gitMergeDTO3.setDestinationBranch("bar");
|
||||
|
||||
MergeStatusDTO mergeStatusDTO3 = centralGitService.isBranchMergable(bazArtifact.getId(), artifactType, gitMergeDTO3, GitType.FILE_SYSTEM).block();
|
||||
assertThat(mergeStatusDTO3).isNotNull();
|
||||
assertThat(mergeStatusDTO3.isMergeAble()).isTrue();
|
||||
|
||||
GitStatusDTO barStatus2 = centralGitService.getStatus(barArtifactId, artifactType, true, GitType.FILE_SYSTEM).block();
|
||||
assertThat(barStatus2).isNotNull();
|
||||
assertThat(barStatus2.getIsClean()).isTrue();
|
||||
|
||||
MergeStatusDTO barToBazMergeStatus = centralGitService.mergeBranch(bazArtifact.getId(), artifactType, gitMergeDTO3, GitType.FILE_SYSTEM).block();
|
||||
|
||||
assertThat(barToBazMergeStatus).isNotNull();
|
||||
assertThat(barToBazMergeStatus.isMergeAble()).isTrue();
|
||||
assertThat(barToBazMergeStatus.getStatus()).contains("ALREADY_UP_TO_DATE");
|
||||
|
||||
// Since fast-forward should succeed here, top of commit should not change
|
||||
try (Git git = Git.open(path.toFile())) {
|
||||
Iterable<RevCommit> commits = git.log().call();
|
||||
Iterator<RevCommit> commitIterator = commits.iterator();
|
||||
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
|
||||
}
|
||||
|
||||
// Disconnect artifact and verify non-existence of `foo`, `bar` and `baz`
|
||||
Artifact disconnectedArtifact = centralGitService.detachRemote(artifactId, artifactType, GitType.FILE_SYSTEM).block();
|
||||
|
||||
assertThat(disconnectedArtifact).isNotNull();
|
||||
assertThat(disconnectedArtifact.getGitArtifactMetadata()).isNull();
|
||||
|
||||
// TODO: This needs to be generified for artifacts
|
||||
Application deletedFooArtifact = applicationService.findById(checkedOutFooArtifact.getId()).block();
|
||||
assertThat(deletedFooArtifact).isNull();
|
||||
Application deletedBarArtifact = applicationService.findById(barArtifactId).block();
|
||||
assertThat(deletedBarArtifact).isNull();
|
||||
Application deletedBazArtifact = applicationService.findById(bazArtifact.getId()).block();
|
||||
assertThat(deletedBazArtifact).isNull();
|
||||
Application existingMasterArtifact = applicationService.findById(artifactId).block();
|
||||
assertThat(existingMasterArtifact).isNotNull();
|
||||
|
||||
// Verify FS is clean after disconnect
|
||||
boolean repoDirectoryNotExists = Files.notExists(path);
|
||||
assertThat(repoDirectoryNotExists).isTrue();
|
||||
}
|
||||
|
||||
private AutoCommitResponseDTO.AutoCommitResponse getAutocommitProgress(String artifactId, Artifact artifact, GitArtifactMetadata artifactMetadata) {
|
||||
AutoCommitResponseDTO autoCommitProgress = centralGitService.getAutoCommitProgress(artifactId, artifact.getArtifactType(), artifactMetadata.getRefName()).block();
|
||||
|
||||
assertThat(autoCommitProgress).isNotNull();
|
||||
return autoCommitProgress.getAutoCommitResponse();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user