|
|
|
"use strict";
|
|
|
|
|
|
|
|
import { createReadStream, createWriteStream, exists } from "fs";
|
|
|
|
import * as glob from "glob";
|
|
|
|
import * as JSONParse from "json-parse-safe";
|
|
|
|
import { join } from "path";
|
|
|
|
import { ReadableStreamBuffer, WritableStreamBuffer } from "stream-buffers";
|
|
|
|
import * as _ from "underscore";
|
|
|
|
import { createGunzip, createGzip } from "zlib";
|
|
|
|
|
|
|
|
const reportFilename = "report.json.gz";
|
|
|
|
const maxAttemptsNumber = 100;
|
|
|
|
const attemptsTimeout = 30000;
|
|
|
|
const reportReadTimeout = 5000;
|
|
|
|
const directoryCheckTimeout = 2000;
|
|
|
|
const attemptsDebugFrequency = 10;
|
|
|
|
|
|
|
|
const readableStreamBufferOptions = {
|
|
|
|
chunkSize: 262144,
|
|
|
|
frequency: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
const getAllErrors = (report: Report): Message[] => (report.result && report.result.errors && report.result.errors.$allMessages) || [];
|
|
|
|
const getAllWarns = (report: Report): Message[] => (report.result && report.result.warns && report.result.errors.$allMessages) || [];
|
|
|
|
const getAllInfos = (report: Report): Message[] => (report.result && report.result.infos && report.result.errors.$allMessages) || [];
|
|
|
|
|
|
|
|
export const writeReport = (releaseDir, err, result: ReportResult | undefined, callback) => {
|
|
|
|
const data = JSON.stringify({
|
|
|
|
date: Date.now(),
|
|
|
|
err,
|
|
|
|
result,
|
|
|
|
} as Report);
|
|
|
|
|
|
|
|
const readable = new ReadableStreamBuffer(readableStreamBufferOptions);
|
|
|
|
const writeStream = createWriteStream(join(releaseDir, reportFilename));
|
|
|
|
|
|
|
|
readable
|
|
|
|
.on("error", callback)
|
|
|
|
.pipe(createGzip())
|
|
|
|
.on("error", callback)
|
|
|
|
.pipe(writeStream)
|
|
|
|
.on("error", callback)
|
|
|
|
.on("finish", () => {
|
|
|
|
writeStream.end();
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
|
|
|
|
readable.put(data);
|
|
|
|
readable.stop();
|
|
|
|
};
|
|
|
|
|
|
|
|
export const readReport = (releaseDir, callback) => {
|
|
|
|
const readStream = createReadStream(join(releaseDir, reportFilename));
|
|
|
|
const writable = new WritableStreamBuffer();
|
|
|
|
|
|
|
|
readStream
|
|
|
|
.on("error", callback)
|
|
|
|
.pipe(createGunzip())
|
|
|
|
.on("error", callback)
|
|
|
|
.pipe(writable)
|
|
|
|
.on("error", callback)
|
|
|
|
.on("finish", () => {
|
|
|
|
readStream.destroy();
|
|
|
|
|
|
|
|
const data = writable.getContentsAsString();
|
|
|
|
if (!data) {
|
|
|
|
return callback("ReportFileNotFound");
|
|
|
|
}
|
|
|
|
|
|
|
|
const { error, value }: { error: any, value?: Report } = JSONParse(data);
|
|
|
|
if (error) {
|
|
|
|
return callback("ReportFileMalformed");
|
|
|
|
}
|
|
|
|
|
|
|
|
return callback(null, value);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
export const loadReport = (app, options, callback) => {
|
|
|
|
const releaseDir = join(app.get("releasepath"), options.owner, options.reponame, options.branch, options.rev);
|
|
|
|
|
|
|
|
glob("**", {
|
|
|
|
cwd: releaseDir,
|
|
|
|
mark: true,
|
|
|
|
}, (err, files) => {
|
|
|
|
if (err) {
|
|
|
|
return callback(err, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
const reportFile = join(releaseDir, reportFilename);
|
|
|
|
|
|
|
|
return exists(reportFile, (reportFileExists) => {
|
|
|
|
if (!reportFileExists) {
|
|
|
|
return callback("ReportFileNotFound", options);
|
|
|
|
}
|
|
|
|
|
|
|
|
return readReport(releaseDir, (readErr, report) => {
|
|
|
|
if (readErr) {
|
|
|
|
return callback(readErr, _.extend(options, { files }));
|
|
|
|
}
|
|
|
|
|
|
|
|
return callback(null, _.extend(options, {
|
|
|
|
files,
|
|
|
|
report,
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
export const getStatusMessageFromRelease = (app, originalOptions, callback) => {
|
|
|
|
const options = _.extend(originalOptions, { attemptsGetReport: (Number(originalOptions.attemptsGetReport) || Number()) + 1 });
|
|
|
|
const releaseDir = join(app.get("releasepath"), options.owner, options.reponame, options.branch, options.rev);
|
|
|
|
const reportFile = join(releaseDir, reportFilename);
|
|
|
|
|
|
|
|
exists(reportFile, (reportFileExists) => {
|
|
|
|
if (!reportFileExists) {
|
|
|
|
return setTimeout(() => exists(releaseDir, (dirExists) => {
|
|
|
|
if (!dirExists) {
|
|
|
|
return callback("Release directory not found. Probably repository hooks are not configured");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.attemptsGetReport > maxAttemptsNumber) {
|
|
|
|
return callback("Report file not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Maybe it is building right now
|
|
|
|
if (!(options.attemptsGetReport % attemptsDebugFrequency) && options.onTenthAttempt) {
|
|
|
|
options.onTenthAttempt();
|
|
|
|
}
|
|
|
|
|
|
|
|
return setTimeout(() => exports.getStatusMessageFromRelease(app, options, callback), attemptsTimeout);
|
|
|
|
}), directoryCheckTimeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
return setTimeout(() => readReport(releaseDir, (readErr, report) => {
|
|
|
|
if (readErr) {
|
|
|
|
return callback(readErr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (report.result === "MBSNotFound") {
|
|
|
|
return callback("mbs.json is not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
const errors = getAllErrors(report);
|
|
|
|
const warns = getAllWarns(report);
|
|
|
|
const infos = getAllInfos(report);
|
|
|
|
|
|
|
|
if (errors.length + warns.length) {
|
|
|
|
const formattedErrors = _.map(errors, (message) => `ERR: ${message.message}`);
|
|
|
|
const formattedWarns = _.map(warns, (message) => `WARN: ${message.message}`);
|
|
|
|
|
|
|
|
return callback(formattedErrors.concat(formattedWarns).join("\r\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!report.result || report.err) {
|
|
|
|
return callback(`CRITICAL ERROR: ${report.err}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return callback(null, (infos[infos.length - 1] || { message: "OK" }).message);
|
|
|
|
}), reportReadTimeout);
|
|
|
|
});
|
|
|
|
};
|