Add Self-Hosted Apache Maven Registry

You are viewing article number 3 of 3 in the series Self-Hosted GitHub Runners: Learn as I Go (LAIG)

This post describes the procedure for adding a self-hosted Maven registry to our evolving Docker Compose stack.

Docker Compose Stack With Dkr Maven Registries 1

Reposilite will be used as the Maven host.

Configure Authentication for Reposilite Host

  • Generate an access token by interactively running a reposilite container (a docker volume is used to persist data):
$ docker run -it --name reposilite -v reposilite-data:/app/data -p 80:8080 dzikoysk/reposilite:nightly
  • Once the following message is displayed
INFO |   Latest version of Reposilite: 3.5.12
INFO |
INFO | For help, type 'help' or '?'    

generate an access token by entering,

token-generate admin-token m
  • This should create a management token (‘m‘) named: admin-token:
INFO | Generated new access token for admin-token with 'm' permissions. Secret:
INFO | /NXCrAC5*******************aTQejFHM5zhW5Syv/g8v7g3Z49lv

To login to the dashboard

  • navigate to http://localhost
  • choose “Sign in
  • enter admin-token for Name
  • enter the management token generated as the secret
Reposilite Dashboard
  • Stop the server
INFO | For help, type 'help' or '?'
stop
  • remove the container
$ docker container remove reposilite

Add Reposilite Service to Docker Compose

Add reposilite service and volume definitions to the docker compose file.

docker-compose.yml
version: "3.8"

services:
  self-hosted-runner:
    image: ubuntu-self-hosted-gh-runner:latest
    container_name: self-hosted-runner
    privileged: true
    volumes:
      - var-lib-docker:/var/lib/docker
    networks:
      - local-net
    environment:
      - REPO_OWNER=<git-repo-owner>
      - REPOSITORY=<git-repo-name>
      - RUNNER_ADMIN_TOKEN=<classic personal access token>
      - RUNNER_LABELS={"labels":["self-hosted","jammy-amd64"]}
      - RUNNER_NAME=self-hosted-jammy-amd64
      - RUNNER_CONFIG_ARGS=--unattended --replace --disableupdate --no-default-labels

  registry:
    image: registry:latest
    container_name: registry
    hostname: registry
    volumes:
      - ./auth:/auth
      - var-lib-registry:/var/lib/registry
    ports:
      - "5000:5000"
    networks:
      - local-net
    environment:
      - REGISTRY_AUTH=htpasswd
      - REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm"
      - REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
      - REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/var/lib/registry

  reposilite:
    image: dzikoysk/reposilite:nightly
    hostname: reposilite
    container_name: reposilite
    volumes:
      - reposilite-data:/app/data
    ports:
      - "80:8080"
    networks:
      - local-net

networks:
  local-net:
    name: local-net

volumes:
  var-lib-docker:
    name: var-lib-docker
  var-lib-registry:
    name: var-lib-registry
  reposilite-data:
    name: reposilite-data

Test by using Maven to Build a Sample Java App

Couple of things to note before moving on to testing. The Maven (mvn) binary will need to be installed onto the self-hosted runner OS. Alternatively, a Maven action can be used if installing onto the runner OS is not a feasible option.

Examples in subsequent sections assume Maven 3.8.8 has been installed.

A basic workflow will be used to perform the following steps:

  • build a Java app using Maven
  • publish components to our local reposilite Maven host

All components referenced be located in GitHub repository:

https://github.com/techtoaster-io/github-self-hosted-runner-maven-java

Before moving onto the next section, start the docker compose stack and check to ensure the 3 services/containers launch without error.

Obtain Maven Repository Metadata

  • Login to reposilite http://localhost using token name (admin-token) and corresponding secret value, as generated in previous sections
  • Navigate to the private Maven repository and note down the corresponding xml metadata
Reposilite Private Repo
  • Metadata should appears as follows
<repository>
  <id>reposilite-repository-private</id>
  <name>Reposilite Repository</name>
  <url>http://localhost/private</url>
</repository>
  • The reposilite service definition in our docker-compose.yml was defined using hostname:reposilite, and internal docker compose port 8080, therefore the url in the repository metadata will need to be updated:
<repository>
  <id>reposilite-repository-private</id>
  <name>Reposilite Repository</name>
  <url>http://reposilite/private:8080</url>
</repository>

Prepare POM.xml

Substitute the xml repository definition above into the <distributionManagement> section of the project’ pom.xml file.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.techtoaster.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>my-app</name>
  <url>https://github.com/techtoaster-io/github-self-hosted-runner-maven-java</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
  <distributionManagement>
    <repository>
      <id>reposilite-repository-private</id>
      <name>Reposilite Repository</name>
      <url>http://reposilite:8080/private</url>
    </repository>
  </distributionManagement>
</project>

Define Repository Variable to Store Contents of Maven settings.xml

Define a new repository variable named MAVEN_SETTINGS_XML with the following contents:

<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd" xmlns="http://maven.apache.org/SETTINGS/1.1.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <servers>
        <server>
            <id>reposilite-repository-private</id>
            <username>${env.MAVEN_REPOSILITE_USERNAME}</username>
            <password>${env.MAVEN_REPOSILITE_TOKEN}</password>
        </server>
    </servers>
</settings>
Settings Xml Repo Var

Server username and password have been replaced with environment variables, respectively as

${env.MAVEN_REPOSILITE_USERNAME}${env.MAVEN_REPOSILITE_TOKEN}

Repository secrets will need to be defined to store the values of both variables.

Define Repository Secrets

Create the following repository secrets to store the reposilite server username/secret token:

Secret name: MAVEN_REPOSILITE_USERNAME
Secret: admin-token
Secret name: MAVEN_REPOSILITE_TOKEN
Secret: <value as generated in section Configure Authentication for Reposilite Host"
Repo Secrets 1

Run Workflow

The sample GHA workflow below builds the sample Java project using Maven and publishes the output package to the reposilite repository:

name: Sample Workflow to Build Java App with Maven

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: [self-hosted]
    steps:
      # reference : https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#publishing-using-apache-maven
      # NOTE: The settings.xml file is created in the Actions $HOME/.m2 directory.
      # If you have an existing settings.xml file at that location, it will be overwritten.
      # See below for using the settings-path to change your settings.xml file location.
      # If you don't want to overwrite the settings.xml file, you can set overwrite-settings: false

      - name: checkout
        uses: actions/checkout@v4

      - name: Retrieve var MAVEN_SETTINGS_XML and write to file
        run: |
          echo -e '${{ vars.MAVEN_SETTINGS_XML }}' > ${{ github.workspace }}/settings.xml
          cat ${{ github.workspace }}/settings.xml

      - name: Setup Maven Action
        uses: actions/setup-java@v4
        with:
          java-version: "17"
          distribution: "temurin"
          settings-path: ${{ github.workspace }} # refer previous step
          overwrite-settings: false
          server-id: reposilite-repository-private # Value of the distributionManagement/repository/id field of the pom.xml
          server-username: MAVEN_REPOSILITE_USERNAME # env variable for username in deploy
          server-password: MAVEN_REPOSILITE_TOKEN # env variable for token in deploy

      - name: Publish to Reposilite
        run: |
          mvn --settings ${{ github.workspace }}/settings.xml clean dependency:copy-dependencies package
          mvn --settings ${{ github.workspace }}/settings.xml package
          mvn --settings ${{ github.workspace }}/settings.xml deploy
        env:
          MAVEN_REPOSILITE_USERNAME: ${{ secrets.MAVEN_REPOSILITE_USERNAME }}
          MAVEN_REPOSILITE_TOKEN: ${{ secrets.MAVEN_REPOSILITE_TOKEN }}

Output log:

Workflow Log

Check the reposilite host to ensure the package was uploaded successfully.

Uploaded To Reposilite
Series Navigation<< Adding a Local Docker Registry