From e6a6eb8a61a40baa951cbb9222b7c64c8e1959a5 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 6 Mar 2026 08:26:48 +0100 Subject: [PATCH 1/6] CI: Modernize workflows for 1.x branch Replace Artifactory-only push.yml with full CI/CD workflow suite from master, adapted for 1.x branch: - deploy.yml: Build, test, Sonar scan, snapshot publish to Maven Central - maven-jreleaser-release.yml: Reusable JReleaser release workflow - gitflow-release-finish.yml: Reusable release finish workflow - gitflow-hotfix-finish.yml: Reusable hotfix finish workflow - release-start.yml, release-finish.yml: Gitflow release management - release-manual.yml: Emergency release fallback - hotfix-start.yml, hotfix-finish.yml: Gitflow hotfix management - hotfix-manual.yml: Emergency hotfix fallback All workflows use Java 8 (temurin) and target 1.x branch. --- .github/workflows/deploy.yml | 80 +++++ .github/workflows/gitflow-hotfix-finish.yml | 248 ++++++++++++++ .github/workflows/gitflow-release-finish.yml | 315 ++++++++++++++++++ .github/workflows/hotfix-finish.yml | 29 ++ .github/workflows/hotfix-manual.yml | 84 +++++ .github/workflows/hotfix-start.yml | 20 ++ .github/workflows/maven-jreleaser-release.yml | 174 ++++++++++ .github/workflows/push.yml | 76 ----- .github/workflows/release-finish.yml | 38 +++ .github/workflows/release-manual.yml | 84 +++++ .github/workflows/release-start.yml | 21 ++ 11 files changed, 1093 insertions(+), 76 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/gitflow-hotfix-finish.yml create mode 100644 .github/workflows/gitflow-release-finish.yml create mode 100644 .github/workflows/hotfix-finish.yml create mode 100644 .github/workflows/hotfix-manual.yml create mode 100644 .github/workflows/hotfix-start.yml create mode 100644 .github/workflows/maven-jreleaser-release.yml delete mode 100644 .github/workflows/push.yml create mode 100644 .github/workflows/release-finish.yml create mode 100644 .github/workflows/release-manual.yml create mode 100644 .github/workflows/release-start.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..162309f --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,80 @@ +name: Build and Deploy +on: + push: + branches: + - 1.x + pull_request: + branches: + - 1.x +jobs: + maven-verify: + runs-on: ubuntu-24.04 + outputs: + version: ${{ steps.get-version.outputs.version }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Install xmlstarlet + run: | + sudo rm -rf /var/lib/apt/lists/* + sudo apt-get update + sudo apt-get -y install xmlstarlet + - uses: actions/setup-java@v4 + with: + java-version: 8 + distribution: temurin + - name: Cache Maven dependencies + uses: actions/cache@v5 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + ${{ runner.os }}-maven- + ${{ runner.os }}- + + - name: Get version from pom.xml + id: get-version + run: | + VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Detected version: $VERSION" + + - name: Run maven build + run: mvn package + + - name: Sonar Scan + env: + SONAR_TOKEN: ${{ secrets.ENTUR_SONAR_PASSWORD }} + SONAR_PROJECT_NAME: ${{ github.event.repository.name }} + SONAR_PROJECT_KEY: entur_${{ github.event.repository.name }} + run: | + mvn -Psonar org.jacoco:jacoco-maven-plugin:prepare-agent verify \ + org.jacoco:jacoco-maven-plugin:report sonar:sonar \ + -Dmaven.main.skip \ + -DskipTests \ + -Dsonar.projectKey=${SONAR_PROJECT_KEY} \ + -Dsonar.organization=enturas-github \ + -Dsonar.projectName=${SONAR_PROJECT_NAME} \ + -Dsonar.host.url=https://sonarcloud.io \ + -Dsonar.token=${SONAR_TOKEN} + + - name: Upload artifact + uses: actions/upload-artifact@v4.6.2 + with: + name: build-artifacts + path: target/*.jar + + publish-snapshot: + name: Publish snapshot to Maven Central + if: github.repository_owner == 'entur' && github.event_name == 'push' && github.ref == 'refs/heads/1.x' + needs: maven-verify + uses: ./.github/workflows/maven-jreleaser-release.yml + with: + version: ${{ needs.maven-verify.outputs.version }} + snapshot: true + skip_version_update: true + java_version: 8 + java_distribution: temurin + secrets: inherit diff --git a/.github/workflows/gitflow-hotfix-finish.yml b/.github/workflows/gitflow-hotfix-finish.yml new file mode 100644 index 0000000..f8c1b6e --- /dev/null +++ b/.github/workflows/gitflow-hotfix-finish.yml @@ -0,0 +1,248 @@ +name: Hotfix Finish (Gitflow) + +on: + workflow_call: + inputs: + hotfix_branch: + description: 'Hotfix branch to finish (e.g., hotfix/2.0.16.1)' + required: true + type: string + merge_to_main: + description: 'Cherry-pick hotfix commits to base branch' + required: false + type: boolean + default: true + runner: + description: 'Runner to use for jobs' + required: false + type: string + default: "ubuntu-24.04" + java_version: + description: 'Java version to use' + required: false + type: number + default: 21 + java_distribution: + description: 'Java distribution to use' + required: false + type: string + default: "liberica" + version_tag_prefix: + description: 'Prefix for version tags' + required: false + type: string + default: "v" + artifact_group_id: + description: 'Maven group ID for summary links (e.g., io.entur)' + required: false + type: string + default: "" + artifact_ids: + description: 'Comma-separated artifact IDs for summary links (e.g., my-library,my-cli)' + required: false + type: string + default: "" + base_branch: + description: 'Base branch to cherry-pick changes to (e.g., main, master, develop)' + required: false + type: string + default: "main" + secrets: + SONATYPE_AUTH_USER: + required: true + SONATYPE_AUTH_TOKEN: + required: true + SONATYPE_GPG_KEY_PUBLIC: + required: true + SONATYPE_GPG_KEY: + required: true + SONATYPE_GPG_KEY_PASSWORD: + required: true + +jobs: + get-hotfix-version: + runs-on: ${{ inputs.runner || 'ubuntu-24.04' }} + outputs: + version: ${{ steps.get_version.outputs.version }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.hotfix_branch }} + fetch-depth: 0 + + - uses: actions/setup-java@v4 + with: + java-version: ${{ inputs.java_version || 21 }} + distribution: ${{ inputs.java_distribution || 'liberica' }} + + - name: Get hotfix version + id: get_version + run: | + VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) + # Remove -SNAPSHOT if present + VERSION="${VERSION%-SNAPSHOT}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Hotfix version: $VERSION" + + create-tag: + name: Create hotfix tag + needs: get-hotfix-version + runs-on: ${{ inputs.runner || 'ubuntu-24.04' }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.hotfix_branch }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Create and push tag + run: | + VERSION="${{ needs.get-hotfix-version.outputs.version }}" + TAG="${{ inputs.version_tag_prefix || 'v' }}${VERSION}" + + echo "Creating tag: $TAG from branch ${{ inputs.hotfix_branch }}" + git tag -a "$TAG" -m "Hotfix $VERSION" + git push origin "$TAG" + + publish-hotfix: + name: Publish to Maven Central + needs: [get-hotfix-version, create-tag] + runs-on: ${{ inputs.runner || 'ubuntu-24.04' }} + env: + JRELEASER_MAVENCENTRAL_URL: "https://central.sonatype.com/api/v1/publisher" + JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_ACTIVE: "RELEASE" + JRELEASER_DEPLOY_MAVEN_NEXUS2_ACTIVE: "SNAPSHOT" + JRELEASER_NEXUS2_URL: "https://ossrh-staging-api.central.sonatype.com/service/local" + JRELEASER_NEXUS2_SNAPSHOT_URL: "https://central.sonatype.com/repository/maven-snapshots" + JRELEASER_OVERWRITE: true + JRELEASER_UPDATE: true + JRELEASER_GIT_ROOT_SEARCH: true + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.version_tag_prefix || 'v' }}${{ needs.get-hotfix-version.outputs.version }} + fetch-depth: 0 + + - uses: actions/setup-java@v4 + with: + java-version: ${{ inputs.java_version || 21 }} + distribution: ${{ inputs.java_distribution || 'liberica' }} + cache: maven + + - name: Install xmlstarlet + run: | + sudo rm -rf /var/lib/apt/lists/* + sudo apt-get update + sudo apt-get -y install xmlstarlet + + - name: JReleaser Release to Maven Central + uses: entur/ror-gha-workflows/.github/actions/jreleaser-release@v1 + with: + version: ${{ needs.get-hotfix-version.outputs.version }} + version_tag_prefix: ${{ inputs.version_tag_prefix || 'v' }} + github_token: ${{ secrets.GITHUB_TOKEN }} + sonatype_username: ${{ secrets.SONATYPE_AUTH_USER }} + sonatype_password: ${{ secrets.SONATYPE_AUTH_TOKEN }} + gpg_public_key: ${{ secrets.SONATYPE_GPG_KEY_PUBLIC }} + gpg_secret_key: ${{ secrets.SONATYPE_GPG_KEY }} + gpg_passphrase: ${{ secrets.SONATYPE_GPG_KEY_PASSWORD }} + artifactory_user: ${{ secrets.ARTIFACTORY_AUTH_USER }} + artifactory_token: ${{ secrets.ARTIFACTORY_AUTH_TOKEN }} + + - name: Upload Build Reports + if: failure() + uses: actions/upload-artifact@v4 + with: + name: jreleaser-reports + path: | + **/target/site + **/target/reports/ + **/target/surefire-reports + + merge-to-base-branch: + name: Cherry-pick hotfix to base branch + needs: [get-hotfix-version, publish-hotfix] + if: inputs.merge_to_main == true + runs-on: ${{ inputs.runner || 'ubuntu-24.04' }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.base_branch || 'main' }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Cherry-pick hotfix commits + run: | + HOTFIX_BRANCH="${{ inputs.hotfix_branch }}" + BASE_BRANCH="${{ inputs.base_branch || 'main' }}" + echo "Cherry-picking commits from $HOTFIX_BRANCH to $BASE_BRANCH" + + # Get the base commit (where hotfix branched from) + git fetch origin "$HOTFIX_BRANCH" + + # Find all commits in the hotfix branch + HOTFIX_COMMITS=$(git log --reverse --pretty=format:"%H" origin/$HOTFIX_BRANCH --not $(git merge-base origin/$BASE_BRANCH origin/$HOTFIX_BRANCH)) + + # Cherry-pick each commit + for commit in $HOTFIX_COMMITS; do + echo "Cherry-picking commit: $commit" + git cherry-pick "$commit" || { + echo "::warning::Cherry-pick conflict on commit $commit. Resolve manually." + git cherry-pick --abort + exit 1 + } + done + + - name: Push to base branch + run: | + BASE_BRANCH="${{ inputs.base_branch || 'main' }}" + git push origin "$BASE_BRANCH" + + - name: Delete hotfix branch + continue-on-error: true + run: | + HOTFIX_BRANCH="${{ inputs.hotfix_branch }}" + echo "Deleting hotfix branch: $HOTFIX_BRANCH" + git push origin --delete "$HOTFIX_BRANCH" || echo "Branch already deleted" + + - name: Create summary + run: | + VERSION="${{ needs.get-hotfix-version.outputs.version }}" + TAG_PREFIX="${{ inputs.version_tag_prefix || 'v' }}" + GROUP_ID="${{ inputs.artifact_group_id }}" + ARTIFACT_IDS="${{ inputs.artifact_ids }}" + + cat >> $GITHUB_STEP_SUMMARY <> $GITHUB_STEP_SUMMARY + done + fi + + BASE_BRANCH="${{ inputs.base_branch || 'main' }}" + + cat >> $GITHUB_STEP_SUMMARY <> $GITHUB_OUTPUT + echo "Release version: $VERSION" + + create-tag: + name: Create release tag + needs: get-release-version + runs-on: ${{ inputs.runner || 'ubuntu-24.04' }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.release_branch }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Create and push tag + run: | + VERSION="${{ needs.get-release-version.outputs.version }}" + TAG="${{ inputs.version_tag_prefix || 'v' }}${VERSION}" + + echo "Creating tag: $TAG from branch ${{ inputs.release_branch }}" + git tag -a "$TAG" -m "Release $VERSION" + git push origin "$TAG" + + publish-release: + name: Publish to Maven Central + needs: [get-release-version, create-tag] + runs-on: ${{ inputs.runner || 'ubuntu-24.04' }} + env: + JRELEASER_MAVENCENTRAL_URL: "https://central.sonatype.com/api/v1/publisher" + JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_ACTIVE: "RELEASE" + JRELEASER_DEPLOY_MAVEN_NEXUS2_ACTIVE: "SNAPSHOT" + JRELEASER_NEXUS2_URL: "https://ossrh-staging-api.central.sonatype.com/service/local" + JRELEASER_NEXUS2_SNAPSHOT_URL: "https://central.sonatype.com/repository/maven-snapshots" + JRELEASER_OVERWRITE: true + JRELEASER_UPDATE: true + JRELEASER_GIT_ROOT_SEARCH: true + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.version_tag_prefix || 'v' }}${{ needs.get-release-version.outputs.version }} + fetch-depth: 0 + + - uses: actions/setup-java@v4 + with: + java-version: ${{ inputs.java_version || 21 }} + distribution: ${{ inputs.java_distribution || 'liberica' }} + cache: maven + + - name: Install xmlstarlet + run: | + sudo rm -rf /var/lib/apt/lists/* + sudo apt-get update + sudo apt-get -y install xmlstarlet + + - name: JReleaser Release to Maven Central + uses: entur/ror-gha-workflows/.github/actions/jreleaser-release@v1 + with: + version: ${{ needs.get-release-version.outputs.version }} + version_tag_prefix: ${{ inputs.version_tag_prefix || 'v' }} + github_token: ${{ secrets.GITHUB_TOKEN }} + sonatype_username: ${{ secrets.SONATYPE_AUTH_USER }} + sonatype_password: ${{ secrets.SONATYPE_AUTH_TOKEN }} + gpg_public_key: ${{ secrets.SONATYPE_GPG_KEY_PUBLIC }} + gpg_secret_key: ${{ secrets.SONATYPE_GPG_KEY }} + gpg_passphrase: ${{ secrets.SONATYPE_GPG_KEY_PASSWORD }} + artifactory_user: ${{ secrets.ARTIFACTORY_AUTH_USER }} + artifactory_token: ${{ secrets.ARTIFACTORY_AUTH_TOKEN }} + + - name: Upload Build Reports + if: failure() + uses: actions/upload-artifact@v4 + with: + name: jreleaser-reports + path: | + **/target/site + **/target/reports/ + **/target/surefire-reports + + merge-release-to-base: + name: Merge release branch to base branch + needs: [get-release-version, publish-release] + runs-on: ${{ inputs.runner || 'ubuntu-24.04' }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.base_branch || 'main' }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Merge release branch + run: | + RELEASE_BRANCH="${{ inputs.release_branch }}" + BASE_BRANCH="${{ inputs.base_branch || 'main' }}" + VERSION="${{ needs.get-release-version.outputs.version }}" + + echo "Merging $RELEASE_BRANCH into $BASE_BRANCH" + git fetch origin "$RELEASE_BRANCH" + git merge "origin/$RELEASE_BRANCH" -m "chore: merge release $VERSION into $BASE_BRANCH" + git push origin "$BASE_BRANCH" + + update-base-branch: + name: Update base branch to next SNAPSHOT version + needs: [get-release-version, merge-release-to-base] + runs-on: ${{ inputs.runner || 'ubuntu-24.04' }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.base_branch || 'main' }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - uses: actions/setup-java@v4 + with: + java-version: ${{ inputs.java_version || 21 }} + distribution: ${{ inputs.java_distribution || 'liberica' }} + + - name: Calculate next development version + id: next_version + run: | + CURRENT_VERSION="${{ needs.get-release-version.outputs.version }}" + + # Use custom version if provided, otherwise increment + if [ -n "${{ inputs.next_version }}" ]; then + NEXT_VERSION="${{ inputs.next_version }}" + # Ensure it has -SNAPSHOT + if [[ ! "$NEXT_VERSION" =~ -SNAPSHOT$ ]]; then + NEXT_VERSION="${NEXT_VERSION}-SNAPSHOT" + fi + else + INCREMENT_TYPE="${{ inputs.next_version_increment }}" + + # Parse version components + IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION" + MAJOR="${VERSION_PARTS[0]:-0}" + MINOR="${VERSION_PARTS[1]:-0}" + PATCH="${VERSION_PARTS[2]:-0}" + + # Calculate next version based on increment type + case "$INCREMENT_TYPE" in + major) + MAJOR=$((MAJOR + 1)) + MINOR=0 + PATCH=0 + ;; + minor) + MINOR=$((MINOR + 1)) + PATCH=0 + ;; + patch) + PATCH=$((PATCH + 1)) + ;; + *) + echo "Error: Invalid increment type: $INCREMENT_TYPE" + exit 1 + ;; + esac + + NEXT_VERSION="$MAJOR.$MINOR.$PATCH-SNAPSHOT" + fi + + echo "next_version=$NEXT_VERSION" >> $GITHUB_OUTPUT + echo "Next development version: $NEXT_VERSION" + + - name: Update base branch version + run: | + NEXT_VERSION="${{ steps.next_version.outputs.next_version }}" + BASE_BRANCH="${{ inputs.base_branch || 'main' }}" + echo "Updating $BASE_BRANCH to: $NEXT_VERSION" + + mvn -B versions:set -DnewVersion="$NEXT_VERSION" -DgenerateBackupPoms=false -DprocessAllModules=true + + git add '*/pom.xml' pom.xml + git commit -m "chore: prepare for next development iteration $NEXT_VERSION [skip ci]" + git push origin "$BASE_BRANCH" + + - name: Delete release branch + continue-on-error: true + run: | + RELEASE_BRANCH="${{ inputs.release_branch }}" + echo "Deleting release branch: $RELEASE_BRANCH" + git push origin --delete "$RELEASE_BRANCH" || echo "Branch already deleted" + + - name: Create summary + run: | + VERSION="${{ needs.get-release-version.outputs.version }}" + TAG_PREFIX="${{ inputs.version_tag_prefix || 'v' }}" + GROUP_ID="${{ inputs.artifact_group_id }}" + ARTIFACT_IDS="${{ inputs.artifact_ids }}" + + cat >> $GITHUB_STEP_SUMMARY <> $GITHUB_STEP_SUMMARY + done + fi + + BASE_BRANCH="${{ inputs.base_branch || 'main' }}" + + cat >> $GITHUB_STEP_SUMMARY <> $GITHUB_OUTPUT + echo "Publishing hotfix version: $VERSION" + + - name: JReleaser Release to Maven Central + uses: entur/ror-gha-workflows/.github/actions/jreleaser-release@v1 + with: + version: ${{ steps.get_version.outputs.version }} + version_tag_prefix: v + github_token: ${{ secrets.GITHUB_TOKEN }} + sonatype_username: ${{ secrets.SONATYPE_AUTH_USER }} + sonatype_password: ${{ secrets.SONATYPE_AUTH_TOKEN }} + gpg_public_key: ${{ secrets.SONATYPE_GPG_KEY_PUBLIC }} + gpg_secret_key: ${{ secrets.SONATYPE_GPG_KEY }} + gpg_passphrase: ${{ secrets.SONATYPE_GPG_KEY_PASSWORD }} + artifactory_user: ${{ secrets.ARTIFACTORY_AUTH_USER }} + artifactory_token: ${{ secrets.ARTIFACTORY_AUTH_TOKEN }} + + - name: Upload Build Reports + if: failure() + uses: actions/upload-artifact@v4 + with: + name: jreleaser-reports + path: | + **/target/site + **/target/reports/ + **/target/surefire-reports diff --git a/.github/workflows/hotfix-start.yml b/.github/workflows/hotfix-start.yml new file mode 100644 index 0000000..e933321 --- /dev/null +++ b/.github/workflows/hotfix-start.yml @@ -0,0 +1,20 @@ +name: Hotfix Start (Gitflow) + +on: + workflow_dispatch: + inputs: + hotfix_version: + description: 'Hotfix version (e.g., 1.0.15.1 or leave empty to auto-increment patch)' + required: false + type: string + base_tag: + description: 'Base tag to create hotfix from (e.g., v1.0.15). Leave empty to use latest 1.x.' + required: false + type: string + +jobs: + hotfix-start: + uses: entur/ror-gha-workflows/.github/workflows/hotfix-start.yml@v1 + with: + hotfix_version: ${{ inputs.hotfix_version }} + base_tag: ${{ inputs.base_tag }} diff --git a/.github/workflows/maven-jreleaser-release.yml b/.github/workflows/maven-jreleaser-release.yml new file mode 100644 index 0000000..33d568c --- /dev/null +++ b/.github/workflows/maven-jreleaser-release.yml @@ -0,0 +1,174 @@ +name: JReleaser Release to Maven Central (Maven) + +on: + workflow_call: + inputs: + runner: + description: "Job Runner" + required: false + type: string + default: "ubuntu-24.04" + git_ref: + description: "Option to override git reference to checkout before build" + type: string + required: false + settings-xml_file: + description: "Link to the external settings.xml file" + required: false + type: string + default: "" + java_version: + description: "Java Runtime Version" + required: false + type: number + default: 21 + java_distribution: + description: "Java distribution for Setup-Java" + required: false + type: string + default: "liberica" + version: + description: "The version to release (e.g., '1.3.0' or '1.3.0-SNAPSHOT'). This is required for this standalone workflow." + required: true + type: string + version_file_name: + description: "The name of the file containing the version number" + required: false + type: string + default: "pom.xml" + version_tag_prefix: + description: "The prefix for the git tag (e.g., 'v' for 'v1.0.0')" + required: false + type: string + default: "v" + snapshot: + description: "Whether the artifact is a snapshot or not" + required: false + type: boolean + default: false + skip_version_update: + description: "Skip setting version in pom.xml (useful if version is already set)" + required: false + type: boolean + default: false + secrets: + GIT_SSH_KEY: + description: "Option to override git ssh key to checkout before build" + required: false + SONATYPE_AUTH_USER: + required: true + SONATYPE_AUTH_TOKEN: + required: true + SONATYPE_GPG_KEY_PUBLIC: + required: true + SONATYPE_GPG_KEY: + required: true + SONATYPE_GPG_KEY_PASSWORD: + required: true + + outputs: + version: + description: Published version + value: ${{ jobs.release.outputs.version }} + +concurrency: + group: maven-jreleaser-release-${{ github.ref }} + +jobs: + release: + runs-on: ${{ inputs.runner }} + permissions: + contents: write + issues: write + packages: write + env: + JRELEASER_MAVENCENTRAL_URL: "https://central.sonatype.com/api/v1/publisher" + JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_ACTIVE: "RELEASE" + JRELEASER_DEPLOY_MAVEN_NEXUS2_ACTIVE: "SNAPSHOT" + JRELEASER_NEXUS2_URL: "https://ossrh-staging-api.central.sonatype.com/service/local" + JRELEASER_NEXUS2_SNAPSHOT_URL: "https://central.sonatype.com/repository/maven-snapshots" + JRELEASER_OVERWRITE: true + JRELEASER_UPDATE: true + JRELEASER_GIT_ROOT_SEARCH: true + outputs: + version: ${{ steps.release.outputs.version }} + steps: + - name: Checkout code + uses: actions/checkout@v6 + if: ${{ inputs.git_ref == '' }} + with: + fetch-depth: 0 + ssh-key: ${{ secrets.GIT_SSH_KEY }} + + - name: Checkout code with reference + uses: actions/checkout@v6 + if: ${{ inputs.git_ref != '' }} + with: + ref: ${{ inputs.git_ref }} + fetch-depth: 0 + ssh-key: ${{ secrets.GIT_SSH_KEY }} + + - name: Set up Java ${{ inputs.java_version }} + uses: actions/setup-java@v4 + with: + java-version: ${{ inputs.java_version }} + distribution: ${{ inputs.java_distribution }} + cache: maven + + - name: Copy maven settings + if: ${{ inputs.settings-xml_file != '' }} + run: | + wget ${{ inputs.settings-xml_file }} -O ~/.m2/settings.xml + + - name: Install xmlstarlet + run: | + sudo rm -rf /var/lib/apt/lists/* + sudo apt-get update + sudo apt-get -y install xmlstarlet + + - name: Validate version input + shell: bash + env: + GHA_MAVENCENTRAL_VERSION: ${{ inputs.version }} + run: | + if [ -z "$GHA_MAVENCENTRAL_VERSION" ]; then + echo "Error: version input is required for this workflow" + exit 1 + fi + echo "Using version: $GHA_MAVENCENTRAL_VERSION" + + - name: Set version + if: ${{ inputs.skip_version_update == false }} + shell: bash + env: + GHA_MAVENCENTRAL_VERSION_FILE_NAME: ${{ inputs.version_file_name}} + GHA_MAVENCENTRAL_VERSION: ${{ inputs.version }} + JFROG_USER: ${{ secrets.ARTIFACTORY_AUTH_USER }} + JFROG_PASS: ${{ secrets.ARTIFACTORY_AUTH_TOKEN }} + run: | + mvn -B versions:set -f $GHA_MAVENCENTRAL_VERSION_FILE_NAME -DnewVersion=$GHA_MAVENCENTRAL_VERSION -DgenerateBackupPoms=false -DprocessAllModules=true + + - name: JReleaser Release to Maven Central + id: release + uses: entur/ror-gha-workflows/.github/actions/jreleaser-release@v1 + with: + version: ${{ inputs.version }} + version_tag_prefix: ${{ inputs.version_tag_prefix }} + github_token: ${{ secrets.GITHUB_TOKEN }} + sonatype_username: ${{ secrets.SONATYPE_AUTH_USER }} + sonatype_password: ${{ secrets.SONATYPE_AUTH_TOKEN }} + gpg_public_key: ${{ secrets.SONATYPE_GPG_KEY_PUBLIC }} + gpg_secret_key: ${{ secrets.SONATYPE_GPG_KEY }} + gpg_passphrase: ${{ secrets.SONATYPE_GPG_KEY_PASSWORD }} + artifactory_user: ${{ secrets.ARTIFACTORY_AUTH_USER }} + artifactory_token: ${{ secrets.ARTIFACTORY_AUTH_TOKEN }} + + - name: Upload Build Reports + if: failure() + uses: actions/upload-artifact@v4 + with: + name: jreleaser-reports + path: | + **/target/site + **/target/reports/ + **/target/surefire-reports diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml deleted file mode 100644 index 6ff8ce5..0000000 --- a/.github/workflows/push.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Build and push -on: - push: - branches: - - 1.x - pull_request: - branches: - - 1.x -env: - JFROG_USER: ${{ secrets.ARTIFACTORY_AUTH_USER }} - JFROG_PASS: ${{ secrets.ARTIFACTORY_AUTH_TOKEN }} - -jobs: - maven-package: - if: "!contains(github.event.head_commit.message, 'ci skip')" - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install xmlstarlet - run: | - sudo rm -rf /var/lib/apt/lists/* - sudo apt-get update - sudo apt-get -y install xmlstarlet - - name: Copy maven settings - run: | - wget https://raw.githubusercontent.com/entur/ror-maven-settings/master/.m2/settings.xml -O .github/workflows/settings.xml - - uses: actions/setup-java@v4 - with: - java-version: 8.0.452+9 - distribution: temurin - - name: Cache Maven dependencies - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - ${{ runner.os }}-maven- - ${{ runner.os }}- - - - name: Run maven build - run: mvn package -s .github/workflows/settings.xml - deploy-artifactory: - if: github.repository_owner == 'entur' && github.event_name == 'push' && github.ref == 'refs/heads/1.x' - needs: [ maven-package ] - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install xmlstarlet - run: | - sudo rm -rf /var/lib/apt/lists/* - sudo apt-get update - sudo apt-get -y install xmlstarlet - - name: Copy maven settings - run: | - wget https://raw.githubusercontent.com/entur/ror-maven-settings/master/.m2/settings.xml -O .github/workflows/settings.xml - - uses: actions/setup-java@v4 - with: - java-version: 8.0.452+9 - distribution: temurin - - name: Cache Maven dependencies - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - ${{ runner.os }}-maven- - ${{ runner.os }}- - - - name: Deploy to Entur Artifactory - run: mvn deploy -s .github/workflows/settings.xml -DskipTests diff --git a/.github/workflows/release-finish.yml b/.github/workflows/release-finish.yml new file mode 100644 index 0000000..699717f --- /dev/null +++ b/.github/workflows/release-finish.yml @@ -0,0 +1,38 @@ +name: Release Finish (Gitflow) + +on: + workflow_dispatch: + inputs: + release_branch: + description: 'Release branch to finish (e.g., release/1.0.16)' + required: true + type: string + next_version_increment: + description: 'Next version increment type for 1.x' + required: false + type: choice + default: 'patch' + options: + - major + - minor + - patch + next_version: + description: 'Or specify exact next version for 1.x (e.g., 1.0.17-SNAPSHOT)' + required: false + type: string + +jobs: + release-finish: + uses: ./.github/workflows/gitflow-release-finish.yml + with: + release_branch: ${{ inputs.release_branch }} + next_version_increment: ${{ inputs.next_version_increment }} + next_version: ${{ inputs.next_version }} + base_branch: 1.x + runner: ubuntu-24.04 + java_version: 8 + java_distribution: temurin + version_tag_prefix: v + artifact_group_id: org.entur + artifact_ids: netex-java-model + secrets: inherit diff --git a/.github/workflows/release-manual.yml b/.github/workflows/release-manual.yml new file mode 100644 index 0000000..b88ea87 --- /dev/null +++ b/.github/workflows/release-manual.yml @@ -0,0 +1,84 @@ +name: Manual Release (Emergency/Fallback) + +# This workflow is a FALLBACK for emergency situations. +# For normal releases, use: "Release Start" → "Release Finish" workflows +# Only use this if the gitflow workflows fail or for emergency releases. + +on: + workflow_dispatch: + inputs: + git_ref: + description: 'Git tag to release (e.g., v2.0.16). Tag must already exist!' + required: true + type: string + version: + description: 'Version to release (if different from tag, e.g., 2.0.16)' + required: false + type: string + +jobs: + publish-release: + name: Publish release to Maven Central + runs-on: ubuntu-24.04 + env: + JRELEASER_MAVENCENTRAL_URL: "https://central.sonatype.com/api/v1/publisher" + JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_ACTIVE: "RELEASE" + JRELEASER_DEPLOY_MAVEN_NEXUS2_ACTIVE: "SNAPSHOT" + JRELEASER_NEXUS2_URL: "https://ossrh-staging-api.central.sonatype.com/service/local" + JRELEASER_NEXUS2_SNAPSHOT_URL: "https://central.sonatype.com/repository/maven-snapshots" + JRELEASER_OVERWRITE: true + JRELEASER_UPDATE: true + JRELEASER_GIT_ROOT_SEARCH: true + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.inputs.git_ref }} + fetch-depth: 0 + + - uses: actions/setup-java@v4 + with: + java-version: 11 + distribution: liberica + cache: maven + + - name: Install xmlstarlet + run: | + sudo rm -rf /var/lib/apt/lists/* + sudo apt-get update + sudo apt-get -y install xmlstarlet + + - name: Get version from tag or input + id: get_version + run: | + if [ -n "${{ github.event.inputs.version }}" ]; then + VERSION="${{ github.event.inputs.version }}" + else + GIT_REF="${{ github.event.inputs.git_ref }}" + VERSION="${GIT_REF#v}" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Publishing version: $VERSION" + + - name: JReleaser Release to Maven Central + uses: entur/ror-gha-workflows/.github/actions/jreleaser-release@v1 + with: + version: ${{ steps.get_version.outputs.version }} + version_tag_prefix: v + github_token: ${{ secrets.GITHUB_TOKEN }} + sonatype_username: ${{ secrets.SONATYPE_AUTH_USER }} + sonatype_password: ${{ secrets.SONATYPE_AUTH_TOKEN }} + gpg_public_key: ${{ secrets.SONATYPE_GPG_KEY_PUBLIC }} + gpg_secret_key: ${{ secrets.SONATYPE_GPG_KEY }} + gpg_passphrase: ${{ secrets.SONATYPE_GPG_KEY_PASSWORD }} + artifactory_user: ${{ secrets.ARTIFACTORY_AUTH_USER }} + artifactory_token: ${{ secrets.ARTIFACTORY_AUTH_TOKEN }} + + - name: Upload Build Reports + if: failure() + uses: actions/upload-artifact@v4 + with: + name: jreleaser-reports + path: | + **/target/site + **/target/reports/ + **/target/surefire-reports diff --git a/.github/workflows/release-start.yml b/.github/workflows/release-start.yml new file mode 100644 index 0000000..ee3ac47 --- /dev/null +++ b/.github/workflows/release-start.yml @@ -0,0 +1,21 @@ +name: Release Start (Gitflow) + +on: + workflow_dispatch: + inputs: + release_version: + description: 'Release version (e.g., 1.0.16). Leave empty to auto-detect from develop.' + required: false + type: string + base_branch: + description: 'Base branch to create release from' + required: false + type: string + default: '1.x' + +jobs: + release-start: + uses: entur/ror-gha-workflows/.github/workflows/release-start.yml@v1 + with: + release_version: ${{ inputs.release_version }} + base_branch: ${{ inputs.base_branch }} From 0fc28f063d59c1d83d0fe42d153af58ad23bcaa7 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 6 Mar 2026 08:27:22 +0100 Subject: [PATCH 2/6] Add CLAUDE.md for Claude Code guidance Adapted from master (43827cd) for the 1.x branch: - Java Version: 8 (instead of 11+) - CI triggers on 1.x branch --- CLAUDE.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..6c729cd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,73 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**netex-java-model** is a Java library that generates JAXB model classes from NeTEx (Network Timetable Exchange) XML Schema Definition (XSD) files. The XSD files are downloaded from https://github.com/entur/NeTEx and compiled into Java classes at build time. + +- **Organization**: Entur AS (Norwegian public transport data provider) +- **License**: EUPL-1.2 +- **Java Version**: 8 (builds on 8 in CI) +- **Package**: `org.rutebanken.netex.model` (generated), `org.rutebanken.netex` and `org.rutebanken.util` (source) + +## Build Commands + +```bash +# Full build (downloads XSD, generates JAXB classes, compiles, tests) +mvn clean install + +# Run tests only +mvn test + +# Package without tests +mvn package -DskipTests + +# Validate build setup +mvn validate +``` + +**Prerequisites**: `xmlstarlet` must be installed (`apt install xmlstarlet` on Debian/Ubuntu). + +## Project Structure + +``` +├── bin/ # Build scripts +│ ├── netex-download-extract.sh # Downloads NeTEx XSD from GitHub +│ ├── annotation-replacer.sh # Post-processes generated Java files +│ └── version_updater.sh # Updates version references +├── bindings.xjb # JAXB customization bindings +├── src/main/java/org/rutebanken/ +│ ├── netex/ # Utilities (validation, client, toString style) +│ └── util/ # XML adapters for Java 8 time types +├── src/main/resources/xsd/ # NeTEx XSD files (downloaded at build time) +├── src/test/ # JUnit 5 tests for marshalling/unmarshalling +└── target/generated-sources/ # JAXB-generated model classes (build artifact) +``` + +## Key Technical Details + +- **JAXB Bindings**: The `bindings.xjb` file customizes code generation, including: + - Java 8 time type adapters (`LocalDateTime`, `LocalTime`, `Duration`) + - Property name conflict resolution + - Package naming (`org.rutebanken.netex.model`) + +- **Generated Code**: Model classes are generated in `target/generated-sources/` during build. Do not manually edit generated code. + +- **NeTEx Version**: Configured in `pom.xml` via `` property (currently 1.16). + +- **Validation**: `NeTExValidator` class provides XML schema validation against NeTEx XSD. + +## CI/CD + +GitHub Actions workflows in `.github/workflows/`: +- `deploy.yml`: Main build, test, Sonar scan, and snapshot publishing (triggers on `1.x` branch) +- Release workflows: GitFlow-based release management +- Publishes to Maven Central via JReleaser + +## Testing + +Tests verify JAXB marshalling/unmarshalling of various NeTEx frame types: +- `MarshalUnmarshalTest` - Round-trip serialization +- `UnmarshalServiceFrameTest`, `UnmarshalSiteFrameTest`, etc. - Frame-specific tests +- `NeTExValidatorTest` - Schema validation tests From 9836661000cb475b15c1ee6aead0bf8d18edd809 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 6 Mar 2026 08:37:50 +0100 Subject: [PATCH 3/6] Change NeTEx source branch from update_dsj to master Align with master branch (32f88ec) to use the main NeTEx source branch for XSD downloads. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bdd036a..f558ade 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 1.8 NeTEx - update_dsj + master https://github.com/entur/${netexRepoName} 1.16 From eabf7841c79adb462b075383ac2ad0008e2998ed Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 6 Mar 2026 08:50:49 +0100 Subject: [PATCH 4/6] Backport unmarshalTopographicPlaceWithPolygon test from master Add missing test that verifies unmarshalling of TopographicPlace elements with GML Polygon geometry. --- .../netex/model/UnmarshalSiteFrameTest.java | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/rutebanken/netex/model/UnmarshalSiteFrameTest.java b/src/test/java/org/rutebanken/netex/model/UnmarshalSiteFrameTest.java index 424d243..125f0f9 100644 --- a/src/test/java/org/rutebanken/netex/model/UnmarshalSiteFrameTest.java +++ b/src/test/java/org/rutebanken/netex/model/UnmarshalSiteFrameTest.java @@ -24,6 +24,7 @@ import java.math.BigDecimal; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; class UnmarshalSiteFrameTest extends AbstractUnmarshalFrameTest { @@ -170,10 +171,48 @@ void unmarshalSiteFrame() throws JAXBException { GroupOfStopPlaces groupOfStopPlaces = siteFrame.getGroupsOfStopPlaces().getGroupOfStopPlaces().get(0); assertEquals("Lillehammer", groupOfStopPlaces.getName().getValue()); assertEquals("NSR:StopPlace:420", groupOfStopPlaces.getMembers().getStopPlaceRef().get(0).getValue().getRef()); + } + + @Test + void unmarshalTopographicPlaceWithPolygon() throws JAXBException { + String xml = "" + + "" + + " 2016-05-18T15:00:00.0+01:00" + + " NHR" + + " " + + " " + + " " + + " " + + " " + + " Oslo" + + " " + + " municipality" + + " " + + " " + + " " + + " 59.91 10.75 59.92 10.76 59.91 10.77 59.90 10.76 59.91 10.75" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + ""; + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); - } + @SuppressWarnings("unchecked") + JAXBElement jaxbElement = (JAXBElement) unmarshaller + .unmarshal(new ByteArrayInputStream(xml.getBytes())); + PublicationDeliveryStructure publicationDeliveryStructure = jaxbElement.getValue(); + SiteFrame siteFrame = (SiteFrame) publicationDeliveryStructure.getDataObjects().getCompositeFrameOrCommonFrame().get(0).getValue(); + TopographicPlace topographicPlace = siteFrame.getTopographicPlaces().getTopographicPlace().get(0); + assertEquals("Oslo", topographicPlace.getDescriptor().getName().getValue()); + assertEquals(TopographicPlaceTypeEnumeration.MUNICIPALITY, topographicPlace.getTopographicPlaceType()); + assertNotNull(topographicPlace.getPolygon()); + } } From f8bfa84db1048d2615dbe05bc5de5dee295de63a Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 6 Mar 2026 08:56:00 +0100 Subject: [PATCH 5/6] Fix enforcer plugin failure in sonar profile on Java 8 The sonar profile was overriding jdk.version to 11, causing the maven-enforcer-plugin's requireJavaVersion rule to reject Java 8. Remove the override so the default jdk.version (1.8) is used. --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index f558ade..d35e954 100644 --- a/pom.xml +++ b/pom.xml @@ -701,7 +701,6 @@ - 11 3.9.1.2184 From 95c1ab74860d075ce0a7822fab2895ed76864181 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 6 Mar 2026 08:58:55 +0100 Subject: [PATCH 6/6] Remove Sonar scan from CI for 1.x branch Sonar requires Java 11+ which is incompatible with the Java 8 build target of the 1.x branch. --- .github/workflows/deploy.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 162309f..8cd6fb3 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -44,22 +44,6 @@ jobs: - name: Run maven build run: mvn package - - name: Sonar Scan - env: - SONAR_TOKEN: ${{ secrets.ENTUR_SONAR_PASSWORD }} - SONAR_PROJECT_NAME: ${{ github.event.repository.name }} - SONAR_PROJECT_KEY: entur_${{ github.event.repository.name }} - run: | - mvn -Psonar org.jacoco:jacoco-maven-plugin:prepare-agent verify \ - org.jacoco:jacoco-maven-plugin:report sonar:sonar \ - -Dmaven.main.skip \ - -DskipTests \ - -Dsonar.projectKey=${SONAR_PROJECT_KEY} \ - -Dsonar.organization=enturas-github \ - -Dsonar.projectName=${SONAR_PROJECT_NAME} \ - -Dsonar.host.url=https://sonarcloud.io \ - -Dsonar.token=${SONAR_TOKEN} - - name: Upload artifact uses: actions/upload-artifact@v4.6.2 with: