Skip to content

Commit

Permalink
Add Gradle dependency locking (#138)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorwojda authored Feb 12, 2021
1 parent 1db7041 commit 18473d9
Show file tree
Hide file tree
Showing 13 changed files with 554 additions and 103 deletions.
35 changes: 26 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ This project brings to table set of best practices, tools, and solutions:
* Static analysis tools
* Dependency Injection
* Material design
* Gradle
* [Dependency locks](https://docs.gradle.org/current/userguide/dependency_locking.html)
* [Versions catalog](https://docs.gradle.org/7.0-milestone-1/userguide/platforms.html)

## Tech-stack

Expand Down Expand Up @@ -149,27 +152,40 @@ Below diagram presents application data flow when a user interacts with `album l

![app_data_flow](https://github.com/igorwojda/android-showcase/blob/master/misc/image/app_data_flow.png?raw=true)

## External dependencies
## Dependency management

This project utilizes multiple mechanics to eaisly share the same versions of dependencies.
### App library dependencies

External dependencies (libraries) are defined using [versions catalog](https://docs.gradle.org/7.0-milestone-1/userguide/platforms.html) feature in the [settings.gradle](./settings.gradle) file. These dynamic library versions are locked using Gradle [docking dependency](https://docs.gradle.org/current/userguide/dependency_locking.html) mechanism - concrete dependency versions are stored in `MODULE_NAME/gradle.lockfile` files.

To update lock files run `gw :app:assembleDebug --write-locks` command and commit updated `gradle.lockfile` files to repository.

All the external dependencies (external libraries) are defined in the single place - Gradle `buildSrc` folder. This approach allows to easily
manage dependencies and use the same dependency version across all modules. Because each feature module depends on the `app` module
we can easily share all core dependencies without redefining them in each feature module.
Each feature module depends on the `app` module, so dependencies are shared without need to add them explicitly in each feature module.

[and more...](https://github.com/igorwojda/android-showcase/blob/master/buildSrc/src/main/kotlin/LibraryDependency.kt)
### Gradle plugin dependencies

External dependencies (external libraries) are defined using [pluginManagement](https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_management). Dependency definitions are stored in the [settings.gradle](./settings.gradle) file.

Dynamic versions aren't supported for Gradle plugins, so [docking dependency](https://docs.gradle.org/current/userguide/dependency_locking.html) mechanism can't be used (like for app library dependencies), and thus versions of some libraries & plugins have to be hardcoded in the [gradle.properties](./gradle.properties) file.
### Shared dependencies

Gradle is missing proper build-in mechanism to share dependency versions between app library dependency and Gradle plugin dependency eg. [Navigation component](https://developer.android.com/guide/navigation/navigation-getting-started) library uses [Safe Args](https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args) Gradle plugin with the same version.

To enable sharing all versions that are used for both plugins and librares are defined in [gradle.properties](./gradle.properties).

Unfortunately this technique cannot be applied to older Gradle plugins (usingadded by `classpath`, not by `pluginManagament`), so some version in the [gradle.properties](./gradle.properties) are still duplicated.
## Ci pipeline

[CI pipeline](https://github.com/igorwojda/android-showcase/tree/master/.github/workflows) verifies project correctness which each PR.
All of the tasks run in parallel:
CI Pipeline is utilizing [GitHub Actions](https://github.com/features/actions). Complete GitHub Actions config is located in the [.github/workflows](https://github.com/igorwojda/android-showcase/tree/master/.github/workflows) folder.

These are all of the Gradle tasks that are [GitHub Actions](https://github.com/features/actions):
Series of workflows runs (in parallel) for every opened PR and after merging PR to the `master` branch:
* `./gradlew lintDebug` - runs Android lint
* `./gradlew detekt` - runs detekt
* `./gradlew ktlintCheck` - runs ktlint
* `./gradlew testDebugUnitTest` - run unit tests
* `./gradlew connectedCheck` - run UI tests
* `./gradlew :app:bundleDebug` - create app bundle

### Design decisions

Read related articles to have a better understanding of underlying design decisions and various trade-offs.
Expand Down Expand Up @@ -228,6 +244,7 @@ Other high-quality projects will help you to find solutions that work for your p
* [Roxie](https://github.com/ww-tech/roxie) - solid example of `common state` approach together witch very good
documentation
* [Kotlin Android template](https://github.com/cortinico/kotlin-android-template) - template that lets you create an Android/Kotlin project and be up and running in a few seconds.
* Gradle dependencies can't be eaisly shared between app librares and Gradle plugins https://github.com/gradle/gradle/issues/16077

## Known issues
- `ktlint` `import-ordering` rule conflicts with IDE default formatting rule, so it have to be [disabled](.editorconfig). This is partially fixed in AS 4.2 (see [527](https://github.com/pinterest/ktlint/issues/527) and [KT-10974](https://youtrack.jetbrains.com/issue/KT-10974))
Expand Down
103 changes: 103 additions & 0 deletions app/gradle.lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
androidx.activity:activity-ktx:1.2.0=debugCompileClasspath,debugRuntimeClasspath
androidx.activity:activity:1.2.0=debugCompileClasspath,debugRuntimeClasspath
androidx.annotation:annotation-experimental:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.annotation:annotation:1.2.0-beta01=debugCompileClasspath,debugRuntimeClasspath
androidx.appcompat:appcompat-resources:1.3.0-beta01=debugCompileClasspath,debugRuntimeClasspath
androidx.appcompat:appcompat:1.3.0-beta01=debugCompileClasspath,debugRuntimeClasspath
androidx.arch.core:core-common:2.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.arch.core:core-runtime:2.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.cardview:cardview:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.collection:collection-ktx:1.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.collection:collection:1.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.constraintlayout:constraintlayout-core:1.0.0-alpha3=debugCompileClasspath,debugRuntimeClasspath
androidx.constraintlayout:constraintlayout:2.1.0-alpha2=debugCompileClasspath,debugRuntimeClasspath
androidx.coordinatorlayout:coordinatorlayout:1.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.core:core-ktx:1.5.0-beta01=debugCompileClasspath,debugRuntimeClasspath
androidx.core:core:1.5.0-beta01=debugCompileClasspath,debugRuntimeClasspath
androidx.cursoradapter:cursoradapter:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.customview:customview:1.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.databinding:viewbinding:4.1.2=debugCompileClasspath,debugRuntimeClasspath
androidx.documentfile:documentfile:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.drawerlayout:drawerlayout:1.1.1=debugCompileClasspath,debugRuntimeClasspath
androidx.dynamicanimation:dynamicanimation:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.exifinterface:exifinterface:1.3.2=debugRuntimeClasspath
androidx.fragment:fragment-ktx:1.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.fragment:fragment:1.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.interpolator:interpolator:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.legacy:legacy-support-core-utils:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-common-java8:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-common:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-livedata-core:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-livedata-ktx:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-livedata:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-runtime-ktx:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-runtime:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.lifecycle:lifecycle-viewmodel:2.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.loader:loader:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.localbroadcastmanager:localbroadcastmanager:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-common-ktx:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-common:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-dynamic-features-fragment:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-dynamic-features-runtime:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-fragment-ktx:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-fragment:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-runtime-ktx:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-runtime:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-ui-ktx:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.navigation:navigation-ui:2.3.3=debugCompileClasspath,debugRuntimeClasspath
androidx.print:print:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.recyclerview:recyclerview:1.2.0-beta01=debugCompileClasspath,debugRuntimeClasspath
androidx.savedstate:savedstate-ktx:1.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.savedstate:savedstate:1.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.tracing:tracing:1.0.0=debugRuntimeClasspath
androidx.transition:transition:1.3.0=debugCompileClasspath,debugRuntimeClasspath
androidx.vectordrawable:vectordrawable-animated:1.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.vectordrawable:vectordrawable:1.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.versionedparcelable:versionedparcelable:1.1.0=debugCompileClasspath,debugRuntimeClasspath
androidx.viewpager2:viewpager2:1.0.0=debugCompileClasspath,debugRuntimeClasspath
androidx.viewpager:viewpager:1.0.0=debugCompileClasspath,debugRuntimeClasspath
com.airbnb.android:lottie:2.8.0=debugCompileClasspath,debugRuntimeClasspath
com.android.tools.build:aapt2:4.1.2-6503028=_internal_aapt2_binary
com.facebook.stetho:stetho-okhttp3:1.5.0=debugCompileClasspath,debugRuntimeClasspath
com.facebook.stetho:stetho:1.5.0=debugCompileClasspath,debugRuntimeClasspath
com.google.android.material:material:1.3.0=debugCompileClasspath,debugRuntimeClasspath
com.google.android.play:core:1.9.1=debugCompileClasspath,debugRuntimeClasspath
com.google.code.findbugs:jsr305:2.0.1=debugCompileClasspath,debugRuntimeClasspath
com.jakewharton.timber:timber:4.7.1=debugCompileClasspath,debugRuntimeClasspath
com.squareup.moshi:moshi:1.8.0=debugCompileClasspath,debugRuntimeClasspath
com.squareup.okhttp3:logging-interceptor:4.10.0-RC1=debugCompileClasspath,debugRuntimeClasspath
com.squareup.okhttp3:okhttp:4.10.0-RC1=debugCompileClasspath,debugRuntimeClasspath
com.squareup.okio:okio:2.10.0=debugCompileClasspath,debugRuntimeClasspath
com.squareup.retrofit2:converter-moshi:2.9.0=debugCompileClasspath,debugRuntimeClasspath
com.squareup.retrofit2:retrofit:2.9.0=debugCompileClasspath,debugRuntimeClasspath
commons-cli:commons-cli:1.2=debugCompileClasspath,debugRuntimeClasspath
io.coil-kt:coil-base:1.1.1=debugCompileClasspath,debugRuntimeClasspath
io.coil-kt:coil:1.1.1=debugCompileClasspath,debugRuntimeClasspath
org.jetbrains.intellij.deps:trove4j:1.0.20181211=kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.4.21=debugRuntimeClasspath
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.4.30=kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.4.30=kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-parcelize-runtime:1.4.21=debugRuntimeClasspath
org.jetbrains.kotlin:kotlin-reflect:1.4.30=debugCompileClasspath,debugRuntimeClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-script-runtime:1.4.30=kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-stdlib-common:1.4.30=debugCompileClasspath,debugRuntimeClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10=debugCompileClasspath,debugRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10=debugCompileClasspath,debugRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:1.4.30=debugCompileClasspath,debugRuntimeClasspath,kotlinCompilerClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2=debugCompileClasspath,debugRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2=debugCompileClasspath,debugRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2=debugCompileClasspath,debugRuntimeClasspath
org.jetbrains:annotations:13.0=kotlinCompilerClasspath
org.jetbrains:annotations:16.0.1=debugCompileClasspath,debugRuntimeClasspath
org.kodein.di:kodein-di-core-jvm:6.5.5=debugCompileClasspath,debugRuntimeClasspath
org.kodein.di:kodein-di-core:6.5.5=debugCompileClasspath,debugRuntimeClasspath
org.kodein.di:kodein-di-framework-android-core:6.5.5=debugCompileClasspath,debugRuntimeClasspath
org.kodein.di:kodein-di-framework-android-x:6.5.5=debugCompileClasspath,debugRuntimeClasspath
org.kodein.di:kodein-di-generic-jvm:6.5.5=debugCompileClasspath,debugRuntimeClasspath
empty=debugAnnotationProcessorClasspath,debugReverseMetadataValues,kotlinCompilerPluginClasspath
21 changes: 15 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ plugins {
id(GradlePluginId.SAFE_ARGS) apply false
}

dependencyLocking {
lockAllConfigurations()
}

// all projects = root project + sub projects
allprojects {
repositories {
Expand Down Expand Up @@ -41,6 +45,12 @@ allprojects {
exclude { element -> element.file.path.contains("generated/") }
}
}

// Gradle dependency locking - lock all configurations of the app
// More: https://docs.gradle.org/current/userguide/dependency_locking.html
dependencyLocking {
lockAllConfigurations()
}
}

subprojects {
Expand Down Expand Up @@ -73,13 +83,12 @@ tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString()
}

/*
Mimics all static checks that run on CI.
Note that this task is intended to run locally (not on CI), because on CI we prefer to have parallel execution
and separate reports for each of the checks (multiple statuses eg. on github PR page).
*/
task("staticCheck") {
description =
"""Mimics all static checks that run on CI.
Note that this task is intended to run locally (not on CI), because on CI we prefer to have parallel execution
and separate reports for each check (multiple statuses eg. on github PR page).
""".trimMargin()

group = "verification"
afterEvaluate {
// Filter modules with "lintDebug" task (non-Android modules do not have lintDebug task)
Expand Down
Loading

0 comments on commit 18473d9

Please sign in to comment.