ci(docs-infra): add explicit return types to methods

This commit is contained in:
Pete Bacon Darwin 2018-08-10 11:10:22 +01:00
parent 36c4c8daa9
commit d604ef7cf0
14 changed files with 59 additions and 46 deletions

View File

@ -24,7 +24,7 @@ export class BuildCleaner {
} }
// Methods - Public // Methods - Public
public async cleanUp() { public async cleanUp(): Promise<void> {
try { try {
this.logger.log('Cleaning up builds and downloads'); this.logger.log('Cleaning up builds and downloads');
const openPrs = await this.getOpenPrNumbers(); const openPrs = await this.getOpenPrNumbers();
@ -38,17 +38,17 @@ export class BuildCleaner {
} }
} }
public async cleanBuilds(openPrs: number[]) { public async cleanBuilds(openPrs: number[]): Promise<void> {
const existingBuilds = await this.getExistingBuildNumbers(); const existingBuilds = await this.getExistingBuildNumbers();
await this.removeUnnecessaryBuilds(existingBuilds, openPrs); await this.removeUnnecessaryBuilds(existingBuilds, openPrs);
} }
public async cleanDownloads(openPrs: number[]) { public async cleanDownloads(openPrs: number[]): Promise<void> {
const existingDownloads = await this.getExistingDownloads(); const existingDownloads = await this.getExistingDownloads();
await this.removeUnnecessaryDownloads(existingDownloads, openPrs); await this.removeUnnecessaryDownloads(existingDownloads, openPrs);
} }
public getExistingBuildNumbers() { public getExistingBuildNumbers(): Promise<number[]> {
return new Promise<number[]>((resolve, reject) => { return new Promise<number[]>((resolve, reject) => {
fs.readdir(this.buildsDir, (err, files) => { fs.readdir(this.buildsDir, (err, files) => {
if (err) { if (err) {
@ -65,14 +65,14 @@ export class BuildCleaner {
}); });
} }
public async getOpenPrNumbers() { public async getOpenPrNumbers(): Promise<number[]> {
const api = new GithubApi(this.githubToken); const api = new GithubApi(this.githubToken);
const githubPullRequests = new GithubPullRequests(api, this.githubOrg, this.githubRepo); const githubPullRequests = new GithubPullRequests(api, this.githubOrg, this.githubRepo);
const prs = await githubPullRequests.fetchAll('open'); const prs = await githubPullRequests.fetchAll('open');
return prs.map(pr => pr.number); return prs.map(pr => pr.number);
} }
public removeDir(dir: string) { public removeDir(dir: string): void {
try { try {
if (shell.test('-d', dir)) { if (shell.test('-d', dir)) {
shell.chmod('-R', 'a+w', dir); shell.chmod('-R', 'a+w', dir);
@ -83,7 +83,7 @@ export class BuildCleaner {
} }
} }
public removeUnnecessaryBuilds(existingBuildNumbers: number[], openPrNumbers: number[]) { public removeUnnecessaryBuilds(existingBuildNumbers: number[], openPrNumbers: number[]): void {
const toRemove = existingBuildNumbers.filter(num => !openPrNumbers.includes(num)); const toRemove = existingBuildNumbers.filter(num => !openPrNumbers.includes(num));
this.logger.log(`Existing builds: ${existingBuildNumbers.length}`); this.logger.log(`Existing builds: ${existingBuildNumbers.length}`);
@ -100,7 +100,7 @@ export class BuildCleaner {
forEach(dir => this.removeDir(dir)); forEach(dir => this.removeDir(dir));
} }
public getExistingDownloads() { public getExistingDownloads(): Promise<string[]> {
const artifactFile = path.basename(this.artifactPath); const artifactFile = path.basename(this.artifactPath);
return new Promise<string[]>((resolve, reject) => { return new Promise<string[]>((resolve, reject) => {
fs.readdir(this.downloadsDir, (err, files) => { fs.readdir(this.downloadsDir, (err, files) => {
@ -113,7 +113,7 @@ export class BuildCleaner {
}); });
} }
public removeUnnecessaryDownloads(existingDownloads: string[], openPrNumbers: number[]) { public removeUnnecessaryDownloads(existingDownloads: string[], openPrNumbers: number[]): void {
const toRemove = existingDownloads.filter(filePath => { const toRemove = existingDownloads.filter(filePath => {
const {pr} = getPrInfoFromDownloadPath(filePath); const {pr} = getPrInfoFromDownloadPath(filePath);
return !openPrNumbers.includes(pr); return !openPrNumbers.includes(pr);

View File

@ -13,7 +13,7 @@ import {BuildCleaner} from './build-cleaner';
_main(); _main();
// Functions // Functions
function _main() { function _main(): void {
const buildCleaner = new BuildCleaner( const buildCleaner = new BuildCleaner(
AIO_BUILDS_DIR, AIO_BUILDS_DIR,
AIO_GITHUB_ORGANIZATION, AIO_GITHUB_ORGANIZATION,

View File

@ -55,7 +55,7 @@ export class CircleCiApi {
* @param buildNumber The CircleCI build number that generated the artifact. * @param buildNumber The CircleCI build number that generated the artifact.
* @returns A promise to the info about the build * @returns A promise to the info about the build
*/ */
public async getBuildInfo(buildNumber: number) { public async getBuildInfo(buildNumber: number): Promise<BuildInfo> {
try { try {
const baseUrl = `${CIRCLE_CI_API_URL}/${this.githubOrg}/${this.githubRepo}/${buildNumber}`; const baseUrl = `${CIRCLE_CI_API_URL}/${this.githubOrg}/${this.githubRepo}/${buildNumber}`;
const response = await fetch(`${baseUrl}?${this.tokenParam}`); const response = await fetch(`${baseUrl}?${this.tokenParam}`);
@ -73,7 +73,7 @@ export class CircleCiApi {
* @param artifactPath The path, within the build to the artifact. * @param artifactPath The path, within the build to the artifact.
* @returns A promise to the URL that can be requested to download the actual build artifact file. * @returns A promise to the URL that can be requested to download the actual build artifact file.
*/ */
public async getBuildArtifactUrl(buildNumber: number, artifactPath: string) { public async getBuildArtifactUrl(buildNumber: number, artifactPath: string): Promise<string> {
const baseUrl = `${CIRCLE_CI_API_URL}/${this.githubOrg}/${this.githubRepo}/${buildNumber}`; const baseUrl = `${CIRCLE_CI_API_URL}/${this.githubOrg}/${this.githubRepo}/${buildNumber}`;
try { try {
const response = await fetch(`${baseUrl}/artifacts?${this.tokenParam}`); const response = await fetch(`${baseUrl}/artifacts?${this.tokenParam}`);

View File

@ -56,7 +56,7 @@ export class GithubApi {
} }
// Methods - Protected // Methods - Protected
protected buildPath(pathname: string, params?: RequestParamsOrNull) { protected buildPath(pathname: string, params?: RequestParamsOrNull): string {
if (params == null) { if (params == null) {
return pathname; return pathname;
} }
@ -67,7 +67,7 @@ export class GithubApi {
return `${pathname}${joiner}${search}`; return `${pathname}${joiner}${search}`;
} }
protected request<T>(method: string, path: string, data: any = null) { protected request<T>(method: string, path: string, data: any = null): Promise<T> {
return new Promise<T>((resolve, reject) => { return new Promise<T>((resolve, reject) => {
const options = { const options = {
headers: {...this.requestHeaders}, headers: {...this.requestHeaders},
@ -101,7 +101,7 @@ export class GithubApi {
}); });
} }
protected serializeSearchParams(params: RequestParams) { protected serializeSearchParams(params: RequestParams): string {
return Object.keys(params). return Object.keys(params).
filter(key => params[key] != null). filter(key => params[key] != null).
map(key => `${key}=${encodeURIComponent(String(params[key]))}`). map(key => `${key}=${encodeURIComponent(String(params[key]))}`).

View File

@ -38,7 +38,7 @@ export class GithubPullRequests {
* @param body The body of the comment to post. * @param body The body of the comment to post.
* @returns A promise that resolves when the comment has been posted. * @returns A promise that resolves when the comment has been posted.
*/ */
public addComment(pr: number, body: string) { public addComment(pr: number, body: string): Promise<any> {
assert(pr > 0, `Invalid PR number: ${pr}`); assert(pr > 0, `Invalid PR number: ${pr}`);
assert(!!body, `Invalid or empty comment body: ${body}`); assert(!!body, `Invalid or empty comment body: ${body}`);
return this.api.post<any>(`/repos/${this.repoSlug}/issues/${pr}/comments`, null, {body}); return this.api.post<any>(`/repos/${this.repoSlug}/issues/${pr}/comments`, null, {body});
@ -49,7 +49,7 @@ export class GithubPullRequests {
* @param pr The number of the PR for which to request info. * @param pr The number of the PR for which to request info.
* @returns A promise that is resolves with information about the specified PR. * @returns A promise that is resolves with information about the specified PR.
*/ */
public fetch(pr: number) { public fetch(pr: number): Promise<PullRequest> {
assert(pr > 0, `Invalid PR number: ${pr}`); assert(pr > 0, `Invalid PR number: ${pr}`);
// Using the `/issues/` URL, because the `/pulls/` one does not provide labels. // Using the `/issues/` URL, because the `/pulls/` one does not provide labels.
return this.api.get<PullRequest>(`/repos/${this.repoSlug}/issues/${pr}`); return this.api.get<PullRequest>(`/repos/${this.repoSlug}/issues/${pr}`);
@ -60,7 +60,7 @@ export class GithubPullRequests {
* @param state Only retrieve PRs that have this state. * @param state Only retrieve PRs that have this state.
* @returns A promise that is resolved with information about the requested PRs. * @returns A promise that is resolved with information about the requested PRs.
*/ */
public fetchAll(state: PullRequestState = 'all') { public fetchAll(state: PullRequestState = 'all'): Promise<PullRequest[]> {
const pathname = `/repos/${this.repoSlug}/pulls`; const pathname = `/repos/${this.repoSlug}/pulls`;
const params = {state}; const params = {state};
@ -72,7 +72,7 @@ export class GithubPullRequests {
* @param pr The number of the PR for which to request files. * @param pr The number of the PR for which to request files.
* @returns A promise that resolves to an array of file information * @returns A promise that resolves to an array of file information
*/ */
public fetchFiles(pr: number) { public fetchFiles(pr: number): Promise<FileInfo[]> {
assert(pr > 0, `Invalid PR number: ${pr}`); assert(pr > 0, `Invalid PR number: ${pr}`);
return this.api.get<FileInfo[]>(`/repos/${this.repoSlug}/pulls/${pr}/files`); return this.api.get<FileInfo[]>(`/repos/${this.repoSlug}/pulls/${pr}/files`);
} }

View File

@ -24,7 +24,7 @@ export class GithubTeams {
* Request information about all the organisation's teams in GitHub. * Request information about all the organisation's teams in GitHub.
* @returns A promise that is resolved with information about the teams. * @returns A promise that is resolved with information about the teams.
*/ */
public fetchAll() { public fetchAll(): Promise<Team[]> {
return this.api.getPaginated<Team>(`/orgs/${this.githubOrg}/teams`); return this.api.getPaginated<Team>(`/orgs/${this.githubOrg}/teams`);
} }
@ -34,7 +34,7 @@ export class GithubTeams {
* @param teamIds The team to check for the username. * @param teamIds The team to check for the username.
* @returns a Promise that resolves to `true` if the username is a member of the team. * @returns a Promise that resolves to `true` if the username is a member of the team.
*/ */
public async isMemberById(username: string, teamIds: number[]) { public async isMemberById(username: string, teamIds: number[]): Promise<boolean> {
const getMembership = async (teamId: number) => { const getMembership = async (teamId: number) => {
try { try {
@ -60,7 +60,7 @@ export class GithubTeams {
* @param teamSlugs A collection of slugs that represent the teams to check for the the username. * @param teamSlugs A collection of slugs that represent the teams to check for the the username.
* @returns a Promise that resolves to `true` if the usernane is a member of at least one of the specified teams. * @returns a Promise that resolves to `true` if the usernane is a member of at least one of the specified teams.
*/ */
public async isMemberBySlug(username: string, teamSlugs: string[]) { public async isMemberBySlug(username: string, teamSlugs: string[]): Promise<boolean> {
try { try {
const teams = await this.fetchAll(); const teams = await this.fetchAll();
const teamIds = teams.filter(team => teamSlugs.includes(team.slug)).map(team => team.id); const teamIds = teams.filter(team => teamSlugs.includes(team.slug)).map(team => team.id);

View File

@ -119,7 +119,7 @@ export class BuildCreator extends EventEmitter {
}); });
} }
protected getCandidatePrDirs(pr: number, isPublic: boolean) { protected getCandidatePrDirs(pr: number, isPublic: boolean): {oldPrDir: string, newPrDir: string} {
const hiddenPrDir = path.join(this.buildsDir, HIDDEN_DIR_PREFIX + pr); const hiddenPrDir = path.join(this.buildsDir, HIDDEN_DIR_PREFIX + pr);
const publicPrDir = path.join(this.buildsDir, `${pr}`); const publicPrDir = path.join(this.buildsDir, `${pr}`);

View File

@ -30,7 +30,7 @@ export class BuildRetriever {
* @param buildNum The number of the build for which to retrieve the info. * @param buildNum The number of the build for which to retrieve the info.
* @returns The Github org, repo, PR and latest SHA for the specified build. * @returns The Github org, repo, PR and latest SHA for the specified build.
*/ */
public async getGithubInfo(buildNum: number) { public async getGithubInfo(buildNum: number): Promise<GithubInfo> {
const buildInfo = await this.api.getBuildInfo(buildNum); const buildInfo = await this.api.getBuildInfo(buildNum);
const githubInfo: GithubInfo = { const githubInfo: GithubInfo = {
org: buildInfo.username, org: buildInfo.username,
@ -50,7 +50,7 @@ export class BuildRetriever {
* @param artifactPath the path on CircleCI where the artifact was stored. * @param artifactPath the path on CircleCI where the artifact was stored.
* @returns A promise to the file path where the downloaded file was stored. * @returns A promise to the file path where the downloaded file was stored.
*/ */
public async downloadBuildArtifact(buildNum: number, pr: number, sha: string, artifactPath: string) { public async downloadBuildArtifact(buildNum: number, pr: number, sha: string, artifactPath: string): Promise<string> {
try { try {
const outPath = computeArtifactDownloadPath(this.downloadDir, pr, sha, artifactPath); const outPath = computeArtifactDownloadPath(this.downloadDir, pr, sha, artifactPath);
const downloadExists = await new Promise(resolve => fs.exists(outPath, exists => resolve(exists))); const downloadExists = await new Promise(resolve => fs.exists(outPath, exists => resolve(exists)));
@ -73,7 +73,7 @@ export class BuildRetriever {
} }
} }
function getPrfromBranch(branch: string) { function getPrfromBranch(branch: string): number {
// CircleCI only exposes PR numbers via the `branch` field :-( // CircleCI only exposes PR numbers via the `branch` field :-(
const match = /^pull\/(\d+)$/.exec(branch); const match = /^pull\/(\d+)$/.exec(branch);
if (!match) { if (!match) {

View File

@ -24,7 +24,7 @@ export class BuildVerifier {
* @param pr The number of the PR to check * @param pr The number of the PR to check
* @param significantFilePattern A regex that selects files that are significant. * @param significantFilePattern A regex that selects files that are significant.
*/ */
public async getSignificantFilesChanged(pr: number, significantFilePattern: RegExp) { public async getSignificantFilesChanged(pr: number, significantFilePattern: RegExp): Promise<boolean> {
const files = await this.prs.fetchFiles(pr); const files = await this.prs.fetchFiles(pr);
return files.some(file => significantFilePattern.test(file.filename)); return files.some(file => significantFilePattern.test(file.filename));
} }
@ -40,7 +40,7 @@ export class BuildVerifier {
(await this.teams.isMemberBySlug(prInfo.user.login, this.allowedTeamSlugs)); (await this.teams.isMemberBySlug(prInfo.user.login, this.allowedTeamSlugs));
} }
protected hasLabel(prInfo: PullRequest, label: string) { protected hasLabel(prInfo: PullRequest, label: string): boolean {
return prInfo.labels.some(labelObj => labelObj.name === label); return prInfo.labels.some(labelObj => labelObj.name === label);
} }
} }

View File

@ -21,7 +21,7 @@ import {UploadServerFactory} from './upload-server-factory';
_main(); _main();
// Functions // Functions
function _main() { function _main(): void {
UploadServerFactory UploadServerFactory
.create({ .create({
buildArtifactPath: AIO_ARTIFACT_PATH, buildArtifactPath: AIO_ARTIFACT_PATH,

View File

@ -156,7 +156,7 @@ export class UploadServerFactory {
return middleware; return middleware;
} }
public static createBuildCreator(prs: GithubPullRequests, buildsDir: string, domainName: string) { public static createBuildCreator(prs: GithubPullRequests, buildsDir: string, domainName: string): BuildCreator {
const buildCreator = new BuildCreator(buildsDir); const buildCreator = new BuildCreator(buildsDir);
const postPreviewsComment = (pr: number, shas: string[]) => { const postPreviewsComment = (pr: number, shas: string[]) => {
const body = shas. const body = shas.

View File

@ -7,7 +7,7 @@ import {UploadError} from './upload-error';
* @param res The response to configure as an error. * @param res The response to configure as an error.
* @param err The error that needs to be reported. * @param err The error that needs to be reported.
*/ */
export async function respondWithError(res: express.Response, err: any) { export async function respondWithError(res: express.Response, err: any): Promise<void> {
if (!(err instanceof UploadError)) { if (!(err instanceof UploadError)) {
err = new UploadError(500, String((err && err.message) || err)); err = new UploadError(500, String((err && err.message) || err));
} }

View File

@ -42,7 +42,7 @@ class Helper {
} }
// Methods - Public // Methods - Public
public cleanUp() { public cleanUp(): void {
while (this.cleanUpFns.length) { while (this.cleanUpFns.length) {
// Clean-up fns remove themselves from the list. // Clean-up fns remove themselves from the list.
this.cleanUpFns[0](); this.cleanUpFns[0]();
@ -66,7 +66,7 @@ class Helper {
} }
} }
public createDummyBuild(pr: number, sha: string, isPublic = true, force = false, legacy = false) { public createDummyBuild(pr: number, sha: string, isPublic = true, force = false, legacy = false): CleanUpFn {
const prDir = this.getPrDir(pr, isPublic); const prDir = this.getPrDir(pr, isPublic);
const shaDir = this.getShaDir(prDir, sha, legacy); const shaDir = this.getShaDir(prDir, sha, legacy);
const idxPath = path.join(shaDir, 'index.html'); const idxPath = path.join(shaDir, 'index.html');
@ -101,7 +101,7 @@ class Helper {
}); });
} }
public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory) { public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory): void {
Object.keys(this.portPerScheme).forEach(scheme => suiteFactory(scheme, this.portPerScheme[scheme])); Object.keys(this.portPerScheme).forEach(scheme => suiteFactory(scheme, this.portPerScheme[scheme]));
} }
@ -137,13 +137,13 @@ class Helper {
} }
public writeBuildFile(pr: number, sha: string, relFilePath: string, content: string, isPublic = true, public writeBuildFile(pr: number, sha: string, relFilePath: string, content: string, isPublic = true,
legacy = false) { legacy = false): void {
const shaDir = this.getShaDir(this.getPrDir(pr, isPublic), sha, legacy); const shaDir = this.getShaDir(this.getPrDir(pr, isPublic), sha, legacy);
const absFilePath = path.join(shaDir, relFilePath); const absFilePath = path.join(shaDir, relFilePath);
this.writeFile(absFilePath, {content}, true); this.writeFile(absFilePath, {content}, true);
} }
public writeFile(filePath: string, {content, size}: FileSpecs, force = false) { public writeFile(filePath: string, {content, size}: FileSpecs, force = false): void {
if (!force && fs.existsSync(filePath)) { if (!force && fs.existsSync(filePath)) {
throw new Error(`Refusing to overwrite existing file '${filePath}'.`); throw new Error(`Refusing to overwrite existing file '${filePath}'.`);
} }
@ -206,7 +206,18 @@ export function makeCurl(baseUrl: string) {
}; };
} }
export function payload(buildNum: number) { export interface PayloadData {
data: {
payload: {
build_num: number,
build_parameters: {
CIRCLE_JOB: string;
};
};
};
}
export function payload(buildNum: number): PayloadData {
return { return {
data: { data: {
payload: { payload: {

View File

@ -6,7 +6,7 @@ import {computeShortSha} from '../common/utils';
import {SHA} from './constants'; import {SHA} from './constants';
import {helper} from './helper'; import {helper} from './helper';
function checkFile(filePath: string, remove: boolean) { function checkFile(filePath: string, remove: boolean): boolean {
const exists = existsSync(filePath); const exists = existsSync(filePath);
if (exists && remove) { if (exists && remove) {
// if we expected the file to exist then we remove it to prevent leftover file errors // if we expected the file to exist then we remove it to prevent leftover file errors
@ -15,7 +15,7 @@ function checkFile(filePath: string, remove: boolean) {
return exists; return exists;
} }
function getArtifactPath(prNum: number, sha: string = SHA) { function getArtifactPath(prNum: number, sha: string = SHA): string {
return `${AIO_DOWNLOADS_DIR}/${prNum}-${computeShortSha(sha)}-aio-snapshot.tgz`; return `${AIO_DOWNLOADS_DIR}/${prNum}-${computeShortSha(sha)}-aio-snapshot.tgz`;
} }
@ -35,8 +35,8 @@ function checkFiles(prNum: number, isPublic: boolean, sha: string, isLegacy: boo
return { existingFiles, missingFiles }; return { existingFiles, missingFiles };
} }
class ToExistAsAFile { class ToExistAsAFile implements jasmine.CustomMatcher {
public compare(actual: string, remove = true) { public compare(actual: string, remove = true): jasmine.CustomMatcherResult {
const pass = checkFile(actual, remove); const pass = checkFile(actual, remove);
return { return {
message: `Expected file at "${actual}" ${pass ? 'not' : ''} to exist`, message: `Expected file at "${actual}" ${pass ? 'not' : ''} to exist`,
@ -45,8 +45,8 @@ class ToExistAsAFile {
} }
} }
class ToExistAsAnArtifact { class ToExistAsAnArtifact implements jasmine.CustomMatcher {
public compare(actual: {prNum: number, sha?: string}, remove = true) { public compare(actual: {prNum: number, sha?: string}, remove = true): jasmine.CustomMatcherResult {
const { prNum, sha = SHA } = actual; const { prNum, sha = SHA } = actual;
const filePath = getArtifactPath(prNum, sha); const filePath = getArtifactPath(prNum, sha);
const pass = checkFile(filePath, remove); const pass = checkFile(filePath, remove);
@ -57,8 +57,9 @@ class ToExistAsAnArtifact {
} }
} }
class ToExistAsABuild { class ToExistAsABuild implements jasmine.CustomMatcher {
public compare(actual: {prNum: number, isPublic?: boolean, sha?: string, isLegacy?: boolean}, remove = true) { public compare(actual: {prNum: number, isPublic?: boolean, sha?: string, isLegacy?: boolean}, remove = true):
jasmine.CustomMatcherResult {
const {prNum, isPublic = true, sha = SHA, isLegacy = false} = actual; const {prNum, isPublic = true, sha = SHA, isLegacy = false} = actual;
const {missingFiles} = checkFiles(prNum, isPublic, sha, isLegacy, remove); const {missingFiles} = checkFiles(prNum, isPublic, sha, isLegacy, remove);
return { return {
@ -67,7 +68,8 @@ class ToExistAsABuild {
pass: missingFiles.length === 0, pass: missingFiles.length === 0,
}; };
} }
public negativeCompare(actual: {prNum: number, isPublic?: boolean, sha?: string, isLegacy?: boolean}) { public negativeCompare(actual: {prNum: number, isPublic?: boolean, sha?: string, isLegacy?: boolean}):
jasmine.CustomMatcherResult {
const {prNum, isPublic = true, sha = SHA, isLegacy = false} = actual; const {prNum, isPublic = true, sha = SHA, isLegacy = false} = actual;
const { existingFiles } = checkFiles(prNum, isPublic, sha, isLegacy, false); const { existingFiles } = checkFiles(prNum, isPublic, sha, isLegacy, false);
return { return {