"use strict"; import { join } from "path"; import { exists, readFile, writeFileSync } from "fs"; import { mkdirsSync, remove } from "fs-extra"; import { parallel, queue } from "async"; import * as JSONParse from "json-parse-safe"; import { gitLoader } from "./git/loader"; import { processTask } from "./task-processor"; import { writeReport } from "./report-processor"; import { send as sendMail } from "./mail-sender"; import settings from "../settings"; const codePostfix = ""; const mailLazinessLevel = 1000; const maxDescriptionLength = 140; const maxTmpcodepathLength = 15; const twoDigits = 100; const createFinalState = (isSuccess) => { if (isSuccess) { return "success"; } return "error"; }; const createBuildDoneMessage = (isSuccess, name) => { if (isSuccess) { return `Successfully built ${name}`; } return `Build failed for ${name}`; }; const notifyStatus = (options, notifyStatusCallback) => { const status = { "description": String(options.description || "").substr(0, maxDescriptionLength), "owner": options.owner, "repo": options.reponame, "sha": options.hash, "state": options.state, "target_url": `${settings.siteRoot}status/${options.owner}/${options.reponame}/${options.hash}` }; settings.createGithub(options.owner).repos.createStatus(status, (createStatusErr) => { if (createStatusErr) { console.log(`Error while creating status: ${createStatusErr}`); console.log(status); return notifyStatusCallback(createStatusErr); } return notifyStatusCallback(); }); }; const wrapGitLoader = (skipGitLoader) => { if (!skipGitLoader) { return gitLoader; } return (gitLoaderOptions, gitLoaderCallback) => process.nextTick(gitLoaderCallback); }; export const build = (options, buildCallback) => { const url = options.url; const owner = options.owner; const reponame = options.reponame; const rev = options.rev; const branch = options.branch; const skipGitLoader = options.skipGitLoader; const local = join(options.app.get("gitpath"), "r"); const tmp = join(options.app.get("tmpcodepath"), rev.substr(0, maxTmpcodepathLength)); const exported = tmp + codePostfix; const release = join(options.app.get("releasepath"), owner, reponame, branch, rev); const statusQueue = queue((task: (callback: any) => void, queueCallback) => task(queueCallback), 1); const actualGitLoader = wrapGitLoader(skipGitLoader); const date = new Date(); const versionMajor = date.getFullYear(); const versionMinor = date.getMonth() + 1; const versionBuild = date.getDate(); const versionRev = (date.getHours() * twoDigits) + date.getMinutes(); const version = `${versionMajor}.${versionMinor}.${versionBuild}.${versionRev}`; const versionInfo = `${version}; built from ${rev}; repository: ${owner}/${reponame}; branch: ${branch}`; statusQueue.push((queueCallback) => notifyStatus({ "description": "Preparing to build...", "hash": rev, owner, reponame, "state": "pending" }, queueCallback)); mkdirsSync(release); writeFileSync(join(options.app.get("releasepath"), owner, reponame, branch, "latest.id"), rev); mkdirsSync(join(options.app.get("releasepath"), owner, reponame, "$revs")); writeFileSync(join(options.app.get("releasepath"), owner, reponame, "$revs", `${rev}.branch`), branch); const createErrorMessageForMail = (doneErr) => { if (!doneErr) { return ""; } return `Error message: ${doneErr}\r\n\r\n`; }; const createResultMessageForMail = (result) => { if (!result || !result.messages || !result.messages.$allMessages) { return JSON.stringify(result, null, " "); } return result.messages.$allMessages.map((msg) => `${msg.prefix}\t${msg.message}`).join("\r\n"); }; const done = (doneErr, result?) => { const allErrors = ((result || {}).errors || {}).$allMessages || []; const allWarns = ((result || {}).warns || {}).$allMessages || []; const allInfos = ((result || {}).infos || {}).$allMessages || []; const errorMessage = (allErrors[0] || {}).message || doneErr; const warnMessage = (allWarns[0] || {}).message; const infoMessage = (allInfos[allInfos.length - 1] || {}).message; writeReport(release, doneErr, result, (writeErr) => { statusQueue.push((queueCallback) => parallel([ (parallelCallback) => notifyStatus({ "description": errorMessage || warnMessage || infoMessage || "Success", "hash": rev, owner, reponame, "state": createFinalState(!doneErr) }, parallelCallback), (parallelCallback) => sendMail({ "from": settings.smtp.sender, "headers": { "X-Laziness-level": mailLazinessLevel }, "subject": createBuildDoneMessage(doneErr, `${owner}/${reponame}/${branch}`), "text": `Build status URL: ${settings.siteRoot}status/${owner}/${reponame}/${rev}\r\n\r\n${createErrorMessageForMail(doneErr)}${createResultMessageForMail(result)}`, "to": settings.smtp.receiver }, parallelCallback), (parallelCallback) => { if (doneErr) { return process.nextTick(parallelCallback); } return remove(tmp, parallelCallback); } ], queueCallback)); if (writeErr) { return buildCallback(writeErr); } return buildCallback(doneErr, result); }); }; actualGitLoader({ branch, exported, "hash": rev, local, "remote": `${url}.git` }, (gitLoaderErr) => { if (gitLoaderErr) { console.log(gitLoaderErr); return done(`Git fetch error: ${gitLoaderErr}`); } console.log("Done loading from git"); return exists(join(exported, "mbs.json"), (exists) => { if (!exists) { return done(null, "MBSNotFound"); } return readFile(join(exported, "mbs.json"), (readErr, data) => { if (readErr) { return done(readErr, "MBSUnableToRead"); } const { value, error } = JSONParse(data); if (error) { console.log(`Malformed data: ${data}`); return done(error, "MBSMalformed"); } return processTask(value, { branch, exported, owner, release, reponame, rev, tmp, versionInfo }, (processErr, result) => { if (processErr) { return done(processErr, result); } return done(processErr, result); }); }); }); }); };