Пакування та розповсюдження JavaFX-додатків
Пакування та розповсюдження JavaFX-додатків
Вступ: Від середовища розробника до рук користувача
Уявіть ситуацію, з якою стикається кожен розробник після завершення основного етапу розробки. Додаток запускається бездоганно з командного рядка, усі тести проходять успішно, інтерфейс виглядає саме так, як задумано. Настає момент, коли потрібно передати результат роботи замовнику або кінцевому користувачеві. І тут виникає закономірне питання: як запустити цей додаток на комп'ютері, де не встановлено ні JDK, ні IntelliJ IDEA, ні Maven?
Це питання є ключовим у промисловій розробці програмного забезпечення. Процес перетворення вихідного коду на готовий до розповсюдження продукт називається релізною інженерією (release engineering). Для Java-екосистеми цей процес має свою специфіку, зумовлену особливостями платформи JVM.
Проблема класичного JAR. Стандартний JAR-файл (Java ARchive), який генерують Maven або Gradle, містить лише скомпільований байт-код вашого проєкту. Він не включає ні сторонні бібліотеки (AtlantaFX, Google Guice, HikariCP), ні модулі JavaFX, ні саму JVM. Спроба запустити такий файл командою java -jar audiobook.jar на чистій машині завершиться помилкою ClassNotFoundException при першому ж зверненні до будь-якої зовнішньої залежності.
Еволюція підходів до розповсюдження. Протягом двох десятиліть розвитку Java-екосистеми сформувалося кілька підходів до вирішення цієї проблеми:
Fat JAR / Uber JAR
jlink: Custom Runtime Image
jpackage: Нативний інсталятор
У цій статті ми послідовно пройдемо всі три рівні пакування — від простого Fat JAR до повноцінного нативного інсталятора — та навчимося публікувати релізи у GitHub Releases для зручного розповсюдження серед користувачів.
Частина I. Fat JAR: Перший крок до самодостатнього артефакту
Що таке Fat JAR і чому він так називається
Fat JAR (також відомий як Uber JAR або Shaded JAR) — це JAR-архів, що містить не лише байт-код власного проєкту, а й байт-код усіх його транзитивних залежностей. Назва "fat" (жирний) є красномовною: якщо звичайний JAR платформи аудіокниг може важити 50–100 КБ, то Fat JAR з усіма бібліотеками (AtlantaFX, Guice, HikariCP, H2) зважитиме 30–50 МБ. Це, безумовно, збільшує розмір артефакту, проте забезпечує головне: єдину точку входу у вигляді одного файлу.
jpackage, розглянутий у наступному розділі. Fat JAR є зручним проміжним рішенням — для передачі між розробниками, розгортання на серверах, де JRE вже наявна, або як будівельний блок для jpackage.Налаштування Maven: плагін maven-shade-plugin
Maven не надає вбудованої підтримки Fat JAR. Для цього використовується maven-shade-plugin — один із найпопулярніших плагінів Maven-екосистеми, що дозволяє "затінити" (shade) всі залежності в єдиний архів.
<build>
<plugins>
<!-- Компілятор: вказуємо версію Java -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<!-- Fat JAR: упаковуємо всі залежності в один архів -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<!-- Головний клас: точка входу додатку -->
<transformers>
<transformer implementation=
"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>dev.kostyl.audiobook.Launcher</mainClass>
</transformer>
<!-- Об'єднання META-INF/services (важливо для Guice) -->
<transformer implementation=
"org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<!-- Виключення підписів з JAR-файлів залежностей -->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<!-- Назва вихідного файлу -->
<outputFile>
${project.build.directory}/${project.artifactId}-fat.jar
</outputFile>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Launcher замість App. JavaFX-додатки мають специфіку: якщо головний клас безпосередньо успадковується від javafx.application.Application, запуск через java -jar завершується помилкою java.lang.reflect.InvocationTargetException. Це пов'язано з тим, що JavaFX-модулі не завантажуються автоматично через classpath. Рішення — створити клас-обгортку Launcher, що не успадковується від Application, і саме його вказувати як mainClass.package dev.kostyl.audiobook;
/**
* Клас-обгортка для запуску JavaFX-додатку через Fat JAR.
* Не успадковується від Application — це критично для коректного
* завантаження класів через classpath (не module-path).
*/
public class Launcher {
public static void main(String[] args) {
AudiobookApp.main(args); // Делегуємо до реального Application
}
}
Тепер зберіть проєкт та перевірте результат:
# Збірка Fat JAR
mvn clean package
# Результат у директорії target/
ls -lh target/audiobook-app-fat.jar
# → -rw-r--r-- 1 user staff 42M audiobook-app-fat.jar
# Запуск Fat JAR
java -jar target/audiobook-app-fat.jar
Для Gradle аналогічна функціональність надається через завдання shadowJar із плагіна com.github.johnrengelman.shadow:
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.1.0'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
application {
mainClass = 'dev.kostyl.audiobook.Launcher'
}
shadowJar {
archiveClassifier.set('fat')
mergeServiceFiles() // Аналог ServicesResourceTransformer для Guice
}
# Збірка Fat JAR
./gradlew shadowJar
# Результат у директорії build/libs/
ls -lh build/libs/audiobook-app-fat.jar
# Запуск Fat JAR
java -jar build/libs/audiobook-app-fat.jar
Частина II. jpackage: Нативний інсталятор із вбудованою JVM
Концептуальна основа: що таке jpackage
jpackage — це інструмент командного рядка, що з'явився у складі JDK 16 як стабільна функція (до цього він існував у вигляді інкубаційного модуля з JDK 14). Його призначення — генерація нативних інсталяторів для desktop-додатків на Java, що не вимагають попередньої установки JVM на цільовій машині.
Принцип роботи jpackage полягає у наступному: інструмент бере ваш Fat JAR (або директорію з класами), додає до нього урізаний образ JRE (створений за допомогою jlink), іконку програми, метадані (назва, версія, виробник) та упаковує все це у формат, звичний для кожної операційної системи:
| Операційна система | Формат інсталятора | Результат |
|---|---|---|
| Windows | .msi або .exe (NSIS) | Стандартний Windows Installer |
| macOS | .dmg або .pkg | Образ диска або пакет macOS |
| Linux (Debian/Ubuntu) | .deb | Пакет Debian |
| Linux (RHEL/Fedora) | .rpm | Пакет RPM |
jpackage генерує інсталятор лише для тієї операційної системи, на якій він запускається. Щоб створити інсталятори для Windows, macOS та Linux одночасно, необхідно використовувати CI/CD-систему (наприклад, GitHub Actions) з окремими runner-агентами для кожної платформи. Саме цей підхід ми розглянемо у розділі про GitHub Actions.Передумови: що потрібно перед запуском jpackage
Перш ніж розпочати генерацію інсталятора, переконайтеся, що виконані такі умови:
Крок 1: JDK 17+ із підтримкою jpackage
jpackage є частиною JDK 14+. Перевірте наявність інструменту:
jpackage --version
# → 21.0.3
Якщо команда не знайдена — переконайтеся, що JAVA_HOME вказує на повний JDK (не JRE), та що директорія $JAVA_HOME/bin присутня у PATH.
Крок 2: Інструменти платформи (лише для Windows)
Для генерації .msi-інсталятора на Windows необхідно встановити WiX Toolset — набір інструментів для компіляції MSI-пакетів.
!IMPORTANT
jpackageвимагає виключно WiX Toolset версії 3.x (версії 4.x та 5.x не підтримуються утилітою автоматично). Тому необхідно встановлювати саме версію 3.
Для встановлення WiX Toolset v3 скористайтеся одним із пакетних менеджерів для Windows:
Для Scoop спочатку потрібно підключити репозиторій версій, оскільки WiX 3 є застарілою гілкою:
# Додавання бакету версій
scoop bucket add versions
# Встановлення версії 3 WiX Toolset
scoop install versions/wixtoolset3
Chocolatey за замовчуванням встановлює стабільну гілку WiX Toolset v3:
choco install wixtoolset --yes
Утиліта Winget (Windows Package Manager) дозволяє встановити пакет за його унікальним ідентифікатором:
winget install -e --id WiXToolset.WiXToolset
::
Після встановлення переконайтеся, що шлях до утиліт WiX (зокрема до папки bin, де знаходяться candle.exe та light.exe) додано до системної змінної PATH. Перезапустіть термінал та перевірте наявність інструменту:
# Перевірка наявності WiX
candle.exe --version
# → Windows Installer XML Toolset Compiler version 3.14.0.8606
Для генерації .exe (NSIS-інсталятора) встановіть NSIS з nsis.sourceforge.io.
Крок 3: Зібраний Fat JAR
jpackage вимагає вхідний JAR-файл. Використовуйте Fat JAR, створений у попередньому розділі:
# Maven
mvn clean package
# Gradle
./gradlew shadowJar
::
Структура проєкту перед пакуванням
Для зручності рекомендується розмістити всі ресурси для пакування в окремій директорії packaging/:
audiobook-app/
├── src/
├── target/
│ └── audiobook-app-fat.jar ← зібраний Fat JAR
├── packaging/
│ ├── windows/
│ │ ├── audiobook.ico ← іконка для Windows (.ico)
│ │ └── license.rtf ← ліцензія для MSI-діалогу
│ ├── macos/
│ │ └── audiobook.icns ← іконка для macOS (.icns)
│ └── linux/
│ └── audiobook.png ← іконка для Linux (.png, 128×128)
└── pom.xml
Генерація інсталятора для Windows (.msi)
Наведена команда виконується на Windows-машині (або у Windows runner у GitHub Actions):
!WARNINGПроблема з кирилицею у метаданих на Windows WiX Toolset v3 за замовчуванням використовує західноєвропейську кодову сторінку (наприклад,
1252). Якщо значення параметрів--description,--vendorчи--nameмістять кириличні символи, WiX не зможе скомпілювати інсталятор і поверне помилку виду:light.exe : error LGHT0311 : A string was provided with characters that are not available in the specified database code page '1252'.Рекомендація: Завжди вказуйте опис програми (
--description) та інші метадані виключно англійською мовою. Якщо використання кирилиці є критичним, вам доведеться примусово задавати кодову сторінку за допомогою параметра--win-codepage 1251, проте англомовний варіант є найбільш надійним та сумісним.
jpackage ^
--type msi ^
--name "Audiobook Platform" ^
--app-version "1.0.0" ^
--vendor "Kostyl Dev" ^
--description "Audiobook Management Platform" ^
--input target/ ^
--main-jar audiobook-app-fat.jar ^
--main-class dev.kostyl.audiobook.Launcher ^
--icon packaging/windows/audiobook.ico ^
--dest dist/ ^
--win-dir-chooser ^
--win-menu ^
--win-shortcut ^
--win-menu-group "Kostyl Dev"
msi, exe, dmg, pkg, deb, rpm, app-image.major.minor.patch. Відображається у «Установка та видалення програм».--main-jar. Усі файли з неї потраплять у дистрибутив.Результатом є файл dist/Audiobook Platform-1.0.0.msi, що є повноцінним Windows-інсталятором з майстром встановлення.
Генерація інсталятора для macOS (.dmg)
На macOS-машині:
jpackage \
--type dmg \
--name "Audiobook Platform" \
--app-version "1.0.0" \
--vendor "Kostyl Dev" \
--input target/ \
--main-jar audiobook-app-fat.jar \
--main-class dev.kostyl.audiobook.Launcher \
--icon packaging/macos/audiobook.icns \
--dest dist/ \
--mac-package-name "audiobook-platform" \
--mac-package-identifier "dev.kostyl.audiobook"
--mac-sign та вкажіть --mac-signing-key-user-name "Developer Name (TEAMID)". Процес потребує Apple Developer Program ($99/рік). Для навчальних проєктів підпис не є обов'язковим — користувачі можуть запустити програму через Ctrl+Click → Відкрити.Генерація пакету для Linux (.deb)
На Linux-машині (Debian/Ubuntu):
jpackage \
--type deb \
--name "audiobook-platform" \
--app-version "1.0.0" \
--vendor "Kostyl Dev" \
--input target/ \
--main-jar audiobook-app-fat.jar \
--main-class dev.kostyl.audiobook.Launcher \
--icon packaging/linux/audiobook.png \
--dest dist/ \
--linux-package-name "audiobook-platform" \
--linux-app-category "Education" \
--linux-shortcut
Частина III. Автоматизація: jpackage-maven-plugin та GitHub Actions
Інтеграція jpackage у збірку Maven
Виконання jpackage вручну з командного рядка є зручним для налагодження, але незручним для регулярних релізів. Плагін jpackage-maven-plugin від проєкту io.github.fvarrui дозволяє описати всі параметри пакування безпосередньо у pom.xml та запускати пакування однією командою.
<plugin>
<groupId>io.github.fvarrui</groupId>
<artifactId>javapackager</artifactId>
<version>1.7.6</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>package</goal>
</goals>
<configuration>
<mainClass>dev.kostyl.audiobook.Launcher</mainClass>
<bundleJre>true</bundleJre>
<generateInstaller>true</generateInstaller>
<administratorRequired>false</administratorRequired>
<platform>auto</platform>
<name>Audiobook Platform</name>
<version>1.0.0</version>
<organizationName>Kostyl Dev</organizationName>
<organizationUrl>https://kostyl.dev</organizationUrl>
<iconFile>packaging/app-icon</iconFile><!-- без розширення: плагін підбирає .ico/.icns/.png -->
<outputDirectory>dist</outputDirectory>
<winConfig>
<createDesktopIconLink>true</createDesktopIconLink>
<createStartMenuIconLink>true</createStartMenuIconLink>
<menuGroupName>Kostyl Dev</menuGroupName>
<generateInstaller>true</generateInstaller>
<installerType>msi</installerType>
</winConfig>
<macConfig>
<generateDmg>true</generateDmg>
</macConfig>
<linuxConfig>
<generateDeb>true</generateDeb>
<generateRpm>false</generateRpm>
</linuxConfig>
</configuration>
</execution>
</executions>
</plugin>
Тепер запустити повний цикл збірки та пакування можна однією командою:
mvn clean package
# → target/audiobook-app-fat.jar (Fat JAR)
# → dist/Audiobook Platform-1.0.0.msi (Windows) або
# → dist/Audiobook Platform-1.0.0.dmg (macOS) або
# → dist/audiobook-platform_1.0.0.deb (Linux)
GitHub Actions: Збірка інсталяторів для всіх платформ
Найпрофесійніший підхід — автоматична збірка інсталяторів для всіх трьох платформ при кожному новому тегу релізу. GitHub Actions надає безкоштовні runner-агенти для Windows, macOS та Ubuntu.
Створіть файл .github/workflows/release.yml у репозиторії:
name: Release — Build Installers
# Запускається при пуші тегу виду v1.0.0, v2.3.1 тощо
on:
push:
tags:
- 'v*.*.*'
jobs:
build-installers:
# Матриця: три паралельних завдання для трьох ОС
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
# 1. Клонування репозиторію
- name: Checkout repository
uses: actions/checkout@v4
# 2. Встановлення JDK 25 (включає jpackage)
- name: Set up JDK 25
uses: actions/setup-java@v4
with:
java-version: '25'
distribution: 'temurin'
cache: maven
# 3. Встановлення WiX Toolset (лише для Windows)
- name: Install WiX Toolset (Windows only)
if: matrix.os == 'windows-latest'
run: |
choco install wixtoolset --yes
# 4. Встановлення інструментів збірки RPM (лише для Linux)
- name: Install RPM packaging tools (Linux only)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y rpm
# 5. Збірка Fat JAR
- name: Build Fat JAR
run: mvn clean package -DskipTests
# 6. Підготовка директорії вхідних файлів для jpackage
- name: Prepare jpackage input
run: |
mkdir -p jpackage-input
cp target/audiobook-app-fat.jar jpackage-input/
# 7. Визначення версії з тегу (v1.0.0 → 1.0.0)
- name: Extract version from tag
id: version
shell: bash
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
# 8. Генерація нативного інсталятора через jpackage
- name: Package installer (Windows)
if: matrix.os == 'windows-latest'
shell: pwsh
run: |
jpackage `
--type msi `
--name "Audiobook Platform" `
--app-version "${{ steps.version.outputs.VERSION }}" `
--vendor "Kostyl Dev" `
--input jpackage-input/ `
--main-jar audiobook-app-fat.jar `
--main-class dev.kostyl.audiobook.Launcher `
--icon packaging/windows/audiobook.ico `
--dest dist/ `
--win-dir-chooser `
--win-menu `
--win-shortcut
- name: Package installer (macOS)
if: matrix.os == 'macos-latest'
run: |
jpackage \
--type dmg \
--name "Audiobook Platform" \
--app-version "${{ steps.version.outputs.VERSION }}" \
--vendor "Kostyl Dev" \
--input jpackage-input/ \
--main-jar audiobook-app-fat.jar \
--main-class dev.kostyl.audiobook.Launcher \
--icon packaging/macos/audiobook.icns \
--dest dist/
- name: Package installer (Linux DEB & RPM)
if: matrix.os == 'ubuntu-latest'
run: |
# Створення DEB-пакета для Debian/Ubuntu
jpackage \
--type deb \
--name "audiobook-platform" \
--app-version "${{ steps.version.outputs.VERSION }}" \
--vendor "Kostyl Dev" \
--input jpackage-input/ \
--main-jar audiobook-app-fat.jar \
--main-class dev.kostyl.audiobook.Launcher \
--icon packaging/linux/audiobook.png \
--dest dist/ \
--linux-shortcut
# Створення RPM-пакета для RedHat/Fedora/CentOS
jpackage \
--type rpm \
--name "audiobook-platform" \
--app-version "${{ steps.version.outputs.VERSION }}" \
--vendor "Kostyl Dev" \
--input jpackage-input/ \
--main-jar audiobook-app-fat.jar \
--main-class dev.kostyl.audiobook.Launcher \
--icon packaging/linux/audiobook.png \
--dest dist/ \
--linux-shortcut
# Копіювання Fat JAR у директорію dist, щоб він також потрапив у реліз
cp target/audiobook-app-fat.jar dist/
# 9. Збереження артефактів для наступного завдання
- name: Upload installer artifact
uses: actions/upload-artifact@v4
with:
name: installer-${{ matrix.os }}
path: dist/
retention-days: 1
# Окреме завдання: публікація GitHub Release
publish-release:
needs: build-installers
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all installer artifacts
uses: actions/download-artifact@v4
with:
path: installers/
merge-multiple: true
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: "Audiobook Platform ${{ github.ref_name }}"
body: |
## Що нового у ${{ github.ref_name }}
Опис змін цього релізу.
## Завантаження
| Операційна система / Платформа | Файл |
|---|---|
| Windows 10/11 | `*.msi` |
| macOS 12+ | `*.dmg` |
| Ubuntu/Debian | `*.deb` |
| RedHat/Fedora | `*.rpm` |
| Будь-яка (з встановленою JRE) | `*.jar` |
files: installers/**
draft: false
prerelease: false
Частина IV. Публікація у GitHub Releases
Принцип GitHub Releases
GitHub Releases — це механізм GitHub для публікації версіонованих випусків програмного забезпечення. Кожен реліз прив'язується до конкретного git-тегу та може містити довільну кількість файлів-артефактів: інсталяторів, архівів із вихідним кодом, документації.
На відміну від звичайного коміту, реліз має:
- Версійний тег (наприклад,
v1.0.0) — незмінний покажчик у git-історії. - Назву та опис — що змінилося, які нові можливості з'явилися, які помилки виправлено.
- Бінарні файли — безпосередньо файли для завантаження кінцевими користувачами.
Публікація релізу вручну через git і GitHub CLI
Якщо ви не використовуєте GitHub Actions, реліз можна опублікувати в кілька кроків.
Крок 1. Переконайтеся, що всі зміни закоміченні та ви знаходитесь на гілці main:
git status
# → On branch main, nothing to commit, working tree clean
git log --oneline -3
# → a1b2c3d Фінальна версія платформи аудіокниг
# → 9f8e7d6 Виправлення валідації форм
# → 5c4b3a2 Інтеграція AtlantaFX
Крок 2. Створіть анотований тег для нового релізу:
# Синтаксис: git tag -a <тег> -m "<повідомлення>"
git tag -a v1.0.0 -m "Перший стабільний реліз Audiobook Platform"
# Перевірка: переглянути всі теги
git tag -l
# → v1.0.0
# Переглянути деталі тегу
git show v1.0.0
git tag v1.0.0 (без -a) створює легкий тег — простий покажчик на коміт. git tag -a v1.0.0 -m "..." створює анотований тег — окремий git-об'єкт із автором, датою та повідомленням. Для релізів завжди слід використовувати анотовані теги: вони несуть більше семантики та краще відображаються в інтерфейсі GitHub.Крок 3. Запушіть тег до віддаленого репозиторію:
# Пуш конкретного тегу
git push origin v1.0.0
# АБО пуш усіх локальних тегів одночасно
git push origin --tags
Крок 4. Після пуші тегу, якщо ви налаштували GitHub Actions із файлом release.yml, workflow запуститься автоматично. Якщо ж публікуєте вручну — використайте GitHub CLI:
# Встановлення GitHub CLI (якщо ще не встановлено)
# macOS: brew install gh
# Windows: choco install gh
# Linux: sudo apt install gh
# Автентифікація
gh auth login
# Публікація релізу з файлами
gh release create v1.0.0 \
dist/*.msi \
dist/*.dmg \
dist/*.deb \
--title "Audiobook Platform v1.0.0" \
--notes "## Перший реліз
Платформа для управління та прослуховування аудіокниг.
### Що включено
- Повний CRUD для аудіокниг
- MVVM-архітектура з Guice
- Сучасний інтерфейс на AtlantaFX
- Інтеграція з H2 базою даних"
Крок 5. Переконайтеся, що реліз опублікований:
# Перегляд усіх релізів у репозиторії
gh release list
# → TITLE TAG PUBLISHED
# → Audiobook Platform v1.0.0 v1.0.0 about 1 minute ago
# Відкрити реліз у браузері
gh release view v1.0.0 --web
Semantic Versioning: угода щодо нумерації версій
Щоб версії релізів несли змістовну інформацію для користувачів та інших розробників, дотримуйтеся стандарту Semantic Versioning (SemVer) у форматі MAJOR.MINOR.PATCH:
Збільшується, коли вносяться несумісні зміни API або поведінки програми. Наприклад: зміна формату бази даних, видалення функції, кардинальна зміна UI.
Приклад: v1.0.0 → v2.0.0 — нова версія не читає базу даних від v1.x.x.
Збільшується при додаванні нових можливостей, які не порушують сумісність із попередніми версіями.
Приклад: v1.0.0 → v1.1.0 — додано нову вкладку «Статистика» без зміни наявної функціональності.
Збільшується при виправленні помилок без додавання нових функцій.
Приклад: v1.1.0 → v1.1.1 — виправлено падіння додатку при спробі відкрити порожній список.
Підсумок: Повний релізний цикл
Нижче наведено зведений огляд повного шляху від вихідного коду до рук користувача:
Розробка та коміти
Розробіть функціональність, покрийте тестами, закомітьте зміни. Дотримуйтесь конвенції Conventional Commits для структурованих повідомлень комітів.
Оновлення версії у pom.xml
mvn versions:set -DnewVersion=1.0.0
Збірка Fat JAR
mvn clean package
# → target/audiobook-app-fat.jar
Генерація нативного інсталятора
# Запускається на відповідній платформі або через GitHub Actions
jpackage --type msi ... --dest dist/
Тегування та публікація
git tag -a v1.0.0 -m "Реліз v1.0.0"
git push origin v1.0.0
# GitHub Actions автоматично збирає інсталятори та публікує реліз
Fat JAR
Коли використовувати: Розгортання на серверах, передача між розробниками, як вхідний артефакт для jpackage.
Команда: mvn clean package або ./gradlew shadowJar
jpackage
Коли використовувати: Розповсюдження кінцевим користувачам, створення інсталяторів для всіх ОС.
Команда: jpackage --type msi|dmg|deb ...
GitHub Releases
Коли використовувати: Публічне розповсюдження, версіонування, централізоване сховище артефактів.
Команда: git tag -a v1.0.0 && git push origin v1.0.0
jpackage для своєї поточної ОС. Нарешті, налаштуйте GitHub Actions для автоматизації збірки на всіх трьох платформах. Кожен крок є самодостатнім і надає цінність незалежно від наступних.AtlantaFX: Сучасні теми для JavaFX додатків
Від стандартного Modena до сучасного дизайну: AtlantaFX як колекція готових тем (Primer, Nord, Cupertino, Dracula), додаткові контроли, CSS Variables, utility classes, інтеграція з MVVM-додатками.
Введення в теорію баз даних
Історія розвитку баз даних, порівняння моделей зберігання даних та встановлення MS SQL Server