diff --git a/dev-infra/caretaker/check/check.ts b/dev-infra/caretaker/check/check.ts index 9fdfb8b984..ebd51125f8 100644 --- a/dev-infra/caretaker/check/check.ts +++ b/dev-infra/caretaker/check/check.ts @@ -9,6 +9,7 @@ import {GitClient} from '../../utils/git'; import {getCaretakerConfig} from '../config'; +import {printCiStatus} from './ci'; import {printG3Comparison} from './g3'; import {printGithubTasks} from './github'; import {printServiceStatuses} from './services'; @@ -21,7 +22,9 @@ export async function checkServiceStatuses(githubToken: string) { /** The GitClient for interacting with git and Github. */ const git = new GitClient(githubToken, config); + // TODO(josephperrott): Allow these checks to be loaded in parallel. await printServiceStatuses(); await printGithubTasks(git, config.caretaker); await printG3Comparison(git); + await printCiStatus(git); } diff --git a/dev-infra/caretaker/check/ci.ts b/dev-infra/caretaker/check/ci.ts new file mode 100644 index 0000000000..2e568e14d5 --- /dev/null +++ b/dev-infra/caretaker/check/ci.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import fetch from 'node-fetch'; + +import {bold, green, info, red} from '../../utils/console'; +import {GitClient} from '../../utils/git'; + + +/** The results of checking the status of CI. */ +interface StatusCheckResult { + status: 'success'|'failed'|'canceled'|'infrastructure_fail'|'timedout'|'failed'|'no_tests'; + timestamp: Date; + buildUrl: string; +} + +/** Retrieve and log status of CI for the project. */ +export async function printCiStatus(git: GitClient) { + info.group(bold(`CI`)); + // TODO(josephperrott): Expand list of branches checked to all active branches. + await printStatus(git, 'master'); + info.groupEnd(); + info(); +} + +/** Log the status of CI for a given branch to the console. */ +async function printStatus(git: GitClient, branch: string) { + const result = await getStatusOfBranch(git, branch); + const branchName = branch.padEnd(10); + if (result === null) { + info(`${branchName} was not found on CircleCI`); + } else if (result.status === 'success') { + info(`${branchName} ✅`); + } else { + info(`${branchName} ❌ (Ran at: ${result.timestamp.toLocaleString()})`); + } +} + +/** Get the CI status of a given branch from CircleCI. */ +async function getStatusOfBranch(git: GitClient, branch: string): Promise { + const {owner, name} = git.remoteConfig; + const url = `https://circleci.com/api/v1.1/project/gh/${owner}/${name}/tree/${ + branch}?limit=1&filter=completed&shallow=true`; + const result = (await fetch(url).then(result => result.json()))?.[0]; + + if (result) { + return { + status: result.outcome, + timestamp: new Date(result.stop_time), + buildUrl: result.build_url + }; + } + return null; +}