diff --git a/.github/scripts/extract_coverage.sh b/.github/scripts/extract_coverage.sh new file mode 100644 index 0000000..1611c30 --- /dev/null +++ b/.github/scripts/extract_coverage.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Environment variables +PACKAGE_NAME=${PACKAGE_NAME:-"SnappThemingSVGSupport"} +SOURCE_PATH=${SOURCE_PATH:-"$PACKAGE_NAME/Sources"} +OUTPUT_FILE=${COVERAGE_SUMMARY_FILE:-"pr_coverage_summary.txt"} +DECIMAL_PLACES=6 + +# Use CODECOV_PATH from environment if provided, otherwise get it +if [ -z "$CODECOV_PATH" ]; then + CODECOV_PATH=$(swift test --enable-code-coverage --show-codecov-path) +fi +echo "Using coverage report at: $CODECOV_PATH" + +# Extract all line coverage data for files containing SOURCE_PATH +FILES_LINE_COUNTS=$(jq -r --arg path "$SOURCE_PATH" '.data[0].files[] | select(.filename | contains($path)) | .summary.lines' "$CODECOV_PATH") + +total_lines=0 +covered_lines=0 + +# Loop through each file's line count data +for lines_data in $(echo "$FILES_LINE_COUNTS" | jq -c '.'); do + # Extract total and covered lines for each file + total=$(echo "$lines_data" | jq '.count') + covered=$(echo "$lines_data" | jq '.covered') + + # Add to the total lines and covered lines + total_lines=$((total_lines + total)) + covered_lines=$((covered_lines + covered)) +done + +# Calculate the average line coverage percentage +if [ $total_lines -gt 0 ]; then + average_coverage=$(echo "scale=$DECIMAL_PLACES; $covered_lines * 100 / $total_lines" | bc) +else + average_coverage=0 +fi + +average_coverage_rounded=$(echo "$average_coverage" | awk '{print int($1 * 100 + 0.5) / 100}') +average_coverage_with_percentage="${average_coverage_rounded}%" + +# Save to output file +cat < "$OUTPUT_FILE" +| ID | Name | Executable Lines | Coverage | +|----|------|-----------------:|---------:| +| 0 | $PACKAGE_NAME | $total_lines | **$average_coverage_with_percentage** | +EOF + +echo "Coverage report generated with $average_coverage_with_percentage coverage" diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml new file mode 100644 index 0000000..73182a0 --- /dev/null +++ b/.github/workflows/run_tests.yml @@ -0,0 +1,85 @@ +# This workflow will test a Swift project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift + +name: Package Test + +env: + EXTRACT_COVERAGE: '.github/scripts/extract_coverage.sh' + CREATE_COMMENT: '.github/scripts/create_pr_comment.js' + COVERAGE_REPORT_PATH: './coverage/coverage.xcresult' + XCODE_VERSION: 'Xcode_16.2.app' + XCODE_PATH: '/Applications/Xcode_16.2.app/Contents/Developer' + COVERAGE_SUMMARY_FILE: 'pr_coverage_summary.txt' + BOT_COMMENT_HEADER: '### 🛡️ Code Coverage Report' + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + + - name: List Xcode Installations + run: sudo ls -1 /Applications | grep "Xcode" + + - name: Select Xcode + run: sudo xcode-select -s ${{ env.XCODE_PATH }} + + - name: Run Swift Tests with Coverage + run: | + swift test --enable-swift-testing --enable-code-coverage + CODECOV_PATH=$(swift test --enable-code-coverage --show-codecov-path) + echo "CODECOV_PATH=${CODECOV_PATH}" >> $GITHUB_ENV + + - name: Run Coverage Extraction Script + run: bash ${{ env.EXTRACT_COVERAGE }} + + - name: Comment on Pull Request + if: github.event_name == 'pull_request' + uses: actions/github-script@v6 + with: + script: | + const fs = require('fs'); + const coverageOutput = fs.readFileSync('${{ env.COVERAGE_SUMMARY_FILE }}', 'utf8'); + + const newComment = ` + ${{ env.BOT_COMMENT_HEADER }} + + ${coverageOutput} + + _Generated by GitHub Actions._ + `; + + // Fetch existing comments + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + }); + + // Identify and delete previous bot comments + const botComments = comments.data.filter(comment => + comment.body.includes("${{ env.BOT_COMMENT_HEADER }}") + ); + + for (const botComment of botComments) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + }); + } + + // Create a comment on the pull request + await github.rest.issues.createComment({ + issue_number: context.payload.pull_request.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: newComment, + }); diff --git a/SnappThemingSVGSupport.xctestplan b/SnappThemingSVGSupport.xctestplan new file mode 100644 index 0000000..5dc1df6 --- /dev/null +++ b/SnappThemingSVGSupport.xctestplan @@ -0,0 +1,32 @@ +{ + "configurations" : [ + { + "id" : "A7A2AE7E-6FA3-49EF-A068-BB0BC519E557", + "name" : "Configuration 1", + "options" : { + + } + } + ], + "defaultOptions" : { + "codeCoverage" : { + "targets" : [ + { + "containerPath" : "container:", + "identifier" : "SnappThemingSVGSupport", + "name" : "SnappThemingSVGSupport" + } + ] + } + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:", + "identifier" : "SnappThemingSVGSupportTests", + "name" : "SnappThemingSVGSupportTests" + } + } + ], + "version" : 1 +}