ci: Test geos integration of geosPythonPackages for every new PR #459
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
| name: geosPythonPackages CI | |
| on: pull_request | |
| # Cancels in-progress workflows for a PR when updated | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Checks if PR title follows conventional semantics | |
| semantic_pull_request: | |
| permissions: | |
| pull-requests: write # for amannn/action-semantic-pull-request to analyze PRs and | |
| statuses: write # for amannn/action-semantic-pull-request to mark status of analyzed PR | |
| contents: read | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check if the PR name has conventional semantics | |
| if: github.event_name == 'pull_request' | |
| uses: amannn/action-semantic-pull-request@v5.5.3 | |
| id: lint_pr_title | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| wip: true | |
| # Configure that a scope doesn't need to be provided. | |
| requireScope: false | |
| - name: Skip the check on main branch | |
| if: github.ref_name == 'main' | |
| run: | | |
| echo "This is not a Pull-Request, skipping" | |
| build: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 3 | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12"] | |
| package-name: | |
| - geos-ats | |
| - geos-geomechanics | |
| - geos-mesh | |
| - geos-posp | |
| - geos-timehistory | |
| - geos-trame | |
| - geos-utils | |
| - geos-xml-tools | |
| - hdf5-wrapper | |
| - pygeos-tools | |
| include: | |
| - package-name: geos-geomechanics | |
| dependencies: "geos-utils" | |
| - package-name: geos-mesh | |
| dependencies: "geos-utils geos-geomechanics" | |
| - package-name: geos-posp | |
| dependencies: "geos-utils geos-mesh geos-geomechanics" | |
| - package-name: pygeos-tools | |
| dependencies: "geos-utils geos-mesh" | |
| - package-name: geos-timehistory | |
| dependencies: "hdf5-wrapper" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: mpi4py/setup-mpi@v1 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| - name: Install package | |
| # working-directory: ./${{ matrix.package-name }} | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install pytest yapf toml | |
| DEPS="${{ matrix.dependencies || '' }}" | |
| if [ -n "$DEPS" ]; then | |
| echo "Installing additional dependencies: $DEPS" | |
| for dep in $DEPS; do | |
| python -m pip install ./$dep | |
| done | |
| fi | |
| echo "Installing main package..." | |
| python -m pip install ./${{ matrix.package-name }}/[test] | |
| - name: Lint with yapf | |
| # working-directory: ./${{ matrix.package-name }} | |
| run: | | |
| yapf -r --diff ./${{ matrix.package-name }} --style .style.yapf | |
| - name: Test with pytest | |
| #working-directory: ./${{ matrix.package-name }} | |
| run: | |
| # python -m pytest ./${{ matrix.package-name }} --doctest-modules --junitxml=junit/test-results.xml --cov-report=xml --cov-report=html | | |
| # wrap pytest to avoid error when no tests in the package | |
| sh -c 'python -m pytest ./${{ matrix.package-name }}; ret=$?; [ $ret = 5 ] && exit 0 || exit $ret' | |
| # Step 1: Validate that all standard CI tests pass BEFORE checking GEOS integration | |
| validate_standard_ci: | |
| runs-on: ubuntu-latest | |
| needs: [semantic_pull_request, build] | |
| steps: | |
| - name: Check standard CI results | |
| run: | | |
| echo "Checking standard CI results..." | |
| echo "Semantic PR check: ${{ needs.semantic_pull_request.result }}" | |
| echo "Build and test: ${{ needs.build.result }}" | |
| # All standard tests must pass before proceeding | |
| if [[ "${{ needs.semantic_pull_request.result }}" != "success" ]]; then | |
| echo "❌ Semantic PR check failed - fix PR title before proceeding" | |
| exit 1 | |
| fi | |
| if [[ "${{ needs.build.result }}" != "success" ]]; then | |
| echo "❌ Build and test failed - fix code issues before proceeding" | |
| exit 1 | |
| fi | |
| echo "✅ All standard CI tests passed! Ready for GEOS integration testing." | |
| # Step 2: Only after standard tests pass, check for GEOS integration label | |
| check_geos_integration_label: | |
| runs-on: ubuntu-latest | |
| needs: [validate_standard_ci] | |
| permissions: | |
| pull-requests: read | |
| outputs: | |
| should_test_geos: ${{ steps.check_label.outputs.should_test_geos }} | |
| has_label: ${{ steps.check_label.outputs.has_label }} | |
| steps: | |
| - name: Check for GEOS integration test label | |
| id: check_label | |
| run: | | |
| set -e # Exit immediately if a command exits with a non-zero status. | |
| echo "Standard CI tests have passed. Now checking for GEOS integration requirements..." | |
| # Check if the PR has the 'test-geos-integration' label | |
| # -fsS gives Fails on Error (f), Silent on Success (s), Shows Error Message (S) | |
| pr_json=$(curl -fsS -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ | |
| https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}) | |
| LABELS=$(echo ${pr_json} | jq -r '[.labels[].name] | join(",")') | |
| echo "PR labels: ${LABELS}" | |
| if [[ "${LABELS}" == *"test-geos-integration"* ]]; then | |
| echo "has_label=true" >> $GITHUB_OUTPUT | |
| echo "should_test_geos=true" >> $GITHUB_OUTPUT | |
| echo "✅ Found 'test-geos-integration' label - will proceed with GEOS integration testing" | |
| else | |
| echo "has_label=false" >> $GITHUB_OUTPUT | |
| echo "should_test_geos=false" >> $GITHUB_OUTPUT | |
| echo "❌ Missing 'test-geos-integration' label" | |
| echo "" | |
| echo "REQUIRED: This PR must have the 'test-geos-integration' label to be merged" | |
| echo "This ensures that changes are tested against GEOS before merging" | |
| echo "" | |
| echo "To add the label:" | |
| echo "1. Go to your PR page" | |
| echo "2. Click on 'Labels' in the right sidebar" | |
| echo "3. Add the 'test-geos-integration' label" | |
| exit 1 | |
| fi | |
| # Step 3: Only trigger GEOS integration if label exists AND standard tests passed | |
| trigger_geos_integration_test: | |
| runs-on: ubuntu-latest | |
| needs: [check_geos_integration_label] | |
| if: needs.check_geos_integration_label.outputs.should_test_geos == 'true' | |
| permissions: | |
| pull-requests: read | |
| outputs: | |
| workflow_run_id: ${{ steps.trigger_workflow.outputs.workflow_run_id }} | |
| steps: | |
| - name: Get PR information | |
| id: pr_info | |
| run: | | |
| echo "Triggering GEOS integration test..." | |
| pr_json=$(curl -fsSL -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ | |
| "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}") | |
| LABELS=$(echo "${pr_json}" | jq -r '[.labels[].name] | join(",")') | |
| PR_HEAD_REPO=$(echo ${pr_json} | jq -r '.head.repo.clone_url') | |
| PR_HEAD_BRANCH=$(echo ${pr_json} | jq -r '.head.ref') | |
| PR_HEAD_SHA=$(echo ${pr_json} | jq -r '.head.sha') | |
| echo "pr_repo=${PR_HEAD_REPO}" >> $GITHUB_OUTPUT | |
| echo "pr_branch=${PR_HEAD_BRANCH}" >> $GITHUB_OUTPUT | |
| echo "pr_sha=${PR_HEAD_SHA}" >> $GITHUB_OUTPUT | |
| echo "PR Repository: ${PR_HEAD_REPO}" | |
| echo "PR Branch: ${PR_HEAD_BRANCH}" | |
| echo "PR SHA: ${PR_HEAD_SHA}" | |
| - name: Trigger GEOS integration test workflow | |
| id: trigger_workflow | |
| run: | | |
| set -e | |
| echo "All standard tests passed ✅" | |
| echo "GEOS integration label found ✅" | |
| echo "Now triggering GEOS integration test via repository dispatch..." | |
| # Use repository dispatch to trigger the integration test | |
| # This will create a repository_dispatch event that can be handled by any branch | |
| response=$(curl -fsS -X POST \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.GEOS_INTEGRATION_TOKEN }}" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| https://api.github.com/repos/GEOS-DEV/GEOS/dispatches \ | |
| -d '{ | |
| "event_type": "python-package-integration-test", | |
| "client_payload": { | |
| "target_branch": "ci/benedicto/testGeosPythonPackagesIntegration", | |
| "python_package_repo": "${{ steps.pr_info.outputs.pr_repo }}", | |
| "python_package_branch": "${{ steps.pr_info.outputs.pr_branch }}", | |
| "python_package_pr": "${{ github.event.number }}", | |
| "requested_by": "${{ github.actor }}", | |
| "pr_sha": "${{ steps.pr_info.outputs.pr_sha }}" | |
| } | |
| }') | |
| echo "Repository dispatch response: $response" | |
| echo "✅ GEOS integration test triggered successfully via repository dispatch" | |
| echo "Target branch: ci/benedicto/testGeosPythonPackagesIntegration" | |
| echo "Python package repo: ${{ steps.pr_info.outputs.pr_repo }}" | |
| echo "Python package branch: ${{ steps.pr_info.outputs.pr_branch }}" | |
| # For repository dispatch, we can't immediately get a run ID | |
| # We'll need to wait and search for runs with our payload data | |
| echo "workflow_run_id=dispatch_triggered" >> $GITHUB_OUTPUT | |
| # Step 4: Wait for GEOS integration results (only if triggered) | |
| wait_for_geos_integration_result: | |
| runs-on: ubuntu-latest | |
| needs: [trigger_geos_integration_test] | |
| if: needs.trigger_geos_integration_test.outputs.workflow_run_id == 'dispatch_triggered' | |
| outputs: | |
| geos_test_result: ${{ steps.wait_for_result.outputs.geos_test_result }} | |
| steps: | |
| - name: Wait for GEOS integration test to complete | |
| id: wait_for_result | |
| run: | | |
| echo "Waiting for GEOS integration test to complete via repository dispatch..." | |
| echo "This may take 15-30 minutes..." | |
| # Wait for the workflow to start and complete (with timeout) | |
| timeout=1800 # 30 minutes | |
| elapsed=0 | |
| interval=60 # Check every minute for repository dispatch | |
| found_run=false | |
| workflow_run_id="" | |
| # First, wait for the workflow to appear (up to 5 minutes) | |
| echo "Looking for triggered workflow run..." | |
| search_timeout=300 # 5 minutes to find the workflow | |
| search_elapsed=0 | |
| while [ $search_elapsed -lt $search_timeout ] && [ "$found_run" == "false" ]; do | |
| echo "Searching for workflow run... (${search_elapsed}s elapsed)" | |
| # Look for recent workflow runs of our dispatch workflow | |
| runs_response=$(curl -fsS -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.GEOS_INTEGRATION_TOKEN }}" \ | |
| "https://api.github.com/repos/GEOS-DEV/GEOS/actions/workflows/python_package_integration_dispatch.yml/runs?per_page=10&created=>$(date -d '10 minutes ago' -Iseconds)") | |
| # Look for a run that was triggered recently and is for our PR | |
| for run_id in $(echo "$runs_response" | jq -r '.workflow_runs[].id'); do | |
| run_details=$(curl -fsS -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.GEOS_INTEGRATION_TOKEN }}" \ | |
| "https://api.github.com/repos/GEOS-DEV/GEOS/actions/runs/${run_id}") | |
| # Check if this run is for our PR (by checking timing and trigger) | |
| trigger_event=$(echo "$run_details" | jq -r '.event') | |
| if [[ "$trigger_event" == "repository_dispatch" ]]; then | |
| echo "Found repository dispatch workflow run: ${run_id}" | |
| workflow_run_id="$run_id" | |
| found_run=true | |
| break | |
| fi | |
| done | |
| if [ "$found_run" == "false" ]; then | |
| sleep 30 | |
| search_elapsed=$((search_elapsed + 30)) | |
| fi | |
| done | |
| if [ "$found_run" == "false" ]; then | |
| echo "❌ Could not find triggered workflow run within ${search_timeout} seconds" | |
| echo "geos_test_result=not_found" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| echo "✅ Found workflow run: ${workflow_run_id}" | |
| echo "Now waiting for completion..." | |
| # Now wait for completion | |
| while [ $elapsed -lt $timeout ]; do | |
| run_details=$(curl -fsS -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.GEOS_INTEGRATION_TOKEN }}" \ | |
| "https://api.github.com/repos/GEOS-DEV/GEOS/actions/runs/${workflow_run_id}") | |
| run_status=$(echo "$run_details" | jq -r '.status') | |
| conclusion=$(echo "$run_details" | jq -r '.conclusion') | |
| echo "Workflow status: ${run_status}, conclusion: ${conclusion} (${elapsed}s elapsed)" | |
| if [[ "${run_status}" == "completed" ]]; then | |
| echo "Workflow completed with conclusion: ${conclusion}" | |
| if [[ "${conclusion}" == "success" ]]; then | |
| echo "✅ GEOS integration test PASSED" | |
| echo "geos_test_result=success" >> $GITHUB_OUTPUT | |
| exit 0 | |
| else | |
| echo "❌ GEOS integration test FAILED" | |
| echo "geos_test_result=failure" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| fi | |
| sleep $interval | |
| elapsed=$((elapsed + interval)) | |
| done | |
| echo "❌ TIMEOUT: GEOS integration test did not complete within ${timeout} seconds" | |
| echo "geos_test_result=timeout" >> $GITHUB_OUTPUT | |
| exit 1 | |
| # Step 5: Final validation - requires ALL tests to pass | |
| final_validation: | |
| runs-on: ubuntu-latest | |
| needs: [check_geos_integration_label, wait_for_geos_integration_result] | |
| if: always() | |
| steps: | |
| - name: Final merge validation | |
| run: | | |
| echo "=== FINAL MERGE VALIDATION ===" | |
| echo "" | |
| echo "Standard CI Tests: ✅ PASSED (already validated)" | |
| echo "GEOS Integration Label: ${{ needs.check_geos_integration_label.outputs.has_label == 'true' && '✅ PRESENT' || '❌ MISSING' }}" | |
| echo "GEOS Integration Tests: ${{ needs.wait_for_geos_integration_result.outputs.geos_test_result == 'success' && '✅ PASSED' || needs.wait_for_geos_integration_result.result == 'skipped' && '⏭️ SKIPPED (no label)' || '❌ FAILED' }}" | |
| echo "" | |
| # Check label requirement | |
| if [[ "${{ needs.check_geos_integration_label.outputs.has_label }}" != "true" ]]; then | |
| echo "❌ INVALID: Missing 'test-geos-integration' label" | |
| echo "" | |
| echo "This PR cannot be merged without the 'test-geos-integration' label" | |
| echo "Please add the label and wait for GEOS integration tests to pass" | |
| exit 1 | |
| fi | |
| # Check GEOS test results (only if they were supposed to run) | |
| if [[ "${{ needs.wait_for_geos_integration_result.result }}" == "failure" || "${{ needs.wait_for_geos_integration_result.outputs.geos_test_result }}" == "failure" ]]; then | |
| echo "❌ INVALID: GEOS integration tests failed" | |
| echo "" | |
| echo "The changes in this PR break GEOS functionality" | |
| echo "Please check the GEOS workflow logs and fix the issues" | |
| echo "GEOS workflow: https://github.com/GEOS-DEV/GEOS/actions/runs/${{ needs.trigger_geos_integration_test.outputs.workflow_run_id }}" | |
| exit 1 | |
| fi | |
| if [[ "${{ needs.wait_for_geos_integration_result.outputs.geos_test_result }}" == "timeout" ]]; then | |
| echo "❌ INVALID: GEOS integration tests timed out" | |
| echo "" | |
| echo "Please check the GEOS workflow manually and re-run if needed" | |
| echo "GEOS workflow: https://github.com/GEOS-DEV/GEOS/actions/runs/${{ needs.trigger_geos_integration_test.outputs.workflow_run_id }}" | |
| exit 1 | |
| fi | |
| # All validations passed | |
| echo "✅ VALID: All requirements met" | |
| echo "" | |
| echo "This PR is ready for review and merge:" | |
| echo " ✅ Standard CI tests passed" | |
| echo " ✅ 'test-geos-integration' label present" | |
| echo " ✅ GEOS integration tests passed" | |
| echo "" | |
| echo "🎉 Ready for merge!" |