diff --git a/src/api/db/migrations.ts b/src/api/db/migrations.ts index 1bc30743f..b963d06fa 100644 --- a/src/api/db/migrations.ts +++ b/src/api/db/migrations.ts @@ -32,6 +32,7 @@ export function create(): Promise { t.string('user_html_url'); t.string('username'); t.string('password'); + t.string('access_token'); t.timestamps(); })) .then(() => schema.createTableIfNotExists('builds', (t: knex.TableBuilder) => { diff --git a/src/api/github-commit-status.ts b/src/api/github-commit-status.ts new file mode 100644 index 000000000..05b61a6fb --- /dev/null +++ b/src/api/github-commit-status.ts @@ -0,0 +1,95 @@ +import * as request from 'request'; + +function sendRequest(url: string, data: any, headers: any): Promise { + return new Promise((resolve, reject) => { + let options = { + url: url, + method: 'POST', + headers: headers, + json: data + }; + + request(options, (err, response, body) => { + if (err) { + reject(err); + } else { + if (response.statusCode < 300 && response.statusCode >= 200) { + resolve(body); + } else { + reject({ + statusCode: response.statusCode, + response: body + }); + } + } + }); + }); +} + +export function setGitHubStatusSuccess( + gitUrl: string, abstruseUrl: string, token: string): Promise { + let data = { + 'state': 'success', + 'target_url': abstruseUrl, + 'description': 'The Abstruse CI build succeeded', + 'context': 'continuous-integration/abstruse' + }; + + let header = { + 'Authorization': `token ${token}`, + 'User-Agent': 'Abstruse' + }; + + return sendRequest(gitUrl, data, header); +} + +export function setGitHubStatusPending( + gitUrl: string, abstruseUrl: string, token: string): Promise { + let data = { + 'state': 'pending', + 'target_url': abstruseUrl, + 'description': 'The Abstruse CI build succeeded', + 'context': 'continuous-integration/abstruse' + }; + + let header = { + 'Authorization': `token ${token}`, + 'User-Agent': 'Abstruse' + }; + + return sendRequest(gitUrl, data, header); +} + +export function setGitHubStatusError( + gitUrl: string, abstruseUrl: string, token: string): Promise { + let data = { + 'state': 'error', + 'target_url': abstruseUrl, + 'description': 'The Abstruse CI build succeeded', + 'context': 'continuous-integration/abstruse' + }; + + let header = { + 'Authorization': `token ${token}`, + 'User-Agent': 'Abstruse' + }; + + return sendRequest(gitUrl, data, header); +} + +export function setGitHubStatusFailure( + gitUrl: string, abstruseUrl: string, token: string): Promise { + let data = { + 'state': 'failure', + 'target_url': abstruseUrl, + 'description': 'The Abstruse CI build succeeded', + 'context': 'continuous-integration/abstruse' + }; + + let header = { + 'Authorization': `token ${token}`, + 'User-Agent': 'Abstruse' + }; + + return sendRequest(gitUrl, data, header); +} diff --git a/src/api/process-manager.ts b/src/api/process-manager.ts index 7348af6f0..461f5a509 100644 --- a/src/api/process-manager.ts +++ b/src/api/process-manager.ts @@ -10,6 +10,8 @@ import { killContainer } from './docker'; import * as logger from './logger'; import { blue, yellow, green, cyan } from 'chalk'; import { getConfig, getHttpJsonResponse } from './utils'; +import { setGitHubStatusError, setGitHubStatusFailure, + setGitHubStatusPending, setGitHubStatusSuccess } from './github-commit-status'; export interface BuildMessage { type: string; @@ -131,7 +133,13 @@ export function startBuild(data: any): Promise { data.build_id = build.id; delete data.repositories_id; delete data.pr; - insertBuildRun(data); + insertBuildRun(data) + .then(() => { + let gitUrl = + `https://api.github.com/repos/${data.head_full_name}/statuses/${data.sha}`; + let abstruseUrl = `${config.url}/build/${build.id}`; + return setGitHubStatusPending(gitUrl, abstruseUrl, repository.token); + }); const jobsCommands = generateCommands(repository.clone_url, repoDetails.config); return jobsCommands.reduce((prev, commands, i) => { @@ -319,6 +327,13 @@ function prepareJob(buildId: number, jobId: number, cmds: any, sshAndVnc = false job_id: jobId, data: 'jobFailed' }); + }) + .then(() => getBuild(buildId)) + .then(build => { + let gitUrl = + `https://api.github.com/repos/${build.head_full_name}/statuses/${build.sha}`; + let abstruseUrl = `${config.url}/build/${buildId}`; + return setGitHubStatusFailure(gitUrl, abstruseUrl, build.repository.token); }); }, () => { dbJob.getLastRunId(jobId) @@ -331,6 +346,13 @@ function prepareJob(buildId: number, jobId: number, cmds: any, sshAndVnc = false getLastRunId(buildId) .then(id => { updateBuildRun({ id: id, end_time: new Date()}); + }) + .then(() => getBuild(buildId)) + .then(build => { + let gitUrl = + `https://api.github.com/repos/${build.head_full_name}/statuses/${build.sha}`; + let abstruseUrl = `${config.url}/build/${buildId}`; + return setGitHubStatusSuccess(gitUrl, abstruseUrl, build.repository.token); }); } Promise.resolve(); @@ -384,6 +406,12 @@ export function restartBuild(buildId: number): Promise { return jobs.reduce((prev, curr) => { return prev.then(() => queueJob(buildId, curr.id)); }, Promise.resolve()); + }) + .then(() => { + let gitUrl = + `https://api.github.com/repos/${build.head_full_name}/statuses/${build.sha}`; + let abstruseUrl = `${config.url}/build/${build.id}`; + return setGitHubStatusPending(gitUrl, abstruseUrl, build.repository.token); }); }); } diff --git a/src/api/utils.ts b/src/api/utils.ts index 1a86b53ec..aaaf4eee8 100644 --- a/src/api/utils.ts +++ b/src/api/utils.ts @@ -8,6 +8,7 @@ import * as uuid from 'uuid'; import * as request from 'request'; const defaultConfig = { + url: null, secret: 'thisIsSecret', port: 6500, wsport: 6501, diff --git a/src/api/webhooks.ts b/src/api/webhooks.ts index 22f4bcdcb..03a9172e0 100644 --- a/src/api/webhooks.ts +++ b/src/api/webhooks.ts @@ -1,8 +1,9 @@ import * as express from 'express'; import * as crypto from 'crypto'; -import { getConfig } from './utils'; +import { getConfig, getFilePath } from './utils'; import { pingRepository, createPullRequest, synchronizePullRequest } from './db/repository'; import { startBuild } from './process-manager'; +import { writeJsonFile } from './fs'; const config: any = getConfig(); export const webhooks = express.Router(); @@ -35,7 +36,10 @@ webhooks.post('/github', (req: express.Request, res: express.Response) => { switch (ev) { case 'ping': - pingRepository(payload) + let config: any = getConfig(); + config.url = req.headers.host; + writeJsonFile(getFilePath('config.json'), config) + .then(() => pingRepository(payload)) .then(repo => res.status(200).json({ msg: 'ok' })) .catch(err => res.status(400).json(err)); break;