@nrwl/insights
Version:
Nx Insights plugin for Nx
232 lines • 8.36 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const rxjs_1 = require("rxjs");
const tasks_runner_v2_1 = require("@nrwl/workspace/src/tasks-runner/tasks-runner-v2");
const fs = require("fs");
const fs_1 = require("fs");
const path = require("path");
const uuid_1 = require("uuid");
const operators_1 = require("rxjs/operators");
const axios = require('axios');
const tar = require('tar');
class InsightsApi {
constructor(baseUrl, authToken) {
this.uniqRunId = uuid_1.v4();
const [_, access] = new Buffer(authToken, 'base64').toString().split('|');
this.isReadWrite = access === 'read-write';
this.axiosInstance = axios.create({
baseURL: baseUrl,
timeout: 30000,
headers: { authorization: authToken }
});
}
startTask(task) {
return this.axiosInstance.post('/nx-insights/tasks/start', {
taskId: `${task.id}-${this.uniqRunId}`,
startTime: new Date().toISOString(),
target: task.target.target,
projectName: task.target.project,
hash: task.hash
});
}
endTask(task) {
return this.axiosInstance.post('/nx-insights/tasks/end', {
taskId: `${task.id}-${this.uniqRunId}`,
endTime: new Date().toISOString()
});
}
storeCache(hash) {
return __awaiter(this, void 0, void 0, function* () {
const resp = yield this.axiosInstance({
method: 'get',
url: `/nx-insights/cache/store/${hash}`
});
if (!resp.data || !resp.data.url || !(typeof resp.data.url === 'string')) {
throw new Error(`Invalid remote cache response: ${JSON.stringify(resp.data)}`);
}
return resp.data.url;
});
}
retrieveCache(hash) {
return __awaiter(this, void 0, void 0, function* () {
const resp = yield this.axiosInstance({
method: 'get',
url: `/nx-insights/cache/${hash}`
});
if (!resp.data || !resp.data.url || !(typeof resp.data.url === 'string')) {
throw new Error(`Invalid remote cache response: ${JSON.stringify(resp.data)}`);
}
return resp.data.url;
});
}
}
class InsightsRemoteCache {
constructor(api) {
this.api = api;
}
retrieve(hash, cacheDirectory) {
return __awaiter(this, void 0, void 0, function* () {
try {
const url = yield this.api.retrieveCache(hash);
const tgz = this.createFileName(hash, cacheDirectory);
yield this.downloadFile(url, tgz);
this.createCommitFile(hash, cacheDirectory);
return true;
}
catch (e) {
if (e.response && e.response.status === 404) {
// cache miss. print nothing
}
else {
this.printErrorMessage(e);
}
return false;
}
});
}
store(hash, cacheDirectory) {
return __awaiter(this, void 0, void 0, function* () {
if (this.api.isReadWrite) {
try {
const url = yield this.api.storeCache(hash);
const tgz = yield this.createFile(hash, cacheDirectory);
yield this.uploadFile(url, tgz);
return true;
}
catch (e) {
this.printErrorMessage(e);
return false;
}
}
else {
return true;
}
});
}
printErrorMessage(e) {
if (e.code === 'ECONNREFUSED' ||
e.code === 'EAI_AGAIN' ||
e.code === 'ENOTFOUND') {
console.error(`Error: Cannot connect to remote cache.`);
}
else if (e.response.status === 401) {
console.error(`Error: Invalid Nx Insights access token.`);
}
else {
console.error(e.message);
}
}
createFileName(hash, cacheDirectory) {
return path.join(cacheDirectory, `${hash}.tar.gz`);
}
downloadFile(url, tgz) {
return __awaiter(this, void 0, void 0, function* () {
const resp = yield axios(url, {
method: 'GET',
responseType: 'stream',
maxContentLength: 1000 * 1000 * 200
});
const q = resp.data.pipe(tar.x({
cwd: path.dirname(tgz)
}));
return new Promise(res => {
q.on('close', () => {
res();
});
});
});
}
createCommitFile(hash, cacheDirectory) {
fs_1.writeFileSync(path.join(cacheDirectory, `${hash}.commit`), 'true');
}
createFile(hash, cacheDirectory) {
return __awaiter(this, void 0, void 0, function* () {
const tgz = this.createFileName(hash, cacheDirectory);
yield tar.c({
gzip: true,
file: tgz,
cwd: cacheDirectory
}, [hash]);
return tgz;
});
}
uploadFile(url, tgz) {
return __awaiter(this, void 0, void 0, function* () {
yield axios(url, {
method: 'PUT',
data: fs.readFileSync(tgz),
headers: { 'Content-Type': 'application/octet-stream' },
maxContentLength: 1000 * 1000 * 200
});
});
}
}
const insightsTaskRunner = (tasks, options, context) => {
if (process.env.NX_INSIGHTS_AUTH_TOKEN || options.accessToken) {
const api = createApi(options);
const lifeCycle = new TaskRunnerLifeCycle(api);
const remoteCache = new InsightsRemoteCache(api);
const res = tasks_runner_v2_1.tasksRunnerV2(tasks, Object.assign({}, options, { remoteCache, lifeCycle }), context);
return lifeCycle.complete().pipe(operators_1.concatMap(() => res));
}
else {
return tasks_runner_v2_1.tasksRunnerV2(tasks, options, context);
}
};
class TaskRunnerLifeCycle {
constructor(api) {
this.api = api;
this.promises = {};
this.errors = [];
}
startTask(task) {
if (this.api.isReadWrite) {
this.promises[`${task.id}-start`] = this.api.startTask(task).catch(e => {
this.errors.push(e.message);
});
}
}
endTask(task, code) {
if (this.api.isReadWrite) {
const endPromise = this.promises[`${task.id}-start`]
.then(() => {
return this.api.endTask(task);
})
.catch((e) => {
this.errors.push(e.message);
});
this.promises[`${task.id}-end`] = endPromise;
}
}
complete() {
return rxjs_1.from(Promise.all(Object.values(this.promises)).then(() => {
if (this.errors.length > 0) {
this.printNxInsightsStatus();
}
}));
}
printNxInsightsStatus() {
console.warn(`We were unable to send the data to Nx Insights:`);
console.error(`Errors:`);
console.error(this.errors[0]);
}
}
function createApi(options) {
const baseUrl = options.insightsUrl || 'https://nrwl.api.io';
if (process.env.NX_INSIGHTS_AUTH_TOKEN) {
return new InsightsApi(baseUrl, process.env.NX_INSIGHTS_AUTH_TOKEN);
}
else {
return new InsightsApi(baseUrl, options.accessToken);
}
}
exports.default = insightsTaskRunner;
//# sourceMappingURL=insights-task-runner.js.map