diff --git a/.github/scripts/publish-maven-repo.sh b/.github/scripts/publish-maven-repo.sh
new file mode 100755
index 0000000..efbab65
--- /dev/null
+++ b/.github/scripts/publish-maven-repo.sh
@@ -0,0 +1,140 @@
+#!/usr/bin/env bash
+#
+# Publishes the Maven artifacts of this project into a separate, public Git
+# repository that holds a plain Maven repository layout. That repository is
+# served read-only (and token-free) through raw.githubusercontent.com and can
+# hold the artifacts of several libraries side by side, because Maven's layout
+# already namespaces them by groupId and artifactId.
+#
+# Usage:
+# .github/scripts/publish-maven-repo.sh [version]
+#
+# Without an argument the version from pom.xml is published as-is (typically a
+# SNAPSHOT). With an argument the pom version is set to it for the build only;
+# pom.xml is restored afterwards, so the working tree keeps its development
+# version and nothing has to be committed back to the source branch.
+#
+# Environment:
+# MAVEN_REPO_REMOTE git remote of the target repository
+# (default: git@github.com:Tob1as864/maven-repo.git)
+# MAVEN_REPO_BRANCH branch to publish to (default: main)
+# CHECKOUT_DIR where to clone it (default: .maven-repo)
+# PUSH set to "false" for a local dry run (default: true)
+
+set -euo pipefail
+
+REMOTE="${MAVEN_REPO_REMOTE:-git@github.com:Tob1as864/maven-repo.git}"
+BRANCH="${MAVEN_REPO_BRANCH:-main}"
+CHECKOUT_DIR="${CHECKOUT_DIR:-.maven-repo}"
+PUSH="${PUSH:-true}"
+RELEASE_VERSION="${1:-}"
+
+MVN="${MVN:-mvn}"
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+cd "$REPO_ROOT"
+
+log() { printf '\n==> %s\n' "$*"; }
+
+# --- clone the target repository ---------------------------------------------
+log "Cloning $REMOTE"
+rm -rf "$CHECKOUT_DIR"
+git clone --quiet --depth 1 "$REMOTE" "$CHECKOUT_DIR"
+
+REPO_DIR="$(cd "$CHECKOUT_DIR" && pwd)"
+
+# A freshly created repository has no commits, so HEAD is unborn and no branch
+# ref exists yet; -B creates the branch in that case and switches to it in all
+# others.
+if [ "$(git -C "$REPO_DIR" rev-parse --abbrev-ref HEAD)" != "$BRANCH" ]; then
+ git -C "$REPO_DIR" checkout -q -B "$BRANCH"
+fi
+
+# --- determine and validate the version to publish ---------------------------
+if [ -n "$RELEASE_VERSION" ]; then
+ log "Setting project version to $RELEASE_VERSION"
+ POM_BACKUP="$(mktemp)"
+ cp pom.xml "$POM_BACKUP"
+ # Restore the development version even when the build below fails.
+ trap 'cp "$POM_BACKUP" "$REPO_ROOT/pom.xml"; rm -f "$POM_BACKUP"' EXIT
+ "$MVN" -B --no-transfer-progress versions:set \
+ -DnewVersion="$RELEASE_VERSION" -DgenerateBackupPoms=false
+fi
+
+VERSION="$("$MVN" -B -q --no-transfer-progress help:evaluate \
+ -Dexpression=project.version -DforceStdout)"
+GROUP_ID="$("$MVN" -B -q --no-transfer-progress help:evaluate \
+ -Dexpression=project.groupId -DforceStdout)"
+ARTIFACT_ID="$("$MVN" -B -q --no-transfer-progress help:evaluate \
+ -Dexpression=project.artifactId -DforceStdout)"
+ARTIFACT_DIR="$REPO_DIR/$(printf '%s' "$GROUP_ID" | tr '.' '/')/$ARTIFACT_ID/$VERSION"
+
+case "$VERSION" in
+ *-SNAPSHOT) ;;
+ *)
+ # Released versions are immutable: never silently overwrite one.
+ if [ -d "$ARTIFACT_DIR" ]; then
+ echo "ERROR: $GROUP_ID:$ARTIFACT_ID:$VERSION already exists in $REMOTE." >&2
+ echo " Bump the version or delete it there first." >&2
+ exit 1
+ fi
+ ;;
+esac
+
+# --- build and deploy into the checkout --------------------------------------
+log "Deploying $GROUP_ID:$ARTIFACT_ID:$VERSION into $REPO_DIR"
+"$MVN" -B --no-transfer-progress -Prelease clean deploy -Dmaven.repo.dir="$REPO_DIR"
+
+# --- landing page ------------------------------------------------------------
+# The repository is shared by several libraries, so only write a README when it
+# has none yet; an existing one is maintained by hand and must not be clobbered.
+if [ ! -e "$REPO_DIR/README.md" ]; then
+ cat > "$REPO_DIR/README.md" <<'README'
+# Maven repository
+
+This repository holds released Java artifacts in Maven repository layout. Its
+contents are **generated** by the release workflows of the individual library
+repositories - do not commit here by hand.
+
+Consume it without any authentication:
+
+```xml
+
+
+ tob1as864
+ https://raw.githubusercontent.com/Tob1as864/maven-repo/main
+
+
+```
+
+Then declare the library you need as an ordinary dependency. Browse the
+directory tree above for the available groupIds, artifacts and versions.
+README
+fi
+
+# --- commit and push ----------------------------------------------------------
+git -C "$REPO_DIR" add -A
+if git -C "$REPO_DIR" diff --cached --quiet; then
+ log "No changes to publish"
+ exit 0
+fi
+
+git -C "$REPO_DIR" commit -q -m "Publish $GROUP_ID:$ARTIFACT_ID $VERSION"
+log "Committed $GROUP_ID:$ARTIFACT_ID $VERSION"
+
+if [ "$PUSH" != "true" ]; then
+ log "PUSH=$PUSH - skipping push (dry run)"
+ exit 0
+fi
+
+for delay in 2 4 8 16 0; do
+ if git -C "$REPO_DIR" push -u origin "$BRANCH"; then
+ log "Pushed to $REMOTE ($BRANCH)"
+ exit 0
+ fi
+ [ "$delay" -eq 0 ] && break
+ echo "Push failed, retrying in ${delay}s ..." >&2
+ sleep "$delay"
+done
+
+echo "ERROR: could not push to $REMOTE" >&2
+exit 1
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..e419376
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,89 @@
+name: Publish to Maven repo
+
+on:
+ push:
+ tags:
+ - 'v*'
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Version to publish, e.g. 1.2.0. Leave empty to publish the current pom version (SNAPSHOT).'
+ required: false
+ default: ''
+
+# Only the checkout of this repository is needed; writing to the Maven
+# repository goes through the deploy key, not through GITHUB_TOKEN.
+permissions:
+ contents: read
+
+# The job pushes to a shared repository, so never run two of them at once.
+concurrency:
+ group: maven-repo-publish
+ cancel-in-progress: false
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '17'
+ cache: maven
+
+ - name: Determine version
+ id: version
+ run: |
+ if [ "${{ github.event_name }}" = "push" ]; then
+ # Tag v1.2.0 publishes version 1.2.0.
+ echo "value=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
+ else
+ echo "value=${{ github.event.inputs.version }}" >> "$GITHUB_OUTPUT"
+ fi
+
+ - name: Set up deploy key for the Maven repository
+ env:
+ DEPLOY_KEY: ${{ secrets.MAVEN_REPO_DEPLOY_KEY }}
+ run: |
+ if [ -z "$DEPLOY_KEY" ]; then
+ echo "::error::Secret MAVEN_REPO_DEPLOY_KEY is not set. See the README" \
+ "section 'One-time setup of the publishing credentials'."
+ exit 1
+ fi
+ # A wrong key format fails much later with an opaque SSH error, so
+ # reject the common mistakes here with an actionable message.
+ case "$(printf '%s' "$DEPLOY_KEY" | head -n1)" in
+ '-----BEGIN '*'PRIVATE KEY-----')
+ ;;
+ 'PuTTY-User-Key-File'*)
+ echo "::error::MAVEN_REPO_DEPLOY_KEY holds a PuTTY .ppk key, which OpenSSH" \
+ "cannot read. In PuTTYgen use Conversions -> Export OpenSSH key and" \
+ "store that file's full contents instead."
+ exit 1
+ ;;
+ *)
+ echo "::error::MAVEN_REPO_DEPLOY_KEY is not an OpenSSH private key. It must" \
+ "contain the complete key file, starting with a line" \
+ "'-----BEGIN OPENSSH PRIVATE KEY-----' - not a single line copied out" \
+ "of it."
+ exit 1
+ ;;
+ esac
+ mkdir -p ~/.ssh
+ chmod 700 ~/.ssh
+ # printf keeps the trailing newline OpenSSH requires; a here-string would not.
+ printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/id_ed25519
+ chmod 600 ~/.ssh/id_ed25519
+ ssh-keyscan -t rsa,ecdsa,ed25519 github.com >> ~/.ssh/known_hosts 2>/dev/null
+
+ - name: Configure git
+ run: |
+ git config --global user.name 'github-actions[bot]'
+ git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
+
+ - name: Publish
+ run: .github/scripts/publish-maven-repo.sh "${{ steps.version.outputs.value }}"
diff --git a/.gitignore b/.gitignore
index 0ded4a7..a4351e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ target/
*.class
.idea/
*.iml
+.maven-repo/
diff --git a/README.md b/README.md
index 67874f2..0ea34b2 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,103 @@ document puts the ReqIF elements into the default namespace
(``) or into a prefixed one (``).
The same holds for the embedded XHTML (`xhtml:div`, `reqif-xhtml:div`, ...).
+# Using reqif4j as a dependency
+
+Released artifacts are published into the separate, public repository
+[Tob1as864/maven-repo](https://github.com/Tob1as864/maven-repo), which holds a
+plain Maven repository layout and is served over `raw.githubusercontent.com`.
+No GitHub token and no `settings.xml` entry is needed.
+
+Maven:
+
+```xml
+
+
+ tob1as864
+ https://raw.githubusercontent.com/Tob1as864/maven-repo/main
+
+
+
+
+
+ de.uni_stuttgart.ils
+ reqif4j
+ 1.1.0
+
+
+```
+
+The repository `` is just a local name for the declaration — pick any name
+that is unique inside your own pom; it is unrelated to the library's
+`artifactId`. Its only technical purpose is linking a repository to matching
+`` credentials or mirrors in `settings.xml`, neither of which this
+repository needs.
+
+To use a development build, additionally allow snapshots for the repository.
+Maven enables them by default, so this is only needed if you switched them off:
+
+```xml
+true
+```
+
+Gradle:
+
+```kotlin
+repositories {
+ maven { url = uri("https://raw.githubusercontent.com/Tob1as864/maven-repo/main") }
+}
+
+dependencies {
+ implementation("de.uni_stuttgart.ils:reqif4j:1.1.0")
+}
+```
+
+Sources and javadoc jars are published alongside every version, so IDEs can
+show the API documentation. Note that raw.githubusercontent.com is CDN-cached
+for a few minutes, so a freshly published version may not resolve immediately.
+
+## Publishing a new version
+
+`.github/workflows/release.yml` builds the artifacts and commits them into the
+`maven-repo` repository. It runs when a `v*` tag is pushed (tag `v1.2.0`
+publishes version `1.2.0`), or on demand via *Actions -> Publish to Maven repo
+-> Run workflow*, where an empty version input publishes the current SNAPSHOT.
+
+Release versions are immutable: publishing a version that already exists there
+fails instead of overwriting it. The pom version is only changed for the build,
+so no version bump is committed to this repository.
+
+The same publish step can be run locally, without pushing:
+
+```
+PUSH=false .github/scripts/publish-maven-repo.sh 1.2.0
+```
+
+### One-time setup of the publishing credentials
+
+The workflow authenticates against `maven-repo` with an SSH deploy key, which
+grants write access to that one repository only:
+
+1. Create the key pair locally, without a passphrase:
+ `ssh-keygen -t ed25519 -C "reqif4j release workflow" -f maven-repo-key -N ""`
+2. In **Tob1as864/maven-repo** -> *Settings -> Deploy keys -> Add deploy key*:
+ paste the contents of `maven-repo-key.pub` and tick **Allow write access**.
+3. In **this** repository -> *Settings -> Secrets and variables -> Actions ->
+ New repository secret* (a repository secret, not an environment secret):
+ name `MAVEN_REPO_DEPLOY_KEY`, value the **complete** contents of the private
+ key file `maven-repo-key`, from `-----BEGIN OPENSSH PRIVATE KEY-----` through
+ `-----END OPENSSH PRIVATE KEY-----`.
+4. Delete both local key files.
+
+The secret must hold an OpenSSH private key in its original multi-line form;
+the workflow rejects anything else before it starts publishing. PuTTY's own
+`.ppk` format does not work - if you generate the key with PuTTYgen, use
+*Conversions -> Export OpenSSH key* and store that exported file's contents.
+The key must not have a passphrase, because the workflow runs unattended.
+
+The same deploy key setup is repeated per library that publishes into
+`maven-repo`; each library repository gets its own key.
+
# Build & Test
The project builds with Maven (Java 17+):
diff --git a/pom.xml b/pom.xml
index 41f49f3..d65751f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,13 +11,47 @@
reqif4j
Java parser for ReqIF (Requirements Interchange Format) documents
+ https://github.com/Tob1as864/ReqIF-Parser-Java
+
+
+
+ GNU General Public License, version 3
+ https://www.gnu.org/licenses/gpl-3.0.txt
+ repo
+
+
+
+
+ scm:git:https://github.com/Tob1as864/ReqIF-Parser-Java.git
+ scm:git:git@github.com:Tob1as864/ReqIF-Parser-Java.git
+ https://github.com/Tob1as864/ReqIF-Parser-Java
+
17
UTF-8
5.10.2
+
+ ${project.build.directory}/maven-repo
+
+
+ git-maven-repo
+ Git-hosted Maven repository (maven-repo branch)
+ file://${maven.repo.dir}
+
+
+ git-maven-repo
+ Git-hosted Maven repository (maven-repo branch)
+ file://${maven.repo.dir}
+
+
+
org.junit.jupiter
@@ -34,6 +68,66 @@
maven-surefire-plugin
3.2.5
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 3.1.1
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+ 3.1.1
+
+
+ org.codehaus.mojo
+ versions-maven-plugin
+ 2.16.2
+
+
+
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.3.0
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.6.3
+
+ none
+ true
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+