Add comprehensive tests for utils.R and improve coverage for low-coverage files #37
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
| # GitHub Copilot Environment Setup for lavaanExtra R Package | |
| # This workflow sets up the development environment before Copilot starts working | |
| # Based on: https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environment | |
| name: copilot-setup-steps | |
| on: | |
| workflow_dispatch: | |
| # This workflow is automatically triggered by GitHub Copilot | |
| # before the agent starts working in the repository | |
| pull_request: | |
| types: [opened, synchronize] | |
| jobs: | |
| copilot-setup-steps: | |
| # Optional: only run on Copilot agent commits; remove this if you also want your own pushes to trigger it | |
| if: > | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'pull_request' && | |
| github.actor == 'github-copilot') | |
| runs-on: ubuntu-latest | |
| env: | |
| GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| R_KEEP_PKG_SOURCE: yes | |
| _R_CHECK_CRAN_INCOMING_: false | |
| _R_CHECK_FORCE_SUGGESTS_: false | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| - name: Determine if R setup is needed | |
| id: check_r_needed | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| echo "Checking if R environment setup is needed..." | |
| R_NEEDED=true | |
| # If this is a PR, use the PR's changed files (authoritative) | |
| if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then | |
| PR_NUMBER=$(jq -r '.number' < "$GITHUB_EVENT_PATH") | |
| echo "PR number: $PR_NUMBER" | |
| # Get changed files from the PR | |
| FILES=$(curl -sS -H "Authorization: Bearer ${GITHUB_TOKEN}" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files?per_page=100" \ | |
| | jq -r '.[].filename') | |
| echo "Files in PR:" | |
| echo "${FILES}" | |
| # If ANY file is not in the "docs/config-only" set -> need R | |
| # Allowed-only set (docs/config): tweak as you like | |
| # Note: use a temporary file to avoid word-splitting issues | |
| echo "${FILES}" > /tmp/pr_files.txt | |
| if grep -E '\.(R|Rmd|Rd)$' /tmp/pr_files.txt >/dev/null || \ | |
| grep -E '^(R/|tests/|man/|vignettes/|src/)' /tmp/pr_files.txt >/dev/null; then | |
| echo "Detected R-related paths in PR." | |
| R_NEEDED=true | |
| else | |
| # Anything outside the allowed docs/config list? Then R is needed. | |
| if grep -v -E '^(\.github/copilot-instructions\.md|\.github/workflows/|README\.md|\.gitignore|\.Rbuildignore|NEWS\.md|CONTRIBUTING\.md|LICENSE|CITATION\.cff|DESCRIPTION|cran-comments\.md|.+\.md$|.+\.ya?ml$)$' /tmp/pr_files.txt | grep -E '.' >/dev/null; then | |
| echo "Changes go beyond docs/config-only." | |
| R_NEEDED=true | |
| else | |
| echo "PR is docs/config-only." | |
| R_NEEDED=false | |
| fi | |
| fi | |
| else | |
| # No PR context (Copilot run, pushes, etc.) | |
| # Fallback: fast probe + light heuristics | |
| echo "No PR context; using probe/heuristics." | |
| # Example probe: if R already present and renv/cache present, skip | |
| if command -v R >/dev/null 2>&1 && [ -f "renv.lock" ] && [ -d "~/.cache/R" ]; then | |
| echo "R and cache present; likely no heavy setup needed." | |
| R_NEEDED=false | |
| else | |
| # Last-commit heuristic (with full history available) | |
| # Compare against the immediate parent only, to avoid "recent history" bias | |
| if git rev-parse --verify HEAD~1 >/dev/null 2>&1; then | |
| CHANGED=$(git diff --name-only HEAD~1...HEAD || true) | |
| else | |
| CHANGED=$(git ls-files) # first run in a new repo clone | |
| fi | |
| echo "${CHANGED}" > /tmp/changed.txt | |
| if grep -E '\.(R|Rmd|Rd)$' /tmp/changed.txt >/dev/null || \ | |
| grep -E '^(R/|tests/|man/|vignettes/|src/)' /tmp/changed.txt >/dev/null; then | |
| R_NEEDED=true | |
| else | |
| if grep -v -E '^(\.github/copilot-instructions\.md|\.github/workflows/|README\.md|\.gitignore|\.Rbuildignore|NEWS\.md|CONTRIBUTING\.md|LICENSE|CITATION\.cff|DESCRIPTION|cran-comments\.md|.+\.md$|.+\.ya?ml$)$' /tmp/changed.txt | grep -E '.' >/dev/null; then | |
| R_NEEDED=true | |
| else | |
| R_NEEDED=false | |
| fi | |
| fi | |
| fi | |
| fi | |
| echo "R_NEEDED=${R_NEEDED}" | tee -a "$GITHUB_OUTPUT" | |
| echo "" | |
| echo "=== FINAL DECISION ===" | |
| if [ "$R_NEEDED" == "true" ]; then | |
| echo "✓ R setup WILL be run" | |
| echo "Reason: Analysis indicates R functionality is needed" | |
| else | |
| echo "✓ R setup WILL BE SKIPPED" | |
| echo "Reason: Only documentation/config files detected" | |
| fi | |
| echo "Decision: R_NEEDED=$R_NEEDED" | |
| - name: Setup R | |
| if: steps.check_r_needed.outputs.R_NEEDED == 'true' | |
| uses: r-lib/actions/setup-r@v2 | |
| with: | |
| r-version: 'release' | |
| use-public-rspm: true | |
| - name: Setup pandoc (required for building vignettes and reprex) | |
| if: steps.check_r_needed.outputs.R_NEEDED == 'true' | |
| uses: r-lib/actions/setup-pandoc@v2 | |
| - name: Install system dependencies | |
| if: steps.check_r_needed.outputs.R_NEEDED == 'true' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libcurl4-openssl-dev \ | |
| libssl-dev \ | |
| libxml2-dev | |
| - name: Setup R user library | |
| if: steps.check_r_needed.outputs.R_NEEDED == 'true' | |
| run: | | |
| mkdir -p ~/R/library | |
| echo 'R_LIBS_USER=~/R/library' >> ~/.Renviron | |
| shell: bash | |
| - name: Install R dependencies using pak | |
| if: steps.check_r_needed.outputs.R_NEEDED == 'true' | |
| uses: r-lib/actions/setup-r-dependencies@v2 | |
| with: | |
| # Use stable pak for reliable dependency resolution | |
| dependencies: '"hard"' | |
| # Install core package dependencies and essential development tools | |
| extra-packages: | | |
| local::. | |
| any::testthat | |
| any::lintr | |
| any::styler | |
| any::roxygen2 | |
| any::devtools | |
| any::reprex | |
| any::knitr | |
| any::rmarkdown | |
| any::clipr | |
| needs: check | |
| # Let r-lib/actions handle caching automatically (more efficient than manual cache) | |
| # NOTE: Suggested packages are NOT installed during setup to save time. | |
| # Only core dependencies (Imports/Depends) and essential development tools are installed. | |
| # Additional packages are installed on-demand based on specific PR requirements. | |
| # See copilot-instructions.md for targeted installation guidance. | |
| # NOTE: The local package (lavaanExtra) is automatically built and installed | |
| # by setup-r-dependencies@v2 when using 'local::.', eliminating the need | |
| # for manual build/install steps | |
| - name: Verify R installation and packages | |
| if: steps.check_r_needed.outputs.R_NEEDED == 'true' | |
| run: | | |
| cat("=== R Version ===\n") | |
| print(R.version.string) | |
| cat("\n=== Core Development Packages ===\n") | |
| required_packages <- c("lavaan", "insight", "testthat", "lintr", "styler", "roxygen2", "reprex") | |
| for (pkg in required_packages) { | |
| if (requireNamespace(pkg, quietly = TRUE)) { | |
| cat("✓", pkg, "- version", as.character(packageVersion(pkg)), "\n") | |
| } else { | |
| cat("✗", pkg, "- NOT AVAILABLE\n") | |
| } | |
| } | |
| cat("\n=== lavaanExtra Package (auto-installed by setup-r-dependencies) ===\n") | |
| if (requireNamespace("lavaanExtra", quietly = TRUE)) { | |
| library(lavaanExtra) | |
| cat("✓ lavaanExtra - version", as.character(packageVersion("lavaanExtra")), "\n") | |
| } else { | |
| cat("✗ lavaanExtra - NOT AVAILABLE\n") | |
| } | |
| shell: Rscript {0} | |
| - name: Test core lavaanExtra functionality | |
| if: steps.check_r_needed.outputs.R_NEEDED == 'true' | |
| run: | | |
| library(lavaanExtra) | |
| cat("=== Testing Core Functions ===\n") | |
| # Test basic data function | |
| test_data <- data.frame(id = c(1,1,2,3), val = c(1,2,3,4)) | |
| duplicates <- extract_duplicates(test_data, id = "id") | |
| cat("✓ extract_duplicates() works\n") | |
| # Basic verification that package loads correctly | |
| cat("✓ lavaanExtra package loads and core functions work\n") | |
| cat("=== Core Function Tests Complete ===\n") | |
| shell: Rscript {0} | |
| - name: Display environment summary | |
| run: | | |
| echo "=== GitHub Copilot Environment Setup Complete ===" | |
| echo "" | |
| if [ "${{ steps.check_r_needed.outputs.R_NEEDED }}" == "true" ]; then | |
| echo "✓ Optimized R development environment configured using pak:" | |
| echo " - R and essential system dependencies installed" | |
| echo " - Core package dependencies (Imports/Depends) installed via setup-r-dependencies" | |
| echo " - lavaanExtra package automatically built and installed" | |
| echo " - Essential development tools (lintr, styler, roxygen2, devtools)" | |
| echo " - reprex dependencies ready (reprex + knitr + rmarkdown + pandoc)" | |
| echo " - Efficient pak-based dependency resolution with automatic caching" | |
| echo " - Suggested packages NOT installed (install as needed per PR)" | |
| echo " - Minimal testing performed for faster setup" | |
| echo "" | |
| echo "The environment is ready for R package development work." | |
| echo "Copilot can now build, test, lint, and create reproducible examples." | |
| else | |
| echo "✓ Minimal environment configured:" | |
| echo " - Repository checked out and ready" | |
| echo " - R setup SKIPPED - only documentation/config changes detected" | |
| echo "" | |
| if [ "${GITHUB_EVENT_NAME:-}" = "pull_request" ]; then | |
| echo "Decision based on PR file analysis using GitHub API" | |
| else | |
| echo "Decision based on git diff analysis" | |
| fi | |
| echo "The environment is ready for documentation and configuration work." | |
| echo "Perfect for version bumps, NEWS.md updates, and text editing tasks." | |
| fi | |
| shell: bash |