Don’t let Maven block you!
As developers, we’ve all encountered build failures in CI (or on our local machines) due to Maven repository issues preventing dependency downloads.
> Could not download foo-lib-1.0.aar (org.acme.foo:foo-lib:1.0)
> Could not get resource 'https://mavenrepo.com/org/acme/foo/foo-lib/1.0/foo-lib-1.0.aar'.
> Could not GET 'https://mavenrepo.com/org/acme/foo/foo-lib/1.0/foo-lib-1.0.aar'. Received status code 403 from server:
There could be many reasons for this: the Maven repository is currently down or is having an incident, the repository has been shut down (hello, JCenter), and the dependency is old and not republished elsewhere.
Usually, the dependencies are cached by Gradle, but the cache can expire.
If the cache is not on our side, there’s another way instead of just waiting for the problem to be fixed: we can create a Maven Local
repository for the failing dependency inside the project.
Create a project-specific Maven Local Repository
A Maven Local
repository is a simple directory on a local machine’s home folder (~/.m2
on macOS, for example) that stores the dependencies artifacts.
.
└── .m2
└── repository
├── com
│ └── acme
│ └── ...
└── org
└── company
└── ...
Such a folder can be placed anywhere, even inside a project. This way, it’s possible to create a Maven local
repository for the org.acme.foo:foo-lib
library that cannot be downloaded.
~/my-project/.m2
.
└── .m2
└── repository
└── com
└── acme
└── foo
└── foo-lib
└── 1.0
└── ...
However, the library’s binary is needed. Otherwise, the repository is useless. The binary can be retrieved inside the Gradle cache, of course where it’s available. In my scenario, the CI failed because the cache expired, but my local environment was still working.
Gradle is caching the binaries of the dependencies in the .gradle/caches/modules-2/files-2.1
folder. In the case of theorg.acme.foo:foo-lib
library, the binaries will be in the .gradle/caches/modules-2/files-2.1/org.acme.foo
folder.
.
└── foo-lib
└── 1.0
├── 296c9bacc53c3c2a8a328cd233b43156f0efb9bb
│ └── foo-lib-1.0.aar
└── 3095a593f57c0aff8e93596b12e4588fbfa3a7e3
└── foo-lib-1.0.pom
The .aar
and the .pom
files can now be moved to the local Maven repository that was created inside the project.
~/my-project/.m2
.
└── .m2
└── repository
└── com
└── acme
└── foo
└── foo-lib
└── 1.0
├── foo-lib-1.0.aar
└── foo-lib-1.0.pom
As the last step, Gradle needs to know that he must grab only the library from the local Maven repository, not from the Internet. To do so, repositories settings in the settings.gradle.kts
file need to be modified.
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
google()
// Define local Maven repository for specific dependencies
maven {
// Point to the project-specific Maven repository
url = uri("file://${rootProject.projectDir}/.m2/repository")
content {
// Only use this repository for the specified dependency
includeModule("org.acme.foo", "foo-lib")
}
}
}
}
And voilà! The project can still be built with the local version of the missing dependency until it becomes available again.