gitly
Version:
An API to download and/or extract git repositories
149 lines (148 loc) • 6.02 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = clone;
const cross_spawn_1 = __importDefault(require("cross-spawn"));
const promises_1 = require("node:fs/promises");
const node_path_1 = __importDefault(require("node:path"));
const tar = __importStar(require("tar"));
const archive_1 = require("./archive");
const error_1 = require("./error");
const execute_1 = __importDefault(require("./execute"));
const exists_1 = __importDefault(require("./exists"));
const offline_1 = require("./offline");
const parse_1 = __importDefault(require("./parse"));
/**
* Uses local git installation to clone a repository to the destination.
* @param repository The repository to clone
* @param options The options to use
* @returns The path to the cloned repository
* @throws {GitlyCloneError} When the repository fails to clone
* @note This method requires a local git installation
* @note This method caches the repository by default
* @example
* ```js
* // ...
* const path = await clone('iwatakeshi/git-copy')
* // ...
* ```
*/
async function clone(repository, options = {}) {
const info = (0, parse_1.default)(repository, options);
const archivePath = (0, archive_1.getArchivePath)(info, options);
const directory = archivePath.replace(/\.tar\.gz$/, '');
let order = [];
const local = async () => (0, exists_1.default)(`${archivePath}.tar.gz`);
const remote = async () => {
var _a;
// If the repository is cached, remove the old cache
if (await (0, exists_1.default)(archivePath)) {
/* istanbul ignore next */
await (0, promises_1.rm)(archivePath);
}
// Prevent second order command injection
const depth = ((_a = options === null || options === void 0 ? void 0 : options.git) === null || _a === void 0 ? void 0 : _a.depth) || 1;
if (repository.includes('--upload-pack') ||
directory.includes('--upload-pack')) {
throw new error_1.GitlyCloneError('Invalid argument');
}
/* istanbul ignore if */
if (typeof depth !== 'number') {
throw new error_1.GitlyCloneError('Invalid depth option');
}
/* istanbul ignore if */
if (info.href.includes('--upload-pack')) {
throw new error_1.GitlyCloneError('Invalid argument');
}
const child = (0, cross_spawn_1.default)('git', [
'clone',
'--depth',
depth.toString(),
info.href,
directory,
]);
await new Promise((resolve, reject) => {
child.on('error', (reason) => reject(new error_1.GitlyCloneError(reason.message)));
child.on('close', (code) => {
/* istanbul ignore next */
if (code === 0) {
// delete the .git directory to make the archive smaller
(0, promises_1.rm)(node_path_1.default.resolve(directory, '.git'), { recursive: true })
.then(() =>
// Create the archive after cloning
tar.create({
gzip: true,
file: archivePath,
// Go one level up to include the repository name in the archive
cwd: node_path_1.default.resolve(archivePath, '..'),
portable: true,
}, [info.type]))
.then(() => (0, promises_1.rm)(node_path_1.default.resolve(directory), {
recursive: true,
}))
.then(resolve)
.catch((error) => reject(new error_1.GitlyCloneError(error.message)));
} /* istanbul ignore next */
else {
reject(new error_1.GitlyCloneError('Failed to clone the repository'));
}
});
});
return archivePath;
};
/* istanbul ignore next */
if ((await (0, offline_1.isOffline)()) || options.cache) {
order = [local];
}
else if (options.force || ['master', 'main'].includes(info.type)) {
order = [remote, local];
}
try {
const result = await (0, execute_1.default)(order);
if (typeof result === 'boolean') {
return archivePath;
}
return result;
}
catch (error) {
if (options.throw) {
throw error;
}
}
return '';
}