1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 17:33:23 +01:00

feat: github actions to automatically create and close bug report issue (#20391)

* feat(action): github action to create bug report issue at RC cut

* feat(action): github action to close bug report issue once release is ready

* fix(action): replace main by master

* fix(action): create event does not support branch filter

* fix(action): indentation

* fix(action): actionlint erors

* fix(action): actionlint erors 2

* fix(action): actionlint erors 3

* fix(action): replace npm by yarn for consistency
This commit is contained in:
Gauthier Petetin 2023-08-18 12:57:04 -03:00 committed by GitHub
parent ca1ddeb59b
commit 23249b68b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 204 additions and 2 deletions

View File

@ -0,0 +1,120 @@
import * as core from '@actions/core';
import { context, getOctokit } from '@actions/github';
import { GitHub } from '@actions/github/lib/utils';
main().catch((error: Error): void => {
console.error(error);
process.exit(1);
});
async function main(): Promise<void> {
// "GITHUB_TOKEN" is an automatically generated, repository-specific access token provided by GitHub Actions.
// We can't use "GITHUB_TOKEN" here, as its permissions are scoped to the repository where the action is running.
// "GITHUB_TOKEN" does not have access to other repositories, even when they belong to the same organization.
// As we want to update bug report issues which are not located in the same repository,
// we need to create our own "BUG_REPORT_TOKEN" with "repo" permissions.
// Such a token allows to access other repositories of the MetaMask organisation.
const personalAccessToken = process.env.BUG_REPORT_TOKEN;
if (!personalAccessToken) {
core.setFailed('BUG_REPORT_TOKEN not found');
process.exit(1);
}
const repoOwner = context.repo.owner; // MetaMask
const bugReportRepo = process.env.BUG_REPORT_REPO;
if (!bugReportRepo) {
core.setFailed('BUG_REPORT_REPO not found');
process.exit(1);
}
// Extract branch name from the context
const branchName: string = context.payload.pull_request?.head.ref || "";
// Extract semver version number from the branch name
const releaseVersionNumberMatch = branchName.match(/^Version-v(\d+\.\d+\.\d+)$/);
if (!releaseVersionNumberMatch) {
core.setFailed(`Failed to extract version number from branch name: ${branchName}`);
process.exit(1);
}
const releaseVersionNumber = releaseVersionNumberMatch[1];
// Initialise octokit, required to call Github GraphQL API
const octokit: InstanceType<typeof GitHub> = getOctokit(personalAccessToken);
const bugReportIssue = await retrieveOpenBugReportIssue(octokit, repoOwner, bugReportRepo, releaseVersionNumber);
if (!bugReportIssue) {
throw new Error(`No open bug report issue was found for release ${releaseVersionNumber} on ${repoOwner}/${bugReportRepo} repo`);
}
if (bugReportIssue.title?.toLocaleLowerCase() !== `v${releaseVersionNumber} Bug Report`.toLocaleLowerCase()) {
throw new Error(`Unexpected bug report title: "${bugReportIssue.title}" instead of "v${releaseVersionNumber} Bug Report"`);
}
console.log(`Closing bug report issue with title "${bugReportIssue.title}" and id: ${bugReportIssue.id}`);
await closeIssue(octokit, bugReportIssue.id);
console.log(`Issue with id: ${bugReportIssue.id} successfully closed`);
}
// This function retrieves the issue titled "vx.y.z Bug Report" on a specific repo
async function retrieveOpenBugReportIssue(octokit: InstanceType<typeof GitHub>, repoOwner: string, repoName: string, releaseVersionNumber: string): Promise<{
id: string;
title: string;
} | undefined> {
const retrieveOpenBugReportIssueQuery = `
query RetrieveOpenBugReportIssue {
search(query: "repo:${repoOwner}/${repoName} type:issue is:open in:title v${releaseVersionNumber} Bug Report", type: ISSUE, first: 1) {
nodes {
... on Issue {
id
title
}
}
}
}
`;
const retrieveOpenBugReportIssueQueryResult: {
search: {
nodes: {
id: string;
title: string;
}[];
};
} = await octokit.graphql(retrieveOpenBugReportIssueQuery);
const bugReportIssues = retrieveOpenBugReportIssueQueryResult?.search?.nodes;
return bugReportIssues?.length > 0 ? bugReportIssues[0] : undefined;
}
// This function closes a Github issue, based on its ID
async function closeIssue(octokit: InstanceType<typeof GitHub>, issueId: string): Promise<string> {
const closeIssueMutation = `
mutation CloseIssue($issueId: ID!) {
updateIssue(input: {id: $issueId, state: CLOSED}) {
clientMutationId
}
}
`;
const closeIssueMutationResult: {
updateIssue: {
clientMutationId: string;
};
} = await octokit.graphql(closeIssueMutation, {
issueId,
});
const clientMutationId = closeIssueMutationResult?.updateIssue?.clientMutationId;
return clientMutationId;
}

View File

@ -37,4 +37,4 @@ jobs:
env:
RELEASE_LABEL_TOKEN: ${{ secrets.RELEASE_LABEL_TOKEN }}
NEXT_SEMVER_VERSION: ${{ env.NEXT_SEMVER_VERSION }}
run: npm run add-release-label-to-pr-and-linked-issues
run: yarn run add-release-label-to-pr-and-linked-issues

View File

@ -35,4 +35,4 @@ jobs:
id: check-pr-has-required-labels
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm run check-pr-has-required-labels
run: yarn run check-pr-has-required-labels

34
.github/workflows/close-bug-report.yml vendored Normal file
View File

@ -0,0 +1,34 @@
name: Close release bug report issue when release branch gets merged
on:
pull_request:
branches:
- master
types:
- closed
jobs:
close-bug-report:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'Version-v')
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 1 # This retrieves only the latest commit.
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
cache: yarn
- name: Install dependencies
run: yarn --immutable
- name: Close release bug report issue
id: close-release-bug-report-issue
env:
BUG_REPORT_REPO: MetaMask-planning
BUG_REPORT_TOKEN: ${{ secrets.BUG_REPORT_TOKEN }}
run: yarn run close-release-bug-report-issue

47
.github/workflows/create-bug-report.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Create release bug report issue when release branch gets created
on: create
jobs:
create-bug-report:
runs-on: ubuntu-latest
steps:
- name: Extract version from branch name if release branch
id: extract_version
run: |
if [[ "$GITHUB_REF" == "refs/heads/Version-v"* ]]; then
version="${GITHUB_REF#refs/heads/Version-v}"
echo "New release branch($version), continue next steps"
echo "version=$version" >> "$GITHUB_ENV"
else
echo "Not a release branch, skip next steps"
fi
- name: Create bug report issue on planning repo
if: env.version
uses: octokit/request-action@v2.x
with:
route: POST /repos/MetaMask/MetaMask-planning/issues
owner: MetaMask
title: v${{ env.version }} Bug Report
body: |
This bug report was automatically created by a GitHub action upon the creation of release branch `Version-v${{ env.version }}` (release cut).
**Expected actions for release engineers:**
1. Convert this issue into a Zenhub epic and link all bugs identified during the release regression testing phase to this epic.
2. After completing the first regression run, move this epic to "Regression Completed" on the [Extension Release Regression board](https://app.zenhub.com/workspaces/extension-release-regression-6478c62d937eaa15e95c33c5/board?filterLogic=any&labels=release-${{ env.version }},release-task).
Note that once the release is prepared for store submission, meaning the `Version-v${{ env.version }}` branch merges into `master`, another GitHub action will automatically close this epic.
labels: |
[
"type-bug",
"regression-RC",
"release-${{ env.version }}"
]
env:
GITHUB_TOKEN: ${{ secrets.BUG_REPORT_TOKEN }}

View File

@ -98,6 +98,7 @@
"validate-branch-name": "validate-branch-name",
"add-release-label-to-pr-and-linked-issues": "ts-node ./.github/scripts/add-release-label-to-pr-and-linked-issues.ts",
"check-pr-has-required-labels": "ts-node ./.github/scripts/check-pr-has-required-labels.ts",
"close-release-bug-report-issue": "ts-node ./.github/scripts/close-release-bug-report-issue.ts",
"audit": "yarn npm audit --recursive --environment production --severity moderate"
},
"resolutions": {