Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/actions/extract-version/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Extract Version
description: >
Extracts the version from the nearest git tag matching v<major>.<minor>.<patch>.
If HEAD is exactly on that tag, snapshot=false. Otherwise snapshot=true.

outputs:
major:
description: Major version number
value: ${{ steps.extract.outputs.major }}
minor:
description: Minor version number
value: ${{ steps.extract.outputs.minor }}
patch:
description: Patch version number
value: ${{ steps.extract.outputs.patch }}
snapshot:
description: "true if HEAD is not on a release tag, false if it is"
value: ${{ steps.extract.outputs.snapshot }}
version:
description: "Full version string, e.g. 2.0.67 or 2.0.67-SNAPSHOT"
value: ${{ steps.extract.outputs.version }}

runs:
using: composite
steps:
- name: Extract version from nearest tag
id: extract
shell: bash
run: |
TAG_PATTERN="^v([0-9]+)\.([0-9]+)\.([0-9]+)$"

# Check if HEAD itself carries a matching tag
HEAD_TAG=$(git tag --points-at HEAD | grep -E "$TAG_PATTERN" | head -1)

if [[ -n "$HEAD_TAG" ]]; then
TAG="$HEAD_TAG"
SNAPSHOT="false"
else
# Find the nearest ancestor tag matching strict semver format
TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*" 2>/dev/null)

if [[ -z "$TAG" ]] || [[ ! "$TAG" =~ $TAG_PATTERN ]]; then
echo "ERROR: No tag matching v<major>.<minor>.<patch> found in git history." >&2
exit 1
fi
SNAPSHOT="true"
fi

# Parse the version components
[[ "$TAG" =~ $TAG_PATTERN ]]
MAJOR="${BASH_REMATCH[1]}"
MINOR="${BASH_REMATCH[2]}"
PATCH="${BASH_REMATCH[3]}"

echo "Found tag: $TAG (snapshot=$SNAPSHOT)"
VERSION="${MAJOR}.${MINOR}.${PATCH}"
[[ "$SNAPSHOT" == "true" ]] && VERSION="${VERSION}-SNAPSHOT"
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
echo "minor=$MINOR" >> "$GITHUB_OUTPUT"
echo "patch=$PATCH" >> "$GITHUB_OUTPUT"
echo "snapshot=$SNAPSHOT" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
7 changes: 2 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
maven-package:
runs-on: ubuntu-24.04
Expand All @@ -15,8 +12,8 @@ jobs:
fetch-depth: 0
- uses: actions/setup-java@v5
with:
java-version: 17.0.13
distribution: liberica
java-version: '17'
distribution: zulu
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
Expand Down
89 changes: 89 additions & 0 deletions .github/workflows/deploy-snapshot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: Publish Snapshot

concurrency:
group: deploy-master
cancel-in-progress: false

on:
push:
branches:
- master
- 187-create-mobilitydata-maven-packages # TEMPORARY: remove after testing
workflow_dispatch: # Manual trigger

env:
java_version: '17'
java_distribution: 'zulu'

jobs:
publish-snapshot:
if: "github.event_name == 'workflow_dispatch' || !contains(github.event.head_commit.message, 'ci skip')"
runs-on: ubuntu-24.04
env:
HAS_1PASSWORD: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN != '' && vars.ONE_PASSWORD_SECRET_REFERENCES != '' }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up JDK ${{ env.java_version }}-${{ env.java_distribution }}
uses: actions/setup-java@v5
with:
java-version: ${{ env.java_version }}
distribution: ${{ env.java_distribution }}
- 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: Load secrets from 1Password
if: env.HAS_1PASSWORD == 'true'
uses: MobilityData/gtfs-validator/.github/actions/extract-1password-secret@master
with:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
VARIABLES_TO_EXTRACT: 'MAVEN_GPG_PASSPHRASE, MAVEN_GPG_PRIVATE_KEY, MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME, MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD'
ONE_PASSWORD_SECRET_REFERENCES: ${{ vars.ONE_PASSWORD_SECRET_REFERENCES }}
- name: Load secrets from GitHub secrets (fallback for forks without 1Password)
if: env.HAS_1PASSWORD != 'true'
env:
GPG_KEY: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
GPG_PASS: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
MVN_USER: ${{ secrets.MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME }}
MVN_PASS: ${{ secrets.MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD }}
run: |
{
echo "MAVEN_GPG_PASSPHRASE=$GPG_PASS"
echo "MAVEN_GPG_PRIVATE_KEY<<GPG_EOF"
echo "$GPG_KEY"
echo "GPG_EOF"
echo "MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME=$MVN_USER"
echo "MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD=$MVN_PASS"
} >> "$GITHUB_ENV"
- name: Setup GPG and JReleaser credentials
run: |
echo "$MAVEN_GPG_PRIVATE_KEY" | gpg --batch --import
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep '^sec' | awk '{print $2}' | cut -d'/' -f2 | head -1)
{
echo "JRELEASER_GPG_PUBLIC_KEY<<GPG_EOF"
gpg --armor --export "$GPG_KEY_ID"
echo "GPG_EOF"
echo "JRELEASER_GPG_SECRET_KEY<<GPG_EOF"
gpg --armor --export-secret-keys "$GPG_KEY_ID"
echo "GPG_EOF"
echo "JRELEASER_GPG_PASSPHRASE=$MAVEN_GPG_PASSPHRASE"
echo "JRELEASER_NEXUS2_MAVEN_CENTRAL_USERNAME=$MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME"
echo "JRELEASER_NEXUS2_MAVEN_CENTRAL_PASSWORD=$MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD"
} >> "$GITHUB_ENV"
- name: Stage artifacts
run: mvn deploy -Ppublication -Dprettier.skip=true
- name: Publish snapshot to Maven Central
run: mvn jreleaser:deploy -Djreleaser.output.directory=out
- name: Upload JReleaser output
if: always()
uses: actions/upload-artifact@v4
with:
name: jreleaser-snapshot-logs
path: out/
57 changes: 0 additions & 57 deletions .github/workflows/deploy.yml

This file was deleted.

105 changes: 105 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Release

# Triggered by GitHub release events:
# - Creating a pre-release with tag vX.Y.Z → builds and stages artifacts to Maven Central
# (USER_MANAGED, pending). Review them at https://central.sonatype.com, then convert
# the pre-release to a full release when satisfied.
# - Publishing a full release → rebuilds and publishes artifacts to Maven Central immediately.
#
# The project version is derived automatically from the git tag via maven-git-versioning-extension.
# No pom.xml version commits are needed.
on:
release:
types: [prereleased, released]

env:
java_version: '17'
java_distribution: 'zulu'

jobs:
release:
runs-on: ubuntu-24.04
permissions:
contents: write
env:
HAS_1PASSWORD: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN != '' && vars.ONE_PASSWORD_SECRET_REFERENCES != '' }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
# Check out the exact tagged commit so the version is correctly resolved from the tag.
ref: ${{ github.event.release.tag_name }}
- name: Set up JDK ${{ env.java_version }}-${{ env.java_distribution }}
uses: actions/setup-java@v5
with:
java-version: ${{ env.java_version }}
distribution: ${{ env.java_distribution }}
- 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: Load secrets from 1Password
if: env.HAS_1PASSWORD == 'true'
uses: MobilityData/gtfs-validator/.github/actions/extract-1password-secret@master
with:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
VARIABLES_TO_EXTRACT: 'MAVEN_GPG_PASSPHRASE, MAVEN_GPG_PRIVATE_KEY, MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME, MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD'
ONE_PASSWORD_SECRET_REFERENCES: ${{ vars.ONE_PASSWORD_SECRET_REFERENCES }}
- name: Load secrets from GitHub secrets (fallback for forks without 1Password)
if: env.HAS_1PASSWORD != 'true'
env:
GPG_KEY: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
GPG_PASS: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
MVN_USER: ${{ secrets.MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME }}
MVN_PASS: ${{ secrets.MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD }}
run: |
{
echo "MAVEN_GPG_PASSPHRASE=$GPG_PASS"
echo "MAVEN_GPG_PRIVATE_KEY<<GPG_EOF"
echo "$GPG_KEY"
echo "GPG_EOF"
echo "MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME=$MVN_USER"
echo "MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD=$MVN_PASS"
} >> "$GITHUB_ENV"
- name: Setup GPG and JReleaser credentials
run: |
echo "$MAVEN_GPG_PRIVATE_KEY" | gpg --batch --import
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep '^sec' | awk '{print $2}' | cut -d'/' -f2 | head -1)
{
echo "JRELEASER_GPG_PUBLIC_KEY<<GPG_EOF"
gpg --armor --export "$GPG_KEY_ID"
echo "GPG_EOF"
echo "JRELEASER_GPG_SECRET_KEY<<GPG_EOF"
gpg --armor --export-secret-keys "$GPG_KEY_ID"
echo "GPG_EOF"
echo "JRELEASER_GPG_PASSPHRASE=$MAVEN_GPG_PASSPHRASE"
echo "JRELEASER_MAVENCENTRAL_SONATYPE_USERNAME=$MAVEN_CENTRAL_PORTAL_TOKEN_USERNAME"
echo "JRELEASER_MAVENCENTRAL_SONATYPE_PASSWORD=$MAVEN_CENTRAL_PORTAL_TOKEN_PASSWORD"
} >> "$GITHUB_ENV"
- name: Stage artifacts
run: mvn deploy -Ppublication -Dprettier.skip=true
- name: Deploy to Maven Central
# prereleased: USER_MANAGED — artifacts sit pending in the portal for review.
# released: AUTOMATIC — artifacts are published immediately after validation.
# jreleaser:deploy only handles Maven Central; GitHub release management is done by the GitHub UI.
env:
JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_SONATYPE_PUBLISHING_TYPE: ${{ github.event.action == 'prereleased' && 'USER_MANAGED' || 'AUTOMATIC' }}
run: mvn jreleaser:deploy -Djreleaser.output.directory=out
- name: Upload CLI fat jar to GitHub release
run: |
gh release upload "${{ github.event.release.tag_name }}" \
gbfs-validator-java-cli/target/gbfs-validator-cli.jar \
--clobber
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload JReleaser output
if: always()
uses: actions/upload-artifact@v4
with:
name: jreleaser-release-logs
path: out/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
# generated by maven-git-versioning-extension
.git-versioned-pom.xml

.vscode
10 changes: 10 additions & 0 deletions .mvn/extensions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 https://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
<extension>
<groupId>me.qoomon</groupId>
<artifactId>maven-git-versioning-extension</artifactId>
<version>9.11.0</version>
</extension>
</extensions>
23 changes: 23 additions & 0 deletions .mvn/maven-git-versioning-extension.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="https://github.com/qoomon/maven-git-versioning-extension"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/qoomon/maven-git-versioning-extension
https://github.com/qoomon/maven-git-versioning-extension/raw/master/src/main/resources/configuration-schema.xsd">

<refs>
<!-- Tagged commit: use the tag version directly (e.g. v2.0.67 → 2.0.67) -->
<ref type="tag" pattern="v(?&lt;version&gt;.*)">
<version>${version}</version>
</ref>

<!-- master branch: increment patch and add SNAPSHOT (e.g. after v2.0.66 → 2.0.67-SNAPSHOT) -->
<ref type="branch" pattern="master">
<version>${describe.tag.version.major}.${describe.tag.version.minor}.${describe.tag.version.patch.next}-SNAPSHOT</version>
</ref>

<!-- Any other branch: use next patch version + SNAPSHOT for local development -->
<ref type="branch" pattern=".*">
<version>${describe.tag.version.major}.${describe.tag.version.minor}.${describe.tag.version.patch.next}-SNAPSHOT</version>
</ref>
</refs>
</configuration>
Loading
Loading