HomeBlog

Publishing to GitHub Packages with Gradle and GitHub Actions

04 December, 2019 - 5 min read

In November 2019 GitHub released GitHub Packages and GitHub Actions to the general public. Packages allows library owners to publish their code directly to GitHub. The aim is to be another package hosting repository like npmjs.com or Maven Central, but now the packages and code can live side-by-side. GitHub Packages supports multiple package types, including Docker, RubyGems, NPM, Maven, and NuGet to name a few.

GitHub Actions allows repository owners to automate their workflow with different steps that can be triggered on any repository event. This can include common things like pull request checks and publishing releases, but can it can also run on other events like messaging first time contributors.

Together both of these features are free for open source projects and they work well together to have all of the tasks around library management in a single location. As I was creating a new open source library in Kotlin and Gradle, I thought I would try to use both Actions and Packages and document my experience along the way.

If you just want a quick example, you can view all the code and GitHub files in my library, kotlin-extensions.

Getting Setup

The first step is to make sure our library can build locally and the output is a valid jar file. While we can use Gradle in GitHub Actions with some community created steps, I like to use the Gradle Wrapper. This ensures that all contributors to a project are using the same version of Gradle and we can just use the wrapper in the actions. This will also help us avoid the common "it works on my machine" problem.

$ ./gradlew build

Setting up Gradle publish

Now that we have a successful build, we want to configure Gradle so when we run the publish command it connects to GitHub Packages. I am using the Gradle Kotlin DSL but for more details see the official GitHub Packages documentation on setting up Gradle.

First we need to include the maven-publish plugin as a dependency in build.gradle.kts.

// build.gradle.kts
plugins {
    `maven-publish`
    // Other library plugins...
}

Next we need to configure the publishing task by replacing my-username and my-library with the proper values. GITHUB_ACTOR and GITHUB_TOKEN are going to come from GitHub Actions so don't worry about those for now.

// build.gradle.kts continued...
publishing {
    publications {
        create<MavenPublication>("default") {
            from(components["java"])
            // Include any other artifacts here, like javadocs
        }
    }

    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/{my-username}/{my-library}")
            credentials {
                username = System.getenv("GITHUB_ACTOR")
                password = System.getenv("GITHUB_TOKEN")
            }
        }
    }
}

The last step we need to include is to set the library group and version in gradle.properties. This will allow us to change the version dynamically when we publish without having to update our main build file every release.

# gradle.properties
group=dev.smyrick
version=0.0.1-SNAPSHOT

Creating GitHub Actions

Now that we have Gradle configured the next step is to execute the publish task in GitHub Actions. Create a new workflow file in .github/workflows. A current version of the workflow can be found in the kotlin-extensions repo.

This workflow is publishing a new package when there is a new release in GitHub. This means that there will be tag created with the release version when this workflow is triggered.

# publish-release.yml
name: Publish release

on:
  release:
    types: [published]

Next we need to setup the steps to run on this trigger. We will checkout the code at the tagged version; set up our JVM with a specified version; and finally run the following command, passing in the new version.

./gradlew -Pversion=${version} publish

The version name comes from the git tag value. Since git tags are published with the full name format of refs/tags/xxxx we can run a simple cut to extract the tag name and use that as our version. The GITHUB_ACTOR is set for all GitHub Actions as an environment variable. The GITHUB_TOKEN is set for all actions but it is in the secrets, so we must copy it to an environment variable that we are using in our Gradle configuration. GitHub is smart enough though to not expose this value in the logs even if environment value is logged. See the the Actions documentation for a list of all environment variables.

# publish-release.yml continued...
jobs:
  publish-release:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout latest code
        uses: actions/checkout@v1

      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11

      - name: Publish artifact
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

        # The GITHUB_REF tag comes in the format 'refs/tags/xxx'.
        # If we split on '/' and take the 3rd value,
        # we can get the release name.
        run: |
          NEW_VERSION=$(echo "${GITHUB_REF}" | cut -d "/" -f3)
          echo "New version: ${NEW_VERSION}"
          echo "Github username: ${GITHUB_ACTOR}"
          ./gradlew -Pversion=${NEW_VERSION} publish

Monitoring Packages

After we have Gradle setup and the GitHub Actions checked in, we can release a new version of our library. The release notes from the tag will be copied over as the package description which can be helpful to mention any changes that occured for the new version.

As an example, this tag release resulted in this package being published. From the packages view we can also track the number of downloads and other packages using the specific version. This can be helpful for the new features coming to GitHub around security, which will allow us to alert all dependents of any vulnerabilities in a specific version and ask them to update.


That wraps up this short post. I hope you will find the information helpful so you don’t have to spend an afternoon reading through the GitHub documentation as I did (which is still very good).

Feel free to share your own experience with GitHub Actions and Packages with me on Twitter and follow me for future articles! 😃


This was orginally posted to Medium and moved to my personal blog at a later date.

© 2021 Shane Myrick