Skip to content

ci: add CI/CD workflows, dependabot, and release signing#2

Merged
GeiserX merged 4 commits into
mainfrom
ci/add-workflows
Apr 4, 2026
Merged

ci: add CI/CD workflows, dependabot, and release signing#2
GeiserX merged 4 commits into
mainfrom
ci/add-workflows

Conversation

@GeiserX

@GeiserX GeiserX commented Apr 4, 2026

Copy link
Copy Markdown
Owner

Summary

Matches the CI/CD setup from Pumperly-android:

  • CI workflow (ci.yml): builds debug APK + runs lint on every push/PR to main, uploads debug APK as artifact
  • Release workflow (release.yml): triggered by v* tags — computes versionCode from tag, decodes keystore from secret, builds signed AAB + APK, creates GitHub Release with artifacts
  • Dependabot (dependabot.yml): weekly Gradle updates (Monday), monthly GitHub Actions updates, grouped minor+patch, security groups, ignores major bumps
  • GitGuardian (.gitguardian.yml): suppresses false positives on app/build.gradle.kts
  • build.gradle.kts changes:
    • Dynamic VERSION_NAME/VERSION_CODE from Gradle properties (-P flags) — defaults to 0.1.0 / 1
    • CASHPILOT_* signing config (reads from Gradle properties or env vars)
    • Release buildType wired to signing config when credentials are present
    • buildConfig = true enabled
    • Migrated deprecated kotlinOptions {} to modern kotlin { compilerOptions {} } DSL

Secrets needed for release workflow

Before tagging a release, configure these repo secrets:

  • KEYSTORE_BASE64 — base64-encoded release keystore
  • KEYSTORE_PASSWORD — keystore password
  • KEY_ALIAS — signing key alias
  • KEY_PASSWORD — signing key password

Test plan

  • CI workflow runs on this PR (assembleDebug + lintDebug)
  • Debug APK artifact is uploaded
  • After merge + secrets configured, tag v0.1.0 triggers release workflow

Summary by CodeRabbit

  • Chores

    • Added CI pipeline to run builds, lint and upload debug artifacts on pushes and PRs.
    • Added release workflow to build and publish signed release artifacts on tagged releases.
    • Configured Dependabot to regularly check and group dependency and CI updates.
    • Enforced centralized dependency resolution and stricter repository rules.
    • Adopted dynamic versioning, conditional signing, JVM target update, and enabled BuildConfig generation.
    • Added an Android lint baseline and excluded a build file from secret-scanning.
  • Bug Fixes

    • Refined app notification presence check to improve reliability.

- CI workflow: build debug APK + lint on push/PR to main
- Release workflow: signed AAB+APK on version tags (v*)
- Dependabot: weekly Gradle, monthly GitHub Actions, grouped updates
- GitGuardian: suppress false positives on build.gradle.kts
- build.gradle.kts: dynamic VERSION_NAME/VERSION_CODE from Gradle
  properties, CASHPILOT_* signing config, buildConfig enabled,
  modern kotlin compilerOptions DSL
@coderabbitai

coderabbitai Bot commented Apr 4, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds CI and Release GitHub Actions workflows, Dependabot and GitGuardian configs; updates Gradle settings and app build script for property-based versioning, conditional signing, Kotlin DSL JVM target, BuildConfig generation, and adds an Android lint baseline and a minor map-access tweak in notification listener.

Changes

Cohort / File(s) Summary
CI Workflows
.github/workflows/ci.yml, .github/workflows/release.yml
Added CI workflow (build debug APK, run lint, upload debug artifact) and Release workflow (trigger on v* tags: extract VERSION_NAME/VERSION_CODE, decode keystore from secret, build signed AAB/APK, create GitHub Release and upload artifacts).
Dependency Automation
.github/dependabot.yml
Added Dependabot config: Gradle updates weekly, GitHub Actions monthly, grouping for minor/patch and security updates; ignores major updates; commit prefixes and labels configured.
Build & Signing
app/build.gradle.kts
Switched versionCode/versionName to read from Gradle properties with fallbacks; added conditional release signingConfig populated from properties/env; release build uses signing config only if configured; moved Kotlin JVM target to Kotlin DSL; enabled buildFeatures.buildConfig; added lint baseline.
Gradle Settings
settings.gradle.kts
Replaced dependencyResolution with dependencyResolutionManagement, added @Suppress("UnstableApiUsage"), and repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) while preserving google() and mavenCentral().
Security Scan Config
.gitguardian.yml
Excluded app/build.gradle.kts from GitGuardian path scanning via paths-ignore.
Lint Baseline
app/lint-baseline.xml
Added Android lint baseline recording two ProtectedPermissions issues from the manifest.
Notification Listener
app/src/.../AppNotificationListener.kt
Minor implementation change: replaced packageName in activeNotifications_ with activeNotifications_.containsKey(packageName) for checking active notifications.

Sequence Diagram(s)

sequenceDiagram
participant GH as "GitHub (push/tag)"
participant Runner as "Actions Runner"
participant Gradle as "Gradle"
participant Secrets as "Secrets (KEYSTORE_BASE64)"
participant Storage as "Artifacts / Releases"

GH->>Runner: push / pull_request (main) -> trigger CI
Runner->>Gradle: checkout, setup JDK/Gradle
Runner->>Gradle: ./gradlew assembleDebug, lintDebug
Gradle-->>Runner: debug APK + lint results
Runner->>Storage: upload artifact (app-debug)

Note over GH,Runner: Release flow (on tag v*)
GH->>Runner: push tag vX.Y.Z -> trigger Release
Runner->>Runner: extract VERSION_NAME / VERSION_CODE
Runner->>Secrets: request KEYSTORE_BASE64
Secrets-->>Runner: keystore blob
Runner->>Gradle: ./gradlew bundleRelease assembleRelease (with signing props)
Gradle-->>Runner: AAB / APK
Runner->>Storage: create GitHub Release + upload AAB/APK
Runner->>Runner: delete decoded keystore (always)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped through YAMLs, neat and spry,
Pushing builds where artifacts fly.
Dependabot nibbles, secrets tucked tight,
Gradle hums softly into the night.
A tiny lint baseline — CI's delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and specifically summarizes the main changes: adding CI/CD workflows, Dependabot configuration, and release signing setup.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/add-workflows

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
.github/workflows/release.yml (2)

63-65: Publish the ProGuard mapping file with the release artifacts.

release minification is enabled in app/build.gradle.kts, so omitting app/build/outputs/mapping/release/mapping.txt makes post-release stack traces much harder to deobfuscate.

💡 Proposed fix
           files: |
             app/build/outputs/bundle/release/app-release.aab
             app/build/outputs/apk/release/app-release.apk
+            app/build/outputs/mapping/release/mapping.txt
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 63 - 65, Update the artifact list
in the release workflow so the ProGuard/R8 mapping file produced by the release
build is published alongside the APK/AAB; specifically, in the files: block that
currently lists app-release.aab and app-release.apk, add the release mapping
artifact (the mapping.txt generated under the build outputs mapping/release
directory) so deobfuscation info is available for post-release stacktrace
analysis.

38-39: Fail fast when the signing secrets are empty.

Decoding an empty KEYSTORE_BASE64 still leaves a keystore file on disk, so the job fails later during signing with a much less actionable error. Add a quick preflight check for the four required secrets before decoding/building.

💡 Proposed fix
+      - name: Validate release secrets
+        env:
+          KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
+          KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
+          KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
+          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
+        run: |
+          for name in KEYSTORE_BASE64 KEYSTORE_PASSWORD KEY_ALIAS KEY_PASSWORD; do
+            if [ -z "${!name}" ]; then
+              echo "Missing required secret: $name" >&2
+              exit 1
+            fi
+          done
+
       - name: Decode keystore
         run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > app/release.keystore
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 38 - 39, Add a fail-fast
preflight step before the "Decode keystore" step that verifies the required
signing secrets (at minimum KEYSTORE_BASE64, KEYSTORE_PASSWORD, KEY_ALIAS,
KEY_PASSWORD) are non-empty and exits the job with a clear message if any are
missing; locate the step named "Decode keystore" and insert a preceding step
(e.g., "Validate signing secrets") that checks each secret and fails early so
the subsequent decode/build/sign steps do not produce confusing errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.gitguardian.yml:
- Around line 1-2: The .gitguardian.yml currently uses the paths-ignore entry to
skip the entire Gradle script (the "paths-ignore" key with the
"app/build.gradle.kts" pattern); remove that whole-file exclusion and instead
add a narrow allowlist/ignore rule that targets only the known false-positive
token pattern introduced for release signing (e.g., the exact string or a
specific regex for the signing input) so the rest of app/build.gradle.kts
remains scanned; update the "paths-ignore" block to no longer contain the full
filename and replace it with a rule that matches only the false-positive
pattern.

In @.github/workflows/ci.yml:
- Around line 9-12: Add explicit least-privileged GITHUB_TOKEN permissions for
the CI job by declaring permissions: contents: read for the build job; update
the jobs -> build block (the job named "build" that contains runs-on:
ubuntu-latest and steps) to include a permissions entry with contents: read so
the workflow does not inherit broader repo defaults.

In @.github/workflows/release.yml:
- Around line 17-27: The tag parsing step (variables TAG, VERSION, MAJOR, MINOR,
PATCH and computed CODE) accepts many malformed tags and can produce collisions;
change the script to first validate TAG/VERSION against a strict semver patch
regex (e.g. ^v[0-9]+\.[0-9]+\.[0-9]+$) and exit non‑zero with a clear error if
it doesn't match, then safely split VERSION into MAJOR, MINOR, PATCH and compute
CODE (or use larger multipliers) only for validated input so invalid tags like
v1.2, vfoo, or v1.2.3-rc1 are rejected before computing CODE.

In `@app/build.gradle.kts`:
- Around line 63-66: The kotlin { compilerOptions { jvmTarget.set(...) } } block
is currently nested inside the android { } block; move this top-level Kotlin
Gradle plugin configuration out of android by placing the kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) } } block at the
project/module root (same level as android { } and plugins), removing the nested
version so the Kotlin compilerOptions are defined as a top-level extension
rather than inside the android block.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 63-65: Update the artifact list in the release workflow so the
ProGuard/R8 mapping file produced by the release build is published alongside
the APK/AAB; specifically, in the files: block that currently lists
app-release.aab and app-release.apk, add the release mapping artifact (the
mapping.txt generated under the build outputs mapping/release directory) so
deobfuscation info is available for post-release stacktrace analysis.
- Around line 38-39: Add a fail-fast preflight step before the "Decode keystore"
step that verifies the required signing secrets (at minimum KEYSTORE_BASE64,
KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD) are non-empty and exits the job with
a clear message if any are missing; locate the step named "Decode keystore" and
insert a preceding step (e.g., "Validate signing secrets") that checks each
secret and fails early so the subsequent decode/build/sign steps do not produce
confusing errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aca5bb78-8e39-48b6-9814-88f3f4cd3948

📥 Commits

Reviewing files that changed from the base of the PR and between 89a6d39 and 4e3969c.

📒 Files selected for processing (5)
  • .gitguardian.yml
  • .github/dependabot.yml
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • app/build.gradle.kts

Comment thread .gitguardian.yml
Comment on lines +1 to +2
paths-ignore:
- "app/build.gradle.kts"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't exclude the whole Gradle script from secret scanning.

app/build.gradle.kts now wires release-signing inputs. Ignoring the entire file hides any real credential accidentally committed there later; narrow this to the specific false-positive pattern instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitguardian.yml around lines 1 - 2, The .gitguardian.yml currently uses the
paths-ignore entry to skip the entire Gradle script (the "paths-ignore" key with
the "app/build.gradle.kts" pattern); remove that whole-file exclusion and
instead add a narrow allowlist/ignore rule that targets only the known
false-positive token pattern introduced for release signing (e.g., the exact
string or a specific regex for the signing input) so the rest of
app/build.gradle.kts remains scanned; update the "paths-ignore" block to no
longer contain the full filename and replace it with a rule that matches only
the false-positive pattern.

Comment thread .github/workflows/ci.yml
Comment on lines +9 to +12
jobs:
build:
runs-on: ubuntu-latest
steps:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Set explicit read-only token permissions for CI.

This job runs PR code via ./gradlew, but it inherits whatever default GITHUB_TOKEN scope the repository is configured with. Declare contents: read here so the workflow stays least-privileged even if repo defaults change.

🔒 Proposed fix
 jobs:
   build:
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
     steps:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
jobs:
build:
runs-on: ubuntu-latest
steps:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 9 - 12, Add explicit least-privileged
GITHUB_TOKEN permissions for the CI job by declaring permissions: contents: read
for the build job; update the jobs -> build block (the job named "build" that
contains runs-on: ubuntu-latest and steps) to include a permissions entry with
contents: read so the workflow does not inherit broader repo defaults.

Comment on lines +17 to +27
- name: Compute version from tag
id: version
run: |
TAG="${GITHUB_REF#refs/tags/}"
VERSION="${TAG#v}"
# versionCode: major*10000 + minor*100 + patch (e.g. 1.2.3 -> 10203)
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
CODE=$(( ${MAJOR:-0} * 10000 + ${MINOR:-0} * 100 + ${PATCH:-0} ))
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "name=$VERSION" >> "$GITHUB_OUTPUT"
echo "code=$CODE" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate the tag before deriving VERSION_CODE.

v* accepts tags this parser cannot safely encode (v1.2, vfoo, v1.2.3-rc1), and major*10000 + minor*100 + patch collides once minor or patch reaches 100. Fail fast on a strict vMAJOR.MINOR.PATCH tag, or widen the radix.

💡 Proposed fix
       - name: Compute version from tag
         id: version
         run: |
           TAG="${GITHUB_REF#refs/tags/}"
-          VERSION="${TAG#v}"
-          # versionCode: major*10000 + minor*100 + patch (e.g. 1.2.3 -> 10203)
-          IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
-          CODE=$(( ${MAJOR:-0} * 10000 + ${MINOR:-0} * 100 + ${PATCH:-0} ))
+          if [[ ! "$TAG" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
+            echo "Expected tag format vMAJOR.MINOR.PATCH, got: $TAG" >&2
+            exit 1
+          fi
+          MAJOR="${BASH_REMATCH[1]}"
+          MINOR="${BASH_REMATCH[2]}"
+          PATCH="${BASH_REMATCH[3]}"
+          if (( MINOR > 99 || PATCH > 99 )); then
+            echo "MINOR and PATCH must be <= 99 for the current versionCode scheme" >&2
+            exit 1
+          fi
+          VERSION="${MAJOR}.${MINOR}.${PATCH}"
+          CODE=$(( MAJOR * 10000 + MINOR * 100 + PATCH ))
           echo "tag=$TAG" >> "$GITHUB_OUTPUT"
           echo "name=$VERSION" >> "$GITHUB_OUTPUT"
           echo "code=$CODE" >> "$GITHUB_OUTPUT"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 17 - 27, The tag parsing step
(variables TAG, VERSION, MAJOR, MINOR, PATCH and computed CODE) accepts many
malformed tags and can produce collisions; change the script to first validate
TAG/VERSION against a strict semver patch regex (e.g. ^v[0-9]+\.[0-9]+\.[0-9]+$)
and exit non‑zero with a clear error if it doesn't match, then safely split
VERSION into MAJOR, MINOR, PATCH and compute CODE (or use larger multipliers)
only for validated input so invalid tags like v1.2, vfoo, or v1.2.3-rc1 are
rejected before computing CODE.

Comment thread app/build.gradle.kts
Comment on lines +63 to +66
kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In Android Gradle Kotlin DSL projects, is kotlin { compilerOptions { ... } }valid inside theandroid {} block, or must it be a top-level block? Please check the current Kotlin Gradle plugin / Android Gradle plugin documentation.

💡 Result:

In Android Gradle Kotlin DSL projects, kotlin { compilerOptions { ... } } must be a top-level block, not inside the android {} block. The kotlin {} block is provided by the Kotlin Gradle plugin (e.g., kotlin("android") or kotlin("multiplatform")) and configures Kotlin-specific settings like compiler options at the project extension level. The deprecated android { kotlinOptions { ... } } was inside the android {} block, but the replacement kotlin { compilerOptions { ... } } is top-level, as shown in official Kotlin documentation migration examples for Android projects. With Android Gradle Plugin 9.0+ built-in Kotlin support, the Kotlin plugin may be optional, but when using compilerOptions, the top-level kotlin {} block is still required. Example from Kotlin docs for Android project migration: plugins { id("com.android.application") kotlin("android") } kotlin { compilerOptions { jvmTarget = JvmTarget.fromTarget("17") } } android { // Android config, no kotlinOptions here }

Citations:


🏁 Script executed:

cat -n app/build.gradle.kts | sed -n '50,80p'

Repository: GeiserX/CashPilot-android

Length of output: 1129


🏁 Script executed:

cat -n app/build.gradle.kts | head -75

Repository: GeiserX/CashPilot-android

Length of output: 3008


Move kotlin { compilerOptions {} } out of android {} block.

The kotlin { compilerOptions {} } block is a top-level extension provided by the Kotlin Gradle plugin and must not be nested inside the android {} block. According to official Kotlin and Android Gradle plugin documentation, this configuration should appear at the project root level. The deprecated android { kotlinOptions {} } was inside the Android block, but its modern replacement requires a top-level kotlin {} block.

Proposed fix
-    kotlin {
-        compilerOptions {
-            jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
-        }
-    }
-
     buildFeatures {
         buildConfig = true
         compose = true
     }
 }
+
+kotlin {
+    compilerOptions {
+        jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
+    }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
buildFeatures {
buildConfig = true
compose = true
}
}
kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/build.gradle.kts` around lines 63 - 66, The kotlin { compilerOptions {
jvmTarget.set(...) } } block is currently nested inside the android { } block;
move this top-level Kotlin Gradle plugin configuration out of android by placing
the kotlin { compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) } } block at the
project/module root (same level as android { } and plugins), removing the nested
version so the Kotlin compilerOptions are defined as a top-level extension
rather than inside the android block.

GeiserX added 3 commits April 4, 2026 09:49
- Fix dependencyResolution → dependencyResolutionManagement with
  RepositoriesMode.FAIL_ON_PROJECT_REPOS (matches Pumperly)
- Remove keystore from disk after both Gradle steps complete
Kotlin's `in` operator on ConcurrentHashMap maps to containsValue,
not containsKey (KT-18053). Use explicit containsKey for correct
package lookup.
PACKAGE_USAGE_STATS and READ_NETWORK_USAGE_HISTORY are system
permissions accessed via UsageStatsManager after user grants access
in settings — lint ProtectedPermissions errors are expected.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
app/build.gradle.kts (1)

63-67: ⚠️ Potential issue | 🔴 Critical

kotlin { compilerOptions {} } is still nested under android {}.

This appears to be the same unresolved placement issue previously reported; keep this as a top-level kotlin {} block.

In Android Gradle Kotlin DSL projects, must `kotlin { compilerOptions { ... } }` be declared at the top level (outside `android {}`), or is nesting it inside `android {}` supported?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/build.gradle.kts` around lines 63 - 67, The kotlin { compilerOptions {
jvmTarget.set(...) } } block is incorrectly nested inside android { } — move the
entire kotlin { compilerOptions { ... } } declaration to top-level (outside
android { }) so the Kotlin DSL compilerOptions configuration applies globally;
locate the kotlin block in this file and extract it from within android { } and
place it as a standalone top-level kotlin { compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) } } block.
🧹 Nitpick comments (1)
app/build.gradle.kts (1)

51-54: Consider failing fast when release signing is absent for release tasks.

Current logic silently skips signing; that can allow producing/uploading unsigned release artifacts if CI secrets are missing.

Optional fail-fast guard
             val releaseConfig = signingConfigs.findByName("release")
-            if (releaseConfig?.storeFile != null) {
-                signingConfig = releaseConfig
-            }
+            val isReleaseTask = gradle.startParameter.taskNames.any { it.contains("Release", ignoreCase = true) }
+            if (releaseConfig?.storeFile != null) {
+                signingConfig = releaseConfig
+            } else if (isReleaseTask) {
+                throw org.gradle.api.GradleException("Release signing config is missing/incomplete.")
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/build.gradle.kts` around lines 51 - 54, The current block silently skips
signing when the "release" signingConfig is missing; change it to fail fast for
release builds: inspect project.gradle.startParameter.taskNames (or check for
common release tasks like "assembleRelease"/"bundleRelease"), and if any release
task is requested and signingConfigs.findByName("release")?.storeFile is null,
throw a GradleException (or call error(...)) instead of skipping; update the
code around signingConfigs.findByName("release"), releaseConfig and
signingConfig to enforce this guard so CI will fail when release signing is
absent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/build.gradle.kts`:
- Around line 25-39: The current null checks accept empty or whitespace-only
signing inputs (keystorePath, keystorePass, keyAliasValue, keyPass) which can
later break signing; update the guard to treat blank strings as missing by
replacing the null checks with blank-aware checks (e.g., use Kotlin's
isNullOrBlank) so only non-null, non-blank values are used when assigning
storeFile, storePassword, keyAlias and keyPassword.

---

Duplicate comments:
In `@app/build.gradle.kts`:
- Around line 63-67: The kotlin { compilerOptions { jvmTarget.set(...) } } block
is incorrectly nested inside android { } — move the entire kotlin {
compilerOptions { ... } } declaration to top-level (outside android { }) so the
Kotlin DSL compilerOptions configuration applies globally; locate the kotlin
block in this file and extract it from within android { } and place it as a
standalone top-level kotlin { compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) } } block.

---

Nitpick comments:
In `@app/build.gradle.kts`:
- Around line 51-54: The current block silently skips signing when the "release"
signingConfig is missing; change it to fail fast for release builds: inspect
project.gradle.startParameter.taskNames (or check for common release tasks like
"assembleRelease"/"bundleRelease"), and if any release task is requested and
signingConfigs.findByName("release")?.storeFile is null, throw a GradleException
(or call error(...)) instead of skipping; update the code around
signingConfigs.findByName("release"), releaseConfig and signingConfig to enforce
this guard so CI will fail when release signing is absent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5383f08e-78b4-4499-9e94-9a46f3e5f613

📥 Commits

Reviewing files that changed from the base of the PR and between 30d1f09 and adf3e40.

📒 Files selected for processing (2)
  • app/build.gradle.kts
  • app/lint-baseline.xml
✅ Files skipped from review due to trivial changes (1)
  • app/lint-baseline.xml

Comment thread app/build.gradle.kts
Comment on lines +25 to +39
val keystorePath = findProperty("CASHPILOT_KEYSTORE_PATH") as String?
?: System.getenv("CASHPILOT_KEYSTORE_PATH")
val keystorePass = findProperty("CASHPILOT_KEYSTORE_PASSWORD") as String?
?: System.getenv("CASHPILOT_KEYSTORE_PASSWORD")
val keyAliasValue = findProperty("CASHPILOT_KEY_ALIAS") as String?
?: System.getenv("CASHPILOT_KEY_ALIAS")
val keyPass = findProperty("CASHPILOT_KEY_PASSWORD") as String?
?: System.getenv("CASHPILOT_KEY_PASSWORD")

if (keystorePath != null && keystorePass != null && keyAliasValue != null && keyPass != null) {
storeFile = file(keystorePath)
storePassword = keystorePass
keyAlias = keyAliasValue
keyPassword = keyPass
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Treat blank signing inputs as missing configuration.

Using only != null accepts empty strings, which can pass this guard but still break signing later with harder-to-debug errors.

Proposed hardening
-            val keystorePath = findProperty("CASHPILOT_KEYSTORE_PATH") as String?
-                ?: System.getenv("CASHPILOT_KEYSTORE_PATH")
-            val keystorePass = findProperty("CASHPILOT_KEYSTORE_PASSWORD") as String?
-                ?: System.getenv("CASHPILOT_KEYSTORE_PASSWORD")
-            val keyAliasValue = findProperty("CASHPILOT_KEY_ALIAS") as String?
-                ?: System.getenv("CASHPILOT_KEY_ALIAS")
-            val keyPass = findProperty("CASHPILOT_KEY_PASSWORD") as String?
-                ?: System.getenv("CASHPILOT_KEY_PASSWORD")
+            val keystorePath = ((findProperty("CASHPILOT_KEYSTORE_PATH") as String?)
+                ?: System.getenv("CASHPILOT_KEYSTORE_PATH"))?.takeIf { it.isNotBlank() }
+            val keystorePass = ((findProperty("CASHPILOT_KEYSTORE_PASSWORD") as String?)
+                ?: System.getenv("CASHPILOT_KEYSTORE_PASSWORD"))?.takeIf { it.isNotBlank() }
+            val keyAliasValue = ((findProperty("CASHPILOT_KEY_ALIAS") as String?)
+                ?: System.getenv("CASHPILOT_KEY_ALIAS"))?.takeIf { it.isNotBlank() }
+            val keyPass = ((findProperty("CASHPILOT_KEY_PASSWORD") as String?)
+                ?: System.getenv("CASHPILOT_KEY_PASSWORD"))?.takeIf { it.isNotBlank() }

             if (keystorePath != null && keystorePass != null && keyAliasValue != null && keyPass != null) {
                 storeFile = file(keystorePath)
                 storePassword = keystorePass
                 keyAlias = keyAliasValue
                 keyPassword = keyPass
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/build.gradle.kts` around lines 25 - 39, The current null checks accept
empty or whitespace-only signing inputs (keystorePath, keystorePass,
keyAliasValue, keyPass) which can later break signing; update the guard to treat
blank strings as missing by replacing the null checks with blank-aware checks
(e.g., use Kotlin's isNullOrBlank) so only non-null, non-blank values are used
when assigning storeFile, storePassword, keyAlias and keyPassword.

@GeiserX
GeiserX merged commit 92d9428 into main Apr 4, 2026
3 checks passed
@GeiserX
GeiserX deleted the ci/add-workflows branch April 4, 2026 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant