|
| 1 | +# Replace InnoSetup+launch4j+Ant with jpackage for the Windows installer |
| 2 | + |
| 3 | +## Context |
| 4 | + |
| 5 | +The old Windows installer was produced by a Docker image (`kwart/innosetup`) that |
| 6 | +ran Ant + Launch4j to wrap the shaded jar into `.exe` launchers, copied bundled |
| 7 | +JREs from `/opt/jre32` and `/opt/jre64`, and ran `iscc` (InnoSetup) under Wine to |
| 8 | +produce a single fat `.exe` installer that selected 32- or 64-bit at install |
| 9 | +time. This setup was brittle — it depended on a custom Docker image, Wine, Ant, |
| 10 | +Launch4j, and prebuilt JREs. |
| 11 | + |
| 12 | +It has been replaced with `jpackage`, running natively on a `windows-latest` |
| 13 | +GitHub Actions runner. Three artifact types (EXE, MSI, ZIP) are produced for |
| 14 | +x64 only. 32-bit Windows is dropped (Temurin no longer ships 32-bit JDKs), |
| 15 | +but the existing cross-arch no-JRE distribution zip (`jsignpdf-${VERSION}.zip`) |
| 16 | +is kept so that 32-bit users (and Linux/Mac users) can run JSignPdf with their |
| 17 | +own JRE. |
| 18 | + |
| 19 | +## Final artifact set per release |
| 20 | + |
| 21 | +Produced by the ubuntu Maven job: |
| 22 | + |
| 23 | +- `jsignpdf-${VERSION}.zip` — cross-arch, no JRE (existing assembly, unchanged) |
| 24 | + |
| 25 | +Produced by the windows-latest jpackage job: |
| 26 | + |
| 27 | +- `JSignPdf-${VERSION}-win-x64.exe` — WiX-based EXE installer with MPL-2.0 license page |
| 28 | +- `JSignPdf-${VERSION}-win-x64.msi` — WiX-based MSI installer with MPL-2.0 license page |
| 29 | +- `JSignPdf-${VERSION}-win-x64.zip` — zipped jpackage app-image (portable, bundled JRE) |
| 30 | + |
| 31 | +All four are uploaded to the same GitHub Release tag. The cross-arch zip also |
| 32 | +goes to SourceForge. |
| 33 | + |
| 34 | +## Source tree layout |
| 35 | + |
| 36 | +### `distribution/jpackage/` — configuration |
| 37 | + |
| 38 | +- **`common-jvm-options.txt`** — single source of truth for JVM module |
| 39 | + exports/opens shared across all launchers. One option per line, `#` comments |
| 40 | + supported. The heap settings (`-Xms1g`/`-Xmx1g`) were intentionally removed |
| 41 | + to let the JVM use its defaults. Current contents: |
| 42 | + ``` |
| 43 | + --add-exports=jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED |
| 44 | + --add-exports=jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED |
| 45 | + --add-exports=java.base/sun.security.action=ALL-UNNAMED |
| 46 | + --add-exports=java.base/sun.security.rsa=ALL-UNNAMED |
| 47 | + --add-opens=java.base/sun.security.util=ALL-UNNAMED |
| 48 | + ``` |
| 49 | + |
| 50 | +- **`JSignPdfC.properties`** — console add-launcher; contains only |
| 51 | + `win-console=true`. All other settings (java-options, icon) are inherited |
| 52 | + from the main JSignPdf launcher. |
| 53 | + |
| 54 | +Two additional add-launcher files are **generated at build time** by the |
| 55 | +PowerShell script into `distribution/target/jpackage-generated/`: |
| 56 | + |
| 57 | +- **`JSignPdf-swing.properties`** — Swing GUI. Generated because it must |
| 58 | + override `java-options` to add `-Djsignpdf.swing=true` alongside the common |
| 59 | + options (jpackage replaces rather than merges java-options). Generating it |
| 60 | + from `common-jvm-options.txt` avoids duplicating the option list. |
| 61 | + |
| 62 | +- **`InstallCert.properties`** — console launcher for `InstallCert.jar`. |
| 63 | + Generated so it can carry an absolute path to `Certificate.ico` (jpackage |
| 64 | + resolves add-launcher icon paths against the working directory, which varies |
| 65 | + between CI and local builds). |
| 66 | + |
| 67 | +The legacy `-Djsignpdf.home="%EXEDIR%"` flag was intentionally dropped. jpackage |
| 68 | +sets the working directory itself; if a regression surfaces during testing, it |
| 69 | +can be re-added via `--java-options "-Djsignpdf.home=$APPDIR"`. |
| 70 | + |
| 71 | +### `distribution/windows/build-windows-installers.ps1` — build script |
| 72 | + |
| 73 | +PowerShell script invoked by the workflow. Lives in the repo so it can be |
| 74 | +tested locally on a Windows box. Key responsibilities: |
| 75 | + |
| 76 | +1. **Version sanitization**: `$Version` is split into a jpackage-compatible |
| 77 | + numeric `$appVersion` (strips `-RC1`, `-SNAPSHOT`, `.Final` etc., keeps up |
| 78 | + to three numeric components). `$Version` is preserved for release-aligned |
| 79 | + filenames; `$appVersion` is what jpackage receives via `--app-version`. |
| 80 | + |
| 81 | +2. **WiX fallback**: checks for `light.exe` on PATH; if absent, installs WiX |
| 82 | + 3.14 via `choco install wixtoolset` and adds its bin directory to the |
| 83 | + session PATH. |
| 84 | + |
| 85 | +3. **Artifact cleanup**: removes `JSignPdf-*-win-x64.*` from `upload/` so |
| 86 | + repeat local runs don't accumulate stale artifacts. |
| 87 | + |
| 88 | +4. **Jar staging**: copies the shaded `JSignPdf.jar` and `InstallCert.jar` |
| 89 | + from module target directories. |
| 90 | + |
| 91 | +5. **PDF guide**: if `distribution/target/pdf-guide/*.pdf` exists (downloaded |
| 92 | + from the ubuntu job's workflow artifact in CI), stages it into |
| 93 | + `jpackage-app-content/docs/JSignPdf.pdf` and passes it as |
| 94 | + `--app-content` to jpackage. The PDF ends up at `<install>/app/docs/` |
| 95 | + in the installed tree. If absent (local builds), logs a warning and |
| 96 | + proceeds without it. |
| 97 | + |
| 98 | +6. **Common JVM options**: reads `common-jvm-options.txt` and expands into |
| 99 | + `--java-options` pairs for the main launcher. Also generates the Swing |
| 100 | + and InstallCert add-launcher `.properties` files from the same source. |
| 101 | + |
| 102 | +7. **App-image build**: `jpackage --type app-image` with four launchers |
| 103 | + (JSignPdf as main, plus JSignPdf-swing, JSignPdfC, InstallCert as |
| 104 | + add-launchers). Icons: `icons.ico` for the main + console + swing |
| 105 | + launchers, `Certificate.ico` for InstallCert. |
| 106 | + |
| 107 | +8. **Zip**: `Compress-Archive` of the app-image directory (includes the |
| 108 | + `JSignPdf/` parent folder at the zip root). |
| 109 | + |
| 110 | +9. **EXE + MSI**: `jpackage --type exe` and `--type msi` from the app-image, |
| 111 | + with `--license-file` (MPL-2.0), `--win-menu`, `--win-shortcut`, |
| 112 | + `--win-dir-chooser`, and a stable `--win-upgrade-uuid`. |
| 113 | + |
| 114 | +10. **Rename + move**: jpackage output files use `$appVersion` in the |
| 115 | + filename; the script renames them to `$Version` for release-tag alignment |
| 116 | + and moves them into `distribution/target/upload/`. |
| 117 | + |
| 118 | +### `.github/workflows/do-release.yml` — release workflow |
| 119 | + |
| 120 | +Two jobs: |
| 121 | + |
| 122 | +- **`do-release`** (ubuntu-latest, JDK 11): |
| 123 | + - Verifies per-version release notes file exists |
| 124 | + (`distribution/doc/release-notes/${BASE_VERSION}.md`). |
| 125 | + - Maven Central deploy via `release:prepare` / `release:perform`. |
| 126 | + - Organizes artifacts and uploads to SourceForge + GitHub Release (with |
| 127 | + `body_path` pointing at the release notes markdown). |
| 128 | + - Publishes `distribution/target/generated-docs/JSignPdf.pdf` as a workflow |
| 129 | + artifact (`jsignpdf-pdf-guide`) for the windows job. |
| 130 | + - Outputs: `tag`, `version`, `base_version`. |
| 131 | + |
| 132 | +- **`windows-installers`** (windows-latest, JDK 21, `needs: do-release`): |
| 133 | + - Checks out the release tag. |
| 134 | + - Builds `jsignpdf` and `installcert` jars |
| 135 | + (`mvn -B -DskipTests -pl jsignpdf,installcert -am package`). |
| 136 | + - Downloads the PDF guide workflow artifact into |
| 137 | + `distribution/target/pdf-guide/`. |
| 138 | + - Runs `build-windows-installers.ps1 -Version <version>`. |
| 139 | + - Uploads the EXE/MSI/ZIP to the same GitHub Release. |
| 140 | + |
| 141 | +Only `jsignpdf` and `installcert` modules are built in the windows job — |
| 142 | +the distribution module (with asciidoctor) is skipped to keep the job fast. |
| 143 | +The PDF guide is passed via workflow artifact instead. |
| 144 | + |
| 145 | +## Deleted files |
| 146 | + |
| 147 | +- `distribution/windows/JSignPdf.iss` |
| 148 | +- `distribution/windows/ant-build-create-launchers.xml` |
| 149 | +- `distribution/windows/create-jsignpdf-installer.sh` |
| 150 | +- `distribution/windows/launch4j-template.l4j.ini` |
| 151 | +- `distribution/windows/JSignPdf-swing.l4j.ini` |
| 152 | +- `distribution/doc/icon/splash08.bmp` |
| 153 | +- `distribution/doc/icon/splash08.xcf` |
| 154 | +- `distribution/doc/icon/splash_1.0.0.bmp` |
| 155 | +- `distribution/doc/icon/splash_1.0.0.xcf` |
| 156 | + |
| 157 | +## Important constraints |
| 158 | + |
| 159 | +- **`--win-upgrade-uuid` is fixed** (`7b3d6e4c-9a51-4a8b-9b1c-7e8c1a4d2f10`). |
| 160 | + It must remain stable across releases so MSI upgrades work correctly (single |
| 161 | + entry in Add/Remove Programs). Do NOT regenerate it. |
| 162 | + |
| 163 | +- **WiX 3.x required**. jpackage on JDK 17+ uses WiX 3 for both EXE and MSI. |
| 164 | + GitHub `windows-latest` runners ship WiX 3 preinstalled. The script falls |
| 165 | + back to `choco install wixtoolset --version=3.14.0` if `light.exe` is |
| 166 | + missing. |
| 167 | + |
| 168 | +- **Version format**: jpackage `--app-version` accepts only |
| 169 | + `MAJOR[.MINOR[.MICRO]]` (numeric). The script strips non-numeric suffixes |
| 170 | + automatically, but future releases should use clean version numbers. |
| 171 | + |
| 172 | +## Verification |
| 173 | + |
| 174 | +1. **Local Windows smoke test** (JDK 21 + WiX 3): |
| 175 | + ``` |
| 176 | + mvn -B -DskipTests -pl jsignpdf,installcert -am package |
| 177 | + pwsh ./distribution/windows/build-windows-installers.ps1 -Version 3.0.0 |
| 178 | + ``` |
| 179 | + Confirm `distribution/target/upload/` contains |
| 180 | + `JSignPdf-3.0.0-win-x64.{exe,msi,zip}`. |
| 181 | + |
| 182 | +2. **Install + launch test**: |
| 183 | + - EXE installer: verify MPL-2.0 license page, Start menu entries, JSignPdf |
| 184 | + launches. |
| 185 | + - MSI installer: same. |
| 186 | + - Portable zip: extract, run `JSignPdf.exe`, `JSignPdf-swing.exe`, |
| 187 | + `JSignPdfC.exe --help`, `InstallCert.exe`. |
| 188 | + - Verify InstallCert has the Certificate.ico icon. |
| 189 | + - Sign a sample PDF from both JavaFX and Swing UIs. |
| 190 | + |
| 191 | +3. **Upgrade test**: install version N via MSI, then install N+ε over the top, |
| 192 | + confirm a single entry in Add/Remove Programs. |
| 193 | + |
| 194 | +4. **PDF guide**: after installing, confirm `<install>/app/docs/JSignPdf.pdf` |
| 195 | + exists (when built in CI with the workflow artifact). |
| 196 | + |
| 197 | +5. **Artifact set on GitHub Release**: confirm the release page shows |
| 198 | + `jsignpdf-${VERSION}.zip` (cross-arch) plus the three Windows artifacts, |
| 199 | + with release notes as the body. |
| 200 | + |
| 201 | +## Out of scope |
| 202 | + |
| 203 | +- 32-bit Windows installers. |
| 204 | +- macOS / Linux installer packaging. |
| 205 | +- Code-signing the EXE/MSI (no certificate set up; can be added later via |
| 206 | + `signtool sign` in the workflow). |
0 commit comments