Run Eden #8731
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
| # Copyright (c) 2025, Zededa, Inc. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| --- | |
| name: Run Eden | |
| on: # yamllint disable-line rule:truthy | |
| workflow_run: | |
| workflows: ["PR Gate"] | |
| types: [completed] | |
| permissions: | |
| contents: read | |
| actions: read | |
| env: | |
| EDEN_IN_PR_STATUS_TITLE_PREFIX: Eden Runner | |
| EDEN_IN_PR_DETAILS_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| EDEN_PARENT_JOB_TITLE_PREFIX: Eden Tests | |
| RUN_CONTEXT_FILE: run-context.json | |
| jobs: | |
| context: | |
| name: Setup Context from Gate | |
| if: ${{ github.event.workflow_run.conclusion == 'success' }} | |
| runs-on: ubuntu-latest | |
| outputs: | |
| pr_id: ${{ steps.extract.outputs.pr_id }} | |
| original_run_id: ${{ steps.extract.outputs.original_run_id }} | |
| pr_sha: ${{ steps.extract.outputs.pr_sha }} | |
| pr_base_ref: ${{ steps.extract.outputs.pr_base_ref }} | |
| hv: ${{ steps.extract.outputs.hv }} | |
| arch: ${{ steps.extract.outputs.arch }} | |
| platform: ${{ steps.extract.outputs.platform }} | |
| eden_parent_job_title: ${{ steps.titles.outputs.eden_parent_job_title }} | |
| eden_in_pr_status_title: ${{ steps.titles.outputs.eden_in_pr_status_title }} | |
| gate_run_id: ${{ steps.extract.outputs.gate_run_id }} | |
| gate_status_name: ${{ steps.extract.outputs.gate_status_name }} | |
| skip_run: ${{ steps.check_gate.outputs.skip_run }} | |
| steps: | |
| - name: Download | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: run-context | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Check if Gate Passed | |
| id: check_gate | |
| run: | | |
| if [[ ! -f "$RUN_CONTEXT_FILE" ]]; then | |
| echo "::error::Gate context file not found" | |
| echo "skip_run=true" >> "$GITHUB_OUTPUT" | |
| exit 1 | |
| fi | |
| # Check if the file contains only the "exit" command | |
| if [[ $(cat "$RUN_CONTEXT_FILE") == "exit" ]]; then | |
| echo "::error::Gate not satisfied, exiting" | |
| echo "skip_run=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip_run=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Verify | |
| if: ${{ steps.check_gate.outputs.skip_run == 'false' }} | |
| env: | |
| RUN_CONTEXT_FILE: ${{ env.RUN_CONTEXT_FILE }} | |
| REQUIRED_FIELDS: pr_id, original_run_id, pr_sha, pr_base_ref, hv, arch, platform, gate_run_id, gate_status_name | |
| run: | | |
| if [[ ! -f "$RUN_CONTEXT_FILE" ]]; then | |
| echo "$RUN_CONTEXT_FILE file not found" | |
| exit 1 | |
| fi | |
| cat "$RUN_CONTEXT_FILE" | |
| if ! jq -e . $RUN_CONTEXT_FILE > /dev/null; then | |
| echo "$RUN_CONTEXT_FILE is not a valid JSON file" | |
| exit 1 | |
| fi | |
| # Now check for all required fields | |
| has_all_fields=true | |
| # Split the CSV once; spaces in names are not expected, so keep tr | |
| for field in $(echo "$REQUIRED_FIELDS" | tr ',' ' '); do | |
| if ! jq -e --arg f "$field" 'has($f)' "$RUN_CONTEXT_FILE" >/dev/null; then | |
| echo "Missing required field: $field" | |
| has_all_fields=false | |
| fi | |
| done | |
| if [[ $has_all_fields == false ]]; then | |
| echo "Run context is missing required fields" | |
| exit 1 | |
| fi | |
| - name: Extract | |
| if: ${{ steps.check_gate.outputs.skip_run == 'false' }} | |
| id: extract | |
| env: | |
| FIELDS: pr_id, original_run_id, pr_sha, pr_base_ref, hv, arch, platform, gate_run_id, gate_status_name | |
| run: | | |
| # Extract fields from the JSON file | |
| for field in $(echo "$FIELDS" | tr ',' ' '); do | |
| value=$(jq -r --arg f "$field" '.[$f]' $RUN_CONTEXT_FILE) | |
| if [[ -z "$value" || "$value" == "null" ]]; then | |
| echo "Field $field is empty or null" | |
| exit 1 | |
| fi | |
| echo "$field=$value" >> "$GITHUB_OUTPUT" | |
| done | |
| - name: Define Titles | |
| if: ${{ steps.check_gate.outputs.skip_run == 'false' }} | |
| id: titles | |
| run: | | |
| postfix="(${{ steps.extract.outputs.hv }}, ${{ steps.extract.outputs.arch }}, ${{ steps.extract.outputs.platform }})" | |
| echo "eden_parent_job_title=${{ env.EDEN_PARENT_JOB_TITLE_PREFIX }} $postfix" >> "$GITHUB_OUTPUT" | |
| echo "eden_in_pr_status_title=${{ env.EDEN_IN_PR_STATUS_TITLE_PREFIX }} $postfix" >> "$GITHUB_OUTPUT" | |
| check_secrets: | |
| name: Check Secrets | |
| if: github.event.workflow_run.conclusion == 'success' && needs.context.outputs.skip_run == 'false' | |
| needs: context | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check Dockerhub credentials | |
| run: | | |
| if [[ -z "${{ secrets.DOCKERHUB_PULL_USER }}" ]]; then | |
| echo "Warning: DOCKERHUB_PULL_USER secret is not set. This may affect the ability to pull images from Docker Hub."; | |
| fi | |
| status_ui: | |
| name: Reset & Render Eden statuses | |
| runs-on: ubuntu-latest | |
| needs: context | |
| if: needs.context.outputs.skip_run == 'false' | |
| permissions: | |
| statuses: write | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_SHA: ${{ needs.context.outputs.pr_sha }} | |
| EDEN_RUNNER_CTX: ${{ needs.context.outputs.eden_in_pr_status_title }} | |
| EDEN_TESTS_PREFIX: ${{ needs.context.outputs.eden_parent_job_title }} for ${{ needs.context.outputs.pr_base_ref }} | |
| EDEN_DETAILS_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| steps: | |
| - name: Reset Eden Runner & Eden jobs statuses | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| with: | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const sha = process.env.PR_SHA; | |
| // Get the current commit statuses for the PR | |
| const { data: statuses } = await github.rest.repos.listCommitStatusesForRef({ | |
| owner, repo, ref: sha | |
| }); | |
| // Reset the Eden Runner status | |
| await github.rest.repos.createCommitStatus({ | |
| owner, repo, sha, | |
| state: 'pending', | |
| context: process.env.EDEN_RUNNER_CTX, | |
| description: 'Eden tests queued…', | |
| target_url: process.env.EDEN_DETAILS_URL | |
| }); | |
| // Reset the Eden tests non-green statuses | |
| const prefix = `${process.env.EDEN_TESTS_PREFIX} / `; | |
| for (const s of statuses) { | |
| if (s.context.startsWith(prefix) && s.state !== 'success') { | |
| await github.rest.repos.createCommitStatus({ | |
| owner, repo, sha, | |
| state: 'pending', | |
| context: s.context, | |
| description: 'Rerunning…', | |
| target_url: process.env.EDEN_DETAILS_URL | |
| }); | |
| } | |
| } | |
| tests-13-4-stable: | |
| name: ${{ needs.context.outputs.eden_parent_job_title }} for 13.4-stable | |
| needs: context | |
| if: needs.context.outputs.skip_run == 'false' && needs.context.outputs.pr_base_ref == '13.4-stable' | |
| uses: lf-edge/eden/.github/workflows/test.yml@1.0.15 # zizmor: ignore[unpinned-uses] eden release tag versioned with our EVE release branches; a SHA pin would force a cross-repo bump on every eden patch release | |
| secrets: inherit | |
| with: | |
| eve_image: "evebuild/pr:${{ needs.context.outputs.pr_id }}" | |
| eve_log_level: "debug" | |
| eve_artifact_name: "eve-${{ needs.context.outputs.hv }}-${{ needs.context.outputs.arch }}-${{ needs.context.outputs.platform }}" | |
| artifact_run_id: ${{ needs.context.outputs.original_run_id }} | |
| eden_version: "EVE-13.4-stable" | |
| tests-14-5-stable: | |
| name: ${{ needs.context.outputs.eden_parent_job_title }} for 14.5-stable | |
| needs: context | |
| if: needs.context.outputs.skip_run == 'false' && needs.context.outputs.pr_base_ref == '14.5-stable' | |
| uses: lf-edge/eden/.github/workflows/test.yml@1.0.15 # zizmor: ignore[unpinned-uses] eden release tag versioned with our EVE release branches; a SHA pin would force a cross-repo bump on every eden patch release | |
| secrets: inherit | |
| with: | |
| eve_image: "evebuild/pr:${{ needs.context.outputs.pr_id }}" | |
| eve_log_level: "debug" | |
| eve_artifact_name: "eve-${{ needs.context.outputs.hv }}-${{ needs.context.outputs.arch }}-${{ needs.context.outputs.platform }}" | |
| artifact_run_id: ${{ needs.context.outputs.original_run_id }} | |
| eden_version: "EVE-14.5-stable" | |
| tests-16-0-stable: | |
| name: ${{ needs.context.outputs.eden_parent_job_title }} for 16.0-stable | |
| needs: context | |
| if: needs.context.outputs.skip_run == 'false' && needs.context.outputs.pr_base_ref == '16.0-stable' | |
| uses: lf-edge/eden/.github/workflows/test.yml@1.0.15 # zizmor: ignore[unpinned-uses] eden release tag versioned with our EVE release branches; a SHA pin would force a cross-repo bump on every eden patch release | |
| secrets: inherit | |
| with: | |
| eve_image: "evebuild/pr:${{ needs.context.outputs.pr_id }}" | |
| eve_log_level: "debug" | |
| eve_artifact_name: "eve-${{ needs.context.outputs.hv }}-${{ needs.context.outputs.arch }}-${{ needs.context.outputs.platform }}" | |
| artifact_run_id: ${{ needs.context.outputs.original_run_id }} | |
| eden_version: "EVE-16.0-stable" | |
| tests-17-0-stable: | |
| name: ${{ needs.context.outputs.eden_parent_job_title }} for 17.0-stable | |
| needs: context | |
| if: needs.context.outputs.skip_run == 'false' && needs.context.outputs.pr_base_ref == '17.0-stable' | |
| uses: lf-edge/eden/.github/workflows/test.yml@1.0.15 # zizmor: ignore[unpinned-uses] eden release tag versioned with our EVE release branches; a SHA pin would force a cross-repo bump on every eden patch release | |
| secrets: inherit | |
| with: | |
| eve_image: "evebuild/pr:${{ needs.context.outputs.pr_id }}" | |
| eve_log_level: "debug" | |
| eve_artifact_name: "eve-${{ needs.context.outputs.hv }}-${{ needs.context.outputs.arch }}-${{ needs.context.outputs.platform }}" | |
| artifact_run_id: ${{ needs.context.outputs.original_run_id }} | |
| eden_version: "EVE-17.0-stable" | |
| tests-master: | |
| name: ${{ needs.context.outputs.eden_parent_job_title }} for master | |
| needs: context | |
| if: needs.context.outputs.skip_run == 'false' && needs.context.outputs.pr_base_ref == 'master' | |
| uses: lf-edge/eden/.github/workflows/test.yml@master # zizmor: ignore[unpinned-uses] eden master tracks this repo's master branch; a SHA pin would force a cross-repo bump on every eden change | |
| secrets: inherit | |
| with: | |
| eve_image: "evebuild/pr:${{ needs.context.outputs.pr_id }}" | |
| eve_log_level: "debug" | |
| eve_artifact_name: "eve-${{ needs.context.outputs.hv }}-${{ needs.context.outputs.arch }}-${{ needs.context.outputs.platform }}" | |
| artifact_run_id: ${{ needs.context.outputs.original_run_id }} | |
| eden_version: "master" | |
| finalize: | |
| name: Eden Tests Complete | |
| needs: [context, tests-13-4-stable, tests-14-5-stable, tests-16-0-stable, tests-17-0-stable, tests-master] | |
| if: always() && needs.context.outputs.skip_run == 'false' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| statuses: write | |
| steps: | |
| - name: Check test results | |
| id: check | |
| run: | | |
| # Determine which job ran | |
| if [[ "${{ needs.tests-13-4-stable.result }}" != "skipped" ]]; then | |
| result="${{ needs.tests-13-4-stable.result }}" | |
| elif [[ "${{ needs.tests-14-5-stable.result }}" != "skipped" ]]; then | |
| result="${{ needs.tests-14-5-stable.result }}" | |
| elif [[ "${{ needs.tests-16-0-stable.result }}" != "skipped" ]]; then | |
| result="${{ needs.tests-16-0-stable.result }}" | |
| elif [[ "${{ needs.tests-17-0-stable.result }}" != "skipped" ]]; then | |
| result="${{ needs.tests-17-0-stable.result }}" | |
| else | |
| result="${{ needs.tests-master.result }}" | |
| fi | |
| echo "Eden tests result: $result" | |
| echo "result=$result" >> "$GITHUB_OUTPUT" | |
| - name: Update commit status | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| env: | |
| SHA: ${{ needs.context.outputs.pr_sha }} | |
| RESULT: ${{ steps.check.outputs.result }} | |
| EDEN_IN_PR_STATUS_TITLE: ${{ needs.context.outputs.eden_in_pr_status_title }} | |
| with: | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const map = { success:'success', failure:'failure', cancelled:'error' }; | |
| const state = map[process.env.RESULT] || 'error'; | |
| await github.rest.repos.createCommitStatus({ | |
| owner, repo, | |
| sha: process.env.SHA, | |
| state, | |
| context: process.env.EDEN_IN_PR_STATUS_TITLE, | |
| description: `Eden tests ${state}`, | |
| target_url: `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` | |
| }); | |
| - name: Fail if tests failed | |
| if: steps.check.outputs.result != 'success' | |
| run: | | |
| echo "::error::Eden tests failed with result: ${{ steps.check.outputs.result }}" | |
| exit 1 | |
| subjob_statuses: | |
| name: Render each Eden jobs status | |
| if: always() && needs.context.outputs.skip_run == 'false' | |
| needs: [context, finalize] # ensure Eden has finished | |
| runs-on: ubuntu-latest | |
| permissions: | |
| statuses: write | |
| steps: | |
| - name: Surface each Eden job as PR status | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| env: | |
| SHA: ${{ needs.context.outputs.pr_sha }} | |
| EDEN_PARENT_JOB_TITLE: ${{ needs.context.outputs.eden_parent_job_title }} for ${{ needs.context.outputs.pr_base_ref }} | |
| with: | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const runId = process.env.GITHUB_RUN_ID; | |
| const parentName = process.env.EDEN_PARENT_JOB_TITLE; | |
| const prefix = `${parentName} / `; | |
| const { data } = await github.rest.actions.listJobsForWorkflowRun({ | |
| owner, repo, run_id: runId, per_page: 100 | |
| }); | |
| for (const job of data.jobs) { | |
| // child jobs only | |
| if (!job.name.startsWith(prefix)) continue; | |
| // Filter out "Determine best available runner" postfix jobs | |
| if (job.name.includes('Determine best available runner')) continue; | |
| const state = job.conclusion === 'success' | |
| ? 'success' | |
| : job.conclusion === 'failure' ? 'failure' | |
| : 'error'; | |
| await github.rest.repos.createCommitStatus({ | |
| owner, repo, | |
| sha: process.env.SHA, | |
| context: `${job.name}`, | |
| state: state, | |
| target_url: job.html_url, | |
| description:`${state}` | |
| }); | |
| } |