Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: Post comment after e2e smoke #8495

Merged
merged 85 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
069d9c4
Post comment after e2e smoke
Cal-L Feb 1, 2024
6d325ac
Remove extra steps for testing
Cal-L Feb 6, 2024
09fed8c
Provide missing params to step
Cal-L Feb 6, 2024
6e9d35e
Manually apply PR number for now
Cal-L Feb 6, 2024
664d9b3
Create action for detecting Bitrise status
Cal-L Feb 6, 2024
1fe01fd
Fix check-bitrise-status script
Cal-L Feb 7, 2024
9effdf5
Try removing conditional
Cal-L Feb 7, 2024
43d1079
Remove condition
Cal-L Feb 7, 2024
e55a296
Update Bitrise message
Cal-L Feb 7, 2024
ad586b1
Pass PR number to bitrise build
Cal-L Feb 7, 2024
5549e15
Remove PR id
Cal-L Feb 7, 2024
bf3424c
Put PR number
Cal-L Feb 7, 2024
c3a13cc
Provide PR number as env var
Cal-L Feb 7, 2024
d158b51
Use new bitrise endpoint
Cal-L Feb 7, 2024
520c27c
Update bitrise headers
Cal-L Feb 7, 2024
737b6a0
Pass PR number as env var
Cal-L Feb 20, 2024
464c88d
Comment out environments
Cal-L Feb 20, 2024
cdadf86
Change api back to original
Cal-L Feb 20, 2024
ea2ce37
Remove environments
Cal-L Feb 20, 2024
7a358ed
Simplify Bitrise call
Cal-L Feb 20, 2024
762d1da
Remove env vars
Cal-L Feb 20, 2024
ee261d3
Specify application json
Cal-L Feb 20, 2024
2a27413
Pass env vars as strings
Cal-L Feb 20, 2024
de69596
Adjust bitrise message
Cal-L Feb 20, 2024
4263896
Refine messages
Cal-L Feb 20, 2024
bb48a45
Log comment body
Cal-L Feb 20, 2024
04563ca
Provide logs
Cal-L Feb 20, 2024
40f8601
Fix typo
Cal-L Feb 20, 2024
01bc823
Update action triggers
Cal-L Feb 22, 2024
544fa8f
Conditionally run comment step
Cal-L Feb 22, 2024
0d63325
Try setting pipeline build url
Cal-L Feb 22, 2024
f0be5e1
Use pipeline build url
Cal-L Feb 22, 2024
ca07293
Update comment
Cal-L Feb 22, 2024
2068fa4
Remove unused workflow
Cal-L Feb 22, 2024
4be9a61
Update message format
Cal-L Feb 22, 2024
64c57eb
Add comment tag to replace comment
Cal-L Feb 22, 2024
99d2c65
Add tag syntax
Cal-L Feb 22, 2024
bbd9e22
Fix comment tag
Cal-L Feb 22, 2024
eed8755
Format bitrise message
Cal-L Feb 22, 2024
22ad086
Refine messages
Cal-L Feb 22, 2024
7c72e6a
Refine bitrise message
Cal-L Feb 22, 2024
24e13b9
Change comment trigger to edit
Cal-L Feb 22, 2024
9d75bcf
Update trigger
Cal-L Feb 22, 2024
0c4f931
Rename bitrise action files
Cal-L Feb 22, 2024
65c12c5
Fix hardcoded build link
Cal-L Feb 23, 2024
a57dc62
Post fail comment to PR if step failure
Cal-L Feb 23, 2024
cca817c
Fix build failure condition
Cal-L Feb 23, 2024
29c0a15
Add failure condition step
Cal-L Feb 23, 2024
a147f8d
Get latest PR commit hash
Cal-L Feb 23, 2024
1e22620
Force fail
Cal-L Feb 23, 2024
955a913
Always run notify
Cal-L Feb 23, 2024
5d3d7f9
Fix comment step logic
Cal-L Feb 23, 2024
035bc60
Revert "Force fail"
Cal-L Feb 23, 2024
9bc30e3
Add bitrise tags to comments
Cal-L Feb 24, 2024
159874e
Force fail
Cal-L Feb 24, 2024
cc1f64f
Revert "Force fail"
Cal-L Feb 24, 2024
8b19e9e
Edit existing Bitrise comment
Cal-L Feb 25, 2024
a4cab30
Add synchronize condition to check bitrise status
Cal-L Feb 25, 2024
f77947d
Add synchronize condition to check bitrise status
Cal-L Feb 25, 2024
a96e928
Consolidate message for pending and failed Bitrise comment
Cal-L Feb 25, 2024
7795e34
Create scripts types file
Cal-L Feb 26, 2024
0b511ff
Clean up logs
Cal-L Feb 26, 2024
fed8ff6
Verify last commit has passing e2e comment
Cal-L Feb 26, 2024
b916ee5
Merge branch 'main' into ci/bitrise-message-check
Cal-L Feb 26, 2024
20d5c2d
Update fail message
Cal-L Feb 26, 2024
2bc7168
Merge branch 'ci/bitrise-message-check' of https://github.com/MetaMas…
Cal-L Feb 26, 2024
9baf7a5
Update fail message
Cal-L Feb 26, 2024
949a2ea
Consolidate logs
Cal-L Feb 26, 2024
68c61c0
Merge branch 'main' into ci/bitrise-message-check
Cal-L Feb 26, 2024
f005813
Filter out commit that merges from main
Cal-L Feb 27, 2024
d904c62
Merge branch 'ci/bitrise-message-check' of https://github.com/MetaMas…
Cal-L Feb 27, 2024
64045e9
Merge branch 'main' into ci/bitrise-message-check
Cal-L Feb 27, 2024
5bbfe61
Add comments
Cal-L Feb 27, 2024
7827395
Merge branch 'ci/bitrise-message-check' of https://github.com/MetaMas…
Cal-L Feb 27, 2024
6b46801
Merge branch 'main' into ci/bitrise-message-check
Cal-L Feb 27, 2024
ab8385f
Merge branch 'main' into ci/bitrise-message-check
Cal-L Feb 27, 2024
0e7eaa8
Enable test runs on merge from main commits
Cal-L Feb 28, 2024
cb67272
Fix reverse
Cal-L Feb 28, 2024
9c3e1b0
Merge branch 'main' into ci/bitrise-message-check
Cal-L Feb 28, 2024
e8e538a
Check latest commit on label added
Cal-L Feb 28, 2024
a463ab8
Merge branch 'ci/bitrise-message-check' of https://github.com/MetaMas…
Cal-L Feb 28, 2024
6036a02
Clean up check conditions
Cal-L Feb 28, 2024
af333f3
Merge branch 'main' into ci/bitrise-message-check
Cal-L Feb 28, 2024
d249cee
Remove console logs
Cal-L Feb 28, 2024
1c3435f
Merge branch 'ci/bitrise-message-check' of https://github.com/MetaMas…
Cal-L Feb 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading