How we used a simple Bash script to tie Multi-Repo Android projects together

Save time by automating maven local publish across projects

Yashar
4 min readDec 6, 2022

I want to preface this article by saying that this is one of many options to achieve this behavior. I mainly picked this approach for its simplicity and the speed at which it can be implemented in an existing project structure.

The familiar problem

Many Android developers know the pain of inadequate library development experience provided by the AndroidStudio/Google. Jeroen Mols has a great talk where he goes into details and provides workaround solutions for many of them.

I will also not be going into details about maven local publish. With that said, this article is easier to follow if you already have some understanding of it. You can read more on local publishing in this article by Francisco Riádigos.

For this article, I will only focus on the topic of testing multi-repo library projects. By multi-repo, I mean the library modules and the sample app used to test them live in different repositories with no knowledge of one another.

The aim will be to eliminate the necessity of waiting for snapshot publications, and to automatically reflect changes in the library modules in the sample app.

Project set up

Imagine the following project set up:

Repository 3: This repository contains library modules that depend on volatile external libraries that we do not want to directly house in repository 2.

Repository 2: This repository houses library modules that we consider “safe.” But it does depend on some modules from repository 3 for additional functionalities.

Repository 1: Houses our sample app designed to mimic a sample consumer app. Depends on repositories 2 and 3 (either directly or transitively through repository 2).

This example might seem convoluted, but depending on the type of project you are working on, the situation is not that far-fetched.

Testing the changes

Now let’s say you made changes in repositories 3 and 2, how should we test it in our sample app?

Publishing snapshot builds of both repositories to maven and then targeting them in the sample app will take forever.

We can always publish to our local maven repository. This is considerably faster, but doing it manually every time is tedious and error-prone, especially if there are sequential dependencies like above where repository 1 depends on 2, which itself depends on 3.

If only we could automate local publishing…

Automating local publish

At last, let’s create that automation script I teased in the title. In your repository that contains the main sample app (repository 1 in our imaginary case), create a folder named scripts or something to house the following bash code. Make sure to update the path and artifact names to something that matches your project.

#!/usr/bin/bash
# Step 1
cd PATH_TO_YOUR_LOCAL_M2_REPO
rm -rf repository2 &
rm -rf repository3
wait
# Step 2
cd PATH_TO_REPOSITORY3
./gradlew publishToMavenLocal
# Step 3
cd PATH_TO_REPOSITORY2
./gradlew publishToMavenLocal
# Step 4
cd PATH_TO_REPOSITORY1
./gradlew prepareKotlinBuildScriptModel

Let’s call the above script autopublish.sh . It does:

  1. Remove any outdated artifacts from the local maven repository
  2. Builds and publishes project in the repository 3
  3. Builds and publishes project in the repository 2
  4. Syncs sample app project (repository 1) with latest changes

These steps can run either sequentially or in parallel depending on the dependencies are between the repositories.

I am lazy, and I want this script to run every time I run the sample app so any changes I made in the mean time to other projects are always reflected. To do so, we need to hook it up to our project level build.gradle file.

allprojects {
...
repositories {
mavenLocal()
...
}

tasks.whenTaskAdded { task ->
boolean isDebugBuild = task.name == 'assembleDebug'

if (isDebugBuild) {
task.dependsOn(autopublish)
}
}
}

Having mavenLocal() at the top of the repository block means the local versions of the library modules will be referenced even if there are same version codes available on remote.

This is because the dependencies are searched from each repository sequentially until a match is found.

This ensures all local changes are always applied no matter the versioning. If you want to switch between remote and local variants of the library, you should define the local project’s published artifact name as something unique (ex: LocalSnapshot).

It also has a check that only runs the autopublish task when the debug build is being built. This prevents the script from running on the CI/CD environment.

That is all you should have to do. Now any changes made in either library project will be reflected in the sample app. All you have to do is make changes anywhere and hit run in the sample app project like any other android project!

Finishing thoughts

  • While far from perfect, the above setup massively decreased the time we spent when doing quick iterative changes.
  • In addition to saving developer time, it is also helpful for new members to onboard as they don’t have to understand how these projects are connected under the hood.
  • The current bottleneck is that each project is rebuilt on every run. Once the library projects get big enough, we may introduce focus plugin in the mix to further speed up the process.

--

--