win-exe.yml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. name: Build Windows Installer
  2. on:
  3. release:
  4. types: [published]
  5. workflow_dispatch:
  6. inputs:
  7. version:
  8. description: 'Version'
  9. required: false
  10. isDebug:
  11. description: 'Build debug version with console output'
  12. type: boolean
  13. default: false
  14. env:
  15. JAVA_DIST: 'zulu'
  16. JAVA_VERSION: '23.0.1+11'
  17. OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_windows-x64_bin-jmods.zip'
  18. OPENJFX_JMODS_AMD64_HASH: 'f9376d200f5c5b85327d575c1ec1482e6455f19916577f7e2fc9be2f48bb29b6'
  19. WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
  20. WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
  21. defaults:
  22. run:
  23. shell: bash
  24. jobs:
  25. get-version:
  26. uses: ./.github/workflows/get-version.yml
  27. with:
  28. version: ${{ inputs.version }}
  29. build-msi:
  30. name: Build .msi Installer
  31. runs-on: windows-latest
  32. needs: [get-version]
  33. env:
  34. LOOPBACK_ALIAS: 'cryptomator-vault'
  35. WIN_CONSOLE_FLAG: ''
  36. steps:
  37. - name: Upgrade WIX to latest version
  38. run: choco install wixtoolset --version 3.14.1
  39. shell: pwsh
  40. - uses: actions/checkout@v4
  41. - name: Setup Java
  42. uses: actions/setup-java@v4
  43. with:
  44. distribution: ${{ env.JAVA_DIST }}
  45. java-version: ${{ env.JAVA_VERSION }}
  46. check-latest: true
  47. cache: 'maven'
  48. - name: Download and extract JavaFX jmods from Gluon
  49. #In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
  50. run: |
  51. curl --output jfxjmods.zip -L "${{ env.OPENJFX_JMODS_AMD64 }}"
  52. if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) {
  53. throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}.";
  54. }
  55. Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
  56. Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
  57. shell: pwsh
  58. - name: Ensure major jfx version in pom and in jmods is the same
  59. run: |
  60. JMOD_VERSION_AMD64=$(jmod describe jfxjmods/javafx.base.jmod | head -1)
  61. JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@}
  62. JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*}
  63. POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
  64. POM_JFX_VERSION=${POM_JFX_VERSION#*@}
  65. POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
  66. if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
  67. >&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION_AMD64})"
  68. exit 1
  69. fi
  70. - name: Set version
  71. run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
  72. - name: Run maven
  73. run: mvn -B clean package -Pwin -DskipTests -Djavafx.platform=win
  74. - name: Patch target dir
  75. run: |
  76. cp LICENSE.txt target
  77. cp target/cryptomator-*.jar target/mods
  78. - name: Run jlink
  79. #Remark: no compression is applied for improved build compression later (here msi)
  80. run: >
  81. ${JAVA_HOME}/bin/jlink
  82. --verbose
  83. --output runtime
  84. --module-path "jfxjmods;${JAVA_HOME}/jmods"
  85. --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
  86. --strip-native-commands
  87. --no-header-files
  88. --no-man-pages
  89. --strip-debug
  90. --compress zip-0
  91. - name: Change win-console flag if debug is active
  92. if: ${{ inputs.isDebug }}
  93. run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
  94. - name: Run jpackage
  95. run: >
  96. ${JAVA_HOME}/bin/jpackage
  97. --verbose
  98. --type app-image
  99. --runtime-image runtime
  100. --input target/libs
  101. --module-path target/mods
  102. --module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
  103. --dest appdir
  104. --name Cryptomator
  105. --vendor "Skymatic GmbH"
  106. --copyright "(C) 2016 - 2024 Skymatic GmbH"
  107. --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
  108. --java-options "--enable-preview"
  109. --java-options "--enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
  110. --java-options "-Xss5m"
  111. --java-options "-Xmx256m"
  112. --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
  113. --java-options "-Dfile.encoding=\"utf-8\""
  114. --java-options "-Djava.net.useSystemProxies=true"
  115. --java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\""
  116. --java-options "-Dcryptomator.pluginDir=\"@{appdata}/Cryptomator/Plugins\""
  117. --java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\""
  118. --java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\""
  119. --java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\""
  120. --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
  121. --java-options "-Dcryptomator.loopbackAlias=\"${{ env.LOOPBACK_ALIAS }}\""
  122. --java-options "-Dcryptomator.showTrayIcon=true"
  123. --java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
  124. --java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
  125. --java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
  126. --java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json\""
  127. --java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
  128. --resource-dir dist/win/resources
  129. --icon dist/win/resources/Cryptomator.ico
  130. ${WIN_CONSOLE_FLAG}
  131. - name: Patch Application Directory
  132. run: |
  133. cp dist/win/contrib/* appdir/Cryptomator
  134. - name: Set LOOPBACK_ALIAS in patchWebDAV.bat
  135. shell: pwsh
  136. run: |
  137. $patchScript = "appdir\Cryptomator\patchWebDAV.bat"
  138. try {
  139. (Get-Content $patchScript ) -replace '::REPLACE ME', "SET LOOPBACK_ALIAS=`"${{ env.LOOPBACK_ALIAS}}`"" | Set-Content $patchScript
  140. } catch {
  141. Write-Host "Failed to set LOOPBACK_ALIAS for patchWebDAV.bat"
  142. exit 1
  143. }
  144. - name: Fix permissions
  145. run: attrib -r appdir/Cryptomator/Cryptomator.exe
  146. shell: pwsh
  147. - name: Extract jars with DLLs for Codesigning
  148. shell: pwsh
  149. run: |
  150. Add-Type -AssemblyName "System.io.compression.filesystem"
  151. $jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
  152. $jarExtractDir = New-Item -Path ".\appdir\jar-extract" -ItemType Directory
  153. #for all jars inspect
  154. Get-ChildItem -Path $jarFolder -Filter "*.jar" | ForEach-Object {
  155. $jar = [Io.compression.zipfile]::OpenRead($_.FullName)
  156. if (@($jar.Entries | Where-Object {$_.Name.ToString().EndsWith(".dll")} | Select-Object -First 1).Count -gt 0) {
  157. #jars containing dlls extract
  158. Set-Location $jarExtractDir
  159. Expand-Archive -Path $_.FullName
  160. }
  161. $jar.Dispose()
  162. }
  163. - name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
  164. shell: pwsh
  165. run: |
  166. New-Item -Path appdir/jpackage-jmod -ItemType Directory
  167. & $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
  168. Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
  169. - name: Codesign
  170. uses: skymatic/code-sign-action@v3
  171. with:
  172. certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
  173. password: ${{ secrets.WIN_CODESIGN_P12_PW }}
  174. certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
  175. description: Cryptomator
  176. timestampUrl: 'http://timestamp.digicert.com'
  177. folder: appdir
  178. recursive: true
  179. - name: Replace DLLs inside jars with signed ones
  180. shell: pwsh
  181. run: |
  182. $jarExtractDir = Resolve-Path ".\appdir\jar-extract"
  183. $jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
  184. Get-ChildItem -Path $jarExtractDir | ForEach-Object {
  185. $jarName = $_.Name
  186. $jarFile = "${jarFolder}\${jarName}.jar"
  187. Set-Location $_
  188. Get-ChildItem -Path $_ -Recurse -File "*.dll" | ForEach-Object {
  189. # update jar with signed dll
  190. jar --file="$jarFile" --update $(Resolve-Path -Relative -Path $_)
  191. }
  192. }
  193. - name: Generate license for MSI
  194. run: >
  195. mvn -B license:add-third-party "-Djavafx.platform=win"
  196. "-Dlicense.thirdPartyFilename=license.rtf"
  197. "-Dlicense.outputDirectory=dist/win/resources"
  198. "-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
  199. "-Dlicense.includedScopes=compile"
  200. "-Dlicense.excludedGroups=^org\.cryptomator"
  201. "-Dlicense.failOnMissing=true"
  202. "-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
  203. shell: pwsh
  204. - name: Create MSI
  205. run: >
  206. ${JAVA_HOME}/bin/jpackage
  207. --verbose
  208. --type msi
  209. --win-upgrade-uuid bda45523-42b1-4cae-9354-a45475ed4775
  210. --app-image appdir/Cryptomator
  211. --dest installer
  212. --name Cryptomator
  213. --vendor "Skymatic GmbH"
  214. --copyright "(C) 2016 - 2024 Skymatic GmbH"
  215. --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
  216. --win-menu
  217. --win-dir-chooser
  218. --win-shortcut-prompt
  219. --win-update-url "https:\\cryptomator.org\downloads"
  220. --win-menu-group Cryptomator
  221. --resource-dir dist/win/resources
  222. --license-file dist/win/resources/license.rtf
  223. --file-associations dist/win/resources/FAvaultFile.properties
  224. env:
  225. JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
  226. JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
  227. - name: Codesign MSI
  228. uses: skymatic/code-sign-action@v3
  229. with:
  230. certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
  231. password: ${{ secrets.WIN_CODESIGN_P12_PW }}
  232. certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
  233. description: Cryptomator Installer
  234. timestampUrl: 'http://timestamp.digicert.com'
  235. folder: installer
  236. - name: Add possible alpha/beta tags to installer name
  237. run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-x64.msi
  238. - name: Create detached GPG signature with key 615D449FE6E6A235
  239. run: |
  240. echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
  241. echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a Cryptomator-*.msi
  242. env:
  243. GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
  244. GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
  245. - name: Upload artifacts
  246. uses: actions/upload-artifact@v4
  247. with:
  248. name: msi
  249. path: |
  250. Cryptomator-*.msi
  251. Cryptomator-*.asc
  252. if-no-files-found: error
  253. build-exe:
  254. name: Build .exe installer
  255. runs-on: windows-latest
  256. needs: [get-version, build-msi]
  257. steps:
  258. - uses: actions/checkout@v4
  259. - name: Download .msi
  260. uses: actions/download-artifact@v4
  261. with:
  262. name: msi
  263. path: dist/win/bundle/resources
  264. - name: Strip version info from msi file name
  265. run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
  266. - uses: actions/setup-java@v4
  267. with:
  268. distribution: ${{ env.JAVA_DIST }}
  269. java-version: ${{ env.JAVA_VERSION }}
  270. check-latest: true
  271. cache: 'maven'
  272. - name: Generate license for exe
  273. run: >
  274. mvn -B license:add-third-party "-Djavafx.platform=win"
  275. "-Dlicense.thirdPartyFilename=license.rtf"
  276. "-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
  277. "-Dlicense.outputDirectory=dist/win/bundle/resources"
  278. "-Dlicense.includedScopes=compile"
  279. "-Dlicense.excludedGroups=^org\.cryptomator"
  280. "-Dlicense.failOnMissing=true"
  281. "-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
  282. shell: pwsh
  283. - name: Download WinFsp
  284. run: |
  285. curl --output dist/win/bundle/resources/winfsp.msi -L ${{ env.WINFSP_MSI }}
  286. shell: pwsh
  287. - name: Download Legacy-WinFsp uninstaller
  288. run: |
  289. curl --output dist/win/bundle/resources/winfsp-uninstaller.exe -L ${{ env.WINFSP_UNINSTALLER }}
  290. shell: pwsh
  291. - name: Compile to wixObj file
  292. run: >
  293. "${WIX}/bin/candle.exe" dist/win/bundle/bundleWithWinfsp.wxs
  294. -ext WixBalExtension
  295. -ext WixUtilExtension
  296. -out dist/win/bundle/
  297. -dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
  298. -dBundleVendor="Skymatic GmbH"
  299. -dBundleCopyright="(C) 2016 - 2024 Skymatic GmbH"
  300. -dAboutUrl="https://cryptomator.org"
  301. -dHelpUrl="https://cryptomator.org/contact"
  302. -dUpdateUrl="https://cryptomator.org/downloads/"
  303. - name: Create executable with linker
  304. run: >
  305. "${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
  306. -ext WixBalExtension
  307. -ext WixUtilExtension
  308. -out installer/unsigned/Cryptomator-Installer.exe
  309. - name: Detach burn engine in preparation to sign
  310. run: >
  311. "${WIX}/bin/insignia.exe"
  312. -ib installer/unsigned/Cryptomator-Installer.exe
  313. -o tmp/engine.exe
  314. - name: Codesign burn engine
  315. uses: skymatic/code-sign-action@v3
  316. with:
  317. certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
  318. password: ${{ secrets.WIN_CODESIGN_P12_PW }}
  319. certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
  320. description: Cryptomator Installer
  321. timestampUrl: 'http://timestamp.digicert.com'
  322. folder: tmp
  323. - name: Reattach signed burn engine to installer
  324. run : >
  325. "${WIX}/bin/insignia.exe"
  326. -ab tmp/engine.exe installer/unsigned/Cryptomator-Installer.exe
  327. -o installer/Cryptomator-Installer.exe
  328. - name: Codesign EXE
  329. uses: skymatic/code-sign-action@v3
  330. with:
  331. certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
  332. password: ${{ secrets.WIN_CODESIGN_P12_PW }}
  333. certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
  334. description: Cryptomator Installer
  335. timestampUrl: 'http://timestamp.digicert.com'
  336. folder: installer
  337. - name: Add possible alpha/beta tags to installer name
  338. run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-x64.exe
  339. - name: Create detached GPG signature with key 615D449FE6E6A235
  340. run: |
  341. echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
  342. echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a Cryptomator-*.exe
  343. env:
  344. GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
  345. GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
  346. - name: Upload artifacts
  347. uses: actions/upload-artifact@v4
  348. with:
  349. name: exe
  350. path: |
  351. Cryptomator-*.exe
  352. Cryptomator-*.asc
  353. if-no-files-found: error
  354. publish:
  355. name: Publish installers to the github release
  356. if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
  357. runs-on: ubuntu-latest
  358. needs: [build-msi, build-exe]
  359. outputs:
  360. download-url-msi: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }}
  361. download-url-exe: ${{ fromJSON(steps.publish.outputs.assets)[1].browser_download_url }}
  362. steps:
  363. - name: Download installers
  364. uses: actions/download-artifact@v4
  365. with:
  366. merge-multiple: true
  367. - name: Publish .msi on GitHub Releases
  368. id: publish
  369. uses: softprops/action-gh-release@v2
  370. with:
  371. fail_on_unmatched_files: true
  372. token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
  373. # do not change ordering of filelist, required for correct job output
  374. files: |
  375. *.msi
  376. *.exe
  377. *.asc
  378. allowlist-msi:
  379. uses: ./.github/workflows/av-whitelist.yml
  380. needs: [publish]
  381. with:
  382. url: ${{ needs.publish.outputs.download-url-msi }}
  383. secrets: inherit
  384. allowlist-exe:
  385. uses: ./.github/workflows/av-whitelist.yml
  386. needs: [publish]
  387. with:
  388. url: ${{ needs.publish.outputs.download-url-exe }}
  389. secrets: inherit
  390. notify-winget:
  391. name: Notify for winget-release
  392. if: needs.get-version.outputs.versionType == 'stable'
  393. needs: [publish, get-version]
  394. runs-on: ubuntu-latest
  395. steps:
  396. - name: Slack Notification
  397. uses: rtCamp/action-slack-notify@v2
  398. env:
  399. SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
  400. SLACK_USERNAME: 'Cryptobot'
  401. SLACK_ICON: false
  402. SLACK_ICON_EMOJI: ':bot:'
  403. SLACK_CHANNEL: 'cryptomator-desktop'
  404. SLACK_TITLE: "MSI of ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
  405. SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/winget.yml| release to winget>."
  406. SLACK_FOOTER: false
  407. MSG_MINIMAL: true