Skip to content
This repository has been archived by the owner on Jan 26, 2022. It is now read-only.

Commit

Permalink
pre-deploy checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
nickbradley committed Jan 7, 2017
1 parent 72a12e7 commit d5d3266
Show file tree
Hide file tree
Showing 24 changed files with 898 additions and 358 deletions.
7 changes: 7 additions & 0 deletions database/results/views/grades/byTeamDeliverableCommit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"views": {
"byTeamDeliverableCommit": {
"map": "function(doc) {\n if (doc.team && doc.deliverable && doc.commit) {\n var tStats = doc.testStats;\n emit([doc.team, doc.deliverable.deliverable, doc.commit],\n {\n 'deliverable': doc.deliverable,\n 'exitCode': doc.containerExitCode,\n 'buildFailed': doc.buildFailed,\n 'buildMsg': doc.buildMsg,\n 'grade': +(0.8*tStats.passPercent + 0.2*Math.min(doc.coverStats.lines.percentage+5, 100)).toFixed(2),\n 'testGrade': tStats.passPercent,\n 'testSummary': tStats.passCount + ' passing, ' + tStats.failCount + ' failing, ' + tStats.skipCount + ' skipped',\n 'coverageGrade': doc.coverStats.lines.percentage,\n 'failedTests': doc.testReport.allFailures.map(function(test) {\n var name = test.title;\n var code = name.substring(name.indexOf('~')+1, name.lastIndexOf('~'));\n return code + ': ' + name.substring(name.lastIndexOf('~')+1, name.indexOf('.')+1);\n })\n }\n )\n }\n }"
}
}
}
28 changes: 28 additions & 0 deletions dbconfig.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,34 @@ curl -X PUT ${DB_INSTANCE}/settings/_design/current \
}
}'

curl -X PUT ${DB_INSTANCE}/requests/_design/latest \
-H "Content-Type: application/json" \
-d '{
"_id": "_design/latest",
"views": {
"byUserDeliverable": {
"map": "function(doc) {\n if (doc.user && doc.deliverable && doc.isRequest && doc.isProcessed) {\n emit([doc.user, doc.deliverable], doc.timestamp); \n} \n}",
"reduce": "function(key, values, rereduce) {\n return Math.max.apply(null, values); \n}"
}
}
}'

curl -X PUT ${DB_INSTANCE}/results/_design/default \
-H "Content-Type: application/json" \
-d '{
"_id": "_design/default",
"views": {
"byTeamCommitDeliverable": {
"map": "function(doc) {\n if (doc.team && doc.deliverable && doc.commit) {\n emit([doc.team, doc.deliverable.deliverable, doc.commit], {\"visibility\":doc.deliverable.visibility, \"containerExitCode\":doc.containerExitCode, \"buildFailed\":doc.buildFailed, \"buildMsg\":doc.buildMsg, \"testStats\":doc.testStats, \"coverageStats\":doc.coverStats, \"testReport\":doc.testReport, \"timestamp\":doc.timestamp}); \n} \n}"
}
}
}'


curl -X PUT ${DB_INSTANCE}/results/_design/grades \
-H "Content-Type: application/json" \
-d @./database/results/views/grades/byTeamDeliverableCommit.json

# Create users
printf "Creating user ${DB_APP_USERNAME} "
curl -X PUT ${DB_INSTANCE}/_users/org.couchdb.user:${DB_APP_USERNAME} \
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
"scripts": {
"config": "npm install; typings install",
"build": "tsc",
"test": "mocha --recursive"
"test": "mocha --recursive",
"initContainer": "docker create --name autotest-redis redis && docker create --name autotest-db -p 11312:5984 -v /usr/local/var/lib/couchdb couchdb",
"dbconfig": "docker start autotest-db && env $(cat autotest.env | xargs) ./dbconfig.sh && docker stop autotest-db",
"start": "docker start autotest-redis && docker start autotest-db && node src/App.js"
},
"devDependencies": {
"@types/node": "^6.0.52",
Expand Down
Empty file removed sampleOutput.md
Empty file.
26 changes: 26 additions & 0 deletions src/App.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Created by rtholmes on 2016-06-19.
*/

import Server from './rest/Server';
import Log from './Util';

/**
* Starts the server; doesn't listen to whether the start was successful.
*/
export class App {
public initServer() {
Log.info('App::initServer() - start');
let s = new Server();
s.start().then(function (val: boolean) {
Log.info("App::initServer() - started: " + val);
}).catch(function (err: Error) {
Log.error("App::initServer() - ERROR: " + err.message);
});
}
}

// This ends up starting the whole system and listens on a hardcoded port (4321)
Log.info('App - starting');
let app = new App();
app.initServer();
1 change: 1 addition & 0 deletions src/controller/TestJobController.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Log from '../Util';
import {JobQueue, ProcessJobCallback, Job,JobOpts} from '../model/JobQueue';
import {IConfig, AppConfig} from '../Config';
import TestController from './TestController';
Expand Down
165 changes: 106 additions & 59 deletions src/controller/github/CommitCommentController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import {DeliverableRecord} from '../../model/settings/DeliverableRecord';

interface GradeSummary {
deliverable: string;
timeoutExceeded: boolean;
exitCode: number;
buildFailed: boolean;
buildMsg: string;
grade: number;
testGrade: number;
testSummary: string;
coverageSummary: string;
failedTests: string[];
Expand All @@ -38,33 +39,60 @@ export default class CommitCommentContoller {
let response: GithubResponse;

await record.process(data);
await that.store(record);

let isAdmin: boolean = await that.isAdmin(record.user);

if (record.isRequest) {
let lastRequest: Date = await that.latestRequest(record.user, record.deliverable);
let diff: number = +new Date() - +lastRequest;
if (diff > record.deliverableRate || isAdmin) {
let resultRaw: GradeSummary = await that.getResult(record.team, record.commit, record.deliverable);
let body: string = that.formatResult(resultRaw);
response = {
statusCode: 200,
body: body

Log.info('CommitCommentContoller::process() - Request OK.');
try {
let result: GradeSummary = await that.getResult(record.team, record.commit, record.deliverable);
let body: string = that.formatResult(result);
response = {
statusCode: 200,
body: body
}
} catch(err) {
Log.error('CommitCommentContoller::process() - No results for request.');
record.isProcessed = false;
response = {
statusCode: 404,
body: 'No results found. Commit may still be queued for processing. Please try again later.'
}
}
} else {
Log.info('CommitCommentContoller::process() - Request rate exceeded.');
let waitTime: number = record.deliverableRate - diff;
record.isProcessed = false;
response = {
statusCode: 429,
body: 'Sorry, you must wait ' + Moment.duration(diff).humanize() + ' before you make another request'
body: 'Sorry, you must wait ' + Moment.duration(waitTime).humanize() + ' before you make another request.'
}
}
await this.postComment(record.hook, response.body);

try {
//await that.postComment(record.hook, response.body);
} catch(err) {
Log.error('CommitCommentContoller::process() - ERROR. Failed to post result. ' + err);
}
} else {
Log.info('CommitCommentContoller::process() - Not request.');
response = {
statusCode: 204,
body: ''
}
}

try {
await that.store(record);
} catch(err) {
Log.error('CommitCommentContoller::process() - ERROR. Failed to store result. ' + err);
}
Log.info('CommitCommentContoller::process() - Request completed with status ' + response.statusCode + '.');

fulfill(response);

} catch(err) {
Expand All @@ -86,7 +114,9 @@ export default class CommitCommentContoller {
return new Promise<boolean>(async (fulfill, reject) => {
try {
let adminRecord: any = await db.readRecord('admins');
let admins: string[] = adminRecord.body;
let admins: string[] = Object.keys(adminRecord).filter(key => {
return !key.startsWith('_');
});
fulfill(admins.includes(user));
} catch(err) {
reject('Failed to retrieve admin list. ' + err);
Expand All @@ -113,16 +143,19 @@ export default class CommitCommentContoller {
let designName: string = 'latest';
let viewName: string = 'byUserDeliverable';
let params: QueryParameters = {
key: {user: user, deliverable: deliverable}
key: [user, deliverable]
};
let that = this;
return new Promise<Date>(async (fulfill, reject) => {
try {
let view: ViewResponse = await db.view(designName, viewName, params);
let latestDate: number = +view.rows[0].value || 0;
fulfill(new Date(latestDate));
let latestDate: Date = new Date(0);
if (view.rows.length > 0) {
latestDate = new Date(+view.rows[0].value)
}
fulfill(latestDate);
} catch(err) {
reject('Failed to get latest request of user ' + user + ' for deliverable ' + deliverable);
reject('Failed to get latest request of user ' + user + ' for deliverable ' + deliverable + '. ' + err);
}
});
}
Expand All @@ -138,60 +171,65 @@ export default class CommitCommentContoller {
*/
private async getResult(team: string, commit: Commit, deliverable: string): Promise<GradeSummary> {
let db = new Database(this.config.getDBConnection(), 'results');
let designName: string = 'default';
let viewName: string = 'byTeamCommitDeliverable';
let designName: string = 'grades';
let viewName: string = 'byTeamDeliverableCommit';
let params: QueryParameters = {
key: {
team: team,
commit: commit.toString(),
deliverable: deliverable
}
key: [team, deliverable, commit.short]
};

let that = this;
return new Promise<GradeSummary>(async (fulfill, reject) => {
try {
let view: ViewResponse = await db.view(designName, viewName, params);
let publicTests = view.rows.filter(obj => {
return obj.value.visibility == 0
});
let privateTests = view.rows.filter(obj => {
return obj.value.visibility == 1
});
let tStats = privateTests.testStats;
let cStats = privateTests.coverageStats;
// console.log('view is ', view);
// let publicTests = view.rows.filter(obj => {
// return obj.value.visibility == 0
// }).map(obj => {
// return obj.value;
// })[0];
// let privateTests = view.rows.filter(obj => {
// return obj.value.visibility == 0
// }).map(obj => {
// return obj.value;
// })[0];

let publicTests = view.rows[0].value;
let privateTests = view.rows[0].value;

let gradeSummary: GradeSummary = {
deliverable: deliverable,
timeoutExceeded: privateTests.timeoutExceeded,
exitCode: privateTests.exitCode,
buildFailed: privateTests.buildFailed,
buildMsg: privateTests.buildMsg,
grade: await that.grade(deliverable, privateTests),
testSummary: tStats.passCount + ' passing, ' + tStats.failCount + ' failing, ' + tStats.skipCount + ' skipped',
coverageSummary: cStats.statements.percentage + ' statements, ' + cStats.branches.percentage + ' branches, ' + cStats.functions.percentage + ' functions, ' + cStats.lines.percentage + ' lines',
failedTests: publicTests.testReport.allFailures.map(test => {
let name: string = test.fullTitle;
let code: string = test.substring(name.indexOf('~'), name.lastIndexOf('~'));
return code + ': ' + name.substring(name.lastIndexOf('~')+1, name.indexOf('.')+1);
})
grade: privateTests.grade,
testGrade: privateTests.testGrade,
testSummary: privateTests.testSummary,
coverageSummary: privateTests.coverageGrade,
failedTests: publicTests.failedTests
}


fulfill(gradeSummary)
} catch(err) {
reject('Unable to get test result for ' + team + ' commit ' + commit.short + '. ' + err);
}
});
}

private async grade(deliverable: string, privateTests) {
let db: Database = new Database(this.config.getDBConnection(), 'settings');
let record: DeliverableRecord = <DeliverableRecord>(await db.readRecord('deliverables')).body;
let gradeFormula: string = record[deliverable];

let gradeExp: string = gradeFormula.replace(
'<TEST_PERCENTAGE>', privateTests.testStats.passPercent
).replace(
'<COVERAGE_PERCENTAGE>', privateTests.coverageStats.statements.percent
);
return Promise.resolve(eval(gradeExp))
}
// private async grade(deliverable: string, privateTests) {
// let resultsDB = new Database(this.config.getDBConnection(), 'settings');
// let deliverablesDoc = await resultsDB.readRecord('deliverables');
// let deliverableRecord: DeliverableRecord = new DeliverableRecord(deliverablesDoc);
// let gradeFormula: string = deliverableRecord.item(deliverable).gradeFormula;
//
// let gradeExp: string = gradeFormula.replace(
// '<TEST_PERCENTAGE>', privateTests.testStats.passPercent
// ).replace(
// '<COVERAGE_PERCENTAGE>', privateTests.coverageStats.lines.percentage
// );
// console.log('gradeExp', gradeExp);
// return Promise.resolve(eval(gradeExp))
// }



Expand All @@ -211,31 +249,40 @@ export default class CommitCommentContoller {
<BUILD_MSG>
\`\`\`
`;
output.replace(
output = output.replace(
'<GRADE>', '0'
).replace(
'<BUILD_MSG>', gradeSummary.buildMsg
);
} else if (gradeSummary.timeoutExceeded) {
} else if (gradeSummary.exitCode == 124) {
output += `
- Timeout exceeded while executing tests.
`;
output.replace('<GRADE>', '0');
} else {
output = output.replace('<GRADE>', '0');
} else if (gradeSummary.exitCode != 0) {
output += `
- Test summary: <TEST_SUMMARY>
- Coverage summary: <COVERAGE_SUMMARY>
- Autotest encountered an error during testing (<EXIT_CODE>).
`;
output.replace(
output = output.replace(
'<GRADE>', '0'
).replace(
'<EXIT_CODE>', gradeSummary.exitCode.toString()
);
} else {
output += '- Test summary: <TEST_GRADE>% (<TEST_SUMMARY>)\n- Line coverage: <COVERAGE_SUMMARY>%';

output = output.replace(
'<GRADE>', gradeSummary.grade.toFixed()
).replace(
'<TEST_GRADE>', gradeSummary.testGrade.toFixed()
).replace(
'<TEST_SUMMARY>', gradeSummary.testSummary
).replace(
'<COVERAGE_SUMMARY>', gradeSummary.coverageSummary
);

if (gradeSummary.grade < 100) {
output += '\nIt failed the proxy tests:';
if (gradeSummary.failedTests.length > 0) {
output += '\n\nIt failed the tests:\n - ';
output += gradeSummary.failedTests.join('\n - ');
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/controller/github/PushController.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Log from '../../Util';
import {IConfig, AppConfig} from '../../Config';
import {Database} from '../../model/Database';
import PushRecord from '../../model/requests/PushRecord';
Expand All @@ -14,6 +15,7 @@ export default class PushController {
}

async process(data: JSON) {
Log.trace('PushController::process()');
try {
let record = new PushRecord(data);
await this.store(record);
Expand Down Expand Up @@ -42,6 +44,7 @@ export default class PushController {
deliverable: key
}
}
Log.info('PushController::process() - ' + record.team +'#'+ record.commit.short + ' enqueued to run against ' + repo.name + '.');
promises.push(this.enqueue(testJob));
}
}
Expand All @@ -50,7 +53,7 @@ export default class PushController {

return Promise.all(promises);
} catch(err) {
throw 'Failed to process Push request. ' + err;
throw 'Failed to process push request. ' + err;
}
}

Expand Down
Loading

0 comments on commit d5d3266

Please sign in to comment.