diff --git a/.github/scripts/check-bitrise-e2e-smoke/check-e2e-smoke-trigger.ts b/.github/scripts/check-bitrise-e2e-smoke/check-e2e-smoke-trigger.ts new file mode 100644 index 00000000000..c26fdb23d00 --- /dev/null +++ b/.github/scripts/check-bitrise-e2e-smoke/check-e2e-smoke-trigger.ts @@ -0,0 +1,100 @@ +import * as core from '@actions/core'; +import { context, getOctokit } from '@actions/github'; +import { GitHub } from '@actions/github/lib/utils'; + +enum PullRequestTriggerType { + Opened = 'opened', + Reopened = 'reopened', + ReadyForReview = 'ready_for_review', + Labeled = 'labeled', +} + +main().catch((error: Error): void => { + console.error(error); + process.exit(1); +}); + +async function main(): Promise { + const githubToken = process.env.GITHUB_TOKEN; + const e2eLabel = process.env.E2E_LABEL; + const triggerAction = context.payload.action as PullRequestTriggerType; + const pullRequestLink = `https://github.com/MetaMask/metamask-mobile/pull/${context.issue.number}`; + let shouldTriggerE2E = false; + + if (!githubToken) { + core.setFailed('GITHUB_TOKEN not found'); + process.exit(1); + } + + if (!e2eLabel) { + core.setFailed('E2E_LABEL not found'); + process.exit(1); + } + + console.log(`Workflow triggered by the event (${triggerAction})`); + + switch (triggerAction) { + case PullRequestTriggerType.Opened: + shouldTriggerE2E = !context.payload.pull_request?.draft; + if (!shouldTriggerE2E) { + console.log(`Skipping E2E smoke since opened PR is in draft.`); + } else { + console.log(`Starting E2E smoke since opened PR is ready for review.`); + } + break; + case PullRequestTriggerType.Reopened: + shouldTriggerE2E = !context.payload.pull_request?.draft; + if (!shouldTriggerE2E) { + console.log(`Skipping E2E smoke since reopened PR is in draft.`); + } else { + console.log(`Starting E2E smoke since reopened PR is ready for review.`); + } + break; + case PullRequestTriggerType.ReadyForReview: + shouldTriggerE2E = true; + console.log( + `Starting E2E smoke since PR has changed to ready for review.`, + ); + break; + case PullRequestTriggerType.Labeled: + shouldTriggerE2E = e2eLabel === context.payload.label.name; + if (!shouldTriggerE2E) { + console.log( + `Skipping E2E smoke since (${e2eLabel}) is not the label that triggered this workflow.`, + ); + } else { + console.log( + `Starting E2E smoke since (${e2eLabel}) is the label that triggered this workflow.`, + ); + } + break; + } + + if (shouldTriggerE2E) { + // Apply the E2E smoke label + const { owner, repo, number: issue_number } = context.issue; + const octokit: InstanceType = getOctokit(githubToken); + try { + const applyLabelResponse = await octokit.rest.issues.addLabels({ + owner, + repo, + issue_number, + labels: [e2eLabel], + }); + if (applyLabelResponse.status === 200) { + console.log(`Applied (${e2eLabel}) label to PR ${pullRequestLink}`); + } else { + core.setFailed( + `Failed to apply (${e2eLabel}) label to ${pullRequestLink}`, + ); + process.exit(1); + } + } catch (error) { + console.log(`Error occured when applying label: ${error}`); + process.exit(1); + } + } + + // Set the output for the next step to use. + core.setOutput("shouldTriggerE2E", shouldTriggerE2E); +} diff --git a/.github/scripts/check-bitrise-e2e-smoke/run-bitrise-e2e-smoke.ts b/.github/scripts/check-bitrise-e2e-smoke/run-bitrise-e2e-smoke.ts new file mode 100644 index 00000000000..ba41e29745b --- /dev/null +++ b/.github/scripts/check-bitrise-e2e-smoke/run-bitrise-e2e-smoke.ts @@ -0,0 +1,108 @@ +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 { + 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 = getOctokit(githubToken); + + const data = { + hook_info: { + type: 'bitrise', + build_trigger_token: process.env.BITRISE_BUILD_TRIGGER_TOKEN, + }, + build_params: { + branch: process.env.GITHUB_HEAD_REF, + pipeline_id: e2ePipeline, + commit_message: `Triggered by (${workflowName}) workflow in ${pullRequestLink}`, + }, + triggered_by: workflowName, + }; + + const bitriseProjectUrl = `https://app.bitrise.io/app/${process.env.BITRISE_APP_ID}`; + + // Start Bitrise build. + const bitriseBuildResponse = await axios.post( + `${bitriseProjectUrl}/build/start.json`, + data, + { + headers: { + 'Content-Type': 'application/json', + }, + }, + ); + + if (!bitriseBuildResponse.data.build_slug) { + core.setFailed(`Bitrise build slug not found`); + process.exit(1); + } + + const buildLink = `${bitriseProjectUrl}/pipelines/${bitriseBuildResponse.data.build_slug}`; + const message = `E2E test started on Bitrise: ${buildLink}\nYou can also kick off another Bitrise E2E smoke test by removing and re-applying the (${e2eLabel}) label`; + + if (bitriseBuildResponse.status === 201) { + console.log(message); + } 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); + } + + // Post build link in PR comments. + 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); + } +} diff --git a/.github/workflows/check-bitrise-e2e-smoke.yml b/.github/workflows/check-bitrise-e2e-smoke.yml new file mode 100644 index 00000000000..86502729829 --- /dev/null +++ b/.github/workflows/check-bitrise-e2e-smoke.yml @@ -0,0 +1,46 @@ +name: Check Bitrise E2E Smoke Tests + +on: + pull_request: + types: [opened, reopened, ready_for_review, labeled] + +env: + E2E_LABEL: 'Run Smoke E2E' + E2E_PIPELINE: 'pr_smoke_e2e_pipeline' + WORKFLOW_NAME: 'check-bitrise-e2e-smoke' + +jobs: + check-bitrise-e2e-smoke: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' + cache: yarn + + - name: Install Axios + run: yarn add axios + + - name: Install dependencies + run: yarn --immutable + + - name: Check E2E trigger + id: check-trigger + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: yarn run check-e2e-smoke-trigger + + - name: Run Bitrise E2E + if: steps.check-trigger.outputs.shouldTriggerE2E == 'true' + env: + BITRISE_BUILD_TRIGGER_TOKEN: ${{ secrets.BITRISE_BUILD_TRIGGER_TOKEN }} + BITRISE_APP_ID: 'be69d4368ee7e86d' + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: yarn run run-bitrise-e2e-smoke diff --git a/package.json b/package.json index ee341643d5d..c25cbaf0270 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,8 @@ "deduplicate": "yarn yarn-deduplicate", "create-release": "./scripts/set-versions.sh && yarn update-changelog", "add-release-label-to-pr-and-linked-issues": "ts-node ./.github/scripts/add-release-label-to-pr-and-linked-issues.ts", + "check-e2e-smoke-trigger": "ts-node ./.github/scripts/check-bitrise-e2e-smoke/check-e2e-smoke-trigger.ts", + "run-bitrise-e2e-smoke": "ts-node ./.github/scripts/check-bitrise-e2e-smoke/run-bitrise-e2e-smoke.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", "check-template-and-add-labels": "ts-node ./.github/scripts/check-template-and-add-labels.ts",