UNPKG

6.99 kBJavaScriptView Raw
1"use strict";
2/*
3 * MIT License
4 *
5 * Copyright (c) 2017 Choko (choko@curioswitch.org)
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26 return new (P || (P = Promise))(function (resolve, reject) {
27 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
28 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
29 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
30 step((generator = generator.apply(thisArg, _arguments || [])).next());
31 });
32};
33var __importDefault = (this && this.__importDefault) || function (mod) {
34 return (mod && mod.__esModule) ? mod : { "default": mod };
35};
36Object.defineProperty(exports, "__esModule", { value: true });
37const axios_1 = __importDefault(require("axios"));
38const keymanager_1 = require("./keymanager");
39const constants_1 = require("./constants");
40const GITHUB_API_BASE = 'https://api.github.com';
41const GITHUB_URL_REPO_REGEX = /repos\/([^/]+\/[^/]+)\//;
42function statusToState(status) {
43 switch (status) {
44 case 'QUEUED':
45 case 'WORKING':
46 return 'pending';
47 case 'SUCCESS':
48 return 'success';
49 case 'FAILURE':
50 return 'failure';
51 default:
52 return 'error';
53 }
54}
55function statusToDescription(status) {
56 switch (status) {
57 case 'QUEUED':
58 return 'Build queued.';
59 case 'WORKING':
60 return 'Build started.';
61 case 'SUCCESS':
62 return 'Build succeeded.';
63 case 'FAILURE':
64 return 'Build failed.';
65 case 'INTERNAL_ERROR':
66 return 'Build internal error.';
67 case 'TIMEOUT':
68 return 'Build timed out.';
69 case 'CANCELLED':
70 return 'Build cancelled.';
71 default:
72 return 'Build status unknown.';
73 }
74}
75function makeRequest(uri, body) {
76 return __awaiter(this, void 0, void 0, function* () {
77 const repoMatch = GITHUB_URL_REPO_REGEX.exec(uri);
78 if (!repoMatch) {
79 throw new Error('Could not parse github url.');
80 }
81 const githubToken = yield keymanager_1.keyManager.getGithubToken(repoMatch[1]);
82 return axios_1.default.post(uri, body, {
83 headers: {
84 'User-Agent': 'cloudbuild-github',
85 },
86 auth: {
87 username: 'token',
88 password: githubToken,
89 },
90 });
91 });
92}
93function handleBuildEvent(data) {
94 return __awaiter(this, void 0, void 0, function* () {
95 const build = JSON.parse(Buffer.from(data, 'base64').toString('utf8'));
96 let repoName = null;
97 let revisionId = null;
98 if (!build.substitutions) {
99 // No substitutions for triggered builds, we'll post to the commit instead.
100 const repoSource = build.sourceProvenance.resolvedRepoSource;
101 // eslint-disable-next-line prefer-destructuring
102 const cloudbuildRepoName = repoSource.repoName;
103 if (!cloudbuildRepoName.startsWith('github-')) {
104 // Not a github repo.
105 return;
106 }
107 repoName = cloudbuildRepoName
108 .substring('github-'.length)
109 .replace('-', '/');
110 revisionId = repoSource.commitSha;
111 }
112 const statusesUrl = repoName && revisionId
113 ? `${GITHUB_API_BASE}/repos/${repoName}/statuses/${revisionId}`
114 : build.substitutions[constants_1.STATUSES_URL_KEY];
115 if (!statusesUrl) {
116 // A non-triggered build not from the webhook, nothing to do.
117 return;
118 }
119 const status = {
120 state: statusToState(build.status),
121 // eslint-disable-next-line @typescript-eslint/camelcase
122 target_url: build.logUrl,
123 description: statusToDescription(build.status),
124 context: 'ci/cloudbuild',
125 };
126 const statusResponse = yield makeRequest(statusesUrl, status);
127 if (!statusResponse.data.state) {
128 throw new Error(`Failed to set status: ${JSON.stringify(statusResponse.data)}`);
129 }
130 const commentsUrl = repoName && revisionId
131 ? `${GITHUB_API_BASE}/repos/${repoName}/commits/${revisionId}/comments`
132 : build.substitutions[constants_1.COMMENTS_URL_KEY];
133 if (!commentsUrl) {
134 // A non-triggered build not from the webhook, nothing to do (we shouldn't
135 // actually get here since we have a similar check for statuses).
136 return;
137 }
138 let comment;
139 switch (build.status) {
140 case 'QUEUED':
141 case 'WORKING':
142 case 'CANCELLED':
143 case 'STATUS_UNKNOWN':
144 return;
145 case 'SUCCESS': {
146 if (repoName) {
147 // Don't comment on success for triggered builds.
148 return;
149 }
150 comment = `Build succeded. If you have approval, you're ready to merge!\n\nLogs:\n${build.logUrl}`;
151 break;
152 }
153 case 'FAILURE':
154 comment = `Build failed. Check the logs and try again.\n\nLogs:\n${build.logUrl}`;
155 break;
156 default:
157 comment = `Build terminated with unknown error. You may want to retry.\n\nLogs:\n${build.logUrl}`;
158 break;
159 }
160 const commentResponse = yield makeRequest(commentsUrl, {
161 body: comment,
162 });
163 if (!commentResponse.data.id) {
164 throw new Error(`Failed to set comment: ${JSON.stringify(commentResponse.data)}`);
165 }
166 });
167}
168exports.default = handleBuildEvent;
169//# sourceMappingURL=notifier.js.map
\No newline at end of file