Skip to content

Commit 7aa45f5

Browse files
committed
Support for short ids
1 parent 0aaa0cc commit 7aa45f5

File tree

6 files changed

+262
-34
lines changed

6 files changed

+262
-34
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
## Modified
99

1010
- removed old dependencies
11-
11+
- support for short ids and branch names for all endpoints
1212

1313
## [2.1.0]
1414

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# MATLAB-ci
22
[![Build Status](https://travis-ci.com/cortex-lab/matlab-ci.svg?branch=master)](https://travis-ci.com/cortex-lab/matlab-ci)
3-
[![Coverage](https://img.shields.io/badge/coverage-72.35-yellowgreen)](https://img.shields.io/badge/coverage-72.35-yellowgreen)
3+
[![Coverage](https://img.shields.io/badge/coverage-81.07-green)](https://img.shields.io/badge/coverage-72.35-yellowgreen)
44

55
A small set of modules written in Node.js for running automated tests of MATLAB code in response to GitHub events. Also submits code coverage to the Coveralls API.
66

77
Currently unsupported:
88
* Running tests on forked repositories
9-
* Testing multiple repos (unless they are submodules)
9+
* Testing multiple repos (unless they are submodules)
1010

1111
## Getting Started
1212

lib.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ function compareCoverage(job) {
330330
}
331331
log('Comparing coverage for %g -> %g', job.data.sha, job.data.base);
332332
var records;
333-
if (!job.coverage) {
333+
if (!job.data.coverage) {
334334
log('No coverage in job data; loading from records');
335335
records = loadTestRecords([job.data.sha, job.data.base]);
336336
// Filter duplicates just in case
@@ -341,7 +341,7 @@ function compareCoverage(job) {
341341
records = [curr, loadTestRecords(job.data.base)];
342342
}
343343
log('The following records were found: %O', records);
344-
const has_coverage = records.every(o => (typeof o.coverage !== 'undefined' && o.coverage > 0));
344+
const hasCoverage = records.every(o => (o.coverage > 0));
345345

346346
// Check if any errored or failed to update coverage
347347
if (records.filter(o => o.status === 'error').length > 0) {
@@ -350,7 +350,7 @@ function compareCoverage(job) {
350350
job.data.description = 'Failed to determine coverage as tests incomplete due to errors';
351351

352352
// Both records present and they have coverage
353-
} else if (records.length === 2 && has_coverage) {
353+
} else if (records.length === 2 && hasCoverage) {
354354
log('Calculating coverage difference');
355355
// Ensure first record is for head commit
356356
if (records[0].commit === job.data.base) { records.reverse() }

serve.js

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,45 @@ srv.post('/github', async (req, res, next) => {
117117

118118
///////////////////// STATUS DETAILS /////////////////////
119119

120+
/**
121+
* Serve the test records for requested commit id. Returns JSON data for the commit.
122+
* @param {string} id - A commit SHA of any length, or branch name.
123+
* @param {boolean|null} [isBranch] - If true, id treated as a branch name. Inferred from id by default.
124+
* @param {string} [module] - (Sub)module name. REPO_NAME by default.
125+
* @return {Promise} - Resolved to full commit SHA.
126+
*/
127+
function fetchCommit(id, isBranch=null, module) {
128+
isBranch = isBranch === null ? !lib.isSHA(id) : isBranch
129+
const data = {
130+
owner: process.env['REPO_OWNER'],
131+
repo: module || process.env.REPO_NAME,
132+
id: id
133+
};
134+
let endpoint = `GET /repos/:owner/:repo/${isBranch ? 'branches': 'commits'}/:id`;
135+
return request(endpoint, data).then(response => {
136+
return isBranch ? response.data.commit.sha : response.data.sha;
137+
});
138+
}
139+
140+
/**
141+
* Parse the short SHA or branch name and redirect to static reports directory.
142+
*/
143+
srv.get(`/coverage/:id`, (req, res) => {
144+
let id = lib.shortID(req.params.id);
145+
let isSHA = (req.query.branch || !lib.isSHA(req.params.id)) === false;
146+
console.log('Request for test coverage for ' + (isSHA? `commit ${id}` : `branch ${req.params.id}`));
147+
fetchCommit(req.params.id, !isSHA, req.query.module)
148+
.then(id => {
149+
log('Commit ID found: %s', id);
150+
res.redirect(301, `/${ENDPOINT}/coverage/${id}`);
151+
})
152+
.catch(err => {
153+
log('%s', err.message);
154+
res.statusCode = 404;
155+
res.send(`Coverage for ${isSHA? 'commit' : 'branch'} ${req.params.id} not found`);
156+
});
157+
})
158+
120159
/**
121160
* Serve the reports tree as a static resource; allows users to inspect the HTML coverage reports.
122161
* We will add a link to the reports in the check details.
@@ -130,23 +169,16 @@ srv.get(`/${ENDPOINT}/records/:id`, function (req, res) {
130169
let id = lib.shortID(req.params.id);
131170
let isSHA = (req.query.branch || !lib.isSHA(req.params.id)) === false;
132171
console.log('Request for test records for ' + (isSHA? `commit ${id}` : `branch ${req.params.id}`));
133-
const data = {
134-
owner: process.env['REPO_OWNER'],
135-
repo: req.query.module || process.env.REPO_NAME,
136-
id: req.params.id
137-
};
138-
let endpoint = `GET /repos/:owner/:repo/${isSHA ? 'commits' : 'branches'}/:id`;
139-
request(endpoint, data)
140-
.then(response => {
141-
let id = isSHA ? response.data.sha : response.data.commit.sha;
172+
fetchCommit(req.params.id, !isSHA, req.query.module)
173+
.then(id => {
142174
log('Commit ID found: %s', id);
143175
let record = lib.loadTestRecords(id);
144176
if (record) {
145177
res.setHeader('Content-Type', 'application/json');
146178
res.end(JSON.stringify(record));
147179
} else {
148180
res.statusCode = 404;
149-
res.send(`${isSHA? 'Commit' : 'Branch'} ${req.params.id} not recognized.`);
181+
res.send(`${isSHA? 'Commit' : 'Branch'} ${id} not recognized.`);
150182
}
151183
})
152184
.catch(err => {
@@ -163,27 +195,35 @@ srv.get(`/${ENDPOINT}/records/:id`, function (req, res) {
163195
*/
164196
srv.get(`/${ENDPOINT}/:id`, function (req, res) {
165197
let id = lib.shortID(req.params.id);
166-
let isSHA = lib.isSHA(req.params.id);
167198
let log_only = (req.query.type || '').startsWith('log')
199+
let isSHA = (req.query.branch || !lib.isSHA(req.params.id)) === false;
168200
console.log(
169201
`Request for test ${log_only ? 'log' : 'stdout'} for ` +
170202
(isSHA? `commit ${id}` : `branch ${req.params.id}`)
171203
);
172-
let filename = log_only? `test_output.log` : `std_output-${id}.log`;
173-
let logFile = path.join(config.dataPath, 'reports', req.params.id, filename);
174-
fs.readFile(logFile, 'utf8', (err, data) => {
175-
if (err) {
204+
fetchCommit(req.params.id, !isSHA, req.query.module)
205+
.then(id => {
206+
let filename = log_only? `test_output.log` : `std_output-${lib.shortID(req.params.id)}.log`;
207+
let logFile = path.join(config.dataPath, 'reports', id, filename);
208+
fs.readFile(logFile, 'utf8', (err, data) => {
209+
if (err) {
210+
log('%s', err.message);
211+
res.statusCode = 404;
212+
res.send(`Record for ${isSHA? 'commit' : 'branch'} ${id} not found`);
213+
} else {
214+
res.statusCode = 200;
215+
// Wrap in HTML tags so that the formatting is a little nicer.
216+
let preText = '<html lang="en-GB"><body><pre>';
217+
let postText = '</pre></body></html>';
218+
res.send(preText + data + postText);
219+
}
220+
});
221+
})
222+
.catch(err => {
176223
log('%s', err.message);
177224
res.statusCode = 404;
178225
res.send(`Record for ${isSHA? 'commit' : 'branch'} ${req.params.id} not found`);
179-
} else {
180-
res.statusCode = 200;
181-
// Wrap in HTML tags so that the formatting is a little nicer.
182-
let preText = '<html lang="en-GB"><body><pre>';
183-
let postText = '</pre></body></html>';
184-
res.send(preText + data + postText);
185-
}
186-
});
226+
});
187227
});
188228

189229

@@ -367,15 +407,15 @@ async function updateStatus(data, targetURL = '') {
367407
await setAccessToken();
368408
return request("POST /repos/:owner/:repo/statuses/:sha", {
369409
owner: data['owner'] || process.env['REPO_OWNER'],
370-
repo: data['repo'],
410+
repo: data['repo'] || process.env['REPO_NAME'],
371411
headers: {
372412
authorization: `token ${token['token']}`,
373413
accept: "application/vnd.github.machine-man-preview+json"
374414
},
375415
sha: data['sha'],
376416
state: data['status'],
377417
target_url: targetURL,
378-
description: data['description'].substring(0, maxN),
418+
description: (data['description'] || '').substring(0, maxN),
379419
context: data['context']
380420
});
381421
}
@@ -547,5 +587,5 @@ queue.on('finish', (err, job) => { // On job end post result to API
547587
});
548588

549589
module.exports = {
550-
updateStatus, srv, handler, setAccessToken, prepareEnv, runTests, eventCallback
590+
updateStatus, srv, handler, setAccessToken, prepareEnv, runTests, eventCallback, fetchCommit
551591
}

test/lib.test.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,81 @@ describe('Test partial:', function() {
6060
});
6161

6262

63+
/**
64+
* A test for the function getRepoPath
65+
*/
66+
describe('Test getRepoPath:', function() {
67+
it('expect returned from env', function () {
68+
let repoPath = lib.getRepoPath()
69+
expect(repoPath).eq(process.env.REPO_PATH)
70+
});
71+
});
72+
73+
74+
/**
75+
* A test for the function compareCoverage.
76+
* @todo add test for strict compare
77+
*/
78+
describe('Test compareCoverage:', function() {
79+
var job;
80+
81+
beforeEach(function () {
82+
queue.process(async (_job, _done) => {
83+
}); // nop
84+
queue.pile = [];
85+
job = {
86+
data: {
87+
sha: null
88+
}
89+
};
90+
})
91+
92+
it('expect coverage diff', function () {
93+
// Test decrease in coverage
94+
job.data.sha = ids[0];
95+
job.data.base = ids[1];
96+
lib.compareCoverage(job);
97+
expect(job.data.status).eq('failure');
98+
expect(job.data.description).contains('decreased');
99+
expect(queue.pile).empty;
100+
101+
// Test increase in coverage
102+
job.data.coverage = 95.56
103+
lib.compareCoverage(job);
104+
expect(job.data.status).eq('success');
105+
expect(job.data.description).contains('increased');
106+
expect(queue.pile).empty;
107+
});
108+
109+
it('expect ReferenceError', function () {
110+
job.data.base = null;
111+
expect(() => lib.compareCoverage(job)).throws(ReferenceError);
112+
});
113+
114+
it('expect fail status', function () {
115+
job.data.sha = ids[0];
116+
job.data.base = ids[3]; // errored
117+
lib.compareCoverage(job);
118+
expect(job.data.status).eq('failure');
119+
expect(job.data.description).contains('incomplete');
120+
expect(queue.pile).empty;
121+
});
122+
123+
it('expect job added', function () {
124+
// Test decrease in coverage
125+
job.data.sha = ids[2]; // fake
126+
job.data.base = ids[1];
127+
job.data.context = 'coverage';
128+
lib.compareCoverage(job);
129+
expect(queue.pile.length).eq(2);
130+
expect(job.data.skipPost).true; // Job should be skipped to allow time for jobs to run
131+
expect(queue.pile[0].data.sha).eq(ids[1])
132+
expect(queue.pile[1].data.skipPost).false;
133+
expect(queue.pile[1].data.context).eq(job.data.context)
134+
});
135+
});
136+
137+
63138
/**
64139
* A test for the function updateJobFromRecord.
65140
* @todo add test for compareCoverage call

0 commit comments

Comments
 (0)