Structure & lockfiles¶
Project structure and reproducibility: deep nesting, heavy directories, cross-package duplicate assets, and missing lockfiles/wrappers.
25 checks.
gradle-configuration-cache-warning-mode¶
Severity: High
With problems=warn Gradle stores a cache entry even when the configuration that produced it was invalid. Later builds then reuse a task graph whose inputs are incomplete: a task keeps stale output, a changed property never reaches it, and nothing in the build reports a problem. The failure mode is a wrong build result rather than a broken build, which is exactly the kind of defect that survives for months.
How to fix: Remove org.gradle.configuration-cache.problems=warn so the default fail applies, then fix the reported incompatibilities (or list the offending tasks with notCompatibleWithConfigurationCache). Keep warn only as a temporary, tracked step of a migration.
gradle-insecure-repository-protocol¶
Severity: High
Plaintext resolution is unauthenticated: anyone able to observe or reroute the traffic can substitute a dependency, or the Gradle distribution itself, and the build accepts it without complaint. The substitution then becomes durable — a dependency lock or a checksum generated afterwards records the tampered artifact as the expected one, so the compromise is baked into the repository.
How to fix: Switch every repository URL and the wrapper distributionUrl to https:// and drop allowInsecureProtocol = true. If an internal mirror has no TLS, terminate it behind an https proxy rather than opening plaintext resolution for the whole build.
gradle-maven-local-in-resolution¶
Severity: Medium–Low–Info
Gradle does not trust the local Maven repository: it does not verify artifact origin, its metadata can be incomplete, and caching is disabled for it. An artifact that happens to exist in ~/.m2 on one machine silently substitutes for the one the central repository would have served, so the build is green there and resolves something different everywhere else. Declared ahead of the real repositories, it is also a dependency-confusion channel — whichever repository answers first wins the coordinate.
How to fix: Remove mavenLocal() from the repositories used to resolve the app's dependencies. If a local artifact is genuinely needed (e.g. during development of a library consumed by this project), scope it to a flat-file or composite-build substitution instead of the shared, unauthenticated local cache — and never declare it before mavenCentral()/google().
gradle-wrapper-distribution-checksum-missing¶
Severity: Low
The wrapper runs before any of the build's own configuration does, on every machine that builds the project. HTTPS authenticates the server, not the archive — a substituted distribution, a compromised mirror, or a redirected download is executed with full developer privileges, and the build prints nothing to suggest anything changed. The checksum is the only part of the wrapper that verifies what it just downloaded.
How to fix: Run ./gradlew wrapper --gradle-version <version> --gradle-distribution-sha256-sum <sha> (the checksum is published next to each distribution on gradle.org), or set distributionSha256Sum by hand. Re-run it whenever the wrapper version changes.
gradle-wrapper-jar-missing¶
Severity: High
gradle-wrapper.jar is what gradlew executes to download and run the pinned Gradle version. If it is missing from the repo, every fresh checkout and CI run fails to build even though it works on a machine where the jar is already on disk.
How to fix: Run gradle wrapper at the Gradle project root and commit the generated gradle/wrapper/gradle-wrapper.jar. If a broad ignore rule such as *.jar is keeping it out of commits, force-add it with git add -f gradle/wrapper/gradle-wrapper.jar and put !gradle/wrapper/gradle-wrapper.jar after that line.
repo-fastlane-gemfile-lock-missing¶
Severity: Medium–Info
An untracked Gemfile.lock means bundle install can resolve a different fastlane/xcpretty version on each machine and in CI. A minor fastlane release can change match, gym or upload_to_testflight behaviour mid release day, and bundle install silently rewrites the lock rather than failing — everything stays green, just built by a different toolchain than last time.
How to fix: Run bundle install and commit the generated Gemfile.lock.
repo-gradle-jvmargs-no-metaspace¶
Severity: Low
A heap-tuned Gradle daemon with no metaspace bound can grow metaspace unbounded and OOM (java.lang.OutOfMemoryError: Metaspace) on large multi-module builds — an intermittent, machine-dependent build failure that is hard to diagnose.
How to fix: Add an explicit metaspace bound to org.gradle.jvmargs, e.g. -XX:MaxMetaspaceSize=512m alongside your -Xmx value.
repo-gradle-perf-buildcache¶
Severity: Low
Without the build cache, unchanged modules are rebuilt from scratch on every run, and CI cannot reuse task output across jobs — a silent, recurring waste of build time.
How to fix: Add org.gradle.caching=true to the root gradle.properties.
repo-gradle-perf-configcache¶
Severity: Low
Re-running configuration on every invocation is a silent, recurring build-time cost, especially on large multi-module builds. (The configuration cache can surface incompatible tasks — enable it and fix any reported problems, rather than assuming it is free.)
How to fix: Add org.gradle.configuration-cache=true to the root gradle.properties and resolve any incompatible-task problems Gradle reports.
repo-gradle-perf-parallel¶
Severity: Low
Serial task execution leaves cores idle on a multi-module build, silently lengthening every build. (Parallel execution requires decoupled, thread-safe projects — verify before enabling.)
How to fix: Add org.gradle.parallel=true to the root gradle.properties (ensure modules are decoupled).
repo-gradle-toolchain-drift¶
Severity: Low
Mismatched JVM targets across modules can produce class-version errors at runtime and inconsistent desugaring/behavior, and make the build depend on whichever JDK the machine happens to run.
How to fix: Configure a single Java toolchain (e.g. java { toolchain { languageVersion = … } }) in a convention plugin or the root build and apply it everywhere.
repo-gradle-version-catalog-drift¶
Severity: Low
Different versions of the same dependency invite classpath conflicts, duplicated transitive graphs, and runtime errors that depend on resolution order.
How to fix: Move these versions into the version catalog (libs.versions.toml) and reference the aliases from every module.
repo-gradle-wrapper-version-drift¶
Severity: Low
A single, consistent Gradle version across the repository keeps builds reproducible and avoids version-specific behavior differences between modules.
How to fix: Align all gradle-wrapper.properties on the same Gradle version (run ./gradlew wrapper --gradle-version <x> per module).
repo-ios-package-manager-overlap¶
Severity: Low
Splitting dependencies across CocoaPods and SwiftPM increases build complexity, slows resolution, and is a recurring source of version conflicts. Consolidating on one manager is usually preferable.
How to fix: Consider consolidating this module’s iOS dependencies onto a single package manager (SwiftPM or CocoaPods).
repo-kmp-gradle-properties-missing¶
Severity: Low
Without a committed gradle.properties, KMP-specific Gradle flags and JVM tuning are not pinned, producing inconsistent and often slower/flakier multiplatform builds across machines.
How to fix: Add a gradle.properties pinning Kotlin/Gradle settings (e.g. kotlin.code.style, org.gradle.jvmargs) and commit it.
repo-missing-gradle-wrapper¶
Severity: Medium
The Gradle wrapper ensures every developer and CI agent uses the exact same Gradle version, preventing hard-to-debug build failures caused by version mismatches.
How to fix: Run gradle wrapper at the Gradle project root and commit the generated gradle/ directory.
repo-missing-podfile-lock¶
Severity: Medium
An untracked Podfile.lock means pod install can resolve different versions on each machine, leading to build failures or runtime regressions that are difficult to diagnose. A lock file that exists only on the machine that generated it protects nobody else.
How to fix: Remove <rule> from <path> (or negate it with !<expectedLockPath> after that line), then git add -f <expectedLockPath>. CocoaPods lock files belong in version control — that is what makes an install reproducible.
repo-missing-pubspec-lock¶
Severity: Low
A missing lock file causes non-reproducible builds and can introduce subtle bugs when transitive dependency versions drift between machines.
How to fix: Run flutter pub get and commit the generated pubspec.lock.
repo-pub-lock-drift¶
Severity: Medium
A stale pubspec.lock does not reflect declared dependencies, so flutter pub get resolves and rewrites the lock differently on each machine and in CI — builds are not reproducible.
How to fix: Run flutter pub get and commit the regenerated pubspec.lock.
repo-swiftpm-lockfile-missing¶
Severity: Low
Package.resolved pins the exact resolved versions of Swift Package dependencies. Committing it keeps dependency resolution reproducible across developers and CI.
How to fix: Resolve packages (swift package resolve, or build once in Xcode) and commit the generated Package.resolved.
repo-toolchain-pin-missing¶
Severity: Low
An unpinned SDK lets different developers and CI agents build with different Dart/Flutter versions, producing inconsistent analyzer results and hard-to-reproduce build failures.
How to fix: Add an environment: block with an sdk: constraint to pubspec.yaml (and optionally pin Flutter via FVM).
structure-cross-package-duplicates¶
Severity: Varies with what is found
Duplicate files across modules increase repository size, lead to inconsistencies when one copy is updated but not others, and waste build resources.
How to fix: Consolidate the duplicates into a shared module or directory and reference the single source from each consuming module.
structure-deep-nesting¶
Severity: Varies with what is found
Excessively deep directory structures make navigation difficult, suggest overly complex module hierarchies, and can cause path-length issues on some operating systems.
How to fix: Aim for at most 9 levels. Flatten feature folders by co-locating related files instead of nesting by type, and move the deepest trees up to a module or package root of their own so their contents are addressed from there rather than from the repository root. Run find . -mindepth 10 -type d to list all offending directories.
structure-heavy-directory¶
Severity: Varies with what is found
Heavy directories increase clone times and CI build durations. They may contain assets or binaries that should be stored externally.
How to fix: This directory exceeds the <DIRECTORY> threshold. Its largest file is <relativePath> (<size>). Most of the weight is images: compress them (TinyPNG) or convert raster assets to WebP (cwebp -q 80 input.png -o output.webp). Run ls -lhS "<dir>" to see the rest.
structure-module-inventory¶
Severity: Info
Understanding the module structure of the repository helps identify build dependencies, potential code sharing opportunities, and structural complexity.
How to fix: No action required. This is an informational finding for visibility into the repository structure.