UNPKG

7.19 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright (c) 2019 The Polymer Project Authors. All rights reserved.
5 * This code may only be used under the BSD style license found at
6 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
7 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
8 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
9 * Google as part of the polymer project is also subject to an additional IP
10 * rights grant found at http://polymer.github.io/PATENTS.txt
11 */
12var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13 if (k2 === undefined) k2 = k;
14 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
15}) : (function(o, m, k, k2) {
16 if (k2 === undefined) k2 = k;
17 o[k2] = m[k];
18}));
19var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20 Object.defineProperty(o, "default", { enumerable: true, value: v });
21}) : function(o, v) {
22 o["default"] = v;
23});
24var __importStar = (this && this.__importStar) || function (mod) {
25 if (mod && mod.__esModule) return mod;
26 var result = {};
27 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28 __setModuleDefault(result, mod);
29 return result;
30};
31var __importDefault = (this && this.__importDefault) || function (mod) {
32 return (mod && mod.__esModule) ? mod : { "default": mod };
33};
34Object.defineProperty(exports, "__esModule", { value: true });
35exports.createCheck = exports.parseGithubCheckFlag = void 0;
36const got_1 = __importDefault(require("got"));
37const jsonwebtoken = __importStar(require("jsonwebtoken"));
38/**
39 * Parse the --github-check flag.
40 */
41function parseGithubCheckFlag(flag) {
42 const parsed = JSON.parse(flag);
43 if (!parsed.appId || !parsed.installationId || !parsed.repo ||
44 !parsed.commit) {
45 throw new Error(`Invalid --github-check flag. Must be a JSON object ` +
46 `with properties: appId, installationId, repo, and commit.`);
47 }
48 return {
49 label: String(parsed.label || 'Tachometer Benchmarks'),
50 appId: Number(parsed.appId),
51 installationId: Number(parsed.installationId),
52 repo: String(parsed.repo),
53 commit: String(parsed.commit),
54 };
55}
56exports.parseGithubCheckFlag = parseGithubCheckFlag;
57/**
58 * Create a pending GitHub check object and return a function that will mark
59 * the check completed with the given markdown.
60 */
61async function createCheck(config) {
62 const { label, appId, installationId, repo, commit } = config;
63 // We can directly store our GitHub App private key as a secret Travis
64 // environment variable (as opposed to committing it as a file and
65 // configuring to Travis decrypt it), but we have to be careful with the
66 // spaces and newlines that PEM files have, since Travis does a raw Bash
67 // substitution when it sets the variable.
68 //
69 // Given a PEM file from GitHub, the following command will escape spaces
70 // and newlines so that it can be safely pasted into the Travis UI. The
71 // spaces will get unescaped by Bash, and we'll unescape newlines ourselves.
72 //
73 // cat <GITHUB_PEM_FILE>.pem \
74 // | awk '{printf "%s\\\\n", $0}' | sed 's/ /\\ /g'
75 const appPrivateKey = (process.env.GITHUB_APP_PRIVATE_KEY || '').trim().replace(/\\n/g, '\n');
76 if (appPrivateKey === '') {
77 throw new Error('Missing or empty GITHUB_APP_PRIVATE_KEY environment variable, ' +
78 'which is required when using --github-check.');
79 }
80 const appToken = getAppToken(appId, appPrivateKey);
81 const installationToken = await getInstallationToken({ installationId, appToken });
82 // Create the initial Check Run run now, so that it will show up in the
83 // GitHub UI as pending.
84 const checkId = await createCheckRun({ label, repo, commit, installationToken });
85 return (markdown) => completeCheckRun({ label, repo, installationToken, checkId, markdown });
86}
87exports.createCheck = createCheck;
88/**
89 * Create a JSON Web Token (https://tools.ietf.org/html/rfc7519), which allows
90 * us to perform actions as a GitHub App.
91 *
92 * @param appId GitHub App ID. Can be found on the GitHub App settings page.
93 * @param privateKey Text of a PEM private key. Can be generated from the GitHub
94 * App settings page. More info at
95 * https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/
96 */
97function getAppToken(appId, privateKey) {
98 const expireMinutes = 10;
99 const issuedTimestamp = Math.floor(Date.now() / 1000);
100 const expireTimestamp = issuedTimestamp + expireMinutes * 60;
101 const payload = {
102 iss: appId,
103 iat: issuedTimestamp,
104 exp: expireTimestamp,
105 };
106 return jsonwebtoken.sign(payload, privateKey, { algorithm: 'RS256' });
107}
108/**
109 * Create an access token which allows us to perform actions as a GitHub App
110 * Installation.
111 */
112async function getInstallationToken({ installationId, appToken }) {
113 const resp = await got_1.default.post(`https://api.github.com/installations/${installationId}/access_tokens`, {
114 headers: {
115 Accept: 'application/vnd.github.machine-man-preview+json',
116 Authorization: `Bearer ${appToken}`,
117 },
118 });
119 const data = JSON.parse(resp.body);
120 return data.token;
121}
122/**
123 * Create a new GitHub Check Run (a single invocation of a Check on some commit)
124 * and return its identifier.
125 */
126async function createCheckRun({ label, repo, commit, installationToken }) {
127 const resp = await got_1.default.post(`https://api.github.com/repos/${repo}/check-runs`, {
128 headers: {
129 Accept: 'application/vnd.github.antiope-preview+json',
130 Authorization: `Bearer ${installationToken}`,
131 },
132 // https://developer.github.com/v3/checks/runs/#parameters
133 body: JSON.stringify({
134 head_sha: commit,
135 name: label,
136 }),
137 });
138 const data = JSON.parse(resp.body);
139 return data.id;
140}
141/**
142 * Update a GitHub Check run with the given markdown text and mark it as
143 * complete.
144 */
145async function completeCheckRun({ label, repo, installationToken, checkId, markdown }) {
146 await got_1.default.patch(`https://api.github.com/repos/${repo}/check-runs/${checkId}`, {
147 headers: {
148 Accept: 'application/vnd.github.antiope-preview+json',
149 Authorization: `Bearer ${installationToken}`,
150 },
151 // https://developer.github.com/v3/checks/runs/#parameters-1
152 body: JSON.stringify({
153 name: label,
154 completed_at: new Date().toISOString(),
155 // Note that in the future we will likely want to be able to report
156 // a failing check (e.g. if there appears to be a difference greater
157 // than some threshold).
158 conclusion: 'neutral',
159 output: {
160 title: label,
161 summary: 'Benchmark results',
162 text: markdown,
163 }
164 }),
165 });
166}
167//# sourceMappingURL=github.js.map
\No newline at end of file