You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
6.2 KiB
199 lines
6.2 KiB
"use strict";
|
|
|
|
import { join } from "path";
|
|
import { createReadStream, createWriteStream, exists } from "fs";
|
|
import { createGzip, createGunzip } from "zlib";
|
|
import * as glob from "glob";
|
|
import { ReadableStreamBuffer, WritableStreamBuffer } from "stream-buffers";
|
|
import * as _ from "underscore";
|
|
import * as JSONParse from "json-parse-safe";
|
|
|
|
interface Message {
|
|
message: string;
|
|
prefix: string;
|
|
};
|
|
|
|
interface PartialMessagesLeaf {
|
|
$messages: string[];
|
|
};
|
|
|
|
interface PartialMessagesRecursive {
|
|
[propName: string]: Messages;
|
|
};
|
|
|
|
interface PartialMessagesRoot {
|
|
$allMessages: Message[];
|
|
};
|
|
|
|
type Messages = PartialMessagesLeaf & PartialMessagesRecursive;
|
|
|
|
type MessagesRoot = PartialMessagesLeaf & PartialMessagesRecursive & PartialMessagesRoot;
|
|
|
|
interface ReportResult {
|
|
errors: MessagesRoot;
|
|
warns: MessagesRoot;
|
|
infos: MessagesRoot;
|
|
messages: MessagesRoot;
|
|
};
|
|
|
|
interface Report {
|
|
date: number;
|
|
err?: string;
|
|
result?: ReportResult;
|
|
};
|
|
|
|
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, callback) => {
|
|
const data = JSON.stringify({
|
|
"date": Date.now(),
|
|
err,
|
|
result
|
|
});
|
|
|
|
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) {
|
|
return callback(_.map(
|
|
errors, (message) => `ERR: ${message.message}`
|
|
).concat(_.map(
|
|
warns, (message) => `WARN: ${message.message}`
|
|
))
|
|
.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);
|
|
});
|
|
};
|
|
|