From f3af5338d8779f98e8b6ca243acf7be76df4d01d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 07:49:20 +0000 Subject: [PATCH 1/4] Publish artifacts to a git-hosted Maven repository Adds a release pipeline that deploys reqif4j into a generated `maven-repo` branch of this repository. The branch holds a plain Maven repository layout and is served over raw.githubusercontent.com, so consumers need neither a GitHub token nor a settings.xml entry. - pom.xml: distributionManagement pointing at a configurable output directory (`maven.repo.dir`), plus project url/licenses/scm metadata. A `release` profile attaches the sources and javadoc jars; keeping it out of the default build leaves `mvn verify` on CI unchanged. - .github/scripts/publish-maven-repo.sh: checks out the maven-repo branch as a worktree (creating it as an orphan branch on first run), deploys into it and commits the result. Existing versions and the merged maven-metadata.xml are preserved; release versions are immutable and a re-publish fails instead of overwriting. The pom version is only set for the build and restored afterwards, so no version bump lands on the source branch. - .github/workflows/release.yml: runs the script for `v*` tags and via manual dispatch. - ci.yml: skip the generated maven-repo branch, which carries no sources. - README: consumer instructions for Maven and Gradle, and how to publish. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HUkGAHmCwySzzQG74U5C3H --- .github/scripts/publish-maven-repo.sh | 143 ++++++++++++++++++++++++++ .github/workflows/ci.yml | 3 + .github/workflows/release.yml | 55 ++++++++++ .gitignore | 1 + README.md | 60 +++++++++++ pom.xml | 94 +++++++++++++++++ 6 files changed, 356 insertions(+) create mode 100755 .github/scripts/publish-maven-repo.sh create mode 100644 .github/workflows/release.yml diff --git a/.github/scripts/publish-maven-repo.sh b/.github/scripts/publish-maven-repo.sh new file mode 100755 index 0000000..a2a2463 --- /dev/null +++ b/.github/scripts/publish-maven-repo.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +# +# Publishes the Maven artifacts of this project into the `maven-repo` branch of +# this same repository. That branch holds a plain Maven repository layout and is +# served read-only (and token-free) through raw.githubusercontent.com. +# +# 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_BRANCH branch holding the repository (default: maven-repo) +# WORKTREE_DIR checkout location of that branch (default: .maven-repo-branch) +# PUSH set to "false" for a local dry run (default: true) + +set -euo pipefail + +BRANCH="${MAVEN_REPO_BRANCH:-maven-repo}" +WORKTREE_DIR="${WORKTREE_DIR:-.maven-repo-branch}" +PUSH="${PUSH:-true}" +RELEASE_VERSION="${1:-}" + +MVN="${MVN:-mvn}" +REPO_ROOT="$(git rev-parse --show-toplevel)" +cd "$REPO_ROOT" + +log() { printf '\n==> %s\n' "$*"; } + +# --- prepare a worktree holding the maven-repo branch ------------------------ +if [ -e "$WORKTREE_DIR" ]; then + git worktree remove --force "$WORKTREE_DIR" 2>/dev/null || rm -rf "$WORKTREE_DIR" +fi +git worktree prune + +if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then + log "Fetching existing branch '$BRANCH'" + git fetch --no-tags origin "+refs/heads/$BRANCH:refs/remotes/origin/$BRANCH" + git worktree add --detach "$WORKTREE_DIR" "refs/remotes/origin/$BRANCH" + git -C "$WORKTREE_DIR" switch -C "$BRANCH" "refs/remotes/origin/$BRANCH" +else + log "Branch '$BRANCH' does not exist yet - creating it as an orphan branch" + git worktree add --detach "$WORKTREE_DIR" HEAD + git -C "$WORKTREE_DIR" checkout --orphan "$BRANCH" + git -C "$WORKTREE_DIR" rm -rq --cached . 2>/dev/null || true + find "$WORKTREE_DIR" -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} + +fi + +REPO_DIR="$(cd "$WORKTREE_DIR" && pwd)" + +# --- 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_PATH="$("$MVN" -B -q --no-transfer-progress help:evaluate \ + -Dexpression=project.groupId -DforceStdout | tr '.' '/')" +ARTIFACT_ID="$("$MVN" -B -q --no-transfer-progress help:evaluate \ + -Dexpression=project.artifactId -DforceStdout)" +ARTIFACT_DIR="$REPO_DIR/$GROUP_PATH/$ARTIFACT_ID/$VERSION" + +case "$VERSION" in + *-SNAPSHOT) ;; + *) + # Released versions are immutable: never silently overwrite one. + if [ -d "$ARTIFACT_DIR" ]; then + echo "ERROR: version $VERSION already exists in branch '$BRANCH'." >&2 + echo " Bump the version or delete it from that branch first." >&2 + exit 1 + fi + ;; +esac + +# --- build and deploy into the worktree -------------------------------------- +log "Deploying $ARTIFACT_ID:$VERSION into $REPO_DIR" +"$MVN" -B --no-transfer-progress -Prelease clean deploy -Dmaven.repo.dir="$REPO_DIR" + +# --- landing page of the branch ---------------------------------------------- +cat > "$REPO_DIR/README.md" < + + reqif4j + https://raw.githubusercontent.com/Tob1as864/ReqIF-Parser-Java/$BRANCH + true + + + + + de.uni_stuttgart.ils + reqif4j + $VERSION + +\`\`\` +README + +# --- 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 $ARTIFACT_ID $VERSION" +log "Committed $ARTIFACT_ID $VERSION to branch '$BRANCH'" + +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 branch '$BRANCH'" + exit 0 + fi + [ "$delay" -eq 0 ] && break + echo "Push failed, retrying in ${delay}s ..." >&2 + sleep "$delay" +done + +echo "ERROR: could not push branch '$BRANCH'" >&2 +exit 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e2d63a..654d36e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,9 @@ name: CI on: push: + branches-ignore: + # Generated branch holding the published Maven artifacts, nothing to build. + - maven-repo pull_request: jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b0680e8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,55 @@ +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: '' + +permissions: + contents: write + +# The job rewrites the maven-repo branch, 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 + with: + # A full clone; the publish script fetches and pushes the maven-repo branch. + fetch-depth: 0 + + - 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: Configure git + run: | + git config user.name 'github-actions[bot]' + git config 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..7a8ad81 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ target/ *.class .idea/ *.iml +.maven-repo-branch/ diff --git a/README.md b/README.md index 67874f2..d027dd8 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,66 @@ 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 +[`maven-repo`](https://github.com/Tob1as864/ReqIF-Parser-Java/tree/maven-repo) +branch of this repository and served over `raw.githubusercontent.com`. +No GitHub token and no `settings.xml` entry is needed. + +Maven: + +```xml + + + reqif4j + https://raw.githubusercontent.com/Tob1as864/ReqIF-Parser-Java/maven-repo + true + + + + + + de.uni_stuttgart.ils + reqif4j + 1.1.0 + + +``` + +Gradle: + +```kotlin +repositories { + maven { url = uri("https://raw.githubusercontent.com/Tob1as864/ReqIF-Parser-Java/maven-repo") } +} + +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` branch. 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 in the +branch fails instead of overwriting it. The pom version is only changed for the +build, so no version bump is committed to the source branch. + +The same publish step can be run locally, without pushing: + +``` +PUSH=false .github/scripts/publish-maven-repo.sh 1.2.0 +``` + # 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 + + + + + + + + From 99e545f35ce1a6e13a1d54f94589f94869ba2541 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 09:05:06 +0000 Subject: [PATCH 2/4] Explain the repository id and snapshots element in the README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both were shown without explanation. The repository is a free-form local name, unrelated to the artifactId that happens to share it, and is optional because Maven resolves snapshots from a self-declared repository by default — verified by resolving a SNAPSHOT with the element omitted and watching resolution fail only with false. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HUkGAHmCwySzzQG74U5C3H --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index d027dd8..3092f0f 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,14 @@ Maven: ``` +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. The `` element is optional too: Maven resolves +snapshots from a self-declared repository by default, so it is only needed to +switch them *off* (`false`). + Gradle: ```kotlin From 3f0f810cb10fcf74b4eb49fc394bd8dbea756b70 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 11:10:35 +0000 Subject: [PATCH 3/4] Publish into a separate maven-repo repository instead of a branch The Maven artifacts now go into the standalone public repository Tob1as864/maven-repo rather than a maven-repo branch of this repository. Maven's layout namespaces artifacts by groupId and artifactId, so that repository can hold several libraries side by side, and clones of this repository no longer carry the published binaries. - publish-maven-repo.sh: clone the target repository, deploy into it, commit and push. Handles a freshly created, still empty target repository, and only bootstraps a README when the target has none, so a hand-maintained index there is never clobbered. The immutability guard and the pom restore are unchanged. - release.yml: authenticate with an SSH deploy key from the secret MAVEN_REPO_DEPLOY_KEY, which grants write access to the Maven repository only; GITHUB_TOKEN now needs read permission only. Fails with an actionable message when the secret is missing. - ci.yml: revert the branch filter, there is no generated branch here anymore. - README: consumer URLs point at the new repository, plus one-time instructions for creating the deploy key. Snapshots are documented as an opt-out rather than a required element. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HUkGAHmCwySzzQG74U5C3H --- .github/scripts/publish-maven-repo.sh | 105 +++++++++++++------------- .github/workflows/ci.yml | 3 - .github/workflows/release.yml | 29 +++++-- .gitignore | 2 +- README.md | 53 +++++++++---- 5 files changed, 111 insertions(+), 81 deletions(-) diff --git a/.github/scripts/publish-maven-repo.sh b/.github/scripts/publish-maven-repo.sh index a2a2463..efbab65 100755 --- a/.github/scripts/publish-maven-repo.sh +++ b/.github/scripts/publish-maven-repo.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash # -# Publishes the Maven artifacts of this project into the `maven-repo` branch of -# this same repository. That branch holds a plain Maven repository layout and is -# served read-only (and token-free) through raw.githubusercontent.com. +# 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] @@ -13,14 +15,17 @@ # version and nothing has to be committed back to the source branch. # # Environment: -# MAVEN_REPO_BRANCH branch holding the repository (default: maven-repo) -# WORKTREE_DIR checkout location of that branch (default: .maven-repo-branch) -# PUSH set to "false" for a local dry run (default: true) +# 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 -BRANCH="${MAVEN_REPO_BRANCH:-maven-repo}" -WORKTREE_DIR="${WORKTREE_DIR:-.maven-repo-branch}" +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:-}" @@ -30,26 +35,19 @@ cd "$REPO_ROOT" log() { printf '\n==> %s\n' "$*"; } -# --- prepare a worktree holding the maven-repo branch ------------------------ -if [ -e "$WORKTREE_DIR" ]; then - git worktree remove --force "$WORKTREE_DIR" 2>/dev/null || rm -rf "$WORKTREE_DIR" -fi -git worktree prune - -if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then - log "Fetching existing branch '$BRANCH'" - git fetch --no-tags origin "+refs/heads/$BRANCH:refs/remotes/origin/$BRANCH" - git worktree add --detach "$WORKTREE_DIR" "refs/remotes/origin/$BRANCH" - git -C "$WORKTREE_DIR" switch -C "$BRANCH" "refs/remotes/origin/$BRANCH" -else - log "Branch '$BRANCH' does not exist yet - creating it as an orphan branch" - git worktree add --detach "$WORKTREE_DIR" HEAD - git -C "$WORKTREE_DIR" checkout --orphan "$BRANCH" - git -C "$WORKTREE_DIR" rm -rq --cached . 2>/dev/null || true - find "$WORKTREE_DIR" -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} + -fi +# --- clone the target repository --------------------------------------------- +log "Cloning $REMOTE" +rm -rf "$CHECKOUT_DIR" +git clone --quiet --depth 1 "$REMOTE" "$CHECKOUT_DIR" -REPO_DIR="$(cd "$WORKTREE_DIR" && pwd)" +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 @@ -64,55 +62,54 @@ fi VERSION="$("$MVN" -B -q --no-transfer-progress help:evaluate \ -Dexpression=project.version -DforceStdout)" -GROUP_PATH="$("$MVN" -B -q --no-transfer-progress help:evaluate \ - -Dexpression=project.groupId -DforceStdout | tr '.' '/')" +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/$GROUP_PATH/$ARTIFACT_ID/$VERSION" +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: version $VERSION already exists in branch '$BRANCH'." >&2 - echo " Bump the version or delete it from that branch first." >&2 + 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 worktree -------------------------------------- -log "Deploying $ARTIFACT_ID:$VERSION into $REPO_DIR" +# --- 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 of the branch ---------------------------------------------- -cat > "$REPO_DIR/README.md" < "$REPO_DIR/README.md" <<'README' # Maven repository -This branch is **generated** - do not commit to it by hand. It contains the -released artifacts of [reqif4j](https://github.com/Tob1as864/ReqIF-Parser-Java) -in Maven repository layout and is published by -\`.github/workflows/release.yml\` on the default branch. +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 +```xml - reqif4j - https://raw.githubusercontent.com/Tob1as864/ReqIF-Parser-Java/$BRANCH - true + tob1as864 + https://raw.githubusercontent.com/Tob1as864/maven-repo/main +``` - - de.uni_stuttgart.ils - reqif4j - $VERSION - -\`\`\` +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 @@ -121,8 +118,8 @@ if git -C "$REPO_DIR" diff --cached --quiet; then exit 0 fi -git -C "$REPO_DIR" commit -q -m "Publish $ARTIFACT_ID $VERSION" -log "Committed $ARTIFACT_ID $VERSION to branch '$BRANCH'" +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)" @@ -131,7 +128,7 @@ fi for delay in 2 4 8 16 0; do if git -C "$REPO_DIR" push -u origin "$BRANCH"; then - log "Pushed branch '$BRANCH'" + log "Pushed to $REMOTE ($BRANCH)" exit 0 fi [ "$delay" -eq 0 ] && break @@ -139,5 +136,5 @@ for delay in 2 4 8 16 0; do sleep "$delay" done -echo "ERROR: could not push branch '$BRANCH'" >&2 +echo "ERROR: could not push to $REMOTE" >&2 exit 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 654d36e..9e2d63a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,6 @@ name: CI on: push: - branches-ignore: - # Generated branch holding the published Maven artifacts, nothing to build. - - maven-repo pull_request: jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b0680e8..c4eef95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,10 +11,12 @@ on: 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: write + contents: read -# The job rewrites the maven-repo branch, so never run two of them at once. +# The job pushes to a shared repository, so never run two of them at once. concurrency: group: maven-repo-publish cancel-in-progress: false @@ -25,9 +27,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - # A full clone; the publish script fetches and pushes the maven-repo branch. - fetch-depth: 0 - name: Set up JDK 17 uses: actions/setup-java@v4 @@ -46,10 +45,26 @@ jobs: 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 'Publishing a new version' for how to create it." + exit 1 + fi + 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 user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + 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 7a8ad81..a4351e8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ target/ *.class .idea/ *.iml -.maven-repo-branch/ +.maven-repo/ diff --git a/README.md b/README.md index 3092f0f..1171a0d 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ The same holds for the embedded XHTML (`xhtml:div`, `reqif-xhtml:div`, ...). # Using reqif4j as a dependency -Released artifacts are published into the -[`maven-repo`](https://github.com/Tob1as864/ReqIF-Parser-Java/tree/maven-repo) -branch of this repository and served over `raw.githubusercontent.com`. +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: @@ -27,9 +27,8 @@ Maven: ```xml - reqif4j - https://raw.githubusercontent.com/Tob1as864/ReqIF-Parser-Java/maven-repo - true + tob1as864 + https://raw.githubusercontent.com/Tob1as864/maven-repo/main @@ -46,15 +45,20 @@ 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. The `` element is optional too: Maven resolves -snapshots from a self-declared repository by default, so it is only needed to -switch them *off* (`false`). +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/ReqIF-Parser-Java/maven-repo") } + maven { url = uri("https://raw.githubusercontent.com/Tob1as864/maven-repo/main") } } dependencies { @@ -69,13 +73,13 @@ 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` branch. 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. +`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 in the -branch fails instead of overwriting it. The pom version is only changed for the -build, so no version bump is committed to the source branch. +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: @@ -83,6 +87,23 @@ 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*: name `MAVEN_REPO_DEPLOY_KEY`, value the contents of + the private key file `maven-repo-key` (including the BEGIN/END lines). +4. Delete both local key files. + +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+): From d758a37b63ee9516cf9bfe88284189b3ff84cd91 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 17:09:01 +0000 Subject: [PATCH 4/4] Reject malformed deploy keys with an actionable message A wrong value in MAVEN_REPO_DEPLOY_KEY otherwise surfaces as an opaque SSH failure at push time, after the whole build has run. The workflow now checks the secret's first line up front and names the two likely mistakes: a PuTTY .ppk key, and a single line copied out of a key file rather than the whole file. The README states the expected format, points PuTTYgen users at Conversions -> Export OpenSSH key, and notes that a repository secret (not an environment secret) is what the workflow reads. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HUkGAHmCwySzzQG74U5C3H --- .github/workflows/release.yml | 21 ++++++++++++++++++++- README.md | 12 ++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4eef95..e419376 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,9 +51,28 @@ jobs: run: | if [ -z "$DEPLOY_KEY" ]; then echo "::error::Secret MAVEN_REPO_DEPLOY_KEY is not set. See the README" \ - "section 'Publishing a new version' for how to create it." + "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. diff --git a/README.md b/README.md index 1171a0d..0ea34b2 100644 --- a/README.md +++ b/README.md @@ -97,10 +97,18 @@ grants write access to that one repository only: 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*: name `MAVEN_REPO_DEPLOY_KEY`, value the contents of - the private key file `maven-repo-key` (including the BEGIN/END lines). + 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.