Skip to content

Release 1.5.0

Release 1.5.0 #23

Workflow file for this run

name: Swift CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
name: Test and Coverage
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Swift
uses: swift-actions/setup-swift@v3
with:
swift-version: "6.2"
- name: Cache Swift packages
uses: actions/cache@v4
with:
path: .build
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
restore-keys: |
${{ runner.os }}-spm-
- name: Build
run: swift build
- name: Run tests with coverage
id: tests
run: |
swift test --enable-code-coverage 2>&1 | tee test_output.txt
# Extract test count from output
TEST_COUNT=$(grep -oE "Test run with [0-9]+ tests" test_output.txt | grep -oE "[0-9]+" | head -1 || echo "0")
if [ -z "$TEST_COUNT" ] || [ "$TEST_COUNT" = "0" ]; then
# Try alternative pattern
TEST_COUNT=$(grep -oE "[0-9]+ tests? passed" test_output.txt | grep -oE "[0-9]+" | head -1 || echo "0")
fi
if [ -z "$TEST_COUNT" ] || [ "$TEST_COUNT" = "0" ]; then
# Try one more pattern
TEST_COUNT=$(grep -oE "passed.*[0-9]+ tests?" test_output.txt | grep -oE "[0-9]+" | head -1 || echo "0")
fi
echo "test_count=$TEST_COUNT" >> $GITHUB_OUTPUT
echo "📊 Test count: $TEST_COUNT"
if [ "$TEST_COUNT" = "0" ]; then
echo "⚠️ Could not extract test count, showing last 20 lines of output:"
tail -20 test_output.txt || true
fi
- name: Find coverage data
id: coverage
run: |
PROFDATA=$(find .build -name "*.profdata" | head -1)
if [ -z "$PROFDATA" ]; then
echo "❌ No coverage data found"
find .build -type f -name "*.profdata" || echo "No profdata files"
exit 1
fi
echo "profdata=$PROFDATA" >> $GITHUB_OUTPUT
echo "📊 Coverage data: $PROFDATA"
- name: Find test binary
id: binary
run: |
# Try multiple paths for test binary
TEST_BINARY=""
# Method 1: xctest bundle
TEST_BINARY=$(find .build -path "*/EKNetworkPackageTests.xctest/Contents/MacOS/EKNetworkPackageTests" 2>/dev/null | head -1)
# Method 2: Direct binary
if [ -z "$TEST_BINARY" ]; then
TEST_BINARY=$(find .build -name "*PackageTests" -type f -perm +111 2>/dev/null | grep -v ".dSYM" | head -1)
fi
# Method 3: Any test binary
if [ -z "$TEST_BINARY" ]; then
TEST_BINARY=$(find .build -name "*Tests" -type f -perm +111 2>/dev/null | grep -v ".dSYM" | head -1)
fi
if [ -z "$TEST_BINARY" ]; then
echo "❌ Test binary not found"
echo "Searching in .build:"
find .build -type f -perm +111 2>/dev/null | head -10 || true
exit 1
fi
echo "binary=$TEST_BINARY" >> $GITHUB_OUTPUT
echo "🧪 Test binary: $TEST_BINARY"
- name: Generate coverage report
run: |
PROFDATA="${{ steps.coverage.outputs.profdata }}"
TEST_BINARY="${{ steps.binary.outputs.binary }}"
# Detect architecture
ARCH=$(uname -m)
if [ "$ARCH" = "arm64" ]; then
ARCH_FLAG="-arch arm64"
else
ARCH_FLAG="-arch x86_64"
fi
echo "📈 Generating coverage report..."
xcrun llvm-cov show \
-instr-profile "$PROFDATA" \
"$TEST_BINARY" \
$ARCH_FLAG \
-format=text \
-ignore-filename-regex=".*Tests.*|.*Version\.swift|.*EKNetworkVersion\.swift|.*ProgressDelegate\.swift|.*ProgressSessionManager\.swift" \
> coverage_report.txt 2>&1 || {
echo "⚠️ Coverage report generation had issues, but continuing..."
echo "PROFDATA: $PROFDATA"
echo "TEST_BINARY: $TEST_BINARY"
echo "ARCH: $ARCH"
ls -la "$PROFDATA" || true
ls -la "$TEST_BINARY" || true
}
- name: Calculate coverage
id: calc
run: |
if [ ! -f coverage_report.txt ]; then
echo "❌ Coverage report not found"
exit 1
fi
# llvm-cov format: " line_number|execution_count|code"
# Count lines with execution data (lines that have |)
TOTAL_LINES=$(grep -E "^[[:space:]]*[0-9]+\|[[:space:]]*[0-9]+\|" coverage_report.txt 2>/dev/null | wc -l | tr -d ' ' || echo "0")
# Count covered lines (execution count > 0)
COVERED_LINES=$(grep -E "^[[:space:]]*[0-9]+\|[[:space:]]*[1-9][0-9]*\|" coverage_report.txt 2>/dev/null | wc -l | tr -d ' ' || echo "0")
if [ "$TOTAL_LINES" -eq 0 ]; then
echo "⚠️ No lines found in coverage report"
echo "First 20 lines of report:"
head -20 coverage_report.txt || true
exit 1
fi
# Calculate percentage
COVERAGE=$(awk "BEGIN {printf \"%.2f\", ($COVERED_LINES * 100) / $TOTAL_LINES}")
COVERAGE_INT=$(echo "$COVERAGE" | cut -d. -f1)
echo "total=$TOTAL_LINES" >> $GITHUB_OUTPUT
echo "covered=$COVERED_LINES" >> $GITHUB_OUTPUT
echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT
echo "coverage_int=$COVERAGE_INT" >> $GITHUB_OUTPUT
echo ""
echo "✅ Code Coverage Report"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Total lines: $TOTAL_LINES"
echo "Covered lines: $COVERED_LINES"
echo "Coverage: ${COVERAGE}%"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- name: Check coverage threshold
run: |
COVERAGE_INT="${{ steps.calc.outputs.coverage_int }}"
COVERAGE="${{ steps.calc.outputs.coverage }}"
if [ "$COVERAGE_INT" -lt 98 ]; then
echo "❌ Code coverage is ${COVERAGE}%, but required minimum is 98%"
exit 1
else
echo "✅ Code coverage is ${COVERAGE}% (meets 98% requirement)"
fi
- name: Upload coverage report
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: coverage_report.txt
retention-days: 30
- name: Create test summary
run: |
echo "## Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "✅ **Tests:** ${{ steps.tests.outputs.test_count }} passed" >> $GITHUB_STEP_SUMMARY
echo "📊 **Coverage:** ${{ steps.calc.outputs.coverage }}% (${{ steps.calc.outputs.covered }}/${{ steps.calc.outputs.total }} lines)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Coverage Details" >> $GITHUB_STEP_SUMMARY
echo "- Total lines: ${{ steps.calc.outputs.total }}" >> $GITHUB_STEP_SUMMARY
echo "- Covered lines: ${{ steps.calc.outputs.covered }}" >> $GITHUB_STEP_SUMMARY
echo "- Coverage: ${{ steps.calc.outputs.coverage }}%" >> $GITHUB_STEP_SUMMARY
release:
name: Tag and Release
runs-on: ubuntu-latest
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Read version
id: version
run: |
VERSION=$(sed -n 's/.*EKNetworkVersionString = "\(.*\)".*/\1/p' Sources/EKNetwork/Version.swift)
if [ -z "$VERSION" ]; then
echo "❌ Failed to read version from Version.swift"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
- name: Create tag
id: tag
run: |
TAG="${{ steps.version.outputs.tag }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Tag $TAG already exists, skipping tag creation."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"
echo "exists=false" >> "$GITHUB_OUTPUT"
- name: Create GitHub release
if: steps.tag.outputs.exists == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
generate_release_notes: true