@google-automations/issue-utils
Version:
Utilities for github issues
139 lines • 5.52 kB
JavaScript
;
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.closeIssue = exports.addOrUpdateIssue = exports.addOrUpdateIssueComment = void 0;
const gcf_utils_1 = require("gcf-utils");
var issue_comments_1 = require("./issue-comments");
Object.defineProperty(exports, "addOrUpdateIssueComment", { enumerable: true, get: function () { return issue_comments_1.addOrUpdateIssueComment; } });
function issueFromGitHubIssue(owner, repo, issue) {
return {
owner,
repo,
number: issue.number,
title: issue.title,
body: issue.body || '',
labels: issue.labels.map(label => typeof label === 'string' ? label : label.name || ''),
};
}
async function findOpenIssueByCreator(octokit, owner, repo, creator) {
const issues = [];
for (const issue of await octokit.paginate(octokit.issues.listForRepo, {
owner,
repo,
state: 'open',
creator,
})) {
issues.push(issueFromGitHubIssue(owner, repo, issue));
}
return issues;
}
function issueNeedsUpdating(issue, expectedBody) {
return issue.body !== expectedBody;
}
/**
* Return the authenticated username of the installed GitHub app. Apps are
* not allowed to use the users.getAuthenticated() endpoint.
* @param {Octokit} octokit
* @return {string} The username of the authenticated app
*/
async function getAuthenticatedBotUser(octokit) {
var _a;
const { data } = await octokit.rest.apps.getAuthenticated();
const slug = (_a = data === null || data === void 0 ? void 0 : data.slug) !== null && _a !== void 0 ? _a : 'unknown';
return `${slug}[bot]`;
}
/**
* Opens or edits an existing issue that matches the issue title and
* authenticated user.
*
* @param {Octokit} octokit - The Octokit instance.
* @param {string} owner - The owner of the repository.
* @param {string} repo - The name of the repository.
* @param {string} title - The issue title.
* @param {string} body - The issue body.
* @param {string[]} labels - Labels to attach to the issue.
* @param {GCFLogger} logger - A context logger.
* @returns {Issue} The created or updated issue.
*/
async function addOrUpdateIssue(octokit, owner, repo, title, body, labels, logger = gcf_utils_1.logger) {
const issueOpener = await getAuthenticatedBotUser(octokit);
const issues = await findOpenIssueByCreator(octokit, owner, repo, issueOpener);
const issue = issues.find(issue => issue.title === title);
if (issue) {
logger.info(`Found existing issue: #${issue.number}`);
if (!issueNeedsUpdating(issue, body)) {
// no need to update the issue
logger.info('Issue remains unchanged');
logger.metric('issue.unchanged', { opener: issueOpener });
return issue;
}
else {
// issue body has changed, maybe update it
logger.info('Updating issue');
const { data: updatedIssue } = await octokit.issues.update({
owner,
repo,
issue_number: issue.number,
body,
});
logger.metric('issue.updated', { opener: issueOpener });
return issueFromGitHubIssue(owner, repo, updatedIssue);
}
}
// no existing issue - open a new one
logger.info('No existing issue found - opening new issue');
const { data: newIssue } = await octokit.issues.create({
owner,
repo,
title,
body,
labels,
});
logger.metric('issue.opened', { opener: issueOpener });
return issueFromGitHubIssue(owner, repo, newIssue);
}
exports.addOrUpdateIssue = addOrUpdateIssue;
/**
* Closes an existing issue that matches the issue title and
* authenticated user.
*
* @param {Octokit} octokit - The Octokit instance.
* @param {string} owner - The owner of the repository.
* @param {string} repo - The name of the repository.
* @param {string} title - The issue title.
* @param {GCFLogger} logger - A context logger.
* @returns {Issue} The created or updated issue.
*/
async function closeIssue(octokit, owner, repo, title, logger = gcf_utils_1.logger) {
const issueOpener = await getAuthenticatedBotUser(octokit);
const issues = await findOpenIssueByCreator(octokit, owner, repo, issueOpener);
const issue = issues.find(issue => issue.title === title);
if (!issue) {
// No existing issue found, nothing to do.
return null;
}
logger.info(`Found existing issue: #${issue.number}, closing`);
const { data: updatedIssue } = await octokit.issues.update({
owner,
repo,
issue_number: issue.number,
state: 'closed',
state_reason: 'completed',
});
logger.metric('issue.closed', { opener: issueOpener });
return issueFromGitHubIssue(owner, repo, updatedIssue);
}
exports.closeIssue = closeIssue;
//# sourceMappingURL=issue-utils.js.map