-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feat/notifications' of github.com:MetaMask/metamask-mob…
…ile into feat/notifications
- Loading branch information
Showing
297 changed files
with
11,958 additions
and
4,626 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
import * as core from '@actions/core'; | ||
import { context, getOctokit } from '@actions/github'; | ||
import { GitHub } from '@actions/github/lib/utils'; | ||
import { PullRequestTriggerType } from '../scripts.types'; | ||
|
||
main().catch((error: Error): void => { | ||
console.error(error); | ||
process.exit(1); | ||
}); | ||
|
||
async function main(): Promise<void> { | ||
const githubToken = process.env.GITHUB_TOKEN; | ||
const e2eLabel = process.env.E2E_LABEL; | ||
const triggerAction = context.payload.action as PullRequestTriggerType; | ||
const removeAndApplyInstructions = `Remove and re-apply the "${e2eLabel}" label to trigger a E2E smoke test on Bitrise.`; | ||
const mergeFromMainCommitMessagePrefix = `Merge branch 'main' into`; | ||
|
||
if (!githubToken) { | ||
core.setFailed('GITHUB_TOKEN not found'); | ||
process.exit(1); | ||
} | ||
|
||
if (!e2eLabel) { | ||
core.setFailed('E2E_LABEL not found'); | ||
process.exit(1); | ||
} | ||
|
||
const { owner, repo, number: issue_number } = context.issue; | ||
const octokit: InstanceType<typeof GitHub> = getOctokit(githubToken); | ||
|
||
// Get PR information | ||
const { data: prData } = await octokit.rest.pulls.get({ | ||
owner, | ||
repo, | ||
pull_number: issue_number, | ||
}); | ||
|
||
// Check if the e2e smoke label is applied | ||
const labels = prData.labels; | ||
const hasSmokeTestLabel = labels.some((label) => label.name === e2eLabel); | ||
|
||
// Pass check since e2e smoke label is not applied | ||
if (!hasSmokeTestLabel) { | ||
console.log( | ||
`"${e2eLabel}" label not applied. Skipping Bitrise status check.`, | ||
); | ||
return; | ||
} | ||
|
||
// Define Bitrise comment tags | ||
const bitriseTag = '<!-- BITRISE_TAG -->'; | ||
const bitrisePendingTag = '<!-- BITRISE_PENDING_TAG -->'; | ||
const bitriseSuccessTag = '<!-- BITRISE_SUCCESS_TAG -->'; | ||
const bitriseFailTag = '<!-- BITRISE_FAIL_TAG -->'; | ||
|
||
// Get at least the last 30 comments | ||
const numberOfTotalComments = prData.comments; | ||
const numberOfCommentsToCheck = 30; | ||
const lastCommentPage = Math.ceil( | ||
numberOfTotalComments / numberOfCommentsToCheck, | ||
); | ||
const { data: latestCommentBatch } = await octokit.rest.issues.listComments({ | ||
owner, | ||
repo, | ||
issue_number: issue_number, | ||
page: lastCommentPage, | ||
per_page: numberOfCommentsToCheck, | ||
}); | ||
let comments = [...latestCommentBatch]; | ||
if ( | ||
numberOfTotalComments % numberOfCommentsToCheck !== 0 && | ||
lastCommentPage > 1 | ||
) { | ||
// Also fetch previous 30 comments | ||
const { data: previousCommentBatch } = | ||
await octokit.rest.issues.listComments({ | ||
owner, | ||
repo, | ||
issue_number: issue_number, | ||
page: lastCommentPage - 1, | ||
per_page: numberOfCommentsToCheck, | ||
}); | ||
comments = [...previousCommentBatch, ...comments]; | ||
} | ||
|
||
const bitriseComment = comments | ||
.reverse() | ||
.find(({ body }) => body?.includes(bitriseTag)); | ||
|
||
// Bitrise comment doesn't exist | ||
if (!bitriseComment) { | ||
core.setFailed( | ||
`No Bitrise build status comment found. ${removeAndApplyInstructions}`, | ||
); | ||
process.exit(1); | ||
} | ||
|
||
// This regex matches a 40-character hexadecimal string enclosed within <!-- and --> | ||
let bitriseCommentBody = bitriseComment.body || ''; | ||
const commitTagRegex = /<!--\s*([0-9a-f]{40})\s*-->/i; | ||
const hashMatch = bitriseCommentBody.match(commitTagRegex); | ||
let bitriseCommentCommitHash = hashMatch && hashMatch[1] ? hashMatch[1] : ''; | ||
|
||
// Get at least the last 10 commits | ||
const numberOfTotalCommits = prData.commits; | ||
const numberOfCommitsToCheck = 10; | ||
const lastCommitPage = Math.ceil( | ||
numberOfTotalCommits / numberOfCommitsToCheck, | ||
); | ||
const { data: latestCommitBatch } = await octokit.rest.pulls.listCommits({ | ||
owner, | ||
repo, | ||
pull_number: issue_number, | ||
page: lastCommitPage, | ||
per_page: numberOfCommitsToCheck, | ||
}); | ||
let commits = [...latestCommitBatch]; | ||
if ( | ||
numberOfTotalCommits % numberOfCommitsToCheck !== 0 && | ||
lastCommitPage > 1 | ||
) { | ||
// Also fetch previous 10 commits | ||
const { data: previousCommitBatch } = await octokit.rest.pulls.listCommits({ | ||
owner, | ||
repo, | ||
pull_number: issue_number, | ||
page: lastCommitPage - 1, | ||
per_page: numberOfCommitsToCheck, | ||
}); | ||
commits = [...previousCommitBatch, ...commits]; | ||
} | ||
|
||
// Relevant hashes include both merge from main commits and the last non-merge from main commit | ||
const relevantCommitHashes: string[] = []; | ||
for (const commit of commits.reverse()) { | ||
const commitMessage = commit.commit.message; | ||
relevantCommitHashes.push(commit.sha); | ||
if (!commitMessage.includes(mergeFromMainCommitMessagePrefix)) { | ||
break; | ||
} | ||
} | ||
|
||
if (triggerAction === PullRequestTriggerType.Labeled) { | ||
// A Bitrise build was triggered for the last commit | ||
bitriseCommentCommitHash = relevantCommitHashes[0]; | ||
bitriseCommentBody = bitrisePendingTag; | ||
} | ||
|
||
// Check if Bitrise comment hash matches any of the relevant commit hashes | ||
if (relevantCommitHashes.includes(bitriseCommentCommitHash)) { | ||
// Check Bitrise build status from comment | ||
const bitriseCommentPrefix = `Bitrise build status comment for commit ${bitriseCommentCommitHash}`; | ||
if (bitriseCommentBody.includes(bitrisePendingTag)) { | ||
core.setFailed(`${bitriseCommentPrefix} is pending.`); | ||
process.exit(1); | ||
} else if (bitriseCommentBody.includes(bitriseFailTag)) { | ||
core.setFailed(`${bitriseCommentPrefix} has failed.`); | ||
process.exit(1); | ||
} else if (bitriseCommentBody.includes(bitriseSuccessTag)) { | ||
console.log(`${bitriseCommentPrefix} has passed.`); | ||
return; | ||
} else { | ||
core.setFailed( | ||
`${bitriseCommentPrefix} does not contain any build status. Please verify that the build status tag exists in the comment body.`, | ||
); | ||
process.exit(1); | ||
} | ||
} else { | ||
// No build comment found for relevant commits | ||
core.setFailed( | ||
`No Bitrise build comment exists for latest commits. ${removeAndApplyInstructions}`, | ||
); | ||
process.exit(1); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import * as core from '@actions/core'; | ||
import { context, getOctokit } from '@actions/github'; | ||
import { GitHub } from '@actions/github/lib/utils'; | ||
import axios from 'axios'; | ||
|
||
main().catch((error: Error): void => { | ||
console.error(error); | ||
process.exit(1); | ||
}); | ||
|
||
async function main(): Promise<void> { | ||
const e2eLabel = process.env.E2E_LABEL; | ||
const githubToken = process.env.GITHUB_TOKEN; | ||
const e2ePipeline = process.env.E2E_PIPELINE; | ||
const workflowName = process.env.WORKFLOW_NAME; | ||
const pullRequestNumber = context.issue.number; | ||
const repoOwner = context.repo.owner; | ||
const repo = context.repo.repo; | ||
const pullRequestLink = `https://github.com/MetaMask/metamask-mobile/pull/${pullRequestNumber}`; | ||
|
||
if (!githubToken) { | ||
core.setFailed('GITHUB_TOKEN not found'); | ||
process.exit(1); | ||
} | ||
|
||
if (!e2ePipeline) { | ||
core.setFailed('E2E_PIPELINE not found'); | ||
process.exit(1); | ||
} | ||
|
||
const octokit: InstanceType<typeof GitHub> = getOctokit(githubToken); | ||
|
||
// Get the latest commit hash | ||
const pullRequestResponse = await octokit.rest.pulls.get({ | ||
owner: repoOwner, | ||
repo, | ||
pull_number: pullRequestNumber, | ||
}); | ||
|
||
const latestCommitHash = pullRequestResponse.data.head.sha; | ||
|
||
// Configure Bitrise configuration for API call | ||
const data = { | ||
build_params: { | ||
branch: process.env.GITHUB_HEAD_REF, | ||
pipeline_id: e2ePipeline, | ||
environments: [ | ||
{ | ||
mapped_to: 'GITHUB_PR_NUMBER', | ||
value: `${pullRequestNumber}`, | ||
is_expand: true, | ||
}, | ||
{ | ||
mapped_to: 'TRIGGERED_BY_PR_LABEL', | ||
value: `true`, | ||
is_expand: true, | ||
}, | ||
{ | ||
mapped_to: 'GITHUB_PR_HASH', | ||
value: `${latestCommitHash}`, | ||
is_expand: true, | ||
}, | ||
], | ||
commit_message: `Triggered by (${workflowName}) workflow in ${pullRequestLink}`, | ||
}, | ||
hook_info: { | ||
type: 'bitrise', | ||
build_trigger_token: process.env.BITRISE_BUILD_TRIGGER_TOKEN, | ||
}, | ||
triggered_by: workflowName, | ||
}; | ||
|
||
const bitriseProjectUrl = `https://app.bitrise.io/app/${process.env.BITRISE_APP_ID}`; | ||
const bitriseBuildStartUrl = `${bitriseProjectUrl}/build/start.json`; | ||
|
||
// Start Bitrise build. | ||
const bitriseBuildResponse = await axios.post(bitriseBuildStartUrl, data, { | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
}); | ||
|
||
if (!bitriseBuildResponse.data.build_slug) { | ||
core.setFailed(`Bitrise build slug not found`); | ||
process.exit(1); | ||
} | ||
|
||
const bitriseTag = '<!-- BITRISE_TAG -->'; | ||
const bitrisePendingTag = '<!-- BITRISE_PENDING_TAG -->'; | ||
const latestCommitTag = `<!-- ${latestCommitHash} -->`; | ||
const buildLink = `${bitriseProjectUrl}/pipelines/${bitriseBuildResponse.data.build_slug}`; | ||
const message = `## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](${buildLink}) **Bitrise**\n\n🔄🔄🔄 \`${e2ePipeline}\` started on Bitrise...🔄🔄🔄\n\nCommit hash: ${latestCommitHash}\nBuild link: ${buildLink}\n\n>[!NOTE]\n>- This comment will auto-update when build completes\n>- You can kick off another \`${e2ePipeline}\` on Bitrise by removing and re-applying the \`${e2eLabel}\` label on the pull request\n${bitriseTag}\n${bitrisePendingTag}\n\n${latestCommitTag}`; | ||
|
||
if (bitriseBuildResponse.status === 201) { | ||
console.log(`Started Bitrise build at ${buildLink}`); | ||
} else { | ||
core.setFailed( | ||
`Bitrise build request returned with status code ${bitriseBuildResponse.status}`, | ||
); | ||
process.exit(1); | ||
} | ||
|
||
// Reopen conversation in case it's locked | ||
const unlockConvoResponse = await octokit.rest.issues.unlock({ | ||
owner: repoOwner, | ||
repo, | ||
issue_number: pullRequestNumber, | ||
}); | ||
|
||
if (unlockConvoResponse.status === 204) { | ||
console.log(`Unlocked conversation for PR ${pullRequestLink}`); | ||
} else { | ||
core.setFailed( | ||
`Unlock conversation request returned with status code ${unlockConvoResponse.status}`, | ||
); | ||
process.exit(1); | ||
} | ||
|
||
// Look for existing Bitrise comment. | ||
const { data: comments } = await octokit.rest.issues.listComments({ | ||
owner: repoOwner, | ||
repo, | ||
issue_number: pullRequestNumber, | ||
}); | ||
|
||
const bitriseComment = comments.find(({ body }) => | ||
body?.includes(latestCommitTag), | ||
); | ||
|
||
// Existing comment exists for commit hash. Update comment with pending status. | ||
if (bitriseComment) { | ||
const updateCommentResponse = await octokit.rest.issues.updateComment({ | ||
owner: repoOwner, | ||
repo, | ||
issue_number: pullRequestNumber, | ||
body: message, | ||
comment_id: bitriseComment.id, | ||
}); | ||
|
||
if (updateCommentResponse.status === 200) { | ||
console.log(`Updating comment in pull request ${pullRequestLink}`); | ||
} else { | ||
core.setFailed( | ||
`Update comment request returned with status code ${updateCommentResponse.status}`, | ||
); | ||
process.exit(1); | ||
} | ||
} else { | ||
// Post new Bitrise comment in PR. | ||
const postCommentResponse = await octokit.rest.issues.createComment({ | ||
owner: repoOwner, | ||
repo, | ||
issue_number: pullRequestNumber, | ||
body: message, | ||
}); | ||
|
||
if (postCommentResponse.status === 201) { | ||
console.log(`Posting comment in pull request ${pullRequestLink}`); | ||
} else { | ||
core.setFailed( | ||
`Post comment request returned with status code ${postCommentResponse.status}`, | ||
); | ||
process.exit(1); | ||
} | ||
} | ||
} |
Oops, something went wrong.