Skip to content

Commit

Permalink
Replace all Promise with async function, & standardlize codes
Browse files Browse the repository at this point in the history
  • Loading branch information
ColinTree committed Mar 6, 2019
1 parent 076271c commit 9b8754c
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 160 deletions.
140 changes: 62 additions & 78 deletions src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ import exec, { ExecError } from "./utils/exec";
import { WORKSPACE, TEMP_DIR, BUILDER_CONFIG_NAME, OUTPUT_DIR } from "./config";
import Queue from "./utils/queue";

class BuildQueue extends Queue<string> {
constructor() { super(); }
public push(jobId: string) {
super.push(jobId);
console.timeLog("Added job(" + jobId + ")");
Builder.notify();
}
}
export class JobPool {
private static pool = new Map<string, Job>();
public static add(job: Job) {
Expand All @@ -25,26 +17,7 @@ export class JobPool {
return JobPool.pool.has(jobId);
}
}
const buildQueue = new BuildQueue();

export function addBuildQueue(job: Job) {
console.timeLog("Job(" + job.id + ") going to be added into build queue");
// job will be added to build queue by itself after it is ready (in constructor)
job.status = JobStatus.waiting;
buildQueue.push(job.id);
}

export enum BuildType {
"github-repo" = "github-repo",
"source-upload" = "source-upload"
}
export enum JobStatus {
preparing = "preparing",
waiting = "waiting",
building = "building",
done = "done",
failed = "failed"
}
type JobStatus = "preparing" | "waiting" | "building" | "done" | "failed";
export class Job {
private _id: string;
private _extraInfo: { [key: string]: string | number } = {};
Expand All @@ -55,11 +28,11 @@ export class Job {
public status: JobStatus;

public constructor() {
// fs.mkdtempSync(TEMP_DIR) => {TEMP_DIR}/{jobId}
fs.ensureDirSync(TEMP_DIR);
let jobDir = fs.mkdtempSync(TEMP_DIR + "/");
this._id = jobDir.substring(jobDir.lastIndexOf("/") + 1);
this._extraInfo.startTimestamp = Date.now();
this.status = JobStatus.preparing;
this.status = "preparing";
this.attachInfo("startTimestamp", Date.now());
JobPool.add(this);
}

Expand All @@ -68,64 +41,75 @@ export class Job {
}
}

class BuildQueue {
private static queue = new Queue<string>();
public static push(jobId: string) {
BuildQueue.queue.push(jobId);
console.timeLog("Added job(" + jobId + ")");
Builder.notify();
}
public static isEmpty() {
return BuildQueue.queue.isEmpty();
}
public static pop() {
return BuildQueue.queue.pop();
}
}
export function pushBuildQueue(job: Job) {
job.status = "waiting";
BuildQueue.push(job.id);
}

class Builder {
private static builderAvailable = true;

public static notify() {
if (Builder.builderAvailable && !buildQueue.isEmpty()) {
public static async notify() {
if (Builder.builderAvailable && !BuildQueue.isEmpty()) {
Builder.builderAvailable = false;
let jobId = buildQueue.pop();
JobPool.get(jobId).status = JobStatus.building;
Builder.cleanWorkspace()
.then(() => Builder.buildJob(jobId));
let jobId = BuildQueue.pop();
JobPool.get(jobId).status = "building";
Builder.buildJob(jobId);
}
}
private static cleanWorkspace() {
return new Promise<void>(resolve => {
exec("cd " + WORKSPACE + " && git reset --hard HEAD && git clean -f")
.then(stdout => {
console.timeLog("Workspace cleaned");
resolve();
});
})
}
private static async buildJob(jobId: string) {
await exec("cd " + WORKSPACE + " && git reset --hard HEAD && git clean -f");
console.timeLog("Workspace cleaned");

let job = JobPool.get(jobId);
console.timeLog("Going to build job(" + jobId + ")");
let config = JSON.parse(fs.readFileSync(TEMP_DIR + "/" + jobId + "/src/" + BUILDER_CONFIG_NAME, "utf8"));
let targetPath = WORKSPACE + "/appinventor/components/src/" + config.package.split(".").join("/") + "/";
fs.ensureDirSync(targetPath);
fs.emptyDirSync(targetPath);
fs.copySync(TEMP_DIR + "/" + jobId + "/src/", targetPath);
console.log("Copied: " + targetPath);

console.log("Compile started: job(" + jobId + ")");
try {
let job = JobPool.get(jobId);
console.timeLog("Going to build job(" + jobId + ")");
let config = JSON.parse(fs.readFileSync(TEMP_DIR + "/" + jobId + "/src/" + BUILDER_CONFIG_NAME, "utf8"));
let targetPath = WORKSPACE + "/appinventor/components/src/" + config.package.split(".").join("/") + "/";
fs.ensureDirSync(targetPath);
fs.emptyDirSync(targetPath);
fs.copySync(TEMP_DIR + "/" + jobId + "/src/", targetPath);
console.log("Copied: " + targetPath);
console.log("Compile started: job(" + jobId + ")");

await exec("cd " + WORKSPACE + "/appinventor && ant extensions", true);

let zip = new AdmZip();
zip.addLocalFolder(WORKSPACE + "/appinventor/components/build/extensions");
zip.addFile("build-info.json", new Buffer(JSON.stringify(job.extraInfo)));
let zipPath = OUTPUT_DIR + "/" + jobId + ".zip";
zip.writeZip(zipPath);
JobPool.get(jobId).status = JobStatus.done;
console.log("Done job(" + jobId + "): " + zipPath);
Builder.builderAvailable = true;
Builder.notify();
} catch (reason) {
JobPool.get(jobId).status = JobStatus.failed;
if (reason instanceof ExecError) {
let err = <ExecError> reason;
// Notice that it would not work on windows
let stdout = err.stdout.split(WORKSPACE).join("%SERVER_WORKSPACE%/");
let stderr = err.stderr.split(WORKSPACE).join("%SERVER_WORKSPACE%/");
JobPool.get(jobId).attachInfo("failInfo", err.message + ": code(" + err.code + ") stdout:\n" + stdout + "\n\nstderr:\n" + stderr);
} else {
JobPool.get(jobId).attachInfo("failInfo", reason);
}
console.log("Job(" + jobId + ") build failed", reason);
} catch (e) {
e = <ExecError> e;
JobPool.get(jobId).status = "failed";
// Notice that it would not work on windows
let stdout = e.stdout.split(WORKSPACE).join("%SERVER_WORKSPACE%/");
let stderr = e.stderr.split(WORKSPACE).join("%SERVER_WORKSPACE%/");
JobPool.get(jobId).attachInfo("failInfo",
e.message + ": code(" + e.code + ") stdout:\n" + stdout + "\n\nstderr:\n" + stderr);
console.log("Job(" + jobId + ") build failed in part of `ant extensions`");
Builder.builderAvailable = true;
Builder.notify();
}

let zip = new AdmZip();
zip.addLocalFolder(WORKSPACE + "/appinventor/components/build/extensions");
zip.addFile("build-info.json", new Buffer(JSON.stringify(job.extraInfo)));
let zipPath = OUTPUT_DIR + "/" + jobId + ".zip";
zip.writeZip(zipPath);

JobPool.get(jobId).status = "done";
console.log("Done job(" + jobId + "): " + zipPath);

Builder.builderAvailable = true;
Builder.notify();
}
}
76 changes: 37 additions & 39 deletions src/pages/build-with-github-repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { URLSearchParams } from "url";

import { ENABLE_REPO_WHITELIST, TEMP_DIR, inWhitelist } from "../config";
import { responseSuccess, responseError } from "../index";
import { addBuildQueue, Job, JobPool, BuildType, JobStatus } from "../builder";
import { pushBuildQueue, Job, JobPool } from "../builder";

export default (request: IncomingMessage, response: ServerResponse, params: URLSearchParams) => {
if (request.method == "GET") {
Expand All @@ -23,8 +23,7 @@ export default (request: IncomingMessage, response: ServerResponse, params: URLS
return;
}

} else if (request.method == "POST") {
// webhook
} else if (request.method == "POST") { // webhook
let indexOfEvent = request.rawHeaders.indexOf("X-GitHub-Event");
let event: string;
if (indexOfEvent != -1 && (indexOfEvent + 1) < request.rawHeaders.length) {
Expand Down Expand Up @@ -68,11 +67,11 @@ export default (request: IncomingMessage, response: ServerResponse, params: URLS
}
}

function startGithubJob(response: ServerResponse, owner: string, repoName: string, ref: string) {
async function startGithubJob(response: ServerResponse, owner: string, repoName: string, ref: string) {
let job = new Job();
let jobId = job.id;

job.attachInfo("buildType", BuildType["github-repo"]);
job.attachInfo("buildType", "github-repo");
job.attachInfo("owner", owner);
job.attachInfo("repoName", repoName);
job.attachInfo("ref", ref);
Expand All @@ -82,38 +81,37 @@ function startGithubJob(response: ServerResponse, owner: string, repoName: strin
jobId: jobId
});

// Downlaod archieve
new Github().repos.getArchiveLink({
owner: owner,
repo: repoName,
archive_format: "zipball",
ref: ref
})
.then(archieveResponse => new Promise<string>((resolve, reject) => {
let zip = new Admzip(<Buffer> archieveResponse.data);
if (zip.getEntries().length == 0) {
reject("No source found in archieve downloaded.");
return;
}
let entryDir = zip.getEntries()[0].entryName;
zip.extractAllTo(TEMP_DIR + "/" + jobId + "/rawComponentSource/");
fs.moveSync(TEMP_DIR + "/" + jobId + "/rawComponentSource/" + entryDir,
TEMP_DIR + "/" + jobId + "/src");
fs.rmdirSync(TEMP_DIR + "/" + jobId + "/rawComponentSource");
console.timeLog("Source extracted to " + TEMP_DIR + "/" + jobId + "/src");
addBuildQueue(job);
}))
.catch(reason => {
JobPool.get(jobId).status = JobStatus.failed;
if (Object.getPrototypeOf(reason) == Error.prototype) {
let err = <Error> reason;
JobPool.get(jobId).attachInfo("failInfo",
err.name == "HttpError"
? "Cannot load source from github, please check if the ref exists in the specified repo."
: err.message);
} else {
JobPool.get(jobId).attachInfo("failInfo", reason.toString());
}
console.log("Fail prepare source of job(" + jobId + ")", reason);
});
// Downlaod archive
let archiveResponse: Github.Response<Github.ReposGetArchiveLinkResponse>;
try {
archiveResponse = await new Github().repos.getArchiveLink({
owner: owner,
repo: repoName,
archive_format: "zipball",
ref: ref
});
} catch (e) {
let err = <Error> e;
JobPool.get(jobId).status = "failed";
JobPool.get(jobId).attachInfo("failInfo",
err.name == "HttpError"
? "Cannot load source from github, please check if the ref exists in the specified repo."
: err.message);
console.log("Fail prepare source of job(" + jobId + ")", e);
return;
}

// Extract responding archive
let zip = new Admzip(<Buffer> archiveResponse.data);
if (zip.getEntries().length == 0) {
throw "No source found in archive downloaded.";
}
let entryDir = zip.getEntries()[0].entryName;
zip.extractAllTo(TEMP_DIR + "/" + jobId + "/rawComponentSource/");
fs.moveSync(TEMP_DIR + "/" + jobId + "/rawComponentSource/" + entryDir,
TEMP_DIR + "/" + jobId + "/src");
fs.rmdirSync(TEMP_DIR + "/" + jobId + "/rawComponentSource");
console.timeLog("Source extracted to " + TEMP_DIR + "/" + jobId + "/src");

pushBuildQueue(job);
}
8 changes: 3 additions & 5 deletions src/pages/build-with-zip.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as fs from "fs-extra";
import * as AdmZip from "adm-zip";
import * as formidable from "formidable"

Expand All @@ -7,17 +6,16 @@ import { URLSearchParams } from "url";

import { responseError, responseSuccess } from "../index";
import { ENABLE_REPO_WHITELIST, TEMP_DIR } from "../config";
import { Job, addBuildQueue, BuildType } from "../builder";
import { Job, pushBuildQueue } from "../builder";

export default (request: IncomingMessage, response: ServerResponse, params: URLSearchParams) => {
if (ENABLE_REPO_WHITELIST) {
responseError(response, 403, "Currently in white list mode, build with zip is disabled");

} else {
let job = new Job();
job.attachInfo("buildType", "source-upload");

job.attachInfo("buildType", BuildType["source-upload"]);

let type = request.headers["content-type"] || "";
if (!type.includes("multipart/form-data")) {
console.log("Request content type: " + type);
Expand All @@ -35,7 +33,7 @@ export default (request: IncomingMessage, response: ServerResponse, params: URLS
}
let zip = new AdmZip(file.path);
zip.extractAllTo(jobDir + "/src/");
addBuildQueue(job);
pushBuildQueue(job);
responseSuccess(response, {
msg: "Job added.",
jobId: job.id
Expand Down
6 changes: 3 additions & 3 deletions src/pages/check-status.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as fs from "fs-extra";
import { IncomingMessage, ServerResponse } from "http";
import { URLSearchParams } from "url";

import { responseSuccess, responseError } from "../index";
import { JobPool, JobStatus } from "../builder";
import * as fs from "fs-extra";
import { JobPool } from "../builder";
import { OUTPUT_DIR } from "../config";

export default (request: IncomingMessage, response: ServerResponse, params: URLSearchParams) => {
Expand All @@ -20,7 +20,7 @@ export default (request: IncomingMessage, response: ServerResponse, params: URLS
ret[key] = job.extraInfo[key];
}
} else {
ret.status = JobStatus.done;
ret.status = "done";
}
responseSuccess(response, ret);
}
6 changes: 3 additions & 3 deletions src/pages/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as fs from "fs-extra";
import { IncomingMessage, ServerResponse } from "http";
import { URLSearchParams } from "url";

import { JobPool, JobStatus } from "../builder";
import { JobPool } from "../builder";
import { OUTPUT_DIR } from "../config";

export default (request: IncomingMessage, response: ServerResponse, params: URLSearchParams) => {
Expand All @@ -14,8 +14,8 @@ export default (request: IncomingMessage, response: ServerResponse, params: URLS
response.end("Job does not exist.");
return;
}
let status = JobPool.has(jobId) ? JobPool.get(jobId).status : JobStatus.done;
if (status != JobStatus.done) {
let status = JobPool.has(jobId) ? JobPool.get(jobId).status : "done";
if (status != "done") {
console.log("Response end with 404 job is not ready yet");
response.writeHead(404);
response.end("Job not ready yet.");
Expand Down
Loading

0 comments on commit 9b8754c

Please sign in to comment.