Build with Gradle in two modules, and free core of JavaFX

Etappen 1 and 2 of the Compose migration (43 commits).

The Maven build, the single source tree and the JPMS module-info went together.
core now holds the domain and app-desktop the user interface, both built by
Gradle, packaged through jpackage from Gradle, with SpotBugs and PMD wired in.
JavaFX moves to the classpath, which is why the launcher is Main and not the
Application subclass. The PR check, the nightly artifacts, the release and the
AUR packages all build from Gradle; the macOS DMG points at its output.
Verified on Linux, macOS and Windows.

core then loses JavaFX entirely. It observes through kst4contest.observe --
ObservableValue, ObservableRoster, and UiDispatcher as the one boundary a
worker thread crosses to touch the interface. The controller collections become
rosters and the visible station list a pure derivation rather than a
FilteredList/SortedList chain, which is also what fixed two focus defects only
a load test had exposed: the keyboard focus stays where the operator put it,
and the send field keeps it after a row click.

The rule that outlives this commit: ObservableRoster is the store and the list
a view is bound to is a mirror of it, never the other way round.
This commit is contained in:
Rsclub2_2 committed 2026-10-05 22:31:56 +02:00
1 parent bcbeadae95
commit a6fba12f01
360 files changed
+6304 -2422

No files matched your search

+2 -2
View File
@@ -41,8 +41,8 @@ jobs:
- name: Compute pkgver
id: ver
run: |
BASE=$(grep -m1 '<version>' pom.xml \
| sed 's/.*<version>\(.*\)<\/version>.*/\1/' \
BASE=$(grep -m1 '^version=' gradle.properties \
| cut -d= -f2 \
| sed 's/[-.]nightly//')
GIT_PKGVER="${BASE}.r$(git rev-list --count HEAD).g$(git rev-parse --short HEAD)"
+3 -3
View File
@@ -107,9 +107,9 @@ jobs:
sed -i "s/^pkgrel=.*/pkgrel=1/" packaging/aur/kst4contest-bin/PKGBUILD
sed -i "s/^pkgrel=.*/pkgrel=1/" packaging/aur/kst4contest/PKGBUILD
# kst4contest-git: Basis aus pom.xml, Suffix aus git
BASE_VER=$(grep -m1 '<version>' pom.xml \
| sed 's/.*<version>\(.*\)<\/version>.*/\1/' | sed 's/[-.]nightly//')
# kst4contest-git: Basis aus gradle.properties, Suffix aus git
BASE_VER=$(grep -m1 '^version=' gradle.properties \
| cut -d= -f2 | sed 's/[-.]nightly//')
GIT_PKGVER="${BASE_VER}.r$(git rev-list --count HEAD).g$(git rev-parse --short HEAD)"
sed -i "s/^pkgver=.*/pkgver=${GIT_PKGVER}/" packaging/aur/kst4contest-git/PKGBUILD
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
VERSION="${{ github.ref_name }}"
else
VERSION="$(grep -m1 '<version>' pom.xml | sed 's/.*<version>\(.*\)<\/version>.*/\1/')-${GITHUB_SHA::7}"
VERSION="$(grep -m1 '^version=' gradle.properties | cut -d= -f2)-${GITHUB_SHA::7}"
fi
echo "DOC_VERSION=$VERSION" >> "$GITHUB_ENV"
+56 -60
View File
@@ -5,12 +5,15 @@ on:
branches:
- main
paths:
- "src/**"
- "packaging/icons/**"
- "packaging/macos/**"
- "pom.xml"
- "mvnw"
- "mvnw.cmd"
- "core/**"
- "app-desktop/**"
- "*.gradle.kts"
- "gradle.properties"
- "gradle/**"
- "gradlew"
- "gradlew.bat"
- ".github/workflows/nightly-artifacts.yml"
workflow_dispatch:
@@ -33,8 +36,7 @@ jobs:
- name: Resolve nightly version info
shell: pwsh
run: |
$xml = [xml](Get-Content pom.xml)
$version = $xml.project.version
$version = (Select-String -Path gradle.properties -Pattern '^version=').Line.Split('=')[1]
$shortSha = "${{ github.sha }}".Substring(0, 7)
Add-Content -Path $env:GITHUB_ENV -Value "VERSION=$version"
Add-Content -Path $env:GITHUB_ENV -Value "SHORT_SHA=$shortSha"
@@ -53,27 +55,26 @@ jobs:
- name: Build JAR and copy runtime dependencies
shell: pwsh
run: |
.\mvnw.cmd -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
.\gradlew.bat -S :app-desktop:collectRuntime
$jar = Get-ChildItem -Path target -Filter 'praktiKST-*.jar' | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $jar) {
throw "No project JAR produced"
}
Copy-Item $jar.FullName target/dist-libs/app.jar
Copy-Item $jar.FullName app-desktop/build/dist-libs/app.jar
- name: Build app-image with jpackage
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path dist | Out-Null
$addModules = & java packaging/AddModules.java
$addModules = (Select-String -Path gradle.properties -Pattern '^jpackageAddModules=').Line.Split('=')[1]
if ($LASTEXITCODE -ne 0) { throw "Failed to resolve --add-modules from module-info.java" }
jpackage `
--type app-image `
--name praktiKST `
--icon packaging/icons/kst4contest.ico `
--input target/dist-libs `
--input app-desktop/build/dist-libs `
--main-jar app.jar `
--main-class kst4contest.view.Kst4ContestApplication `
--module-path target/dist-libs `
--main-class kst4contest.view.Main `
--add-modules $addModules `
--dest dist
@@ -102,7 +103,7 @@ jobs:
- name: Resolve nightly version info
run: |
VERSION=$(grep -m1 '<version>' pom.xml | sed 's/.*<version>\(.*\)<\/version>.*/\1/')
VERSION=$(grep -m1 '^version=' gradle.properties | cut -d= -f2)
SHORT_SHA="${GITHUB_SHA::7}"
echo "ASSET_BASENAME=KST4Contest-${VERSION}-${SHORT_SHA}" >> "$GITHUB_ENV"
@@ -112,31 +113,30 @@ jobs:
distribution: temurin
java-version: "21"
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Build JAR and copy runtime dependencies
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
- name: Build app-image with jpackage
run: |
mkdir -p dist
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type app-image \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--dest dist
- name: Create AppDir metadata
run: |
mkdir -p target
rm -rf target/KST4Contest.AppDir
cp -a dist/KST4Contest target/KST4Contest.AppDir
@@ -163,6 +163,7 @@ jobs:
- name: Build AppImage
run: |
mkdir -p target
wget -q -O target/appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x target/appimagetool.AppImage
APPIMAGE_EXTRACT_AND_RUN=1 ARCH=x86_64 target/appimagetool.AppImage target/KST4Contest.AppDir "dist/${ASSET_BASENAME}-linux-x86_64.AppImage"
@@ -184,7 +185,7 @@ jobs:
- name: Resolve nightly version info
run: |
VERSION=$(grep -m1 '<version>' pom.xml | sed 's/.*<version>\(.*\)<\/version>.*/\1/')
VERSION=$(grep -m1 '^version=' gradle.properties | cut -d= -f2)
SHORT_SHA="${GITHUB_SHA::7}"
echo "ASSET_BASENAME=KST4Contest-${VERSION}-${SHORT_SHA}" >> "$GITHUB_ENV"
@@ -199,26 +200,24 @@ jobs:
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends fakeroot
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Build JAR and copy runtime dependencies
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
- name: Build Debian package
run: |
mkdir -p dist
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type deb \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--linux-package-deps "libgstreamer1.0-0,libgstreamer-plugins-base1.0-0,gstreamer1.0-plugins-good" \
--dest dist
@@ -245,7 +244,7 @@ jobs:
- name: Resolve nightly version info
run: |
VERSION=$(grep -m1 '<version>' pom.xml | sed 's/.*<version>\(.*\)<\/version>.*/\1/')
VERSION=$(grep -m1 '^version=' gradle.properties | cut -d= -f2)
SHORT_SHA="${GITHUB_SHA::7}"
echo "ASSET_BASENAME=KST4Contest-${VERSION}-${SHORT_SHA}" >> "$GITHUB_ENV"
@@ -260,26 +259,24 @@ jobs:
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends rpm
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Build JAR and copy runtime dependencies
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
- name: Build Fedora package
run: |
mkdir -p dist
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type rpm \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--linux-package-deps "gstreamer1,gstreamer1-plugins-base,gstreamer1-plugins-good" \
--dest dist
@@ -306,7 +303,7 @@ jobs:
- name: Resolve nightly version info
run: |
VERSION=$(grep -m1 '<version>' pom.xml | sed 's/.*<version>\(.*\)<\/version>.*/\1/')
VERSION=$(grep -m1 '^version=' gradle.properties | cut -d= -f2)
SHORT_SHA="${GITHUB_SHA::7}"
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "SHORT_SHA=$SHORT_SHA" >> "$GITHUB_ENV"
@@ -323,31 +320,30 @@ jobs:
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends zstd
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Build JAR and copy runtime dependencies
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
- name: Build app-image with jpackage
run: |
mkdir -p dist
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type app-image \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--dest dist
- name: Build Arch Linux package artifact
run: |
mkdir -p target
ARCH=$(uname -m)
PKGVER=$(printf '%s' "${VERSION}-${SHORT_SHA}" | sed 's/[^[:alnum:].+_]/_/g')
PKGROOT="target/archpkg"
@@ -427,7 +423,7 @@ jobs:
- name: Resolve nightly version info
run: |
VERSION=$(grep -m1 '<version>' pom.xml | sed 's/.*<version>\(.*\)<\/version>.*/\1/')
VERSION=$(grep -m1 '^version=' gradle.properties | cut -d= -f2)
SHORT_SHA="${GITHUB_SHA::7}"
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "SHORT_SHA=$SHORT_SHA" >> "$GITHUB_ENV"
@@ -439,8 +435,8 @@ jobs:
distribution: temurin
java-version: "21"
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Install Flatpak tooling
run: |
@@ -451,23 +447,22 @@ jobs:
- name: Build app-image with jpackage
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
mkdir -p target/flatpak-src
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type app-image \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--dest target/flatpak-src
- name: Create Flatpak manifest
run: |
mkdir -p target
mkdir -p dist
cat > target/de.x08.KST4Contest.yml << 'EOF'
app-id: de.x08.KST4Contest
@@ -515,6 +510,7 @@ jobs:
- name: Build Flatpak repo (nightly)
run: |
mkdir -p target
flatpak-builder --force-clean target/flatpak-build target/de.x08.KST4Contest.yml
flatpak build-export --gpg-sign="$FLATPAK_GPG_KEY_ID" target/flatpak-repo target/flatpak-build nightly
flatpak build-update-repo --gpg-sign="$FLATPAK_GPG_KEY_ID" target/flatpak-repo
@@ -622,7 +618,7 @@ jobs:
- name: Resolve nightly version info
run: |
VERSION=$(grep -m1 '<version>' pom.xml | sed 's/.*<version>\(.*\)<\/version>.*/\1/')
VERSION=$(grep -m1 '^version=' gradle.properties | cut -d= -f2)
SHORT_SHA="${GITHUB_SHA::7}"
ARCH=$(uname -m)
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
@@ -636,8 +632,8 @@ jobs:
distribution: temurin
java-version: "21"
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Import signing certificate
env:
+3 -6
View File
@@ -24,11 +24,8 @@ jobs:
distribution: temurin
java-version: "21"
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Verify packaging module list matches module-info.java
run: java packaging/AddModules.java --verify-pom
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Compile
run: ./mvnw -B -DskipTests compile
run: ./gradlew -S compileJava compileTestJava
+42 -48
View File
@@ -37,27 +37,26 @@ jobs:
- name: Build JAR and copy runtime dependencies
shell: pwsh
run: |
.\mvnw.cmd -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
.\gradlew.bat -S :app-desktop:collectRuntime
$jar = Get-ChildItem -Path target -Filter 'praktiKST-*.jar' | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $jar) {
throw "No project JAR produced"
}
Copy-Item $jar.FullName target/dist-libs/app.jar
Copy-Item $jar.FullName app-desktop/build/dist-libs/app.jar
- name: Build app-image with jpackage
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path dist | Out-Null
$addModules = & java packaging/AddModules.java
$addModules = (Select-String -Path gradle.properties -Pattern '^jpackageAddModules=').Line.Split('=')[1]
if ($LASTEXITCODE -ne 0) { throw "Failed to resolve --add-modules from module-info.java" }
jpackage `
--type app-image `
--name praktiKST `
--icon packaging/icons/kst4contest.ico `
--input target/dist-libs `
--input app-desktop/build/dist-libs `
--main-jar app.jar `
--main-class kst4contest.view.Kst4ContestApplication `
--module-path target/dist-libs `
--main-class kst4contest.view.Main `
--add-modules $addModules `
--dest dist
@@ -89,31 +88,30 @@ jobs:
distribution: temurin
java-version: "21"
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Build JAR and copy runtime dependencies
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
- name: Build app-image with jpackage
run: |
mkdir -p dist
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type app-image \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--dest dist
- name: Create AppDir metadata
run: |
mkdir -p target
rm -rf target/KST4Contest.AppDir
cp -a dist/KST4Contest target/KST4Contest.AppDir
@@ -140,6 +138,7 @@ jobs:
- name: Build AppImage
run: |
mkdir -p target
wget -q -O target/appimagetool.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x target/appimagetool.AppImage
APPIMAGE_EXTRACT_AND_RUN=1 ARCH=x86_64 target/appimagetool.AppImage target/KST4Contest.AppDir dist/KST4Contest-${{ github.ref_name }}-linux-x86_64.AppImage
@@ -169,26 +168,24 @@ jobs:
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends fakeroot
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Build JAR and copy runtime dependencies
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
- name: Build Debian package
run: |
mkdir -p dist
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type deb \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--linux-package-deps "libgstreamer1.0-0,libgstreamer-plugins-base1.0-0,gstreamer1.0-plugins-good" \
--dest dist
@@ -223,26 +220,24 @@ jobs:
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends rpm
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Build JAR and copy runtime dependencies
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
- name: Build Fedora package
run: |
mkdir -p dist
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type rpm \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--linux-package-deps "gstreamer1,gstreamer1-plugins-base,gstreamer1-plugins-good" \
--dest dist
@@ -277,31 +272,30 @@ jobs:
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends zstd
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Build JAR and copy runtime dependencies
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
- name: Build app-image with jpackage
run: |
mkdir -p dist
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type app-image \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--dest dist
- name: Build Arch Linux package artifact
run: |
mkdir -p target
ARCH=$(uname -m)
PKGVER=$(printf '%s' "${{ github.ref_name }}" | sed 's/[^[:alnum:].+_]/_/g')
PKGROOT="target/archpkg"
@@ -384,8 +378,8 @@ jobs:
distribution: temurin
java-version: "21"
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Install Flatpak tooling
run: |
@@ -396,23 +390,22 @@ jobs:
- name: Build app-image with jpackage
run: |
./mvnw -B -DskipTests package dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=target/dist-libs
cp "$(ls -t target/praktiKST-*.jar | head -n 1)" target/dist-libs/app.jar
./gradlew -S :app-desktop:collectRuntime
mkdir -p target/flatpak-src
ADD_MODULES="$(java packaging/AddModules.java)"
ADD_MODULES="$(grep -m1 '^jpackageAddModules=' gradle.properties | cut -d= -f2)"
jpackage \
--type app-image \
--name KST4Contest \
--icon packaging/icons/kst4contest.png \
--input target/dist-libs \
--input app-desktop/build/dist-libs \
--main-jar app.jar \
--main-class kst4contest.view.Kst4ContestApplication \
--module-path target/dist-libs \
--main-class kst4contest.view.Main \
--add-modules "$ADD_MODULES" \
--dest target/flatpak-src
- name: Create Flatpak manifest
run: |
mkdir -p target
mkdir -p dist
cat > target/de.x08.KST4Contest.yml << 'EOF'
app-id: de.x08.KST4Contest
@@ -470,6 +463,7 @@ jobs:
- name: Build Flatpak repo
run: |
mkdir -p target
flatpak-builder --force-clean target/flatpak-build target/de.x08.KST4Contest.yml
flatpak build-export --gpg-sign="$FLATPAK_GPG_KEY_ID" target/flatpak-repo target/flatpak-build "$OSTREE_BRANCH"
flatpak build-update-repo --gpg-sign="$FLATPAK_GPG_KEY_ID" target/flatpak-repo
@@ -520,8 +514,8 @@ jobs:
distribution: temurin
java-version: "21"
- name: Ensure mvnw is executable
run: chmod +x mvnw
- name: Ensure gradlew is executable
run: chmod +x gradlew
- name: Import signing certificate
env:
@@ -0,0 +1,170 @@
name: TEST publish-version-xml (local only)
on:
workflow_dispatch:
jobs:
publish-version-xml:
name: Publish Version Info XML via WebDAV
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.1.7
- name: Generate kst4ContestVersionInfo.xml from GitHub releases + history
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 << 'PYEOF'
import subprocess, json
from xml.etree.ElementTree import Element, SubElement, ElementTree, parse, indent
def safe_version(tag):
cleaned = tag.lstrip('v')
try:
float(cleaned)
except ValueError:
pass
return cleaned
import os, urllib.request
REPO = 'praktimarc/kst4contest'
token = os.environ['GH_TOKEN']
def gh_get(path):
url = f'https://api.github.com/repos/{REPO}{path}'
req = urllib.request.Request(url, headers={
'Authorization': f'Bearer {token}',
'Accept': 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
})
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())
def get_issues_for_release(since, until):
"""Return closed issues in (since, until] categorised by label."""
LABEL_MAP = {'enhancement': 'added', 'bug': 'fixed'}
buckets = {'added': [], 'fixed': []}
page = 1
while True:
items = gh_get(f'/issues?state=closed&per_page=100&page={page}')
if not items:
break
for issue in items:
if 'pull_request' in issue:
continue
closed = issue.get('closed_at') or ''
if not (since < closed <= until):
continue
labels = [l['name'] for l in issue['labels']]
bucket = next((LABEL_MAP[l] for l in labels if l in LABEL_MAP), None)
if bucket:
buckets[bucket].append(f'- {issue["title"]}')
if len(items) < 100:
break
page += 1
return {k: '\n'.join(v) for k, v in buckets.items()}
def clean_body(body):
"""Strip GitHub markdown noise, keep plain text."""
import re
lines = []
for line in (body or '').splitlines():
if re.match(r'^#{1,3}\s', line):
continue
if line.strip().startswith('**Full Changelog**'):
continue
line = re.sub(r'\s+by @\S+ in https://\S+', '', line)
line = re.sub(r'^\* ', '- ', line)
if line.strip():
lines.append(line)
return re.sub(r'\n{3,}', '\n\n', '\n'.join(lines)).strip()
# --- Fetch all releases ---
raw = gh_get('/releases?per_page=100')
gh_releases = [
{
'tagName': r['tag_name'],
'publishedAt': r['published_at'] or '',
'name': r['name'] or r['tag_name'],
'body': r['body'] or '',
'isPrerelease': r['prerelease'],
}
for r in raw
]
# --- Simulate a stable 1.41 release for testing ---
gh_releases.insert(0, {
'tagName': '1.41',
'publishedAt': '2026-06-30T10:00:00Z',
'name': 'Release 1.41',
'body': 'Release 1.41',
'isPrerelease': False,
})
gh_releases.sort(key=lambda r: r['publishedAt'], reverse=True)
stable_releases = [r for r in gh_releases if not r['isPrerelease']]
stable = stable_releases[0] if stable_releases else None
gh_versions = {safe_version(r['tagName']) for r in stable_releases}
history_tree = parse('.github/version-history.xml')
history_entries = history_tree.getroot().findall('changeLog')
root = Element('praktiKST')
lv = SubElement(root, 'latestVersion')
latest_tag = stable['tagName'] if stable else 'unknown'
SubElement(lv, 'versionNumber').text = safe_version(latest_tag)
SubElement(lv, 'adminMessage').text = ''
latest_issues = get_issues_for_release('1970-01-01T00:00:00Z', stable['publishedAt']) if stable else {}
SubElement(lv, 'majorChanges').text = latest_issues.get('added', '')[:300]
win_filename = f'praktiKST-{latest_tag}-windows-x64.zip'
SubElement(lv, 'latestVersionPathOnWebserver').text = (
f'https://github.com/{REPO}/releases/download/{latest_tag}/{win_filename}'
)
nuf = SubElement(root, 'needUpdateSinceLastVersion')
SubElement(nuf, 'filename').text = win_filename
# Build changelog for each stable release using closed issues in its window
for i, rel in enumerate(stable_releases):
until = rel['publishedAt']
since = stable_releases[i + 1]['publishedAt'] if i + 1 < len(stable_releases) else '1970-01-01T00:00:00Z'
s = get_issues_for_release(since, until)
cl = SubElement(root, 'changeLog')
SubElement(cl, 'changedVersionNumber').text = safe_version(rel['tagName'])
SubElement(cl, 'date').text = until[:10]
SubElement(cl, 'description').text = rel['name']
SubElement(cl, 'added').text = s['added']
SubElement(cl, 'changed').text = clean_body(rel['body'])
SubElement(cl, 'fixed').text = s['fixed']
SubElement(cl, 'removed').text = ''
for entry in history_entries:
ver_node = entry.find('changedVersionNumber')
if ver_node is not None and ver_node.text and ver_node.text.strip() in gh_versions:
continue
root.append(entry)
indent(root, space=' ')
tree = ElementTree(root)
with open('kst4ContestVersionInfo.xml', 'w', encoding='utf-8') as f:
f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n')
tree.write(f, encoding='unicode', xml_declaration=False)
print('Generated kst4ContestVersionInfo.xml')
PYEOF
- name: Show generated XML (first 40 lines)
run: head -40 kst4ContestVersionInfo.xml
- name: Upload to WebDAV
run: |
curl --fail --silent --show-error \
--user "${{ secrets.WEBDAV_USER }}:${{ secrets.WEBDAV_PASSWORD }}" \
--upload-file kst4ContestVersionInfo.xml \
"${{ secrets.WEBDAV_URL }}/kst4ContestVersionInfo.xml"