Skip to content

Migrate

Migrate #571

# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# also derived from https://github.com/we-cli/coverage-badge-action
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
schedule:
# Runs daily at 2 AM UTC (adjust timezone as needed)
- cron: '0 7 * * *'
workflow_dispatch:
name: test-coverage-local
jobs:
test-coverage:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
EARTHDATA_TOKEN: ${{ secrets.EARTHDATA_TOKEN }}
NASA_EARTHDATA_TOKEN: ${{ secrets.EARTHDATA_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: |
any::pak
any::covr
any::rstac
any::testthat
needs: coverage
- name: Cache C++ and R dependencies
uses: actions/cache@v4
with:
path: |
~/.cache/R
~/.local/share/R
key: dependencies-${{ runner.os }}-${{ hashFiles('**/DESCRIPTION') }}
restore-keys: |
dependencies-${{ runner.os }}-
- name: Session info
run: |
options(width = 100)
pkgs <- installed.packages()[, "Package"]
sessioninfo::session_info(pkgs, include_base = TRUE)
shell: Rscript {0}
- name: Test coverage
id: coverage
continue-on-error: true
env:
NOT_CRAN: "false"
run: |
Rscript ${{ github.workspace }}/.github/workflows/test-coverage.R ${{ runner.temp }} ${{ github.workspace }}
shell: bash
- name: Show comprehensive test output
if: always()
run: |
echo "========================================"
echo "SEARCHING FOR ALL TEST OUTPUT FILES"
echo "========================================"
echo ""
echo "=== Files in runner.temp ==="
find ${{ runner.temp }} -type f -name "*.Rout*" 2>/dev/null || echo "No .Rout files found"
echo ""
echo "=== Files in workspace ==="
find ${{ github.workspace }} -type f -name "*.Rout*" 2>/dev/null || echo "No .Rout files found"
echo ""
echo "========================================"
echo "CONTENT OF FAILURE FILE"
echo "========================================"
FAIL_FILE=$(find ${{ runner.temp }} -name "testthat.Rout.fail" -type f 2>/dev/null | head -1)
if [ -n "$FAIL_FILE" ]; then
echo "Found failure file: $FAIL_FILE"
echo "--- START OF FILE ---"
cat "$FAIL_FILE"
echo "--- END OF FILE ---"
else
echo "No testthat.Rout.fail file found"
fi
echo ""
echo "========================================"
echo "CONTENT OF SUCCESS FILE"
echo "========================================"
SUCCESS_FILE=$(find ${{ runner.temp }} -name "testthat.Rout" -type f 2>/dev/null | head -1)
if [ -n "$SUCCESS_FILE" ]; then
echo "Found success file: $SUCCESS_FILE"
echo "--- START OF FILE ---"
cat "$SUCCESS_FILE"
echo "--- END OF FILE ---"
else
echo "No testthat.Rout file found"
fi
echo ""
echo "========================================"
echo "CONTENT OF RES FILES"
echo "========================================"
find ${{ runner.temp }} -name "*.Rout.res" -type f 2>/dev/null | while read -r file; do
echo "File: $file"
echo "--- START ---"
cat "$file"
echo "--- END ---"
echo ""
done || echo "No .Rout.res files found"
echo ""
echo "========================================"
echo "PACKAGE DIRECTORY STRUCTURE"
echo "========================================"
echo "Contents of ${{ runner.temp }}/package:"
ls -laR ${{ runner.temp }}/package 2>/dev/null || echo "Directory not found"
echo ""
echo "========================================"
echo "COVERAGE OUTPUT FILE"
echo "========================================"
if [ -f "${{ github.workspace }}/local_cov.Rout" ]; then
echo "Coverage file exists:"
cat ${{ github.workspace }}/local_cov.Rout
else
echo "Coverage file NOT found at ${{ github.workspace }}/local_cov.Rout"
fi
echo ""
echo "========================================"
echo "ALL .Rout FILES CONTENT"
echo "========================================"
find ${{ runner.temp }} -type f -name "*.Rout*" 2>/dev/null | while read -r file; do
echo ""
echo "========== FILE: $file =========="
head -n 200 "$file" 2>/dev/null || echo "Could not read file"
if [ $(wc -l < "$file" 2>/dev/null || echo 0) -gt 200 ]; then
echo "... (file truncated, showing first 200 lines) ..."
fi
done || echo "No .Rout files to display"
shell: bash
- name: Check if coverage file exists
id: check-coverage-file
if: always()
run: |
if [ -f "${{ github.workspace }}/local_cov.Rout" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Coverage file exists"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Coverage file does NOT exist - creating with 0"
echo "0" > ${{ github.workspace }}/local_cov.Rout
fi
shell: bash
- name: Get Values
id: get-values
if: always()
shell: bash
run: |
if [ -f "${{ github.workspace }}/local_cov.Rout" ]; then
COV=$(cat ${{ github.workspace }}/local_cov.Rout | tr -d '[:space:]')
if [ -z "$COV" ]; then
echo "Coverage file is empty, using 0"
COV="0"
fi
else
echo "Coverage file not found, using 0"
COV="0"
fi
echo "Patch coverage read from local_cov.Rout: $COV"
echo "coverage=$COV" >> $GITHUB_OUTPUT
- name: Fail if coverage calculation failed
if: steps.coverage.outcome == 'failure'
run: |
echo "::error::Coverage calculation failed. Check the test output above for details."
exit 1
- name: Checkout gh-pages
id: checkout-gh-pages
if: steps.coverage.outcome == 'success'
uses: actions/checkout@v4
continue-on-error: true
with:
ref: gh-pages
- name: Patch comparison
if: steps.coverage.outcome == 'success'
id: patch-comparison
shell: bash
run: |
cov_patch="${{ steps.get-values.outputs.coverage }}"
# Check if gh-pages checkout succeeded and file exists
if [[ "${{ steps.checkout-gh-pages.outcome }}" == "success" ]] && test -f cov_current.Rout; then
cov_current=$(cat cov_current.Rout)
echo "Current coverage: $cov_current"
else
echo "No gh-pages branch or cov_current.Rout found. Setting current coverage to 0."
cov_current=0
fi
echo "Patch coverage: $cov_patch"
# Pass if coverage exceeds 99%, is at least current coverage,
# or is within a small tolerance to absorb platform-specific
# variability in total coverage calculations.
tolerance=0.5
cov_floor=$(echo "$cov_current - $tolerance" | bc -l)
if (( $(echo "$cov_patch > 99" | bc -l) )); then
echo "✓ Patch coverage ($cov_patch%) exceeds 99% threshold."
echo "cov_update=$cov_patch" >> $GITHUB_OUTPUT
elif (( $(echo "$cov_patch >= $cov_current" | bc -l) )); then
echo "✓ Patch coverage ($cov_patch%) is greater than or equal to current coverage ($cov_current%)."
echo "cov_update=$cov_patch" >> $GITHUB_OUTPUT
elif (( $(echo "$cov_patch >= $cov_floor" | bc -l) )); then
echo "::warning::Patch coverage ($cov_patch%) is below current coverage ($cov_current%)"
echo "::warning::but within allowed tolerance ($tolerance%)."
echo "cov_update=$cov_current" >> $GITHUB_OUTPUT
else
echo "::error::Patch coverage ($cov_patch%) is less than"
echo "::error::current coverage floor ($cov_floor%) and below 99%."
echo "::error::This indicates that new code is not adequately tested or existing coverage was reduced."
exit 1
fi
- name: Overwrite cov_current.Rout
if: ${{ github.event_name == 'push' && steps.coverage.outcome == 'success' }}
shell: bash
run: |
cov_update="${{ steps.patch-comparison.outputs.cov_update }}"
touch cov_current.Rout
echo "$cov_update" > cov_current.Rout
echo "Updated cov_current.Rout with: $cov_update"
- name: Create Badges
if: steps.coverage.outcome == 'success'
shell: bash
run: |
npm i -g badgen-cli
export COV=${{ steps.get-values.outputs.coverage }}
echo "Creating badge with coverage: $COV%"
# Determine color based on coverage
if (( $(echo "$COV >= 95" | bc -l) )); then
COLOR="green"
elif (( $(echo "$COV >= 80" | bc -l) )); then
COLOR="yellow"
elif (( $(echo "$COV >= 60" | bc -l) )); then
COLOR="orange"
else
COLOR="red"
fi
echo "Badge color: $COLOR"
mkdir -p badges
badgen -j coverage -s "$COV%" -c "$COLOR" > badges/coverage.svg
- name: Deploy Badges
if: steps.coverage.outcome == 'success'
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "Update badges [skip ci] & cov_current.Rout"
branch: gh-pages
skip_fetch: true
skip_checkout: true
create_branch: true
- name: Checkout Back
if: always()
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Final Summary
if: always()
run: |
echo "========================================"
echo "WORKFLOW SUMMARY"
echo "========================================"
echo "Coverage calculation: ${{ steps.coverage.outcome }}"
echo "Coverage value: ${{ steps.get-values.outputs.coverage }}%"
echo "Coverage file exists: ${{ steps.check-coverage-file.outputs.exists }}"
if [ "${{ steps.coverage.outcome }}" == "failure" ]; then
echo ""
echo "❌ Coverage calculation FAILED"
echo "Please review the test output above to identify the failing tests"
else
echo ""
echo "✅ Coverage calculation SUCCEEDED"
fi
shell: bash