diff --git a/.claude/completed/015 b/.claude/completed/015 new file mode 100644 index 0000000..f572d2f --- /dev/null +++ b/.claude/completed/015 @@ -0,0 +1 @@ +CI/CD Pipeline configured on Wed Nov 19 09:48:33 UTC 2025 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7a6f0e6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + labels: + - "dependencies" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..57d84cb --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,71 @@ +name: Android CI + +on: + push: + branches: [ master, develop ] + pull_request: + branches: [ master, develop ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + cache: 'gradle' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Run unit tests + run: ./gradlew test + + - name: Upload test reports + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-reports + path: app/build/reports/tests/ + + - name: Run ktlint + run: ./gradlew ktlintCheck + + - name: Run detekt + run: ./gradlew detekt + + - name: Upload detekt report + uses: actions/upload-artifact@v4 + if: always() + with: + name: detekt-report + path: app/build/reports/detekt/ + + build: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + cache: 'gradle' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build debug APK + run: ./gradlew assembleDebug + + - name: Upload APK + uses: actions/upload-artifact@v4 + with: + name: app-debug + path: app/build/outputs/apk/debug/*.apk diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml new file mode 100644 index 0000000..a888227 --- /dev/null +++ b/.github/workflows/pr-checks.yml @@ -0,0 +1,39 @@ +name: PR Checks + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + pr-validation: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + cache: 'gradle' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Compile check + run: ./gradlew compileDebugKotlin + + - name: Lint check + run: ./gradlew lint + + - name: Test coverage + run: ./gradlew jacocoTestReport + + - name: Comment PR with coverage + uses: madrapps/jacoco-report@v1.6.1 + if: github.event_name == 'pull_request' + with: + paths: ${{ github.workspace }}/app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml + token: ${{ secrets.GITHUB_TOKEN }} + min-coverage-overall: 70 + min-coverage-changed-files: 80 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0ed3377 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +name: Release Build + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + cache: 'gradle' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build release APK + run: ./gradlew assembleRelease + + - name: Upload release APK + uses: actions/upload-artifact@v4 + with: + name: app-release + path: app/build/outputs/apk/release/*.apk + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + files: app/build/outputs/apk/release/*.apk + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 348a2d9..5fda28b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -8,6 +8,7 @@ plugins { alias(libs.plugins.hilt) alias(libs.plugins.ktlint) alias(libs.plugins.detekt) + jacoco } android { @@ -137,4 +138,33 @@ dependencies { kspTest(libs.hilt.compiler) androidTestImplementation(libs.hilt.android.testing) kspAndroidTest(libs.hilt.compiler) +} + +tasks.register("jacocoTestReport") { + dependsOn("testDebugUnitTest") + + reports { + xml.required.set(true) + html.required.set(true) + } + + val fileFilter = listOf( + "**/R.class", + "**/R$*.class", + "**/BuildConfig.*", + "**/Manifest*.*", + "**/*Test*.*", + "android/**/*.*", + "**/inject/**/*.*" + ) + + val debugTree = fileTree("${buildDir}/tmp/kotlin-classes/debug") { + exclude(fileFilter) + } + + classDirectories.setFrom(debugTree) + sourceDirectories.setFrom(files("${projectDir}/src/main/java")) + executionData.setFrom(fileTree(buildDir) { + include("jacoco/testDebugUnitTest.exec") + }) } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index b498d24..a4b76b9 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 18330fc..df97d72 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists