ci(aio): add initial implementation for aio-builds setup

This commit is contained in:
Georgios Kalpakas
2017-02-06 20:40:28 +02:00
committed by Chuck Jazdzewski
parent 794f8f4e6a
commit 115164033b
49 changed files with 5876 additions and 11 deletions

View File

@ -0,0 +1,97 @@
// Imports
import {IncomingMessage} from 'http';
import * as https from 'https';
// Constants
const GITHUB_HOSTNAME = 'api.github.com';
// Interfaces - Types
interface RequestParams {
[key: string]: string | number;
}
type RequestParamsOrNull = RequestParams | null;
// Classes
export class GithubApi {
protected requestHeaders: {[key: string]: string};
// Constructor
constructor(protected repoSlug: string, githubToken?: string) {
if (!repoSlug) {
throw new Error('Missing required parameter \'repoSlug\'!');
}
if (!githubToken) {
console.warn('No GitHub access-token specified. Requests will be unauthenticated.');
}
this.requestHeaders = {'User-Agent': `Node/${process.versions.node}`};
if (githubToken) {
this.requestHeaders['Authorization'] = `token ${githubToken}`;
}
}
// Methods - Public
public get<T>(pathname: string, params?: RequestParamsOrNull): Promise<T> {
const path = this.buildPath(pathname, params);
return this.request<T>('get', path);
}
public post<T>(pathname: string, params?: RequestParamsOrNull, data?: any): Promise<T> {
const path = this.buildPath(pathname, params);
return this.request<T>('post', path, data);
}
// Methods - Protected
protected buildPath(pathname: string, params?: RequestParamsOrNull): string {
if (params == null) {
return pathname;
}
const search = (params === null) ? '' : this.serializeSearchParams(params);
const joiner = search && '?';
return `${pathname}${joiner}${search}`;
}
protected request<T>(method: string, path: string, data: any = null): Promise<T> {
return new Promise<T>((resolve, reject) => {
const options = {
headers: {...this.requestHeaders},
host: GITHUB_HOSTNAME,
method,
path,
};
const onError = (statusCode: number, responseText: string) => {
const url = `https://${GITHUB_HOSTNAME}${path}`;
reject(`Request to '${url}' failed (status: ${statusCode}): ${responseText}`);
};
const onSuccess = (responseText: string) => {
try { resolve(JSON.parse(responseText)); } catch (err) { reject(err); }
};
const onResponse = (res: IncomingMessage) => {
const statusCode = res.statusCode || -1;
const isSuccess = (200 <= statusCode) && (statusCode < 400);
let responseText = '';
res.
on('data', d => responseText += d).
on('end', () => isSuccess ? onSuccess(responseText) : onError(statusCode, responseText)).
on('error', reject);
};
https.
request(options, onResponse).
on('error', reject).
end(data && JSON.stringify(data));
});
}
protected serializeSearchParams(params: RequestParams): string {
return Object.keys(params).
filter(key => params[key] != null).
map(key => `${key}=${encodeURIComponent(String(params[key]))}`).
join('&');
}
}

View File

@ -0,0 +1,51 @@
// Imports
import {GithubApi} from './github-api';
// Interfaces - Types
interface PullRequest {
number: number;
}
export type PullRequestState = 'all' | 'closed' | 'open';
// Classes
export class GithubPullRequests extends GithubApi {
// Methods - Public
public addComment(pr: number, body: string): Promise<void> {
if (!(pr > 0)) {
throw new Error(`Invalid PR number: ${pr}`);
} else if (!body) {
throw new Error(`Invalid or empty comment body: ${body}`);
}
return this.post<void>(`/repos/${this.repoSlug}/issues/${pr}/comments`, null, {body});
}
public fetchAll(state: PullRequestState = 'all'): Promise<PullRequest[]> {
process.stdout.write(`Fetching ${state} pull requests...`);
return this.fetchUntilDone(state, 0);
}
// Methods - Protected
protected fetchUntilDone(state: PullRequestState, currentPage: number): Promise<PullRequest[]> {
process.stdout.write('.');
const perPage = 100;
const pathname = `/repos/${this.repoSlug}/pulls`;
const params = {
page: currentPage,
per_page: perPage,
state,
};
return this.get<PullRequest[]>(pathname, params).then(pullRequests => {
if (pullRequests.length < perPage) {
console.log('done');
return pullRequests;
}
return this.fetchUntilDone(state, currentPage + 1).
then(morePullRequests => [...pullRequests, ...morePullRequests]);
});
}
}

View File

@ -0,0 +1,23 @@
export const runTests = (specFiles: string[], helpers?: string[]) => {
// We can't use `import` here, because of the following mess:
// - GitHub project `jasmine/jasmine` is `jasmine-core` on npm and its typings `@types/jasmine`.
// - GitHub project `jasmine/jasmine-npm` is `jasmine` on npm and has no typings.
//
// Using `import...from 'jasmine'` here, would import from `@types/jasmine` (which refers to the
// `jasmine-core` module and the `jasmine` module).
// tslint:disable-next-line: no-var-requires variable-name
const Jasmine = require('jasmine');
const config = {
helpers,
random: true,
spec_files: specFiles,
stopSpecOnExpectationFailure: true,
};
process.on('unhandledRejection', (reason: any) => console.log('Unhandled rejection:', reason));
const runner = new Jasmine();
runner.loadConfig(config);
runner.onComplete((passed: boolean) => process.exit(passed ? 0 : 1));
runner.execute();
};

View File

@ -0,0 +1,11 @@
// Functions
export const getEnvVar = (name: string, isOptional = false): string => {
const value = process.env[name];
if (!isOptional && !value) {
console.error(`ERROR: Missing required environment variable '${name}'!`);
process.exit(1);
}
return value || '';
};