Android Libraries on GitHub Packages
Publish & Consume Libraries with GitHub Packages

TL;DR
An example Android project and walk-through of how to publish and consume libraries via GitHub Packages, along with features and tips such as achieving transitive dependencies through Gradle API dependency, source code availability in published package and local publishing through custom repo or MavenLocal.
GitHub Packages
GitHub Packages is a new package repository, a.k.a artifactory, service from Github. According to its documentation, they support a list of common package standards such as Maven, npm, Docker, RubyGems and NuGet. Android uses Maven to manage library packages. Compared to other Maven solutions, such as Nexus, JitPack & JFrog, Github Packages has become my favorite pick for the following reasons:
- Packages live in one place with their source code repository(Github). Easier for navigation and lookup between the two.
- Share the same authentication credential(Personal Access Token) and permission system on Github.
- Straightforward setup and we don’t need DevOps support.
- Large free data quota for Enterprise accounts.
Why do I use package repositories at work?
As an Application-Platform engineer, my recent work has been to abstract common layers of application architectures(e.g. MVI base classes, View component layer) into central repositories, publish them into libraries then consume them by client apps through Gradle dependencies. Example:

An Example Project
Let me use an example project to demo how to leverage Github Packages as an artifactory to publish and consume Android libraries.
Goals
This is what we would like to achieve with the project:

- droidlibrary published as an Android library
- droidlibrary2 depends on droidlibrary, and itself is published as another Android library
- Application declared to depend on droidlibrary2 only but transitively depends on droidlibrary
- All source code of dependency libraries(droidlibrary & droidlibrary2) are available for client(app) to navigate into
Nuance of Gradle Dependency : API vs. implementation
In the example, droidlibrary2 library depends on droidlibrary with Gradle API declaration, so it can expose droidlibrary as transitive dependency(we will see how so in the later Publishing section). Here is the official doc to explain API vs implementation for Gradle.
api 'com.chenzhang2006.libraries:droidlib:x.x.x'
But app simply declared implementation dependency to consume droidlibrary2:
implementation 'com.chenzhang2006.libraries:droidlib2:x.x.x'
Explaining Publishing Script
Single Gradle Script For Publishing
The best way to compose your publishing script is to aggregate all publishing related script into one single file, called publish.gradle:
Then in build.gradle, you can simply add a 1-liner in the beginning:
apply from: file('publish.gradle')
There are a few points in publish.gradle that are worth calling out:
- Use Maven Publish Gradle Plugin
apply plugin: 'maven-publish'
2. Declare Maven Repositories & Credentials
We declared 2 maven repositories in the example: one for Github Packages(named “GithubPackages”), the other in local directory(named “CustomMavenRepo” for verification/debugging purpose). I will explain how to use the local custom repository in later Perform Publishing section.
There are a few ways to set credentials as Gradle properties or system environment variables. For example, you can simply set GITHUB_USER & GITHUB_PERSONAL_ACCESS_TOKEN in gradle properties under your home directory: ~/.gradle/gradle.properties
Refer to this document for details of credential configuration for GitHub Packages.
3. Declare Publications
In this example, each library only has one publication:
- Note that configurations.api.allDependencies is the part that carried out the magic of exposing API dependencies as transitive dependency and manifest the transitive dependencies in Maven pom.xml(will show in later Perform Publishing section):
- Note that this is what makes the source code jar available in the published package:
Perform Publishing
Publish with Android Studio UI
In Gradle panel of Android Studio, each module that is configured with publishing plugin would display the publishing tasks as available actions. Because we declared 2 maven repositories in the publish script in the example, there are multiple publishing tasks available:
* publish[Publication]ToCustomMavenRepoRepository
* publish[Publication]ToGithubPackagesRepository

- Clicking on those publish[Publication]ToCustomMavenRepoRepository tasks would result in publishing to the local directory, which is a great way to examine locally what artifacts are actually in the package before publishing them to the remote repository.

** If you open pom.xml, note that transitive dependencies are manifested here for maven to understand that droidlib2 transitively depends on droidlib. Finally, that explained why we wanted API Gradle dependency in earlier section.
** Please note that droidlib2-xxx-sources.jar is also generated in the package along with the binary artifact(aar). That is why client app would be able to navigate into source code of the libraries.
- Clicking on those publish[Publication]ToGithubPackagesRepository tasks would post the package to the remote Github Packages.
- Clicking on publish task would trigger both local and remote publishings.
Now if you navigate to Github Packages UI for droidlib2, you should see its transitive dependency and 3 artifacts in its published package.



Command Line or CI/CD Automation
You can achieve the same result as from Android Studio by running Gradle commands for manual publishing. For example:
gradlew :droidlibrary:publish
This command can be run in your CI/CD to achieve automation, if that is desired.
Publish SNAPSHOTs To MavenLocal For Local Integration
This is a very common practice to integrate libraries locally with client apps and verify library functionalities before publishing to remote repositories. In that case, publishToMavenLocal comes to rescue.

Note that in convention, locally published library should be versioned with “-SNAPSHOT” as suffix. E.g. LIB_VERSION = ‘3.0.1-SNAPSHOT’. The client side should match the same version to consume.
Client Side Gradle Setup and Run
In client app, we configured Maven repositories in build.gradle in project root, so clients can access both Github Packages and MavenLocal repositories:
For Github Maven repo setup:
- Hostname of url always starts with maven.pkg.github.com for Github Packages
- Path of url matches the library project(source code)name on Github
Dependency declaration:
implementation 'com.chenzhang2006.libraries:droidlib2:x.x.x'
Now you can spin up the app and verify that it can access both libraries and navigate into source code as described in our Goals section earlier.
Hope the demo and the walk-through helps if the topic fits your need. Feel free to fork repo as playground. Let me know if you hit snags somewhere and I will try my best to answer.