UNPKG

@atomist/automation-client

Version:

Atomist API for software low-level client

156 lines • 6.23 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs-extra"); const os = require("os"); const path = require("path"); const tmp = require("tmp-promise"); const shutdown_1 = require("../../internal/util/shutdown"); const logger_1 = require("../../util/logger"); /** * Directory manager that creates temporary directories in the system * temporary directory. It cleans them up after two hours or on * program exit, if possible. The class only creates a single * instance of itself. * * It uses tmp-promise (built on tmp) to create clean temporary * directories to work with git projects from remotes */ class CleaningTmpDirectoryManager { constructor() { this.root = os.tmpdir(); this.prefix = `atm-${process.pid}-`; this.reapInterval = 1000 * 60 * 30; // 30 minutes this.maxAge = 1000 * 60 * 60 * 2; // 2 hours this.initialized = false; // only create a single instance if (!this.instance) { this.instance = this; } return this.instance; } /** * Create a temporary directory for the provided repository. */ directoryFor(owner, repo, branch, opts) { return __awaiter(this, void 0, void 0, function* () { this.initialize(); const fromTmp = yield tmp.dir({ keep: opts.keep, prefix: this.prefix }); return Object.assign(Object.assign({}, fromTmp), { type: "empty-directory", release: () => this.cleanup(fromTmp.path, opts.keep), invalidate: () => Promise.resolve(), transient: opts.keep === false, provenance: `created with tmp, keep = ${opts.keep}` }); }); } /** * Initialize object, creating interval for cleanup and * registering shutdown hook. */ initialize() { if (this.initialized) { return; } this.initialized = true; setInterval(() => this.reap(this.ageFilter()), this.reapInterval).unref(); shutdown_1.registerShutdownHook(() => this.reap(), 3000, `temporary directory cleanup`); return this; } /* * If !keep, attempts to delete directory. Swallows errors * because it is not that important. */ cleanup(p, keep) { return __awaiter(this, void 0, void 0, function* () { if (keep) { return; } try { yield fs.remove(p); } catch (e) { logger_1.logger.warn(`Failed to remove '${p}': ${e.message}`); } }); } /** * Remove temporary directories created by this object that pass * the filter. All operations of this method are wrapped in a * try/catch block to make it safe for use in timers and * intervals, although the return value will be ignored in those * cases. * * @param filter If this returns `true` when passed the basename of the temporary directory, the directory will be deleted * @return 0 if succesful, 1 otherwise */ reap(filter = this.noFilter) { return __awaiter(this, void 0, void 0, function* () { try { const files = yield fs.readdir(this.root); const tmpDirs = files.filter(f => f.startsWith(this.prefix)); const toRemove = tmpDirs.filter(filter); const errs = []; for (const dir of toRemove) { const dirPath = path.join(this.root, dir); logger_1.logger.debug(`Deleting temporary directory: ${dirPath}`); try { yield fs.remove(dirPath); } catch (e) { e.message = `Failed to remove temporary directory '${dirPath}': ${e.message}`; logger_1.logger.warn(e.message); errs.push(e); } } if (errs.length > 0) { const err = errs[0]; err.message = errs.map(e => e.message).join("; "); throw err; } } catch (e) { logger_1.logger.warn(`Failed to remove temporary directories: ${e.message}`); return 1; } return 0; }); } /** * Filter for reap that performs no filtering, everything gets * deleted. */ noFilter(d) { return true; } /** * Filter directory on age. The returned function returns true if * the age of its argument, as determined by the stat mtime, is * greater than the old, which defaults to 2 hours. * * @param old Age beyond which `true` will be returned * @param now Time to consider the current time, defaults to `Date.now()` */ ageFilter(old = this.maxAge, now) { const ts = (now || Date.now()) - old; return d => { const dirPath = path.join(this.root, d); try { const dirStat = fs.statSync(dirPath); return dirStat.mtimeMs < ts; } catch (e) { logger_1.logger.warn(`Failed to stat temporary directory '${dirPath}', returning false: ${e.message}`); return false; } }; } } /** * Singleton instance of [[CleaningTmpDirectoryManager]]. */ exports.TmpDirectoryManager = new CleaningTmpDirectoryManager(); //# sourceMappingURL=tmpDirectoryManager.js.map