1- name : geosPythonPackages CI
1+ name : geosPythonPackages CI
22on : pull_request
33
44# Cancels in-progress workflows for a PR when updated
@@ -9,13 +9,13 @@ concurrency:
99
1010jobs :
1111 # Checks if PR title follows conventional semantics
12- semantic_pull_request :
12+ semantic_pull_request :
1313 permissions :
14- pull-requests : write # for amannn/action-semantic-pull-request to analyze PRs and
14+ pull-requests : write # for amannn/action-semantic-pull-request to analyze PRs and
1515 statuses : write # for amannn/action-semantic-pull-request to mark status of analyzed PR
16- contents : read
16+ contents : read
1717 runs-on : ubuntu-latest
18-
18+
1919 steps :
2020 - name : Check if the PR name has conventional semantics
2121 if : github.event_name == 'pull_request'
@@ -27,27 +27,42 @@ jobs:
2727 wip : true
2828 # Configure that a scope doesn't need to be provided.
2929 requireScope : false
30-
30+
3131 - name : Skip the check on main branch
3232 if : github.ref_name == 'main'
3333 run : |
3434 echo "This is not a Pull-Request, skipping"
3535
36+ check_draft :
37+ runs-on : ubuntu-latest
38+ needs : [semantic_pull_request]
39+ steps :
40+ - name : Fail if PR is a draft
41+ run : |
42+ if [ "${{ github.event.pull_request.draft }}" = "true" ]; then
43+ echo "✗ This PR is a draft. CI will not proceed."
44+ exit 1
45+ else
46+ echo "✓ This PR is ready for review."
47+ fi
48+
49+ # build and test the standalone CI
3650 build :
3751 runs-on : ubuntu-latest
52+ needs : [check_draft]
3853 strategy :
3954 fail-fast : false
4055 max-parallel : 3
4156 matrix :
42- python-version : ["3.10", "3.11", "3.12"]
57+ python-version : ["3.10","3.11","3.12"]
4358 package-name :
4459 - geos-ats
60+ - geos-utils
4561 - geos-geomechanics
4662 - geos-mesh
4763 - geos-posp
4864 - geos-timehistory
4965 - geos-trame
50- - geos-utils
5166 - geos-xml-tools
5267 - geos-xml-viewer
5368 - hdf5-wrapper
7792 python -m pip install --upgrade pip
7893 python -m pip install pytest yapf toml
7994
80- DEPS="${{ matrix.dependencies || '' }}"
95+ DEPS="${{ matrix.dependencies || '' }}"
8196
8297 if [ -n "$DEPS" ]; then
8398 echo "Installing additional dependencies: $DEPS"
88103
89104 echo "Installing main package..."
90105 python -m pip install ./${{ matrix.package-name }}/[test]
91-
92106 - name : Lint with yapf
93107 # working-directory: ./${{ matrix.package-name }}
94108 run : |
@@ -98,4 +112,285 @@ jobs:
98112 run :
99113 # python -m pytest ./${{ matrix.package-name }} --doctest-modules --junitxml=junit/test-results.xml --cov-report=xml --cov-report=html |
100114 # wrap pytest to avoid error when no tests in the package
101- sh -c 'python -m pytest ./${{ matrix.package-name }}; ret=$?; [ $ret = 5 ] && exit 0 || exit $ret'
115+ sh -c 'python -m pytest ./${{ matrix.package-name }}; ret=$?; [ $ret = 5 ] && exit 0 || exit $ret'
116+
117+ # check if GEOS has label for testing GEOS integration
118+ check_integration_label :
119+ runs-on : ubuntu-latest
120+ needs : [build]
121+ outputs :
122+ has_geos_integration_label : ${{ steps.set-label.outputs.has_label }}
123+ steps :
124+ - name : Check if PR has 'test-geos-integration' label
125+ id : set-label
126+ run : |
127+ echo "Checking for label..."
128+ LABEL_FOUND=false
129+ for label in "${{ toJson(github.event.pull_request.labels) }}"; do
130+ if [[ "$label" == *"test-geos-integration"* ]]; then
131+ LABEL_FOUND=true
132+ echo "Label ${label} found"
133+ break
134+ fi
135+ done
136+ echo "has_label=$LABEL_FOUND" >> $GITHUB_OUTPUT
137+
138+ check_force_integration_label :
139+ runs-on : ubuntu-latest
140+ needs : [build]
141+ outputs :
142+ has_geos_integration_force_label : ${{ steps.set-label.outputs.has_label }}
143+ steps :
144+ - name : Check if PR has 'force-geos-integration' label
145+ id : set-label
146+ run : |
147+ echo "Checking for label..."
148+ LABEL_FOUND=false
149+ for label in "${{ toJson(github.event.pull_request.labels) }}"; do
150+ if [[ "$label" == *"force-geos-integration"* ]]; then
151+ LABEL_FOUND=true
152+ echo "Label ${label} found"
153+ break
154+ fi
155+ done
156+ echo "has_label=$LABEL_FOUND" >> $GITHUB_OUTPUT
157+
158+ # Step 3: Check if GEOS integration is required based on changed files
159+ check_geos_integration_required :
160+ name : Check GEOS Integration Required
161+ runs-on : ubuntu-latest
162+ needs : [build]
163+ if : github.event_name == 'pull_request'
164+ outputs :
165+ geos_integration_required : ${{ steps.check_changes.outputs.required }}
166+ skip_reason : ${{ steps.check_changes.outputs.skip_reason }}
167+ steps :
168+ - name : Checkout code
169+ uses : actions/checkout@v4
170+ with :
171+ fetch-depth : 0 # Fetch all history to compare with base branch
172+ - name : Check if GEOS integration is required
173+ id : check_changes
174+ run : |
175+ echo "Analyzing changed files to determine if GEOS integration test is required..."
176+
177+ # Get list of changed files
178+ git fetch origin ${{ github.base_ref }}
179+ CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
180+
181+ echo "Changed files:"
182+ echo "$CHANGED_FILES"
183+ echo ""
184+
185+ # Define packages that are integrated into GEOS (from GEOS/scripts/setupPythonEnvironment.bash)
186+ GEOS_INTEGRATED_PACKAGES=(
187+ "geos-utils"
188+ "geos-mesh"
189+ "geos-xml-tools"
190+ "hdf5-wrapper"
191+ "pygeos-tools"
192+ "geos-ats"
193+ )
194+
195+ # Define patterns that DON'T require GEOS integration testing
196+ SKIP_PATTERNS=(
197+ "^docs/"
198+ "^\.github/workflows/doc-test\.yml$"
199+ "^\.github/workflows/typing-check\.yml$"
200+ "^README\.md$"
201+ "^\.readthedocs\.yml$"
202+ "^\.gitignore$"
203+ "^\.gitattributes$"
204+ "^\.style\.yapf$"
205+ "^\.ruff\.toml$"
206+ "^\.mypy\.ini$"
207+ # Packages not used in GEOS
208+ "^geos-geomechanics/"
209+ "^geos-posp/"
210+ "^geos-pv/"
211+ "^geos-timehistory/"
212+ "^geos-trame/"
213+ "^geos-xml-viewer/"
214+ )
215+
216+ # Check if any changed file affects GEOS-integrated packages
217+ REQUIRES_GEOS_TEST=false
218+ AFFECTED_PACKAGES=""
219+
220+ for file in $CHANGED_FILES; do
221+ # Check if file matches any skip pattern
222+ SHOULD_SKIP=false
223+ for pattern in "${SKIP_PATTERNS[@]}"; do
224+ if echo "$file" | grep -qE "$pattern"; then
225+ SHOULD_SKIP=true
226+ break
227+ fi
228+ done
229+
230+ if [[ "$SHOULD_SKIP" == "false" ]]; then
231+ # Check if file is in a GEOS-integrated package
232+ for package in "${GEOS_INTEGRATED_PACKAGES[@]}"; do
233+ if echo "$file" | grep -qE "^${package}/"; then
234+ REQUIRES_GEOS_TEST=true
235+ if [[ ! "$AFFECTED_PACKAGES" =~ "$package" ]]; then
236+ AFFECTED_PACKAGES="$AFFECTED_PACKAGES $package"
237+ fi
238+ fi
239+ done
240+
241+ # Check for CI workflow changes that affect GEOS integration
242+ if echo "$file" | grep -qE "^\.github/workflows/(python-package\.yml|test_geos_integration\.yml)$"; then
243+ REQUIRES_GEOS_TEST=true
244+ AFFECTED_PACKAGES="$AFFECTED_PACKAGES [CI-workflows]"
245+ fi
246+
247+ # Check for root-level scripts that might affect integration
248+ if echo "$file" | grep -qE "^install_packages\.sh$"; then
249+ REQUIRES_GEOS_TEST=true
250+ AFFECTED_PACKAGES="$AFFECTED_PACKAGES [install-scripts]"
251+ fi
252+ fi
253+ done
254+
255+ if [[ "$REQUIRES_GEOS_TEST" == "true" ]]; then
256+ echo "✓ GEOS integration test REQUIRED"
257+ echo " Affected packages/components:$AFFECTED_PACKAGES"
258+ echo " These packages are integrated into GEOS and require testing"
259+ echo "required=true" >> "$GITHUB_OUTPUT"
260+ echo "skip_reason=none" >> "$GITHUB_OUTPUT"
261+ else
262+ echo "⊘ GEOS integration test NOT required"
263+ echo " All changes are in documentation, non-integrated packages, or config files"
264+ echo " To force GEOS integration testing, add the 'test-geos-integration' label"
265+ echo "required=false" >> "$GITHUB_OUTPUT"
266+ echo "skip_reason=no-geos-integrated-changes" >> "$GITHUB_OUTPUT"
267+ fi
268+
269+ geos_ci_dispatch :
270+ name : Dispatch cases of GEOS CI
271+ runs-on : ubuntu-latest
272+ outputs :
273+ is_GEOS_CI_skipped : ${{ steps.dispatch.outputs.skipped }}
274+ fwd_geos_integration_required : ${{ steps.dispatch.outputs.fwd_geos_integration_required }}
275+ fwd_skip_reason : ${{ steps.dispatch.outputs.skip_reason }}
276+ needs : [check_geos_integration_required, check_integration_label, check_force_integration_label]
277+ steps :
278+ - name : Dispatch flag and req cases
279+ id : dispatch
280+ run : |
281+ GEOS_REQUIRED="${{ needs.check_geos_integration_required.outputs.geos_integration_required }}"
282+ HAS_TEST_LABEL="${{ needs.check_integration_label.outputs.has_geos_integration_label }}"
283+ HAS_FORCE_LABEL="${{ needs.check_force_integration_label.outputs.has_geos_integration_force_label }}"
284+ SKIP_REASON="${{ needs.check_geos_integration_required.outputs.skip_reason }}"
285+
286+ echo "fwd_geos_integration_required=${GEOS_REQUIRED}" >> "$GITHUB_OUTPUT"
287+ echo "fwd_skip_reason=${SKIP_REASON}" >> "$GITHUB_OUTPUT"
288+
289+ echo "=== GEOS Integration Dispatch ==="
290+ echo "GEOS Required (by file changes): ${GEOS_REQUIRED}"
291+ echo "Has 'test-geos-integration' label: ${HAS_TEST_LABEL}"
292+ echo "Has 'force-geos-integration' label: ${HAS_FORCE_LABEL}"
293+ echo ""
294+
295+ # Case 1: Force label - always run tests
296+ if [[ "$HAS_FORCE_LABEL" == "true" ]]; then
297+ echo "✓ 'force-geos-integration' label present - forcing GEOS integration tests"
298+ echo "skipped=false" >> "$GITHUB_OUTPUT"
299+ exit 0
300+ fi
301+
302+ # Case 2: GEOS required AND test label present - run tests
303+ if [[ "$GEOS_REQUIRED" == "true" && "$HAS_TEST_LABEL" == "true" ]]; then
304+ echo "✓ GEOS integration required and 'test-geos-integration' label present"
305+ echo " Will proceed with GEOS integration tests"
306+ echo "skipped=false" >> "$GITHUB_OUTPUT"
307+ exit 0
308+ fi
309+
310+ # Case 3: GEOS required BUT test label missing - ERROR
311+ if [[ "$GEOS_REQUIRED" == "true" && "$HAS_TEST_LABEL" == "false" ]]; then
312+ echo "✗ ERROR: GEOS integration is required but 'test-geos-integration' label is missing"
313+ echo ""
314+ echo "Your PR modifies GEOS-integrated packages:"
315+ echo " - geos-utils, geos-mesh, geos-xml-tools"
316+ echo " - hdf5-wrapper, pygeos-tools, geos-ats"
317+ echo ""
318+ echo "Action required: Add the 'test-geos-integration' label to this PR"
319+ exit 1
320+ fi
321+
322+ # Case 4: GEOS NOT required BUT test label present - SKIP TESTS
323+ if [[ "$GEOS_REQUIRED" == "false" && "$HAS_TEST_LABEL" == "true" ]]; then
324+ echo "⊘ SKIPPED: 'test-geos-integration' label present but GEOS integration is not required"
325+ echo ""
326+ echo "Your changes only affect:"
327+ echo " - Documentation"
328+ echo " - Non-integrated packages"
329+ echo " - Configuration files"
330+ echo ""
331+ echo "If you want to run GEOS integration tests anyway, use 'force-geos-integration' label to explicitly force testing"
332+ echo "skipped=true" >> "$GITHUB_OUTPUT"
333+ exit 0
334+ fi
335+
336+ # Case 5: GEOS NOT required AND no labels - SKIP TESTS
337+ if [[ "$GEOS_REQUIRED" == "false" && "$HAS_TEST_LABEL" == "false" ]]; then
338+ echo "⊘ GEOS integration not required and no labels present"
339+ echo " Skipping GEOS integration tests"
340+ echo " Reason: ${SKIP_REASON}"
341+ echo "skipped=true" >> "$GITHUB_OUTPUT"
342+ exit 0
343+ fi
344+
345+ # Should never reach here
346+ echo "✗ ERROR: Unexpected state in dispatch logic"
347+ exit 1
348+
349+ # Step 4: Run GEOS integration tests (only if required or label present)
350+ geos_integration_test :
351+ name : GEOS Integration Test
352+ needs : [geos_ci_dispatch]
353+ if : ${{ needs.geos_ci_dispatch.outputs.is_GEOS_CI_skipped.skipped != 'true' }}
354+ uses : ./.github/workflows/test_geos_integration.yml
355+
356+ # Final validation - Summarize CI results
357+ final_validation :
358+ name : Final CI Validation
359+ runs-on : ubuntu-latest
360+ needs : [geos_ci_dispatch, geos_integration_test]
361+ if : always() && github.event_name == 'pull_request'
362+ steps :
363+ - name : Validate CI completion
364+ run : |
365+ echo "Final CI Validation"
366+ echo "==================="
367+
368+ GEOS_REQUIRED="${{ needs.geos_ci_dispatch.outputs.fwd_geos_integration_required }}"
369+ SKIP_REASON="${{ needs.geos_ci_dispatch.outputs.fwd_skip_reason }}"
370+
371+ GEOS_RESULT="${{ needs.geos_integration_test.result }}"
372+
373+ if [[ "$GEOS_REQUIRED" == "true" ]]; then
374+ echo "GEOS integration test was required and triggered"
375+ if [[ "$GEOS_RESULT" == "success" ]]; then
376+ echo "✓ GEOS integration test PASSED"
377+ echo "✓ All CI requirements satisfied - PR can be merged"
378+ else
379+ echo "✗ GEOS integration test FAILED or was skipped"
380+ echo "✗ CI FAILED - PR cannot be merged until GEOS integration passes"
381+ exit 1
382+ fi
383+ else
384+ echo "GEOS integration test was NOT required"
385+ echo "Reason: $SKIP_REASON"
386+ echo ""
387+ echo "Changed files do not affect GEOS-integrated packages:"
388+ echo " - geos-utils, geos-mesh, geos-xml-tools"
389+ echo " - hdf5-wrapper, pygeos-tools, geos-ats"
390+ echo ""
391+ echo "If you want to run GEOS integration tests anyway,"
392+ echo "add the 'test-geos-integration' label to this PR"
393+ echo ""
394+ echo "✓ CI requirements satisfied - PR can be merged"
395+ fi
396+
0 commit comments