@atomist/automation-client
Version:
Atomist API for software low-level client
156 lines • 6.23 kB
JavaScript
;
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