Skip to content

docs: update CHANGELOG.md for v0.1.3 #51

docs: update CHANGELOG.md for v0.1.3

docs: update CHANGELOG.md for v0.1.3 #51

Workflow file for this run

name: PR Checks
on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
push:
branches: [main]
# Cancel in-progress runs for the same PR
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
XCODE_VERSION: '26.0'
APP_NAME: MyMacCleaner
SCHEME: MyMacCleaner
DESTINATION: 'platform=macOS'
jobs:
# ============================================
# Build Validation
# ============================================
build:
name: Build
runs-on: macos-26
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Select Xcode
run: |
sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app || \
sudo xcode-select -s /Applications/Xcode.app
xcodebuild -version
- name: Cache Swift Package Dependencies
uses: actions/cache@v4
with:
path: |
~/Library/Developer/Xcode/DerivedData/**/SourcePackages
.build
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved', '**/*.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: |
${{ runner.os }}-spm-
- name: Resolve Dependencies
run: |
xcodebuild -resolvePackageDependencies \
-project ${{ env.APP_NAME }}.xcodeproj \
-scheme ${{ env.SCHEME }}
- name: Build (Debug)
run: |
xcodebuild build \
-project ${{ env.APP_NAME }}.xcodeproj \
-scheme ${{ env.SCHEME }} \
-destination "${{ env.DESTINATION }}" \
-configuration Debug \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
2>&1 | tee build.log
# Check for warnings (optional - remove if too strict)
# if grep -q "warning:" build.log; then
# echo "::warning::Build produced warnings"
# fi
- name: Build (Release)
run: |
xcodebuild build \
-project ${{ env.APP_NAME }}.xcodeproj \
-scheme ${{ env.SCHEME }} \
-destination "${{ env.DESTINATION }}" \
-configuration Release \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO
# ============================================
# Unit Tests
# ============================================
test:
name: Unit Tests
runs-on: macos-26
timeout-minutes: 20
needs: build
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Select Xcode
run: |
sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app || \
sudo xcode-select -s /Applications/Xcode.app
- name: Cache Swift Package Dependencies
uses: actions/cache@v4
with:
path: |
~/Library/Developer/Xcode/DerivedData/**/SourcePackages
.build
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved', '**/*.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: |
${{ runner.os }}-spm-
- name: Run Unit Tests
run: |
xcodebuild test \
-project ${{ env.APP_NAME }}.xcodeproj \
-scheme ${{ env.SCHEME }} \
-destination "${{ env.DESTINATION }}" \
-testPlan "MyMacCleanerTests" \
-enableCodeCoverage YES \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
2>&1 | xcpretty --report junit --output test-results.xml || true
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: test-results.xml
# ============================================
# UI Tests (optional, runs on macOS host)
# ============================================
ui-test:
name: UI Tests
runs-on: macos-26
timeout-minutes: 30
needs: build
# UI tests may be flaky in CI - make optional
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Select Xcode
run: |
sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app || \
sudo xcode-select -s /Applications/Xcode.app
- name: Cache Swift Package Dependencies
uses: actions/cache@v4
with:
path: |
~/Library/Developer/Xcode/DerivedData/**/SourcePackages
.build
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved', '**/*.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: |
${{ runner.os }}-spm-
- name: Run UI Tests
run: |
xcodebuild test \
-project ${{ env.APP_NAME }}.xcodeproj \
-scheme ${{ env.SCHEME }} \
-destination "${{ env.DESTINATION }}" \
-testPlan "MyMacCleanerUITests" \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
2>&1 | xcpretty || true
# ============================================
# Archive Test (ensures release builds work)
# ============================================
archive:
name: Archive Test
runs-on: macos-26
timeout-minutes: 20
needs: [build, test]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Select Xcode
run: |
sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app || \
sudo xcode-select -s /Applications/Xcode.app
- name: Cache Swift Package Dependencies
uses: actions/cache@v4
with:
path: |
~/Library/Developer/Xcode/DerivedData/**/SourcePackages
.build
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved', '**/*.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: |
${{ runner.os }}-spm-
- name: Archive Build
run: |
xcodebuild archive \
-project ${{ env.APP_NAME }}.xcodeproj \
-scheme ${{ env.SCHEME }} \
-archivePath build/${{ env.APP_NAME }}.xcarchive \
-configuration Release \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
ONLY_ACTIVE_ARCH=NO
# Verify archive was created
if [ ! -d "build/${{ env.APP_NAME }}.xcarchive" ]; then
echo "::error::Archive was not created"
exit 1
fi
# Verify app exists in archive
if [ ! -d "build/${{ env.APP_NAME }}.xcarchive/Products/Applications/${{ env.APP_NAME }}.app" ]; then
echo "::error::App not found in archive"
exit 1
fi
echo "Archive created successfully"
ls -la "build/${{ env.APP_NAME }}.xcarchive/Products/Applications/"
# ============================================
# SwiftLint (code style)
# ============================================
lint:
name: SwiftLint
runs-on: macos-26
timeout-minutes: 5
# Don't block merge on lint issues - just warn
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install SwiftLint
run: |
brew install swiftlint || true
- name: Run SwiftLint
run: |
if command -v swiftlint &> /dev/null; then
swiftlint lint --reporter github-actions-logging || true
else
echo "SwiftLint not available, skipping"
fi
# ============================================
# Code Quality Checks (based on CODE_REVIEW_GUIDELINES.md)
# ============================================
code-quality:
name: Code Quality
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check for print() statements
run: |
echo "Checking for print() statements in production code..."
# Exclude test files and comments
PRINTS=$(grep -rn "print(" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "// " | grep -v "Test" | grep -v "#Preview" || true)
if [ -n "$PRINTS" ]; then
echo "::warning::Found print() statements in production code:"
echo "$PRINTS"
echo ""
echo "Consider removing print() statements before merging."
else
echo "✅ No print() statements found"
fi
- name: Check for force unwraps
run: |
echo "Checking for force unwraps (!)..."
# Look for force unwraps, excluding comments and common safe patterns
FORCE_UNWRAPS=$(grep -rn '\![^=]' ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "//" | grep -v "!=" | grep -v "!important" | grep -v "@IBOutlet" | grep -v "canImport" || true)
COUNT=$(echo "$FORCE_UNWRAPS" | grep -c "!" || echo "0")
if [ "$COUNT" -gt "0" ] && [ -n "$FORCE_UNWRAPS" ]; then
echo "::warning::Found potential force unwraps:"
echo "$FORCE_UNWRAPS" | head -20
echo ""
echo "Review these for safety. Force unwraps should have justification."
else
echo "✅ No obvious force unwraps found"
fi
- name: Check macOS 26 availability patterns
run: |
echo "Checking macOS 26 compatibility patterns..."
# Check for #unavailable (should use #available instead)
UNAVAILABLE=$(grep -rn "#unavailable" ${{ env.APP_NAME }}/ --include="*.swift" || true)
if [ -n "$UNAVAILABLE" ]; then
echo "::error::Found #unavailable patterns (use #available with else instead):"
echo "$UNAVAILABLE"
exit 1
fi
echo "✅ No #unavailable patterns found"
# Check that glassEffect usage has availability checks
GLASS_NO_CHECK=$(grep -rn "\.glassEffect" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "#available" | grep -v "//" || true)
if [ -n "$GLASS_NO_CHECK" ]; then
echo "::warning::Found .glassEffect without #available check:"
echo "$GLASS_NO_CHECK"
echo ""
echo "Ensure all glassEffect calls are wrapped in #available(macOS 26, *)"
else
echo "✅ All glassEffect calls appear to have availability checks"
fi
# Verify macOS 26 patterns exist (sanity check)
MACOS26_COUNT=$(grep -rn "#available(macOS 26" ${{ env.APP_NAME }}/ --include="*.swift" | wc -l || echo "0")
echo "Found $MACOS26_COUNT macOS 26 availability checks"
- name: Check for large files
run: |
echo "Checking for oversized files..."
# Flag Swift files over 500 lines (per guidelines)
LARGE_FILES=$(find ${{ env.APP_NAME }} -name "*.swift" -exec wc -l {} + | awk '$1 > 500 {print}' | grep -v "total" || true)
if [ -n "$LARGE_FILES" ]; then
echo "::warning::Found files exceeding 500 lines:"
echo "$LARGE_FILES"
echo ""
echo "Consider splitting these files per CODE_REVIEW_GUIDELINES.md"
else
echo "✅ All files within size guidelines"
fi
- name: Check for TODO/FIXME comments
run: |
echo "Checking for TODO/FIXME comments..."
TODOS=$(grep -rn "TODO\|FIXME\|HACK\|XXX" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "// TODO: (template)" || true)
COUNT=$(echo "$TODOS" | grep -c "TODO\|FIXME\|HACK\|XXX" || echo "0")
if [ "$COUNT" -gt "0" ] && [ -n "$TODOS" ]; then
echo "::warning::Found $COUNT TODO/FIXME comments:"
echo "$TODOS"
echo ""
echo "Ensure these are tracked issues, not forgotten work."
else
echo "✅ No TODO/FIXME comments found"
fi
- name: Check Theme usage
run: |
echo "Checking for hardcoded values that should use Theme..."
# Check for hardcoded fonts
HARDCODED_FONTS=$(grep -rn "\.font(\.system(size:" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "Theme" || true)
if [ -n "$HARDCODED_FONTS" ]; then
echo "::warning::Found hardcoded font sizes (should use Theme.Typography):"
echo "$HARDCODED_FONTS" | head -10
fi
# Check for hardcoded cornerRadius with numbers
HARDCODED_RADIUS=$(grep -rn "cornerRadius: [0-9]" ${{ env.APP_NAME }}/ --include="*.swift" | grep -v "Theme" || true)
if [ -n "$HARDCODED_RADIUS" ]; then
echo "::warning::Found hardcoded cornerRadius (should use Theme.CornerRadius):"
echo "$HARDCODED_RADIUS" | head -10
fi
echo "✅ Theme usage check complete"
# ============================================
# Documentation Check
# ============================================
docs:
name: Documentation
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check CLAUDE.md exists
run: |
if [ ! -f "CLAUDE.md" ]; then
echo "::error::CLAUDE.md is missing"
exit 1
fi
- name: Check README.md exists
run: |
if [ ! -f "README.md" ]; then
echo "::error::README.md is missing"
exit 1
fi
- name: Check docs folder
run: |
if [ ! -d "docs" ]; then
echo "::warning::docs folder is missing"
fi
# ============================================
# Appcast Validation (for Sparkle updates)
# ============================================
validate-appcast:
name: Validate Appcast
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install xmllint
run: sudo apt-get update && sudo apt-get install -y libxml2-utils
- name: Validate appcast.xml
run: |
if [ -f "appcast.xml" ]; then
# Check if it's valid XML
if ! xmllint --noout appcast.xml; then
echo "::error::appcast.xml is not valid XML"
exit 1
fi
echo "✅ appcast.xml is valid XML"
else
echo "No appcast.xml found (OK for PRs)"
fi
# ============================================
# Summary Job (required for branch protection)
# ============================================
pr-check-complete:
name: PR Checks Complete
runs-on: ubuntu-latest
needs: [build, test, archive, docs, code-quality, validate-appcast]
if: always()
steps:
- name: Check Results
run: |
if [ "${{ needs.build.result }}" != "success" ]; then
echo "::error::Build failed"
exit 1
fi
if [ "${{ needs.test.result }}" != "success" ]; then
echo "::error::Tests failed"
exit 1
fi
if [ "${{ needs.archive.result }}" != "success" ]; then
echo "::error::Archive test failed"
exit 1
fi
if [ "${{ needs.docs.result }}" != "success" ]; then
echo "::error::Documentation check failed"
exit 1
fi
if [ "${{ needs.code-quality.result }}" != "success" ]; then
echo "::error::Code quality checks failed"
exit 1
fi
if [ "${{ needs.validate-appcast.result }}" != "success" ]; then
echo "::error::Appcast validation failed"
exit 1
fi
echo "All required checks passed!"