git-documentdb
Version:
Offline-first database that syncs with Git
247 lines • 11.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.combineDatabaseWithTheirs = void 0;
/* eslint-disable no-await-in-loop */
/**
* GitDocumentDB
* Copyright (c) Hidekazu Kubota
*
* This source code is licensed under the Mozilla Public License Version 2.0
* found in the LICENSE file in the root directory of this source tree.
*/
const path_1 = __importDefault(require("path"));
const isomorphic_git_1 = __importDefault(require("isomorphic-git"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const ulid_1 = require("ulid");
const rimraf_1 = __importDefault(require("rimraf"));
const blob_1 = require("../crud/blob");
const error_1 = require("../error");
const const_1 = require("../const");
const utils_1 = require("../utils");
const remote_engine_1 = require("./remote_engine");
/**
* Clone a remote repository and combine the current local working directory with it.
* TODO: Must catch errors
*
* @throws {@link Err.FileRemoveTimeoutError}
*
* @throws # Errors from RemoteEngine[engineName].clone
* @throws - {@link RemoteErr.InvalidURLFormatError}
* @throws - {@link RemoteErr.NetworkError}
* @throws - {@link RemoteErr.HTTPError401AuthorizationRequired}
* @throws - {@link RemoteErr.HTTPError404NotFound}
* @throws - {@link RemoteErr.CannotConnectError}
*
* @throws - {@link RemoteErr.HttpProtocolRequiredError}
* @throws - {@link RemoteErr.InvalidRepositoryURLError}
* @throws - {@link RemoteErr.InvalidSSHKeyPathError}
*
* @throws - {@link RemoteErr.InvalidAuthenticationTypeError}
*
* @public
*/
// eslint-disable-next-line complexity
async function combineDatabaseWithTheirs(gitDDB, remoteOptions, remoteName) {
// Clone repository if remoteURL exists
const remoteDir = gitDDB.workingDir + '_' + (0, ulid_1.ulid)(Date.now());
const tmpLocalDir = gitDDB.workingDir + '_' + (0, ulid_1.ulid)(Date.now());
const duplicates = [];
try {
await remote_engine_1.RemoteEngine[remoteOptions.connection.engine]
.clone(remoteDir, remoteOptions, remoteName, gitDDB.tsLogger)
.catch(err => {
throw (0, remote_engine_1.wrappingRemoteEngineError)(err);
});
// Add refs to remote branch
const remoteCommitOid = await isomorphic_git_1.default.resolveRef({
fs: fs_extra_1.default,
dir: remoteDir,
ref: `refs/remotes/origin/${gitDDB.defaultBranch}`,
});
await isomorphic_git_1.default.writeRef({
fs: fs_extra_1.default,
dir: remoteDir,
ref: `refs/remotes/${remoteName}/${gitDDB.defaultBranch}`,
value: remoteCommitOid,
force: true,
});
// Overwrite upstream branch
await isomorphic_git_1.default.setConfig({
fs: fs_extra_1.default,
dir: remoteDir,
path: `branch.${gitDDB.defaultBranch}.remote`,
value: remoteName,
});
await isomorphic_git_1.default.setConfig({
fs: fs_extra_1.default,
dir: remoteDir,
path: `branch.${gitDDB.defaultBranch}.merge`,
value: `refs/heads/${gitDDB.defaultBranch}`,
});
const localMetadataList = await (0, utils_1.getAllMetadata)(gitDDB.workingDir, gitDDB.serializeFormat);
const remoteMetadataList = await (0, utils_1.getAllMetadata)(remoteDir, gitDDB.serializeFormat);
const remoteNames = remoteMetadataList.map(meta => meta.name);
for (let i = 0; i < localMetadataList.length; i++) {
const meta = localMetadataList[i];
const localFilePath = path_1.default.resolve(gitDDB.workingDir, meta.name);
const remoteFilePath = path_1.default.resolve(remoteDir, meta.name);
const dir = path_1.default.dirname(remoteFilePath);
await fs_extra_1.default.ensureDir(dir);
const docType = gitDDB.serializeFormat.hasObjectExtension(localFilePath)
? 'json'
: 'text';
// eslint-disable-next-line max-depth
if (docType === 'text') {
// TODO: select binary or text by .gitattribtues
}
if (remoteNames.includes(meta.name)) {
// Add postfix and copy localFilePath to remoteFilePath if remoteFilePath exists
let duplicatedFileName = '';
let duplicatedFileId = '';
let duplicatedFileExt = '';
const postfix = const_1.DUPLICATED_FILE_POSTFIX + gitDDB.dbId;
let original;
let duplicate;
const remoteFile = remoteMetadataList.find(data => data.name === meta.name);
if (docType === 'json') {
let doc;
// eslint-disable-next-line max-depth
if (gitDDB.serializeFormat.format === 'front-matter') {
const txt = fs_extra_1.default.readFileSync(localFilePath, {
encoding: 'utf8',
});
const [, extension] = localFilePath.match(/.+(\..+?)$/);
doc = (0, blob_1.textToJsonDoc)(txt, gitDDB.serializeFormat, extension);
}
else {
doc = fs_extra_1.default.readJSONSync(localFilePath);
}
const _id = meta._id;
// eslint-disable-next-line max-depth
if (doc._id !== undefined) {
doc._id = _id + postfix;
}
duplicatedFileName = _id + postfix + gitDDB.serializeFormat.extension(doc);
duplicatedFileId = _id + postfix;
duplicatedFileExt = gitDDB.serializeFormat.extension(doc);
fs_extra_1.default.writeFileSync(path_1.default.resolve(remoteDir, duplicatedFileName), gitDDB.serializeFormat.serialize(doc).data);
const duplicatedOid = (await isomorphic_git_1.default.hashBlob({ object: gitDDB.serializeFormat.serialize(doc).data })).oid;
original = {
_id,
name: meta.name,
fileOid: remoteFile.fileOid,
type: 'json',
};
duplicate = {
_id: duplicatedFileId,
name: duplicatedFileName,
fileOid: duplicatedOid,
type: 'json',
};
}
else {
// Add postfix before extension.
duplicatedFileExt = path_1.default.extname(localFilePath);
const onlyName = localFilePath.replace(new RegExp(duplicatedFileExt + '$'), '');
duplicatedFileName = onlyName + postfix + duplicatedFileExt;
fs_extra_1.default.copyFileSync(localFilePath, path_1.default.resolve(remoteDir, duplicatedFileName));
original = {
name: meta.name,
fileOid: remoteFile.fileOid,
type: docType,
};
duplicate = {
name: duplicatedFileName,
fileOid: meta.fileOid,
type: docType,
};
}
await isomorphic_git_1.default.add({ fs: fs_extra_1.default, dir: remoteDir, filepath: duplicatedFileName });
duplicates.push({
original,
duplicate,
});
}
else {
// Copy localFilePath to remoteFilePath if remoteFilePath not exists
await fs_extra_1.default.copyFile(localFilePath, remoteFilePath);
await isomorphic_git_1.default.add({ fs: fs_extra_1.default, dir: remoteDir, filepath: meta.name });
}
}
if (localMetadataList.length > 0) {
const commitMessage = 'combine database head with theirs';
await isomorphic_git_1.default.commit({
fs: fs_extra_1.default,
dir: remoteDir,
author: gitDDB.author,
committer: gitDDB.committer,
message: commitMessage,
});
}
await fs_extra_1.default.rename(gitDDB.workingDir, tmpLocalDir);
await fs_extra_1.default.rename(remoteDir, gitDDB.workingDir);
const userName = await isomorphic_git_1.default
.getConfig({ fs: fs_extra_1.default, dir: tmpLocalDir, path: 'user.name' })
.catch(() => undefined);
const userEmail = await isomorphic_git_1.default
.getConfig({ fs: fs_extra_1.default, dir: tmpLocalDir, path: 'user.email' })
.catch(() => undefined);
if (userName) {
await isomorphic_git_1.default.setConfig({
fs: fs_extra_1.default,
dir: gitDDB.workingDir,
path: 'user.name',
value: userName,
});
}
if (userEmail) {
await isomorphic_git_1.default.setConfig({
fs: fs_extra_1.default,
dir: gitDDB.workingDir,
path: 'user.email',
value: userEmail,
});
}
}
catch (e) {
console.log(e);
throw e;
}
finally {
await new Promise((resolve, reject) => {
// Set timeout because rimraf sometimes does not catch EPERM error.
setTimeout(() => {
reject(new error_1.Err.FileRemoveTimeoutError());
}, const_1.FILE_REMOVE_TIMEOUT);
(0, rimraf_1.default)(remoteDir, error => {
if (error) {
reject(error);
}
resolve();
});
});
await new Promise((resolve, reject) => {
// Set timeout because rimraf sometimes does not catch EPERM error.
setTimeout(() => {
reject(new error_1.Err.FileRemoveTimeoutError());
}, const_1.FILE_REMOVE_TIMEOUT);
(0, rimraf_1.default)(tmpLocalDir, error => {
if (error) {
reject(error);
}
resolve();
});
});
}
await gitDDB.loadDbInfo();
const result = {
action: 'combine database',
duplicates,
};
return result;
}
exports.combineDatabaseWithTheirs = combineDatabaseWithTheirs;
//# sourceMappingURL=combine.js.map