Skip to content

chore(android): ignore mlc4j runtime library #6

chore(android): ignore mlc4j runtime library

chore(android): ignore mlc4j runtime library #6

Workflow file for this run

# Nimittam Gallery - Comprehensive CI/CD Pipeline
# Production-grade CI with performance monitoring, automated gates, and quality enforcement
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
paths:
- 'Android/**'
- '.github/workflows/**'
pull_request:
branches: [ main, develop ]
paths:
- 'Android/**'
env:
# Bundle size budgets (in MB)
DEBUG_BUNDLE_MAX_SIZE: 50
RELEASE_BUNDLE_MAX_SIZE: 30
# Performance thresholds
MIN_TEST_COVERAGE: 80
MAX_APK_SIZE_MB: 35
# Gradle optimization
GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=2 -Dorg.gradle.parallel=true"
jobs:
# =============================================================================
# Job 1: Code Quality & Static Analysis
# =============================================================================
code-quality:
name: Code Quality Checks
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./Android/src
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: wrapper
cache-read-only: ${{ github.event_name == 'pull_request' }}
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# ktlint Check
- name: Run ktlint
run: ./gradlew ktlintCheck --continue || true
# Detekt Static Analysis
- name: Run Detekt
run: ./gradlew detekt --continue || true
# Android Lint
- name: Run Android Lint
run: ./gradlew lintDebug --continue
# Upload lint reports
- name: Upload Lint Reports
uses: actions/upload-artifact@v4
if: always()
with:
name: lint-reports
path: |
Android/src/app/build/reports/lint-results-debug.html
Android/src/app/build/reports/detekt.html
retention-days: 7
# =============================================================================
# Job 2: Unit Tests with Coverage
# =============================================================================
unit-tests:
name: Unit Tests & Coverage
runs-on: ubuntu-latest
needs: code-quality
defaults:
run:
working-directory: ./Android/src
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# Run Unit Tests
- name: Run Unit Tests
run: ./gradlew testDebugUnitTest --continue
# Generate Coverage Report
- name: Generate Coverage Report
run: ./gradlew jacocoTestReport --continue || true
# Upload Test Results
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: unit-test-results
path: |
Android/src/app/build/test-results/testDebugUnitTest/
Android/src/app/build/reports/tests/testDebugUnitTest/
retention-days: 7
# Upload Coverage Report
- name: Upload Coverage Report
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: |
Android/src/app/build/reports/jacoco/
retention-days: 7
# Parse Coverage and Fail if Below Threshold
- name: Check Coverage Threshold
run: |
# Try to extract coverage percentage from Jacoco report
COVERAGE_FILE="Android/src/app/build/reports/jacoco/jacocoTestReport/html/index.html"
if [ -f "$COVERAGE_FILE" ]; then
# Extract coverage percentage (simplified parsing)
COVERAGE=$(grep -oP 'Total[^%]+%' "$COVERAGE_FILE" | grep -oP '\d+' | head -1)
if [ -n "$COVERAGE" ] && [ "$COVERAGE" -lt "${{ env.MIN_TEST_COVERAGE }}" ]; then
echo "❌ Code coverage ($COVERAGE%) is below threshold (${{ env.MIN_TEST_COVERAGE }}%)"
exit 1
else
echo "✅ Code coverage ($COVERAGE%) meets threshold (${{ env.MIN_TEST_COVERAGE }}%)"
fi
else
echo "⚠️ Coverage report not found, skipping coverage check"
fi
continue-on-error: true
# =============================================================================
# Job 3: Build Verification
# =============================================================================
build-verification:
name: Build Verification
runs-on: ubuntu-latest
needs: code-quality
strategy:
matrix:
build-type: [debug, release]
defaults:
run:
working-directory: ./Android/src
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# Build APK
- name: Build ${{ matrix.build-type }} APK
run: |
if [ "${{ matrix.build-type }}" == "release" ]; then
./gradlew assembleRelease
else
./gradlew assembleDebug
fi
# Verify Build Artifacts
- name: Verify Build Outputs
run: |
if [ "${{ matrix.build-type }}" == "release" ]; then
ls -la app/build/outputs/apk/release/
ls -la app/build/outputs/bundle/release/ 2>/dev/null || echo "No AAB output"
else
ls -la app/build/outputs/apk/debug/
fi
# Upload Build Artifacts
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.build-type }}-build
path: |
Android/src/app/build/outputs/apk/${{ matrix.build-type }}/
Android/src/app/build/outputs/bundle/${{ matrix.build-type }}/
retention-days: 7
# =============================================================================
# Job 4: Bundle Size Analysis & Budget Enforcement
# =============================================================================
bundle-analysis:
name: Bundle Size Analysis
runs-on: ubuntu-latest
needs: build-verification
defaults:
run:
working-directory: ./Android/src
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Need full history for comparison
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# Build Release APK for Analysis
- name: Build Release APK
run: ./gradlew assembleRelease
# Analyze APK Size
- name: Analyze APK Size
id: apk-size
run: |
APK_PATH=$(find app/build/outputs/apk/release -name "*.apk" | head -1)
if [ -n "$APK_PATH" ]; then
APK_SIZE_BYTES=$(stat -f%z "$APK_PATH" 2>/dev/null || stat -c%s "$APK_PATH")
APK_SIZE_MB=$((APK_SIZE_BYTES / 1024 / 1024))
echo "APK_SIZE_MB=$APK_SIZE_MB" >> $GITHUB_OUTPUT
echo "APK_PATH=$APK_PATH" >> $GITHUB_OUTPUT
echo "📦 APK Size: ${APK_SIZE_MB}MB"
# Check against budget
if [ "$APK_SIZE_MB" -gt "${{ env.RELEASE_BUNDLE_MAX_SIZE }}" ]; then
echo "❌ APK size (${APK_SIZE_MB}MB) exceeds budget (${{ env.RELEASE_BUNDLE_MAX_SIZE }}MB)"
exit 1
else
echo "✅ APK size (${APK_SIZE_MB}MB) within budget (${{ env.RELEASE_BUNDLE_MAX_SIZE }}MB)"
fi
else
echo "❌ No APK found"
exit 1
fi
# Download APK Analyzer
- name: Setup APK Analyzer
run: |
# Create analysis script
cat > analyze_apk.py << 'EOF'
import os
import subprocess
import json
import sys
def analyze_apk(apk_path):
"""Analyze APK contents using aapt"""
if not os.path.exists(apk_path):
print(f"APK not found: {apk_path}")
return None
# Get file listing
result = subprocess.run(
['unzip', '-l', apk_path],
capture_output=True,
text=True
)
analysis = {
'total_size': os.path.getsize(apk_path),
'components': {}
}
# Parse output to categorize files
for line in result.stdout.split('\n')[3:-2]: # Skip header/footer
parts = line.split()
if len(parts) >= 4:
size = int(parts[0])
name = parts[3]
if name.startswith('lib/'):
arch = name.split('/')[1] if '/' in name else 'unknown'
key = f'native_{arch}'
elif name.startswith('res/'):
key = 'resources'
elif name.startswith('assets/'):
key = 'assets'
elif name.startswith('META-INF/'):
key = 'signatures'
elif name.endswith('.dex'):
key = 'dex'
elif name == 'AndroidManifest.xml':
key = 'manifest'
else:
key = 'other'
analysis['components'][key] = analysis['components'].get(key, 0) + size
return analysis
if __name__ == '__main__':
apk_path = sys.argv[1]
analysis = analyze_apk(apk_path)
if analysis:
print(json.dumps(analysis, indent=2))
# Write to file for artifact
with open('apk_analysis.json', 'w') as f:
json.dump(analysis, f, indent=2)
# Print summary
print("\n📊 APK Breakdown:")
for component, size in sorted(analysis['components'].items(),
key=lambda x: x[1], reverse=True):
size_mb = size / (1024 * 1024)
percentage = (size / analysis['total_size']) * 100
print(f" {component}: {size_mb:.2f}MB ({percentage:.1f}%)")
else:
sys.exit(1)
EOF
# Run APK Analysis
- name: Run APK Analysis
run: |
APK_PATH=$(find app/build/outputs/apk/release -name "*.apk" | head -1)
python3 analyze_apk.py "$APK_PATH"
# Upload Analysis Results
- name: Upload Bundle Analysis
uses: actions/upload-artifact@v4
with:
name: bundle-analysis
path: |
Android/src/apk_analysis.json
retention-days: 30
# Compare with baseline (if available)
- name: Compare with Baseline
run: |
# Store current size for future comparison
echo "${{ steps.apk-size.outputs.APK_SIZE_MB }}" > current_apk_size.txt
echo "APK_SIZE_MB=${{ steps.apk-size.outputs.APK_SIZE_MB }}" >> $GITHUB_ENV
# Post PR comment with size info
- name: Post Size Report
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const size = process.env.APK_SIZE_MB;
const budget = 30;
const percentage = ((size / budget) * 100).toFixed(1);
const body = `## 📦 APK Size Report
| Metric | Value | Budget | Status |
|--------|-------|--------|--------|
| APK Size | ${size}MB | ${budget}MB | ${size > budget ? '❌ Exceeded' : '✅ Within'} |
| Utilization | ${percentage}% | 100% | ${percentage > 90 ? '⚠️ High' : '✅ Good'} |
${size > budget ? '⚠️ **Warning:** APK size exceeds the budget. Please review added dependencies or assets.' : ''}
`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
# =============================================================================
# Job 5: Baseline Profile Generation
# =============================================================================
baseline-profile:
name: Generate Baseline Profile
runs-on: ubuntu-latest
needs: build-verification
if: github.ref == 'refs/heads/main'
defaults:
run:
working-directory: ./Android/src
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# Generate Baseline Profile
- name: Generate Baseline Profile
run: ./gradlew :app:generateBaselineProfile || echo "Baseline profile generation skipped (requires macrobenchmark module)"
# Upload Generated Profile
- name: Upload Baseline Profile
uses: actions/upload-artifact@v4
with:
name: baseline-profile
path: |
Android/src/app/src/main/baseline-prof.txt
retention-days: 30
# =============================================================================
# Job 6: Integration Tests
# =============================================================================
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
needs: [unit-tests, build-verification]
strategy:
matrix:
api-level: [29, 33]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Grant execute permission for gradlew
working-directory: ./Android/src
run: chmod +x gradlew
# Enable KVM for emulator acceleration
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
# Run Instrumented Tests
- name: Run Instrumented Tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
target: default
arch: x86_64
working-directory: ./Android/src
script: ./gradlew connectedCheck --continue || true
# Upload Test Results
- name: Upload Instrumented Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: instrumented-test-results-api${{ matrix.api-level }}
path: |
Android/src/app/build/outputs/androidTest-results/
Android/src/app/build/reports/androidTests/
retention-days: 7
# =============================================================================
# Job 7: Security Scan
# =============================================================================
security-scan:
name: Security Scan
runs-on: ubuntu-latest
needs: code-quality
defaults:
run:
working-directory: ./Android/src
steps:
- name: Checkout code
uses: actions/checkout@v4
# Run dependency vulnerability scan
- name: Run OWASP Dependency Check
run: |
# Create a simple dependency check
./gradlew dependencies --configuration releaseRuntimeClasspath > dependencies.txt
cat dependencies.txt
continue-on-error: true
# Upload dependency report
- name: Upload Dependencies
uses: actions/upload-artifact@v4
with:
name: dependency-report
path: Android/src/dependencies.txt
retention-days: 7
# =============================================================================
# Job 8: Performance Regression Detection
# =============================================================================
performance-check:
name: Performance Regression Check
runs-on: ubuntu-latest
needs: build-verification
defaults:
run:
working-directory: ./Android/src
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# Build and measure build time
- name: Measure Build Performance
run: |
echo "Measuring build performance..."
START_TIME=$(date +%s)
./gradlew clean assembleRelease --profile
END_TIME=$(date +%s)
BUILD_TIME=$((END_TIME - START_TIME))
echo "BUILD_TIME_SECONDS=$BUILD_TIME" >> $GITHUB_ENV
echo "⏱️ Build completed in ${BUILD_TIME}s"
# Upload build scan/profile
- name: Upload Build Profile
uses: actions/upload-artifact@v4
with:
name: build-profile
path: Android/src/build/reports/profile/
retention-days: 7
# =============================================================================
# Job 9: Final Status Report
# =============================================================================
final-status:
name: Final Status Report
runs-on: ubuntu-latest
needs:
- code-quality
- unit-tests
- build-verification
- bundle-analysis
- integration-tests
- security-scan
- performance-check
if: always()
steps:
- name: Generate Status Report
run: |
echo "## CI/CD Pipeline Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Code Quality | ${{ needs.code-quality.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Unit Tests | ${{ needs.unit-tests.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Build Verification | ${{ needs.build-verification.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Bundle Analysis | ${{ needs.bundle-analysis.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Integration Tests | ${{ needs.integration-tests.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Security Scan | ${{ needs.security-scan.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Performance Check | ${{ needs.performance-check.result }} |" >> $GITHUB_STEP_SUMMARY
- name: Check Overall Status
run: |
if [ "${{ needs.code-quality.result }}" == "failure" ] || \
[ "${{ needs.unit-tests.result }}" == "failure" ] || \
[ "${{ needs.build-verification.result }}" == "failure" ] || \
[ "${{ needs.bundle-analysis.result }}" == "failure" ]; then
echo "❌ Critical jobs failed"
exit 1
else
echo "✅ All critical jobs passed"
fi