win-exe.yml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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. env:
  14. JAVA_DIST: 'temurin'
  15. JAVA_VERSION: 20
  16. OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_windows-x64_bin-jmods.zip'
  17. OPENJFX_JMODS_AMD64_HASH: '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
  18. defaults:
  19. run:
  20. shell: bash
  21. jobs:
  22. get-version:
  23. uses: ./.github/workflows/get-version.yml
  24. with:
  25. version: ${{ inputs.version }}
  26. build-msi:
  27. name: Build .msi Installer
  28. runs-on: windows-latest
  29. needs: [get-version]
  30. env:
  31. LOOPBACK_ALIAS: 'cryptomator-vault'
  32. WIN_CONSOLE_FLAG: ''
  33. steps:
  34. - uses: actions/checkout@v3
  35. - name: Setup Java
  36. uses: actions/setup-java@v3
  37. with:
  38. distribution: ${{ env.JAVA_DIST }}
  39. java-version: ${{ env.JAVA_VERSION }}
  40. check-latest: true
  41. cache: 'maven'
  42. - name: Download and extract JavaFX jmods from Gluon
  43. #In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
  44. run: |
  45. curl --output jfxjmods.zip -L "${{ env.OPENJFX_JMODS_AMD64 }}"
  46. if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) {
  47. throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}.";
  48. }
  49. Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
  50. Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
  51. shell: pwsh
  52. - name: Ensure major jfx version in pom and in jmods is the same
  53. run: |
  54. JMOD_VERSION_AMD64=$(jmod describe jfxjmods/javafx.base.jmod | head -1)
  55. JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@}
  56. JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*}
  57. POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
  58. POM_JFX_VERSION=${POM_JFX_VERSION#*@}
  59. POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
  60. if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
  61. >&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION_AMD64})"
  62. exit 1
  63. fi
  64. - name: Set version
  65. run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
  66. - name: Run maven
  67. run: mvn -B clean package -Pdependency-check,win -DskipTests
  68. - name: Patch target dir
  69. run: |
  70. cp LICENSE.txt target
  71. cp target/cryptomator-*.jar target/mods
  72. - name: Run jlink
  73. run: >
  74. ${JAVA_HOME}/bin/jlink
  75. --verbose
  76. --output runtime
  77. --module-path "jfxjmods;${JAVA_HOME}/jmods"
  78. --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.crypto.ec,jdk.accessibility,jdk.management.jfr
  79. --strip-native-commands
  80. --no-header-files
  81. --no-man-pages
  82. --strip-debug
  83. --compress=1
  84. - name: Change win-console flag if debug is active
  85. if: ${{ inputs.isDebug }}
  86. run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
  87. - name: Run jpackage
  88. run: >
  89. ${JAVA_HOME}/bin/jpackage
  90. --verbose
  91. --type app-image
  92. --runtime-image runtime
  93. --input target/libs
  94. --module-path target/mods
  95. --module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
  96. --dest appdir
  97. --name Cryptomator
  98. --vendor "Skymatic GmbH"
  99. --copyright "(C) 2016 - 2023 Skymatic GmbH"
  100. --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
  101. --java-options "--enable-preview"
  102. --java-options "--enable-native-access=org.cryptomator.jfuse.win"
  103. --java-options "-Xss5m"
  104. --java-options "-Xmx256m"
  105. --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
  106. --java-options "-Dfile.encoding=\"utf-8\""
  107. --java-options "-Djava.net.useSystemProxies=true"
  108. --java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\""
  109. --java-options "-Dcryptomator.pluginDir=\"@{appdata}/Cryptomator/Plugins\""
  110. --java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\""
  111. --java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\""
  112. --java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\""
  113. --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
  114. --java-options "-Dcryptomator.loopbackAlias=\"${{ env.LOOPBACK_ALIAS }}\""
  115. --java-options "-Dcryptomator.showTrayIcon=true"
  116. --java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
  117. --java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
  118. --java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
  119. --java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
  120. --resource-dir dist/win/resources
  121. --icon dist/win/resources/Cryptomator.ico
  122. ${WIN_CONSOLE_FLAG}
  123. - name: Patch Application Directory
  124. run: |
  125. cp dist/win/contrib/* appdir/Cryptomator
  126. - name: Set LOOPBACK_ALIAS in patchWebDAV.bat
  127. shell: pwsh
  128. run: |
  129. $patchScript = "appdir\Cryptomator\patchWebDAV.bat"
  130. try {
  131. (Get-Content $patchScript ) -replace '::REPLACE ME', "SET LOOPBACK_ALIAS=`"${{ env.LOOPBACK_ALIAS}}`"" | Set-Content $patchScript
  132. } catch {
  133. Write-Host "Failed to set LOOPBACK_ALIAS for patchWebDAV.bat"
  134. exit 1
  135. }
  136. - name: Fix permissions
  137. run: attrib -r appdir/Cryptomator/Cryptomator.exe
  138. shell: pwsh
  139. - name: Codesign binaries inside jars
  140. run: |
  141. $certificate = '${{ secrets.WIN_CODESIGN_P12_BASE64 }}'
  142. $password = '${{ secrets.WIN_CODESIGN_P12_PW }}'
  143. $certificateSHA1 = '5FC94CE149E5B511E621F53A060AC67CBD446B3A'
  144. $description = 'Cryptomator'
  145. $timestampUrl = 'http://timestamp.digicert.com'
  146. $folder = ".\appdir\Cryptomator\app"
  147. $tmpDir = ".\extract"
  148. $signtool = $(Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin\' -Recurse -File signtool.exe | Where-Object { $_.Directory.ToString().EndsWith('x64') } | Select-Object -Last 1).FullName
  149. # preps
  150. Install-Module -Name Microsoft.PowerShell.TextUtility -Force -Confirm:$False
  151. # import certificate
  152. $bytes = ConvertFrom-Base64 -EncodedText $certificate -AsByteArray
  153. Set-Content -Path ".\certFile" -AsByteStream -Value $bytes
  154. & certutil -f -p $password -importpfx ".\certFile"
  155. # create directory to extract every jar to
  156. New-Item -Path $tmpDir -ItemType Directory > $null
  157. # iterate over all jars
  158. Get-ChildItem -Path $folder -Recurse -File *.jar | ForEach-Object {
  159. $jar = Copy-Item $_ -Destination $tmpDir -PassThru
  160. Set-Location -Path $tmpDir
  161. jar --file=$($_.FullName) --extract
  162. Get-ChildItem -Path "." -Recurse -File "*.dll" | ForEach-Object {
  163. # sign
  164. & $signtool sign /sm /tr ${timestampUrl} /td SHA256 /fd SHA256 /d $description /sha1 $certificateSHA1 $_.FullName
  165. # update jar with signed dll
  166. jar --file=$($jar.FullName) --update $(Resolve-Path -Relative -Path $_)
  167. }
  168. # replace old jar with its update
  169. Move-Item -Path $($jar.FullName) -Destination $_ -Force
  170. # clear extraction dir
  171. Remove-Item -Path ".\*" -Force -Recurse
  172. Set-Location -Path ".."
  173. }
  174. # clean up
  175. Remove-Item -Path $tmpDir
  176. shell: pwsh
  177. - name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
  178. run: |
  179. New-Item -Path appdir/jpackage-jmod -ItemType Directory
  180. & $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
  181. Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
  182. shell: pwsh
  183. - name: Codesign
  184. uses: skymatic/code-sign-action@v2
  185. with:
  186. certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
  187. password: ${{ secrets.WIN_CODESIGN_P12_PW }}
  188. certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
  189. description: Cryptomator
  190. timestampUrl: 'http://timestamp.digicert.com'
  191. folder: appdir
  192. recursive: true
  193. - name: Generate license for MSI
  194. run: >
  195. mvn -B license:add-third-party
  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 - 2023 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"
  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@v2
  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@v3
  247. with:
  248. name: msi
  249. path: |
  250. Cryptomator-*.msi
  251. Cryptomator-*.asc
  252. if-no-files-found: error
  253. - name: Publish .msi on GitHub Releases
  254. if: startsWith(github.ref, 'refs/tags/')
  255. uses: softprops/action-gh-release@v1
  256. with:
  257. fail_on_unmatched_files: true
  258. token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
  259. files: |
  260. *.msi
  261. *.asc
  262. build-exe:
  263. name: Build .exe installer
  264. runs-on: windows-latest
  265. needs: [get-version, build-msi]
  266. steps:
  267. - uses: actions/checkout@v3
  268. - name: Download .msi
  269. uses: actions/download-artifact@v3
  270. with:
  271. name: msi
  272. path: dist/win/bundle/resources
  273. - name: Strip version info from msi file name
  274. run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
  275. - uses: actions/setup-java@v3
  276. with:
  277. distribution: ${{ env.JAVA_DIST }}
  278. java-version: ${{ env.JAVA_VERSION }}
  279. check-latest: true
  280. cache: 'maven'
  281. - name: Generate license for exe
  282. run: >
  283. mvn -B license:add-third-party
  284. "-Dlicense.thirdPartyFilename=license.rtf"
  285. "-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
  286. "-Dlicense.outputDirectory=dist/win/bundle/resources"
  287. "-Dlicense.includedScopes=compile"
  288. "-Dlicense.excludedGroups=^org\.cryptomator"
  289. "-Dlicense.failOnMissing=true"
  290. "-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
  291. shell: pwsh
  292. - name: Download WinFsp
  293. run: |
  294. $winfspUrl = (Select-String -Path ".\dist\win\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
  295. curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl
  296. shell: pwsh
  297. - name: Compile to wixObj file
  298. run: >
  299. "${WIX}/bin/candle.exe" dist/win/bundle/bundleWithWinfsp.wxs
  300. -ext WixBalExtension
  301. -ext WixUtilExtension
  302. -out dist/win/bundle/
  303. -dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
  304. -dBundleVendor="Skymatic GmbH"
  305. -dBundleCopyright="(C) 2016 - 2023 Skymatic GmbH"
  306. -dAboutUrl="https://cryptomator.org"
  307. -dHelpUrl="https://cryptomator.org/contact"
  308. -dUpdateUrl="https://cryptomator.org/downloads/"
  309. - name: Create executable with linker
  310. run: >
  311. "${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
  312. -ext WixBalExtension
  313. -ext WixUtilExtension
  314. -out installer/unsigned/Cryptomator-Installer.exe
  315. - name: Detach burn engine in preparation to sign
  316. run: >
  317. "${WIX}/bin/insignia.exe"
  318. -ib installer/unsigned/Cryptomator-Installer.exe
  319. -o tmp/engine.exe
  320. - name: Codesign burn engine
  321. uses: skymatic/code-sign-action@v2
  322. with:
  323. certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
  324. password: ${{ secrets.WIN_CODESIGN_P12_PW }}
  325. certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
  326. description: Cryptomator Installer
  327. timestampUrl: 'http://timestamp.digicert.com'
  328. folder: tmp
  329. - name: Reattach signed burn engine to installer
  330. run : >
  331. "${WIX}/bin/insignia.exe"
  332. -ab tmp/engine.exe installer/unsigned/Cryptomator-Installer.exe
  333. -o installer/Cryptomator-Installer.exe
  334. - name: Codesign EXE
  335. uses: skymatic/code-sign-action@v2
  336. with:
  337. certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
  338. password: ${{ secrets.WIN_CODESIGN_P12_PW }}
  339. certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
  340. description: Cryptomator Installer
  341. timestampUrl: 'http://timestamp.digicert.com'
  342. folder: installer
  343. - name: Add possible alpha/beta tags to installer name
  344. run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-x64.exe
  345. - name: Create detached GPG signature with key 615D449FE6E6A235
  346. run: |
  347. echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
  348. echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a Cryptomator-*.exe
  349. env:
  350. GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
  351. GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
  352. - name: Upload artifacts
  353. uses: actions/upload-artifact@v3
  354. with:
  355. name: exe
  356. path: |
  357. Cryptomator-*.exe
  358. Cryptomator-*.asc
  359. if-no-files-found: error
  360. - name: Publish .msi on GitHub Releases
  361. if: startsWith(github.ref, 'refs/tags/')
  362. uses: softprops/action-gh-release@v1
  363. with:
  364. fail_on_unmatched_files: true
  365. token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
  366. files: |
  367. Cryptomator-*.exe
  368. Cryptomator-*.asc
  369. allowlist:
  370. name: Anti Virus Allowlisting
  371. if: startsWith(github.ref, 'refs/tags/')
  372. runs-on: ubuntu-latest
  373. needs: [build-msi, build-exe]
  374. steps:
  375. - name: Download .msi
  376. uses: actions/download-artifact@v3
  377. with:
  378. name: msi
  379. path: msi
  380. - name: Download .exe
  381. uses: actions/download-artifact@v3
  382. with:
  383. name: exe
  384. path: exe
  385. - name: Collect files
  386. run: |
  387. mkdir files
  388. cp msi/*.msi files
  389. cp exe/*.exe files
  390. - name: Upload to Kaspersky
  391. uses: SamKirkland/FTP-Deploy-Action@v4.3.4
  392. with:
  393. protocol: ftps
  394. server: allowlist.kaspersky-labs.com
  395. port: 990
  396. username: ${{ secrets.ALLOWLIST_KASPERSKY_USERNAME }}
  397. password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
  398. local-dir: files/
  399. - name: Upload to Avast
  400. uses: SamKirkland/FTP-Deploy-Action@v4.3.4
  401. with:
  402. protocol: ftp
  403. server: whitelisting.avast.com
  404. port: 21
  405. username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
  406. password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
  407. local-dir: files/