From a61eaeadafda1dbeefbf63ea68b047d18cb85ed0 Mon Sep 17 00:00:00 2001 From: Radoslaw Krzemien Date: Tue, 21 Feb 2023 14:46:02 +0100 Subject: [PATCH] Refactor test configuration [WiP] Updated the mocks/test repo files configuration to be more readable/maintainable. Also added `name` properties to `preDeploy` workflow steps, since using `name` is the easiest and most stable way of referencing the steps for mocking See: https://github.com/Expensify/App/issues/13604 --- .github/workflows/preDeploy.yml | 21 +- workflow_tests/preDeploy.test.ts | 523 ++++++++++++++++--------------- 2 files changed, 291 insertions(+), 253 deletions(-) diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml index 0e77dbe4704f..27412f07d253 100644 --- a/.github/workflows/preDeploy.yml +++ b/.github/workflows/preDeploy.yml @@ -6,9 +6,11 @@ on: jobs: lint: + name: Run lint workflow uses: Expensify/App/.github/workflows/lint.yml@main test: + name: Run test workflow uses: Expensify/App/.github/workflows/test.yml@main confirmPassingBuild: @@ -17,12 +19,14 @@ jobs: if: ${{ always() }} steps: - - if: ${{ needs.lint.result == 'failure' || needs.test.result == 'failure' }} + - name: Announce failed workflow in Slack + if: ${{ needs.lint.result == 'failure' || needs.test.result == 'failure' }} uses: Expensify/App/.github/actions/composite/announceFailedWorkflowInSlack@main with: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - - if: ${{ needs.lint.result == 'failure' || needs.test.result == 'failure' }} + - name: Exit failed workflow + if: ${{ needs.lint.result == 'failure' || needs.test.result == 'failure' }} run: exit 1 chooseDeployActions: @@ -75,6 +79,7 @@ jobs: createNewVersion: needs: chooseDeployActions if: ${{ fromJSON(needs.chooseDeployActions.outputs.SHOULD_DEPLOY) }} + name: Create new version uses: Expensify/App/.github/workflows/createNewVersion.yml@main secrets: inherit @@ -82,7 +87,8 @@ jobs: needs: [chooseDeployActions, createNewVersion] runs-on: ubuntu-latest steps: - - uses: softprops/turnstyle@ca99add00ff0c9cbc697d22631d2992f377e5bd5 + - name: Run turnstyle + uses: softprops/turnstyle@ca99add00ff0c9cbc697d22631d2992f377e5bd5 with: poll-interval-seconds: 10 env: @@ -105,7 +111,8 @@ jobs: INPUTS: '{ "PULL_REQUEST_NUMBER": "${{ needs.chooseDeployActions.outputs.MERGED_PR }}", "NEW_VERSION": "${{ needs.createNewVersion.outputs.NEW_VERSION }}" }' # Version: 3.0.2 - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - name: Checkout staging + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: ref: staging fetch-depth: 0 @@ -156,7 +163,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - - if: ${{ failure() }} + - name: Announce failed workflow in Slack + if: ${{ failure() }} uses: Expensify/App/.github/actions/composite/announceFailedWorkflowInSlack@main with: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} @@ -189,7 +197,8 @@ jobs: if: ${{ github.actor != 'OSBotify' && !fromJSON(needs.isExpensifyEmployee.outputs.IS_EXPENSIFY_EMPLOYEE) }} steps: # Version: 2.3.4 - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - name: Checkout + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: token: ${{ secrets.OS_BOTIFY_TOKEN }} diff --git a/workflow_tests/preDeploy.test.ts b/workflow_tests/preDeploy.test.ts index ff5701576e97..250fc05f51b6 100644 --- a/workflow_tests/preDeploy.test.ts +++ b/workflow_tests/preDeploy.test.ts @@ -3,38 +3,39 @@ import {Act} from '@kie/act-js'; import path from 'path'; let mockGithub: MockGithub; +const FILES_TO_COPY_INTO_TEST_REPO = [ + { + src: path.resolve(__dirname, '..', '.github', 'actions'), + dest: '.github/actions', + }, + { + src: path.resolve(__dirname, '..', '.github', 'libs'), + dest: '.github/libs', + }, + { + src: path.resolve(__dirname, '..', '.github', 'scripts'), + dest: '.github/scripts', + }, + { + src: path.resolve(__dirname, '..', '.github', 'workflows', 'preDeploy.yml'), + dest: '.github/workflows/preDeploy.yml', + }, + { + src: path.resolve(__dirname, '..', '.github', '.eslintrc.js'), + dest: '.github/.eslintrc.js', + }, + { + src: path.resolve(__dirname, '..', '.github', 'actionlint.yaml'), + dest: '.github/actionlint.yaml', + }, +]; beforeEach(async () => { // create a local repository and copy required files mockGithub = new MockGithub({ repo: { testWorkflowsRepo: { - files: [ - { - src: path.resolve(__dirname, '..', '.github', 'actions'), - dest: '.github/actions', - }, - { - src: path.resolve(__dirname, '..', '.github', 'libs'), - dest: '.github/libs', - }, - { - src: path.resolve(__dirname, '..', '.github', 'scripts'), - dest: '.github/scripts', - }, - { - src: path.resolve(__dirname, '..', '.github', 'workflows', 'preDeploy.yml'), - dest: '.github/workflows/preDeploy.yml', - }, - { - src: path.resolve(__dirname, '..', '.github', '.eslintrc.js'), - dest: '.github/.eslintrc.js', - }, - { - src: path.resolve(__dirname, '..', '.github', 'actionlint.yaml'), - dest: '.github/actionlint.yaml', - }, - ], + files: FILES_TO_COPY_INTO_TEST_REPO, }, }, }); @@ -46,230 +47,258 @@ afterEach(async () => { await mockGithub.teardown(); }); +const LINT_JOB_MOCK_STEPS = [ + { + name: 'Run lint workflow', + mockWith: 'echo [MOCK] Running lint workflow', + }, +]; + +const TEST_JOB_MOCK_STEPS = [ + { + name: 'Run test workflow', + mockWith: 'echo [MOCK] Running test workflow', + }, +]; + +const CONFIRM_PASSING_BUILD_JOB_MOCK_STEPS = [ + { + name: 'Announce failed workflow in Slack', + mockWith: 'echo [MOCK] Announcing failed workflow in slack', + }, + { + name: 'Exit failed workflow', + mockWith: 'echo [MOCK] Exiting with failed status', + }, +]; + +const CHOOSE_DEPLOY_ACTIONS_JOB_MOCK_STEPS = [ + { + name: 'Get merged pull request', + mockWith: 'echo [MOCK] Getting merged pull request', + }, + { + name: 'Check if StagingDeployCash is locked', + mockWith: 'echo [MOCK] Checking StagingDeployCash', + }, + { + name: 'Check if merged pull request was an automated PR', + mockWith: 'echo [MOCK] Checking for automated PR', + }, + { + name: 'Check if merged pull request has `CP Staging` label', + mockWith: 'echo [MOCK] Checking CP Staging', + }, + { + name: 'Check if merged pull request should trigger a deploy', + mockWith: 'echo [MOCK] Checking deploy trigger', + }, +]; + +const SKIP_DEPLOY_MOCK_STEPS = [ + { + name: 'Comment on deferred PR', + mockWith: 'echo [MOCK] Skipping deploy', + }, +]; + +const CREATE_NEW_VERSION_JOB_MOCK_STEPS = [ + { + name: 'Create new version', + mockWith: 'echo [MOCK] Creating new version', + }, +]; + +const UPDATE_STAGING_JOB_MOCK_STEPS = [ + { + name: 'Run turnstyle', + mockWith: 'echo [MOCK] Running turnstyle', + }, + { + name: 'Update staging branch from main', + mockWith: 'echo [MOCK] Updating staging branch', + }, + { + name: 'Cherry-pick PR to staging', + mockWith: 'echo [MOCK] Cherry picking', + }, + { + name: 'Checkout staging', + mockWith: 'echo [MOCK] Checking out staging', + }, + { + name: 'Tag staging', + mockWith: 'echo [MOCK] Tagging staging', + }, + { + name: 'Update StagingDeployCash', + mockWith: 'echo [MOCK] Updating StagingDeployCash', + }, + { + name: 'Find open StagingDeployCash', + mockWith: 'echo [MOCK] Finding open StagingDeployCash', + }, + { + name: 'Comment in StagingDeployCash to alert Applause that a new pull request has been cherry-picked', + mockWith: 'echo [MOCK] Commenting in StagingDeployCash', + }, + { + name: 'Wait for staging deploys to finish', + mockWith: 'echo [MOCK] Waiting for staging deploy to finish', + }, + { + name: 'Comment in StagingDeployCash to alert Applause that cherry-picked pull request has been deployed.', + mockWith: 'echo [MOCK] Commenting in StagingDeployCash', + }, + { + name: 'Announce failed workflow in Slack', + mockWith: 'echo [MOCK] Announcing failed workflow in slack', + }, +]; + +const IS_EXPENSIFY_EMPLOYEE_JOB_MOCK_STEPS = [ + { + name: 'Get merged pull request', + mockWith: 'echo [MOCK] Getting merged pull request', + }, + { + name: 'Check whether the actor is member of Expensify/expensify team', + mockWith: 'echo [MOCK] Checking actor\'s Expensify membership', + }, +]; + +const NEW_CONTRIBUTOR_WELCOME_MESSAGE_JOB_MOCK_STEPS = [ + { + name: 'Checkout', + mockWith: 'echo [MOCK] Checking out', + }, + { + name: 'Get merged pull request', + mockWith: 'echo [MOCK] Getting merged pull request', + }, + { + name: 'Get PR count for ${{ steps.getMergedPullRequest.outputs.author }}', + mockWith: 'echo [MOCK] Getting PR count', + }, + { + name: 'Comment on ${{ steps.getMergedPullRequest.outputs.author }}\'s first pull request!', + mockWith: 'echo [MOCK] Creating comment', + }, +]; + +const E2E_TESTS_JOB_MOCK_STEPS = [ + { + name: 'Checkout', + mockWith: 'echo [MOCK] Checking out', + }, + { + name: 'Setup node', + mockWith: 'echo [MOCK] Setting up node', + }, + { + name: 'Setup ruby', + mockWith: 'echo [MOCK] Setting up ruby', + }, + { + name: 'Gradle cache', + mockWith: 'echo [MOCK] Building with gradle', + }, + { + name: 'Make zip directory for everything to send to AWS Device Farm', + mockWith: 'echo [MOCK] Creating zip directory', + }, + { + name: 'Checkout "Compare" commit', + mockWith: 'echo [MOCK] Checking out compare commit', + }, + { + name: 'Install node packages', + mockWith: 'echo [MOCK] Installing node packages', + }, + { + name: 'Build "Compare" APK', + mockWith: 'echo [MOCK] Building compare apk', + }, + { + name: 'Copy "Compare" APK', + mockWith: 'echo [MOCK] Copying compare apk', + }, + { + name: 'Checkout "Baseline" commit (last release)', + mockWith: 'echo [MOCK] Checking out baseline commit', + }, + { + name: 'Install node packages', + mockWith: 'echo [MOCK] Installing node packages', + }, + { + name: 'Build "Baseline" APK', + mockWith: 'echo [MOCK] Building baseline apk', + }, + { + name: 'Copy "Baseline" APK', + mockWith: 'echo [MOCK] Copying baseline apk', + }, + { + name: 'Checkout previous branch for source code to run on AWS Device farm', + mockWith: 'echo [MOCK] Checking out previous branch', + }, + { + name: 'Copy e2e code into zip folder', + mockWith: 'echo [MOCK] Copying e2e tests', + }, + { + name: 'Zip everything in the zip directory up', + mockWith: 'echo [MOCK] Zipping everything', + }, + { + name: 'Configure AWS Credentials', + mockWith: 'echo [MOCK] Configuring AWS credentials', + }, + { + name: 'Schedule AWS Device Farm test run', + mockWith: 'echo [MOCK] Scheduling AWS test run', + }, + { + name: 'Unzip AWS Device Farm results', + mockWith: 'echo [MOCK] Unzipping test results', + }, + { + name: 'Print AWS Device Farm run results', + mockWith: 'echo [MOCK] Printing test results', + }, + { + name: 'Set output of AWS Device Farm into GitHub ENV', + mockWith: 'echo [MOCK] Setting AWS output', + }, + { + name: 'Get merged pull request', + mockWith: 'echo [MOCK] Getting merged pull request', + }, + { + name: 'Leave output of AWS Device Farm as a PR comment', + mockWith: 'echo [MOCK] Leaving comment with test results', + }, + { + name: 'Check if test failed, if so leave a deploy blocker label', + mockWith: 'echo [MOCK] Checking if tests failed', + }, +]; + const WIP_MOCK_STEPS = { - lint: [ - { - uses: 'Expensify/App/.github/workflows/lint.yml@main', - mockWith: 'echo [MOCK] Running lint workflow', - }, - ], - test: [ - { - uses: 'Expensify/App/.github/workflows/test.yml@main', - mockWith: 'echo [MOCK] Running test workflow', - }, - ], - confirmPassingBuild: [ - { - uses: 'Expensify/App/.github/actions/composite/announceFailedWorkflowInSlack@main', - mockWith: 'echo [MOCK] Announcing failed workflow in slack', - }, - { - run: 'exit 1', - mockWith: 'echo [MOCK] Exiting with failed status', - }, - ], - chooseDeployActions: [ - { - uses: 'actions-ecosystem/action-get-merged-pull-request@59afe90821bb0b555082ce8ff1e36b03f91553d9', - mockWith: 'echo [MOCK] Getting merged pull request', - }, - { - uses: 'Expensify/App/.github/actions/javascript/isStagingDeployLocked@main', - mockWith: 'echo [MOCK] Checking StagingDeployCash', - }, - { - run: 'echo "IS_AUTOMATED_PR=${{ github.actor == \'OSBotify\' }}" >> "$GITHUB_OUTPUT"', - mockWith: 'echo [MOCK] Checking for automated PR', - }, - { - run: 'echo "HAS_CP_LABEL=${{ contains(steps.getMergedPullRequest.outputs.labels, \'CP Staging\') }}" >> "$GITHUB_OUTPUT"', - mockWith: 'echo [MOCK] Checking CP Staging', - }, - { - run: 'echo "SHOULD_DEPLOY=${{ fromJSON(steps.hasCherryPickLabel.outputs.HAS_CP_LABEL) || (!fromJSON(steps.isStagingDeployLocked.outputs.IS_LOCKED) && !fromJSON(steps.isAutomatedPullRequest.outputs.IS_AUTOMATED_PR)) }}" >> "$GITHUB_OUTPUT"', - mockWith: 'echo [MOCK] Checking deploy trigger', - }, - ], - skipDeploy: [ - { - uses: 'actions-ecosystem/action-create-comment@cd098164398331c50e7dfdd0dfa1b564a1873fac', - mockWith: 'echo [MOCK] Skipping deploy', - }, - ], - createNewVersion: [ - { - uses: 'Expensify/App/.github/workflows/createNewVersion.yml@main', - mockWith: 'echo [MOCK] Creating new version', - }, - ], - updateStaging: [ - { - uses: 'softprops/turnstyle@ca99add00ff0c9cbc697d22631d2992f377e5bd5', - mockWith: 'echo [MOCK] Running turnstyle', - }, - { - uses: 'Expensify/App/.github/actions/composite/updateProtectedBranch@main', - mockWith: 'echo [MOCK] Updating staging branch', - }, - { - uses: 'Expensify/App/.github/actions/javascript/triggerWorkflowAndWait@main', - mockWith: 'echo [MOCK] Cherry picking', - }, - { - uses: 'actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8', - mockWith: 'echo [MOCK] Checking out staging', - }, - { - run: 'git tag ${{ needs.createNewVersion.outputs.NEW_VERSION }}', - mockWith: 'echo [MOCK] Tagging staging', - }, - { - uses: 'Expensify/App/.github/actions/javascript/createOrUpdateStagingDeploy@main', - mockWith: 'echo [MOCK] Updating StagingDeployCash', - }, - { - run: 'echo "STAGING_DEPLOY_CASH=$(gh issue list --label StagingDeployCash --json number --jq \'.[0].number\')" >> "$GITHUB_OUTPUT"', - mockWith: 'echo [MOCK] Finding open StagingDeployCash', - }, - { - name: 'Wait for staging deploys to finish', - mockWith: 'echo [MOCK] Waiting for staging deploy to finish', - }, - { - name: 'Comment in StagingDeployCash to alert Applause that a new pull request has been cherry-picked', - mockWith: 'echo [MOCK] Commenting in StagingDeployCash', - }, - { - name: 'Comment in StagingDeployCash to alert Applause that cherry-picked pull request has been deployed.', - mockWith: 'echo [MOCK] Commenting in StagingDeployCash', - }, - { - uses: 'Expensify/App/.github/actions/composite/announceFailedWorkflowInSlack@main', - mockWith: 'echo [MOCK] Announcing failed workflow in slack', - }, - ], - isExpensifyEmployee: [ - { - name: 'Get merged pull request', - mockWith: 'echo [MOCK] Getting merged pull request', - }, - { - name: 'Check whether the actor is member of Expensify/expensify team', - mockWith: 'echo [MOCK] Checking actor\'s Expensify membership', - }, - ], - newContributorWelcomeMessage: [ - { - uses: 'actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8', - mockWith: 'echo [MOCK] Checking out', - }, - { - name: 'Get merged pull request', - mockWith: 'echo [MOCK] Getting merged pull request', - }, - { - name: 'Get PR count for ${{ steps.getMergedPullRequest.outputs.author }}', - mockWith: 'echo [MOCK] Getting PR count', - }, - { - uses: 'actions-ecosystem/action-create-comment@cd098164398331c50e7dfdd0dfa1b564a1873fac', - mockWith: 'echo [MOCK] Creating comment', - }, - ], - 'e2e-tests': [ - { - uses: 'actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8', - mockWith: 'echo [MOCK] Checking out', - }, - { - uses: 'Expensify/App/.github/actions/composite/setupNode@main', - mockWith: 'echo [MOCK] Setting up node', - }, - { - name: 'Gradle cache', - mockWith: 'echo [MOCK] Building with gradle', - }, - { - name: 'Make zip directory for everything to send to AWS Device Farm', - mockWith: 'echo [MOCK] Creating zip directory', - }, - { - name: 'Checkout "Compare" commit', - mockWith: 'echo [MOCK] Checking out compare commit', - }, - { - name: 'Install node packages', - mockWith: 'echo [MOCK] Installing node packages', - }, - { - name: 'Build "Compare" APK', - mockWith: 'echo [MOCK] Building compare apk', - }, - { - name: 'Copy "Compare" APK', - mockWith: 'echo [MOCK] Copying compare apk', - }, - { - name: 'Checkout "Baseline" commit (last release)', - mockWith: 'echo [MOCK] Checking out baseline commit', - }, - { - name: 'Build "Baseline" APK', - mockWith: 'echo [MOCK] Building baseline apk', - }, - { - name: 'Copy "Baseline" APK', - mockWith: 'echo [MOCK] Copying baseline apk', - }, - { - name: 'Checkout previous branch for source code to run on AWS Device farm', - mockWith: 'echo [MOCK] Checking out previous branch', - }, - { - name: 'Copy e2e code into zip folder', - mockWith: 'echo [MOCK] Copying e2e tests', - }, - { - name: 'Zip everything in the zip directory up', - mockWith: 'echo [MOCK] Zipping everything', - }, - { - name: 'Configure AWS Credentials', - mockWith: 'echo [MOCK] Configuring AWS credentials', - }, - { - name: 'Schedule AWS Device Farm test run', - mockWith: 'echo [MOCK] Scheduling AWS test run', - }, - { - name: 'Unzip AWS Device Farm results', - mockWith: 'echo [MOCK] Unzipping test results', - }, - { - name: 'Print AWS Device Farm run results', - mockWith: 'echo [MOCK] Printing test results', - }, - { - name: 'Set output of AWS Device Farm into GitHub ENV', - mockWith: 'echo [MOCK] Setting AWS output', - }, - { - name: 'Get merged pull request', - mockWith: 'echo [MOCK] Getting merged pull request', - }, - { - name: 'Leave output of AWS Device Farm as a PR comment', - mockWith: 'echo [MOCK] Leaving comment with test results', - }, - { - name: 'Check if test failed, if so leave a deploy blocker label', - mockWith: 'echo [MOCK] Checking if tests failed', - }, - ], + lint: LINT_JOB_MOCK_STEPS, + test: TEST_JOB_MOCK_STEPS, + confirmPassingBuild: CONFIRM_PASSING_BUILD_JOB_MOCK_STEPS, + chooseDeployActions: CHOOSE_DEPLOY_ACTIONS_JOB_MOCK_STEPS, + skipDeploy: SKIP_DEPLOY_MOCK_STEPS, + createNewVersion: CREATE_NEW_VERSION_JOB_MOCK_STEPS, + updateStaging: UPDATE_STAGING_JOB_MOCK_STEPS, + isExpensifyEmployee: IS_EXPENSIFY_EMPLOYEE_JOB_MOCK_STEPS, + newContributorWelcomeMessage: NEW_CONTRIBUTOR_WELCOME_MESSAGE_JOB_MOCK_STEPS, + 'e2e-tests': E2E_TESTS_JOB_MOCK_STEPS, }; -test('test preDeploy push to main', async () => { +test('test workflow preDeploy push to main', async () => { // get path to the local test repo const repoPath = mockGithub.repo.getPath('testWorkflowsRepo') || '';