From 7dc94b61cb92ee21c7457b16ba321d034d282978 Mon Sep 17 00:00:00 2001 From: shortstacked Date: Fri, 30 May 2025 10:50:40 +0100 Subject: [PATCH] ci: Implement reusable workflow for PR job eligibility checks (no-changelog) (#15631) --- .github/workflows/check-run-eligibility.yml | 109 ++++++++++++++++++ .github/workflows/e2e-tests-pr.yml | 109 ++++++++---------- .../workflows/test-workflows-pr-approved.yml | 13 ++- 3 files changed, 169 insertions(+), 62 deletions(-) create mode 100644 .github/workflows/check-run-eligibility.yml diff --git a/.github/workflows/check-run-eligibility.yml b/.github/workflows/check-run-eligibility.yml new file mode 100644 index 0000000000..63f7b8294c --- /dev/null +++ b/.github/workflows/check-run-eligibility.yml @@ -0,0 +1,109 @@ +# Determines if conditions are met for running subsequent jobs on a Pull Request. +# +# !! IMPORTANT !! +# This workflow RELIES on being called from a parent workflow triggered by +# a `pull_request` or `pull_request_target` event. It uses `github.event` +# to access PR details. +# +# It checks if all the following conditions are TRUE: +# 1. The PR is NOT from a fork (i.e., it's an internal PR). +# 2. The PR has been approved by a maintainer (`is_pr_approved_by_maintainer`). +# 3. The PR's source branch does NOT match an excluded pattern. +# 4. The PR includes relevant file changes (`paths_filter_patterns`). +# +# It outputs `should_run` as 'true' if ALL conditions pass, 'false' otherwise. + +name: PR Eligibility Check + +on: + workflow_call: + inputs: + is_pr_approved_by_maintainer: + required: true + type: boolean + paths_filter_patterns: + description: "Path filter patterns for 'paths-filter-action'." + required: false + type: string + default: | + not_ignored: + - '!.devcontainer/**' + - '!.github/*' + - '!.github/scripts/*' + - '!.github/workflows/benchmark-*' + - '!.github/workflows/check-*' + - '!.vscode/**' + - '!docker/**' + - '!packages/@n8n/benchmark/**' + - '!**/*.md' + excluded_source_branch_patterns: + description: "Newline-separated list of glob patterns for source branches to EXCLUDE." + required: false + type: string + default: | + release/* + master + + outputs: + should_run: + description: "Outputs 'true' if all eligibility checks pass, otherwise 'false'." + value: ${{ jobs.evaluate_conditions.outputs.run_decision }} + +jobs: + evaluate_conditions: + runs-on: ubuntu-latest + outputs: + run_decision: ${{ steps.evaluate.outputs.should_run }} + steps: + - name: Check out current commit + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Determine changed files + uses: tomi/paths-filter-action@32c62f5ca100c1110406e3477d5b3ecef4666fec # v3.0.2 + id: changed + with: + filters: ${{ inputs.paths_filter_patterns }} + predicate-quantifier: 'every' + + - name: Evaluate Conditions & Set Output + id: evaluate + env: + IS_FORK: ${{ github.event.pull_request.head.repo.fork }} + IS_APPROVED: ${{ inputs.is_pr_approved_by_maintainer }} + FILES_CHANGED: ${{ steps.changed.outputs.not_ignored == 'true' }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + EXCLUDED_PATTERNS: ${{ inputs.excluded_source_branch_patterns }} + run: | + if [[ "$IS_FORK" == "true" ]]; then + is_community="true" + else + is_community="false" + fi + + source_branch_excluded="false" + while IFS= read -r pattern; do + if [[ -n "$pattern" && "$HEAD_REF" == $pattern ]]; then + source_branch_excluded="true" + break + fi + done <<< "$EXCLUDED_PATTERNS" + + echo "--- Checking Conditions ---" + echo "Is NOT Community PR: $([[ "$is_community" == "false" ]] && echo true || echo false)" + echo "Files Changed: $FILES_CHANGED" + echo "Source Branch Excluded: $source_branch_excluded" + echo "Is Approved: $IS_APPROVED" + echo "-------------------------" + + if [[ "$is_community" == "false" && \ + "$FILES_CHANGED" == "true" && \ + "$source_branch_excluded" == "false" && \ + "$IS_APPROVED" == "true" ]]; then + echo "Decision: Conditions met. Setting should_run=true." + echo "should_run=true" >> $GITHUB_OUTPUT + else + echo "Decision: Conditions not met. Setting should_run=false." + echo "should_run=false" >> $GITHUB_OUTPUT + fi \ No newline at end of file diff --git a/.github/workflows/e2e-tests-pr.yml b/.github/workflows/e2e-tests-pr.yml index d46ef9e4e3..175de60c44 100644 --- a/.github/workflows/e2e-tests-pr.yml +++ b/.github/workflows/e2e-tests-pr.yml @@ -9,46 +9,18 @@ concurrency: cancel-in-progress: true jobs: - get-metadata: - name: Get Metadata - runs-on: ubuntu-latest + eligibility_check: + name: Check Eligibility for Test Run if: github.event.review.state == 'approved' - steps: - - name: Check out current commit - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 2 - - - name: Determine changed files - uses: tomi/paths-filter-action@32c62f5ca100c1110406e3477d5b3ecef4666fec # v3.0.2 - id: changed - with: - filters: | - not_ignored: - - '!.devcontainer/**' - - '!.github/*' - - '!.github/scripts/*' - - '!.github/workflows/benchmark-*' - - '!.github/workflows/check-*' - - '!.vscode/**' - - '!docker/**' - - '!packages/@n8n/benchmark/**' - - '!**/*.md' - predicate-quantifier: 'every' - - outputs: - # The workflow should run when: - # - It has changes to files that are not ignored - # - It is not a community PR - # - It is targeting master or a release branch - should_run: ${{ steps.changed.outputs.not_ignored == 'true' && !contains(github.event.pull_request.labels.*.name, 'community') && (github.event.pull_request.base.ref == 'master' || startsWith(github.event.pull_request.base.ref, 'release/')) }} + uses: ./.github/workflows/check-run-eligibility.yml + with: + is_pr_approved_by_maintainer: true run-e2e-tests: name: E2E [Electron/Node 18] uses: ./.github/workflows/e2e-reusable.yml - needs: [get-metadata] - if: ${{ github.event.review.state == 'approved' && needs.get-metadata.outputs.should_run == 'true' }} + needs: [eligibility_check] + if: needs.eligibility_check.outputs.should_run == 'true' with: pr_number: ${{ github.event.pull_request.number }} user: ${{ github.event.pull_request.user.login || 'PR User' }} @@ -56,33 +28,52 @@ jobs: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} post-e2e-tests: - runs-on: ubuntu-latest name: E2E [Electron/Node 18] - Checks - needs: [get-metadata, run-e2e-tests] - if: always() + runs-on: ubuntu-latest + needs: [eligibility_check, run-e2e-tests] + if: always() && needs.eligibility_check.result != 'skipped' steps: - - name: E2E success comment - if: ${{ needs.get-metadata.outputs.should_run == 'true' && needs.run-e2e-tests.outputs.tests_passed == 'true' }} - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 + - name: Determine Outcome and Comment Message + id: determine_outcome + run: | + JOB_OUTCOME="success" + COMMENT_BODY="" + SHOULD_POST_COMMENT="false" + + if [[ "${{ needs.eligibility_check.outputs.should_run }}" == "false" ]]; then + COMMENT_BODY="ℹ️ E2E tests were not run for this PR based on the eligibility criteria." + SHOULD_POST_COMMENT="true" + JOB_OUTCOME="success" + elif [[ "${{ needs.run-e2e-tests.result }}" == "success" ]]; then + COMMENT_BODY=":white_check_mark: All Cypress E2E specs passed" + SHOULD_POST_COMMENT="true" + JOB_OUTCOME="success" + elif [[ "${{ needs.run-e2e-tests.result }}" == "failure" ]]; then + COMMENT_BODY=":warning: Some Cypress E2E specs are failing, please fix them before merging" + SHOULD_POST_COMMENT="true" + JOB_OUTCOME="failure" + else + COMMENT_BODY="ℹ️ E2E tests were scheduled but did not complete as expected (Result: ${{ needs.run-e2e-tests.result }})." + SHOULD_POST_COMMENT="true" + JOB_OUTCOME="failure" + fi + + echo "comment_body=$COMMENT_BODY" >> $GITHUB_OUTPUT + echo "should_post_comment=$SHOULD_POST_COMMENT" >> $GITHUB_OUTPUT + echo "job_outcome=$JOB_OUTCOME" >> $GITHUB_OUTPUT + + - name: Create or Update PR Comment + if: steps.determine_outcome.outputs.should_post_comment == 'true' + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 with: issue-number: ${{ github.event.pull_request.number }} - body: | - :white_check_mark: All Cypress E2E specs passed + body: ${{ steps.determine_outcome.outputs.comment_body }} token: ${{ secrets.GITHUB_TOKEN }} - - name: E2E fail comment - if: needs.run-e2e-tests.result == 'failure' - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 - with: - issue-number: ${{ github.event.pull_request.number }} - body: | - :warning: Some Cypress E2E specs are failing, please fix them before merging - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Success job if community PR - if: ${{ contains(github.event.pull_request.labels.*.name, 'community') }} - run: exit 0 - - - name: Fail job if run-e2e-tests failed - if: ${{ (github.event.review.state != 'approved' && github.event.review.state != 'commented') || needs.run-e2e-tests.result == 'failure' }} - run: exit 1 + - name: Finalize Job Status + run: | + if [[ "${{ steps.determine_outcome.outputs.job_outcome }}" == "failure" ]]; then + exit 1 + else + exit 0 + fi diff --git a/.github/workflows/test-workflows-pr-approved.yml b/.github/workflows/test-workflows-pr-approved.yml index 1121ab4473..0eda5329ee 100644 --- a/.github/workflows/test-workflows-pr-approved.yml +++ b/.github/workflows/test-workflows-pr-approved.yml @@ -6,12 +6,19 @@ on: permissions: contents: read - pull-requests: read jobs: - run_workflow_tests_after_approval: - name: Run Tests on Approved PR + eligibility_check: + name: Check Eligibility for Test Run if: github.event.review.state == 'approved' + uses: ./.github/workflows/check-run-eligibility.yml + with: + is_pr_approved_by_maintainer: true + + run_workflow_tests: + name: Run Tests on Approved Internal PR + needs: [eligibility_check] + if: needs.eligibility_check.outputs.should_run == 'true' uses: ./.github/workflows/test-workflows-callable.yml with: git_ref: ${{ github.event.pull_request.head.sha }}