Skip to content

Commit

Permalink
Merge branch 'main' into release/7.17.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sethkfman authored Mar 1, 2024
2 parents 495c915 + e0698b6 commit a3a413e
Show file tree
Hide file tree
Showing 425 changed files with 16,811 additions and 7,842 deletions.
175 changes: 175 additions & 0 deletions .github/scripts/bitrise/check-bitrise-e2e-status.ts
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);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import * as core from '@actions/core';
import { context, getOctokit } from '@actions/github';
import { GitHub } from '@actions/github/lib/utils';

enum PullRequestTriggerType {
ReadyForReview = 'ready_for_review',
Labeled = 'labeled',
}
import { PullRequestTriggerType } from '../scripts.types';

main().catch((error: Error): void => {
console.error(error);
Expand Down Expand Up @@ -78,5 +74,5 @@ async function main(): Promise<void> {
}

// Set the output for the next step to use.
core.setOutput("shouldTriggerE2E", shouldTriggerE2E);
core.setOutput('shouldTriggerE2E', shouldTriggerE2E);
}
166 changes: 166 additions & 0 deletions .github/scripts/bitrise/run-bitrise-e2e-tests.ts
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);
}
}
}
Loading

0 comments on commit a3a413e

Please sign in to comment.