Ocrvs 11215 #9580
Workflow file for this run
This file contains hidden or 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 Source Code Form is subject to the terms of the Mozilla Public | |
| # License, v. 2.0. If a copy of the MPL was not distributed with this | |
| # file, You can obtain one at https://mozilla.org/MPL/2.0/. | |
| # | |
| # OpenCRVS is also distributed under the terms of the Civil Registration | |
| # & Healthcare Disclaimer located at http://opencrvs.org/license. | |
| # | |
| # Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. | |
| name: Deploy PR to feature environment | |
| on: | |
| pull_request: | |
| types: | |
| - labeled | |
| - opened | |
| - synchronize | |
| workflow_dispatch: | |
| inputs: | |
| pr_number: | |
| description: 'PR number' | |
| required: true | |
| type: string | |
| keep_e2e: | |
| description: 'Keep E2E environment after tests' | |
| required: true | |
| type: boolean | |
| concurrency: | |
| group: ${{ inputs.pr_number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| check_skip_e2e_label: | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| skip_e2e: ${{ steps.set_skip_flag.outputs.SKIP_E2E }} | |
| steps: | |
| - uses: actions/checkout@v3 | |
| - name: Check for skip-e2e label | |
| id: set_skip_flag | |
| run: | | |
| SKIP=false | |
| mapfile -t LABELS < <(jq -r '.pull_request.labels // [] | .[].name' $GITHUB_EVENT_PATH) | |
| echo "All labels on this PR:" | |
| for label in "${LABELS[@]}"; do | |
| echo "- $label" | |
| if [[ "$label" == "Skip e2e" ]]; then | |
| SKIP=true | |
| fi | |
| done | |
| echo "SKIP_E2E=$SKIP" >> $GITHUB_OUTPUT | |
| if [ "$SKIP" == "true" ]; then | |
| echo "skip-e2e label detected — skipping e2e" | |
| fi | |
| echo "SKIP_E2E=$SKIP" >> $GITHUB_OUTPUT | |
| generate_stack_name_and_branch: | |
| needs: check_skip_e2e_label | |
| if: ${{ needs.check_skip_e2e_label.outputs.skip_e2e != 'true' }} | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| branch_name: ${{ steps.set_branch_and_pr_number.outputs.BRANCH_NAME }} | |
| pr_number: ${{ steps.set_branch_and_pr_number.outputs.PR_NUMBER }} | |
| keep_e2e: ${{ steps.set_branch_and_pr_number.outputs.keep_e2e }} | |
| author: ${{ steps.get_author.outputs.AUTHOR }} | |
| slugified_branch: ${{ steps.slugify_bname.outputs.stack }} | |
| e2e_branch: ${{ steps.get_e2e_branch.outputs.branch }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Get input data (when manually triggered) | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| if: ${{ github.event_name == 'workflow_dispatch' }} | |
| run: | | |
| PR_NUMBER=${{ inputs.pr_number }} | |
| PR_DATA=$(gh pr view $PR_NUMBER --json headRefName,headRefOid) | |
| BRANCH_NAME=$(echo "$PR_DATA" | jq -r '.headRefName') | |
| echo "PR_NUMBER=${PR_NUMBER}" >> $GITHUB_ENV | |
| echo "keep_e2e=${{ inputs.keep_e2e }}" >> $GITHUB_ENV | |
| echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV | |
| - name: Get PR labels on push event | |
| uses: 8BitJonny/[email protected] | |
| if: github.event_name == 'push' | |
| id: PR_push | |
| # TODO: Replace curl usage to gh cli | |
| - name: Get PR labels on pull_request event | |
| id: PR_pull | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| pr_number=${{ github.event.pull_request.number }} | |
| response=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos/${{ github.repository }}/issues/${pr_number}") | |
| labels=$(echo "$response" | jq '.labels | map(.name) | join(",")' | sed 's/"//g') | |
| echo pr_labels=$labels >> $GITHUB_OUTPUT | |
| - name: Get input data (when triggered by event) | |
| if: github.event_name != 'workflow_dispatch' | |
| id: label_check | |
| run: | | |
| if [[ ${{github.event_name}} == 'pull_request' ]] | |
| then | |
| labels="${{ steps.PR_pull.outputs.pr_labels }}" | |
| pr_number=${{ github.event.pull_request.number }} | |
| else | |
| labels="${{ steps.PR_push.outputs.pr_labels }}" | |
| pr_number="${{ steps.PR_push.outputs.number }}" | |
| fi | |
| echo "Labels found: $labels" | |
| echo "PR number: $pr_number" | |
| if [[ "${labels}" != *"🔒 Keep e2e"* ]]; then | |
| echo "Label not found or incorrect, skipping dispatch." | |
| echo keep_e2e=false >> $GITHUB_ENV | |
| else | |
| echo "Correct label added, dispatching deploy workflow." | |
| echo keep_e2e=true >> $GITHUB_ENV | |
| fi | |
| echo PR_NUMBER=$pr_number >> $GITHUB_ENV | |
| - name: Get Branch Name (on PR creation) | |
| if: ${{ github.event_name != 'workflow_dispatch' }} | |
| run: | | |
| echo "BRANCH_NAME=$(echo ${{ github.head_ref }})" >> $GITHUB_ENV | |
| - name: Get e2e branch | |
| id: get_e2e_branch | |
| # ls-remote should provide list of branches. When none are found, it should be empty string. | |
| run: | | |
| e2e_repo_url="https://github.com/opencrvs/e2e.git" | |
| if [ -n "$(git ls-remote --heads "$e2e_repo_url" "refs/heads/${{ github.head_ref }}")" ]; then | |
| e2e_branch=${{ github.head_ref }} | |
| elif [ -n "$(git ls-remote --heads "$e2e_repo_url" "refs/heads/${{ github.base_ref }}")" ]; then | |
| e2e_branch="${{ github.base_ref }}" | |
| else | |
| e2e_branch="develop" | |
| fi | |
| echo "e2e branch name=${e2e_branch}" | |
| echo "branch=$e2e_branch" >> $GITHUB_OUTPUT | |
| - name: Get PR Author | |
| id: get_author | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| AUTHOR=$(gh pr view $PR_NUMBER --json author --jq '.author.login') | |
| echo "PR is created by $AUTHOR" | |
| echo "AUTHOR=$AUTHOR" >> $GITHUB_ENV | |
| echo "AUTHOR=$AUTHOR" >> $GITHUB_OUTPUT | |
| - name: Set output variables | |
| id: set_branch_and_pr_number | |
| run: | | |
| echo "BRANCH_NAME=${{ env.BRANCH_NAME }}" >> $GITHUB_OUTPUT | |
| echo "PR_NUMBER=${{ env.PR_NUMBER }}" >> $GITHUB_OUTPUT | |
| echo "keep_e2e=${{ env.keep_e2e }}" >> $GITHUB_OUTPUT | |
| - name: Slugify the branch name | |
| id: slugify_bname | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| function slugify(str) { | |
| return str | |
| .toLowerCase() | |
| .replace(/[^\w\s-]/g, '') | |
| .trim() | |
| .replace(/\s+/g, '-') | |
| .replace(/-+/g, '-') | |
| .substr(0, 30) | |
| .replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, ''); | |
| } | |
| core.setOutput('stack', slugify('${{ env.BRANCH_NAME }}')); | |
| trigger-build: | |
| if: ${{ (github.event_name == 'workflow_dispatch') || (!contains(github.actor, 'bot') && github.event.pull_request.head.repo.fork == false) }} | |
| needs: generate_stack_name_and_branch | |
| uses: ./.github/workflows/build-images-from-branch.yml | |
| with: | |
| branch_tag_name: ${{ needs.generate_stack_name_and_branch.outputs.branch_name }} | |
| skip_security_check: true | |
| secrets: inherit | |
| trigger-e2e: | |
| runs-on: ubuntu-24.04 | |
| needs: [check_skip_e2e_label, generate_stack_name_and_branch, trigger-build] | |
| if: ${{ needs.check_skip_e2e_label.outputs.skip_e2e != 'true' }} | |
| outputs: | |
| run_id: ${{ steps.dispatch_e2e.outputs.run_id }} | |
| deployment_link: ${{ steps.print-links.outputs.deployment_link }} | |
| steps: | |
| - uses: actions/checkout@v3 | |
| - name: Check trigger-build status and exit on non-success | |
| run: | | |
| if [ ${{ needs.trigger-build.outputs.build_status }} == 'success' ]; then | |
| echo "Trigger build completed without issues" | |
| else | |
| echo "Trigger build job failed" | |
| exit 1 | |
| fi | |
| - name: Parse the branch name and set it as environment variable | |
| run: | | |
| BRANCH_NAME=${{ needs.generate_stack_name_and_branch.outputs.branch_name }} | |
| echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV | |
| - name: Get PR Information | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| PR_NUMBER=${{ needs.generate_stack_name_and_branch.outputs.pr_number }} | |
| PR_DATA=$(gh pr view $PR_NUMBER --json headRefName,headRefOid) | |
| HEAD_COMMIT_HASH=$(echo "$PR_DATA" | jq -r '.headRefOid' | cut -c1-7) | |
| echo "HEAD_COMMIT_HASH=${HEAD_COMMIT_HASH}" >> $GITHUB_ENV | |
| - name: Check if branch exists in opencrvs-farajaland repo | |
| run: | | |
| FARAJALAND_REPO=https://github.com/opencrvs/opencrvs-farajaland | |
| BASE_BRANCH="${{ github.base_ref || 'develop' }}" | |
| if git ls-remote $FARAJALAND_REPO refs/heads/${{ env.BRANCH_NAME }} | grep -q "${{ env.BRANCH_NAME }}"; then | |
| COMMIT_HASH=$(git ls-remote $FARAJALAND_REPO refs/heads/${{ env.BRANCH_NAME }} | cut -c1-7) | |
| echo "Branch ${{ env.BRANCH_NAME }} exists in opencrvs-farajaland repo: $COMMIT_HASH" | |
| elif git ls-remote $FARAJALAND_REPO refs/heads/$BASE_BRANCH | grep -q "$BASE_BRANCH"; then | |
| COMMIT_HASH=$(git ls-remote $FARAJALAND_REPO refs/heads/$BASE_BRANCH | cut -c1-7) | |
| echo "Branch ${{ env.BRANCH_NAME }} doesn't exist in opencrvs-farajaland repo. Will use commit hash: '$COMMIT_HASH' from base branch: '$BASE_BRANCH'" | |
| else | |
| COMMIT_HASH=$(git ls-remote $FARAJALAND_REPO refs/heads/develop | cut -c1-7) | |
| echo "Branch ${{ env.BRANCH_NAME }} doesn't exist in opencrvs-farajaland repo. Will use commit hash: '$COMMIT_HASH' from 'develop' branch" | |
| fi | |
| echo "FARAJALAND_COMMIT_HASH=${COMMIT_HASH}" >> $GITHUB_ENV | |
| - name: Parse the stack name | |
| id: generate_stack | |
| run: | | |
| stack=${{ needs.generate_stack_name_and_branch.outputs.slugified_branch }} | |
| echo "stack=${stack}" >> $GITHUB_OUTPUT | |
| - name: Output Variables | |
| run: | | |
| echo """ | |
| PR Branch: ${{ env.BRANCH_NAME }} | |
| PR Head Commit Hash: ${{ env.HEAD_COMMIT_HASH }} | |
| Farajaland Commit Hash: ${{ env.FARAJALAND_COMMIT_HASH }} | |
| Keep e2e: ${{ needs.generate_stack_name_and_branch.outputs.keep_e2e }} | |
| E2E Branch: ${{ needs.generate_stack_name_and_branch.outputs.e2e_branch }} | |
| Stack: ${{ steps.generate_stack.outputs.stack }} | |
| """ | |
| - name: Trigger E2E Workflow | |
| id: dispatch_e2e | |
| uses: actions/github-script@v7 | |
| with: | |
| # TODO: Replace with fine-grained e2e token | |
| github-token: ${{ secrets.E2E_WORKFLOWS_TOKEN }} | |
| script: | | |
| const result = await github.rest.repos.createDispatchEvent({ | |
| owner: 'opencrvs', | |
| repo: 'e2e', | |
| event_type: 'run_e2e', | |
| client_payload: { | |
| actor: '${{ needs.generate_stack_name_and_branch.outputs.author }}', | |
| 'core-image-tag': '${{ env.HEAD_COMMIT_HASH }}', | |
| 'countryconfig-image-tag': '${{ env.FARAJALAND_COMMIT_HASH }}', | |
| stack: '${{ steps.generate_stack.outputs.stack }}', | |
| 'keep-e2e': '${{ needs.generate_stack_name_and_branch.outputs.keep_e2e }}', | |
| 'branch': '${{ needs.generate_stack_name_and_branch.outputs.e2e_branch }}', | |
| 'runtime': '${{ needs.generate_stack_name_and_branch.outputs.runtime }}' | |
| } | |
| }); | |
| console.log(result); | |
| await new Promise(resolve => setTimeout(resolve, 10000)); | |
| const runs = await github.rest.actions.listWorkflowRunsForRepo({ | |
| owner: 'opencrvs', | |
| repo: 'e2e', | |
| event: 'repository_dispatch', | |
| per_page: 1 | |
| }); | |
| if (runs.data.workflow_runs.length > 0) { | |
| const runId = runs.data.workflow_runs[0].id; | |
| console.log(`Captured runId: ${runId}`); | |
| // Set the runId as an output | |
| core.setOutput('run_id', runId); | |
| } else { | |
| throw new Error('No workflow run found.'); | |
| } | |
| - name: Print link to E2E workflow run | |
| id: print-links | |
| run: | | |
| E2E_RUN_LINK="https://github.com/opencrvs/e2e/actions/runs/${{ steps.dispatch_e2e.outputs.run_id }}" | |
| DEPLOYMENT_LINK="https://${{ steps.generate_stack.outputs.stack }}.e2e-k8s.opencrvs.dev" | |
| echo """ | |
| # E2E Environment summary | |
| Action workflow (pipeline): $E2E_RUN_LINK | |
| All Deployments: https://github.com/opencrvs/e2e/deployments/${{ steps.generate_stack.outputs.stack }} | |
| Web link: $DEPLOYMENT_LINK | |
| **NOTE**: Web link is available only while deployment, if you would like to | |
| keep environment accessible for debug purposes, please add '🔒 Keep e2e' to PR | |
| https://github.com/${{ github.repository }}/pull/${{ needs.generate_stack_name_and_branch.outputs.pr_number }} | |
| """ >> $GITHUB_STEP_SUMMARY | |
| echo "deployment_link=$DEPLOYMENT_LINK" >> $GITHUB_OUTPUT | |
| listen-e2e: | |
| needs: [trigger-e2e, generate_stack_name_and_branch] | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Wait for Environment Deployment (Deploy Job) | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.E2E_WORKFLOWS_TOKEN }} | |
| script: | | |
| const owner = 'opencrvs'; | |
| const repo = 'e2e'; | |
| const runId = ${{ needs.trigger-e2e.outputs.run_id }}; | |
| let deployJobCompleted = false; | |
| // Check if deploy job has completed | |
| while (!deployJobCompleted) { | |
| const workflowRun = await github.rest.actions.getWorkflowRun({ | |
| owner, | |
| repo, | |
| run_id: runId | |
| }); | |
| const jobs = await github.rest.actions.listJobsForWorkflowRun({ | |
| owner, | |
| repo, | |
| run_id: runId | |
| }); | |
| const deployJob = jobs.data.jobs.find(job => job.name === 'deploy / seed-data / seed-data'); | |
| const cancelled = jobs.data.jobs.find(job => job.conclusion === 'cancelled'); | |
| if (cancelled) { | |
| throw new Error('E2E workflow was cancelled'); | |
| } | |
| if (deployJob && deployJob.status === 'completed') { | |
| deployJobCompleted = true; | |
| if (deployJob.conclusion !== 'success') { | |
| throw new Error('Deploy job failed'); | |
| } | |
| console.log('Deploy job completed successfully'); | |
| } | |
| if(workflowRun.data.status === 'completed') { | |
| deployJobCompleted = true; | |
| if (workflowRun.data.conclusion !== 'success') { | |
| throw new Error('E2E workflow failed'); | |
| } | |
| } | |
| if (!deployJobCompleted) { | |
| await new Promise(resolve => setTimeout(resolve, 10000)); | |
| } | |
| } | |
| - name: Update github comment on PR | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.CORE_ISSUES_PR_TOKEN }} | |
| script: | | |
| const prNumber = ${{ needs.generate_stack_name_and_branch.outputs.pr_number }}; | |
| const deployMessage = `Your environment is deployed to ${{ needs.trigger-e2e.outputs.deployment_link }}`; | |
| // Check if the comment already exists | |
| const comments = await github.rest.issues.listComments({ | |
| owner: 'opencrvs', | |
| repo: 'opencrvs-core', | |
| issue_number: prNumber | |
| }); | |
| const existingComment = comments.data.find(comment => comment.body.includes(deployMessage)); | |
| if (!existingComment) { | |
| // Add PR comment if it doesn't exist | |
| await github.rest.issues.createComment({ | |
| owner: 'opencrvs', | |
| repo: 'opencrvs-core', | |
| issue_number: prNumber, | |
| body: deployMessage | |
| }); | |
| console.log('PR comment added'); | |
| } else { | |
| console.log('PR comment already exists, skipping...'); | |
| } | |
| - name: Wait for E2E Workflow Completion | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.E2E_WORKFLOWS_TOKEN }} | |
| script: | | |
| const owner = 'opencrvs'; | |
| const repo = 'e2e'; | |
| const runId = ${{ needs.trigger-e2e.outputs.run_id }}; | |
| let status = 'in_progress'; | |
| while (status === 'in_progress' || status === 'queued') { | |
| const run = await github.rest.actions.getWorkflowRun({ | |
| owner, | |
| repo, | |
| run_id: runId | |
| }); | |
| status = run.data.status; | |
| console.log(`Current status: ${status}`); | |
| if (status === 'in_progress' || status === 'queued') { | |
| await new Promise(resolve => setTimeout(resolve, 10000)); | |
| } | |
| } | |
| if (status === 'completed') { | |
| const conclusion = await github.rest.actions.getWorkflowRun({ | |
| owner, | |
| repo, | |
| run_id: runId | |
| }); | |
| console.log(`Workflow finished with conclusion: ${conclusion.data.conclusion}`); | |
| if (conclusion.data.conclusion !== 'success') { | |
| throw new Error('E2E workflow failed'); | |
| } | |
| } |