Index: /trunk/.github/workflows/ant-test.yml
===================================================================
--- /trunk/.github/workflows/ant-test.yml	(revision 19029)
+++ /trunk/.github/workflows/ant-test.yml	(revision 19030)
@@ -20,5 +20,5 @@
       matrix:
         # test against latest update of each major Java version, as well as specific updates of LTS versions:
-        java: [8, 11, 17, 21, 22-ea]
+        java: [8, 11, 17, 21, 22]
         os: [ubuntu-latest, macos-latest, windows-latest]
     name: Java ${{ matrix.java }} on ${{ matrix.os }}
@@ -44,5 +44,5 @@
 
       - name: Install Ant
-        uses: JOSM/JOSMPluginAction/actions/setup-ant@v1
+        uses: JOSM/JOSMPluginAction/actions/setup-ant@v2
 
       - name: Test with Ant
Index: /trunk/.github/workflows/ant.yml
===================================================================
--- /trunk/.github/workflows/ant.yml	(revision 19029)
+++ /trunk/.github/workflows/ant.yml	(revision 19030)
@@ -1,5 +1,4 @@
 name: Java CI Build
 env:
-  junit_platform_version: '1.9.3'
   JAVAFX_VERSION: '17.0.7'
 on:
@@ -60,4 +59,5 @@
           draft: false
           prerelease: ${{ env.josm_prerelease }}
+        if: github.ref == 'refs/heads/master'
 
   build:
@@ -70,6 +70,6 @@
       matrix:
         # test against latest update of each major Java version, as well as specific updates of LTS versions:
-        java: [8, 11, 17, 21, 22-ea]
-        os: [ubuntu-latest, macos-latest, windows-latest]
+        java: [8, 11, 17, 21, 22]
+        os: [ubuntu-latest, macos-14, windows-latest]
     name: Java ${{ matrix.java }} on ${{ matrix.os }}
     steps:
@@ -93,6 +93,14 @@
           java-version: ${{ matrix.java }}
 
+      - name: Setup x64 Java (Mac) ${{ matrix.java }}
+        if: ${{ runner.os == 'macos' && runner.arch == 'ARM64' && always() }}
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: ${{ matrix.java }}
+          architecture: x64
+
       - name: Install Ant
-        uses: JOSM/JOSMPluginAction/actions/setup-ant@v1
+        uses: JOSM/JOSMPluginAction/actions/setup-ant@v2
 
       - name: Optimise images
@@ -114,6 +122,8 @@
         # Calls ant with -Dreleasebuild=true if we're a 'tested' build
         run: |
-          export SIGN_KEYSTORE=certificate.p12
-          echo "$SIGN_CERT" | base64 --decode > $SIGN_KEYSTORE
+          if [ ! -z "${SIGN_CERT}" ]; then
+            export SIGN_KEYSTORE=certificate.p12
+            echo "$SIGN_CERT" | base64 --decode > $SIGN_KEYSTORE
+          fi
           if [ "${{ needs.createrelease.outputs.josm_prerelease }}" == "true" ]; then
             ANT="ant"
@@ -122,8 +132,8 @@
           fi
           $ANT dist
-          rm $SIGN_KEYSTORE
+          if [ ! -z "${SIGN_KEYSTORE}" ]; then rm $SIGN_KEYSTORE; fi
 
       - name: Upload jar
-        if: ${{ always() }}
+        if: ${{ always() && needs.createrelease.outputs.upload_url }}
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -144,10 +154,20 @@
           APPLE_ID_TEAM: ${{ secrets.APPLE_ID_TEAM }}
         run: |
-          if [ ! -f tools/openjfx-${JAVAFX_VERSION}_${{ runner.os }}-jmods.zip ]; then
-            curl -o tools/openjfx-${JAVAFX_VERSION}_${{ runner.os }}-jmods.zip https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-x64_bin-jmods.zip
-          fi
-          unzip tools/openjfx-${JAVAFX_VERSION}_${{ runner.os }}-jmods.zip
-          mv javafx-jmods-${JAVAFX_VERSION}/*.jmod $JAVA_HOME/jmods/
-          ./native/macosx/macos-jpackage.sh ${{ needs.createrelease.outputs.josm_revision }}
+          function setup_openjfx() {
+            if [ ! -f tools/openjfx-${JAVAFX_VERSION}_${{ runner.os }}_${2}-jmods.zip ]; then
+              curl -o tools/openjfx-${JAVAFX_VERSION}_${{ runner.os }}_${2}-jmods.zip https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${1}_bin-jmods.zip
+            fi
+            unzip tools/openjfx-${JAVAFX_VERSION}_${{ runner.os }}_${2}-jmods.zip
+            mv javafx-jmods-${JAVAFX_VERSION}/*.jmod $JAVA_HOME/jmods/
+          }
+
+          if [ ${{ runner.arch }} == "ARM64" ]; then
+            JAVA_HOME="${JAVA_HOME_${{ matrix.java }}_ARM64}" PATH="${JAVA_HOME_${{ matrix.java }}_ARM64}/bin:${PATH}" setup_openjfx aarch64 ARM64
+            JAVA_HOME="${JAVA_HOME_${{ matrix.java }}_X64}" PATH="${JAVA_HOME_${{ matrix.java }}_X64}/bin:${PATH}" setup_openjfx x64 X64
+            JAVA_HOME="${JAVA_HOME_${{ matrix.java }}_ARM64}" ./native/macosx/macos-jpackage.sh ${{ needs.createrelease.outputs.josm_revision }} "${JAVA_HOME_${{ matrix.java }}_X64}"
+          else
+            setup_openjfx x64 X64
+            ./native/macosx/macos-jpackage.sh ${{ needs.createrelease.outputs.josm_revision }}
+          fi
 
       - name: Setup Windows PATH
@@ -173,5 +193,5 @@
 
       - name: Upload macOS app
-        if: ${{ runner.os == 'macos' && matrix.java != '8' && matrix.java != '11' && always() }}
+        if: ${{ runner.os == 'macos' && matrix.java != '8' && matrix.java != '11' && always() && needs.createrelease.outputs.upload_url }}
         uses: actions/upload-release-asset@v1
         env:
@@ -183,6 +203,28 @@
           asset_content_type: application/zip
 
+      - name: Upload macOS app (x64)
+        if: ${{ runner.os == 'macos' && matrix.java != '8' && matrix.java != '11' && always() && runner.arch == 'ARM64' && needs.createrelease.outputs.upload_url }}
+        uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ needs.createrelease.outputs.upload_url }} # This pulls from the CREATE RELEASE job above, referencing its ID to get its outputs object, which include a `upload_url`.
+          asset_path: app/JOSM_${{ matrix.java }}_x86_64.zip
+          asset_name: JOSM-${{ runner.os}}-java${{ matrix.java }}-${{ needs.createrelease.outputs.josm_revision }}-x64.zip
+          asset_content_type: application/zip
+
+      - name: Upload macOS app (aarch64)
+        if: ${{ runner.os == 'macos' && matrix.java != '8' && matrix.java != '11' && always() && runner.arch == 'ARM64' && needs.createrelease.outputs.upload_url }}
+        uses: actions/upload-release-asset@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          upload_url: ${{ needs.createrelease.outputs.upload_url }} # This pulls from the CREATE RELEASE job above, referencing its ID to get its outputs object, which include a `upload_url`.
+          asset_path: app/JOSM_${{ matrix.java }}_arm64.zip
+          asset_name: JOSM-${{ runner.os}}-java${{ matrix.java }}-${{ needs.createrelease.outputs.josm_revision }}-aarch64.zip
+          asset_content_type: application/zip
+
       - name: Upload Windows Installer executable
-        if: ${{ runner.os == 'windows' && matrix.java != '8' && matrix.java != '11' && always() }}
+        if: ${{ runner.os == 'windows' && matrix.java != '8' && matrix.java != '11' && always() && needs.createrelease.outputs.upload_url }}
         uses: actions/upload-release-asset@v1
         env:
@@ -195,5 +237,5 @@
 
       - name: Upload Windows Installer package
-        if: ${{ runner.os == 'windows' && matrix.java != '8' && matrix.java != '11' && always() }}
+        if: ${{ runner.os == 'windows' && matrix.java != '8' && matrix.java != '11' && always() && needs.createrelease.outputs.upload_url }}
         uses: actions/upload-release-asset@v1
         env:
Index: /trunk/native/macosx/macos-jpackage.sh
===================================================================
--- /trunk/native/macosx/macos-jpackage.sh	(revision 19029)
+++ /trunk/native/macosx/macos-jpackage.sh	(revision 19030)
@@ -16,5 +16,5 @@
 if [ -z "${1-}" ]
 then
-    echo "Usage: $0 josm_revision"
+    echo "Usage: $0 josm_revision [other_arch_jdk]"
     exit 1
 fi
@@ -53,45 +53,101 @@
 set -u
 
-echo "Building and signing app"
-# We specifically need the options to not be quoted -- we _want_ the word splitting.
-# shellcheck disable=SC2086
-jpackage $JPACKAGEOPTIONS -n "JOSM" --input dist --main-jar josm-custom.jar \
-    --main-class org.openstreetmap.josm.gui.MainApplication \
-    --icon ./native/macosx/JOSM.icns --type app-image --dest app \
-    --java-options "--add-modules java.scripting,java.sql,javafx.controls,javafx.media,javafx.swing,javafx.web" \
-    --java-options "--add-exports=java.base/sun.security.action=ALL-UNNAMED" \
-    --java-options "--add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED" \
-    --java-options "--add-exports=java.desktop/com.sun.imageio.plugins.jpeg=ALL-UNNAMED" \
-    --java-options "--add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED" \
-    --java-options "--add-opens=java.base/java.lang=ALL-UNNAMED" \
-    --java-options "--add-opens=java.base/java.nio=ALL-UNNAMED" \
-    --java-options "--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED" \
-    --java-options "--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED" \
-    --java-options "--add-opens=java.desktop/javax.imageio.spi=ALL-UNNAMED" \
-    --java-options "--add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED" \
-    --java-options "--add-opens=java.prefs/java.util.prefs=ALL-UNNAMED" \
-    --app-version "$1" \
-    --copyright "JOSM, and all its integral parts, are released under the GNU General Public License v2 or later" \
-    --vendor "JOSM" \
-    --mac-package-identifier de.openstreetmap.josm \
-    --mac-package-signing-prefix de.openstreetmap.josm \
-    --file-associations native/file-associations/bz2.properties \
-    --file-associations native/file-associations/geojson.properties \
-    --file-associations native/file-associations/gpx.properties \
-    --file-associations native/file-associations/gz.properties \
-    --file-associations native/file-associations/jos.properties \
-    --file-associations native/file-associations/joz.properties \
-    --file-associations native/file-associations/osm.properties \
-    --file-associations native/file-associations/xz.properties \
-    --file-associations native/file-associations/zip.properties \
-    --add-modules java.compiler,java.base,java.datatransfer,java.desktop,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.sql,java.transaction.xa,java.xml,jdk.crypto.ec,jdk.jfr,jdk.jsobject,jdk.unsupported,jdk.unsupported.desktop,jdk.xml.dom,javafx.controls,javafx.media,javafx.swing,javafx.web
+function do_jpackage() {
+  echo "Building app (${JAVA_HOME})"
+  # We specifically need the options to not be quoted -- we _want_ the word splitting.
+  # shellcheck disable=SC2086
+  "${JAVA_HOME}/bin/jpackage" $JPACKAGEOPTIONS -n "JOSM" --input dist --main-jar josm-custom.jar \
+      --main-class org.openstreetmap.josm.gui.MainApplication \
+      --icon ./native/macosx/JOSM.icns --type app-image --dest app \
+      --java-options "--add-modules java.scripting,java.sql,javafx.controls,javafx.media,javafx.swing,javafx.web" \
+      --java-options "--add-exports=java.base/sun.security.action=ALL-UNNAMED" \
+      --java-options "--add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED" \
+      --java-options "--add-exports=java.desktop/com.sun.imageio.plugins.jpeg=ALL-UNNAMED" \
+      --java-options "--add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED" \
+      --java-options "--add-opens=java.base/java.lang=ALL-UNNAMED" \
+      --java-options "--add-opens=java.base/java.nio=ALL-UNNAMED" \
+      --java-options "--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED" \
+      --java-options "--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED" \
+      --java-options "--add-opens=java.desktop/javax.imageio.spi=ALL-UNNAMED" \
+      --java-options "--add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED" \
+      --java-options "--add-opens=java.prefs/java.util.prefs=ALL-UNNAMED" \
+      --app-version "$1" \
+      --copyright "JOSM, and all its integral parts, are released under the GNU General Public License v2 or later" \
+      --vendor "JOSM" \
+      --mac-package-identifier de.openstreetmap.josm \
+      --mac-package-signing-prefix de.openstreetmap.josm \
+      --file-associations native/file-associations/bz2.properties \
+      --file-associations native/file-associations/geojson.properties \
+      --file-associations native/file-associations/gpx.properties \
+      --file-associations native/file-associations/gz.properties \
+      --file-associations native/file-associations/jos.properties \
+      --file-associations native/file-associations/joz.properties \
+      --file-associations native/file-associations/osm.properties \
+      --file-associations native/file-associations/xz.properties \
+      --file-associations native/file-associations/zip.properties \
+      --add-modules java.compiler,java.base,java.datatransfer,java.desktop,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.sql,java.transaction.xa,java.xml,jdk.crypto.ec,jdk.jfr,jdk.jsobject,jdk.unsupported,jdk.unsupported.desktop,jdk.xml.dom,javafx.controls,javafx.media,javafx.swing,javafx.web
+  echo "Building done (${JAVA_HOME})."
+}
+function do_signapp() {
+  echo "Compressing app (${1})"
+  ditto -c -k --zlibCompressionLevel 9 --keepParent "app/${1}.app" "app/${1}.zip"
+  if $SIGNAPP; then
+      echo "Signing app (${1})"
+      echo "Preparing for notarization"
+      echo "Uploading to Apple"
+      xcrun notarytool submit --apple-id "$APPLE_ID" --password "$APPLE_ID_PW" --team-id "$APPLE_ID_TEAM" --wait "app/${1}.zip"
+  fi
+}
 
-echo "Building done."
+function merge() {
+  if [ "$(command -v lipo)" ]; then
+    lipo -create -output "${1}" "${2}" "${3}"
+  elif [ "$(command -v llvm-lipo-15)" ]; then
+    llvm-lipo-15 -create -output "${1}" "${2}" "${3}"
+  fi
+}
 
-if $SIGNAPP; then
-    echo "Preparing for notarization"
-    ditto -c -k --zlibCompressionLevel 9 --keepParent app/JOSM.app app/JOSM.zip
+function copy() {
+  # Trim the root path
+  FILE="${1#*/}"
+  if [ ! -e "${2}/${FILE}" ]; then
+    # Only make directories if we aren't looking at the root files
+    if [[ "${FILE}" == *"/"* ]]; then mkdir -p "${2}/${FILE%/*}"; fi
+    if file "${1}" | grep -q 'Mach-O' ; then
+      merge "${2}/${FILE}" "${3}/${FILE}" "${4}/${FILE}"
+      if file "${1}" | grep -q 'executable'; then
+        chmod 755 "${2}/${FILE}"
+      fi
+    else
+      cp -a "${1}" "${2}/${FILE}"
+    fi
+  fi
+}
 
-    echo "Uploading to Apple"
-    xcrun notarytool submit --apple-id "$APPLE_ID" --password "$APPLE_ID_PW" --team-id "$APPLE_ID_TEAM" --wait app/JOSM.zip
+function directory_iterate() {
+  while IFS= read -r -d '' file
+  do
+    copy "${file}" "${2}" "${3}" "${4}" &
+  done <   <(find "${1}" -type f,l -print0)
+  wait
+}
+
+do_jpackage "${1}"
+if [ -n "${2}" ]; then
+  function get_name() {
+    echo "$("${JAVA_HOME}/bin/java" --version | head -n1 | awk '{print $2}' | awk -F'.' '{print $1}')_$(file "${JAVA_HOME}/bin/java" | awk -F' executable ' '{print $2}')"
+  }
+  first="$(get_name)"
+  JAVA_HOME="${2}" second="$(get_name)"
+  mv app/JOSM.app "app/JOSM_${first}.app"
+  JAVA_HOME="${2}" do_jpackage "${1}"
+  mv app/JOSM.app "app/JOSM_${second}.app"
+  mkdir app/JOSM.app
+  (cd app
+  directory_iterate "JOSM_${first}.app" "JOSM.app" "JOSM_${first}.app" "JOSM_${second}.app"
+  directory_iterate "JOSM_${second}.app" "JOSM.app" "JOSM_${first}.app" "JOSM_${second}.app"
+  )
+  do_signapp "JOSM_${first}"
+  do_signapp "JOSM_${second}"
 fi
+do_signapp JOSM
