├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── build.sbt ├── project ├── build.properties └── plugins.sbt └── src └── main └── scala └── example └── Main.scala /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build for Release 2 | 3 | on: 4 | push: 5 | branches: [ release/* ] 6 | 7 | env: 8 | pkg-assembly: 'swingtest-assembly-1.0.jar' 9 | pkg-name: 'SwingTest' 10 | pkg-version: '1.1' 11 | 12 | jobs: 13 | fat: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up JDK 1.8 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: 1.8 21 | - name: Build fat jar 22 | run: sbt assembly 23 | - name: Upload fat jar 24 | uses: actions/upload-artifact@v1 25 | with: 26 | name: jars 27 | path: "target/scala-2.13/${{ env.pkg-assembly }}" 28 | osx: 29 | needs: fat 30 | runs-on: [macos-latest] 31 | steps: 32 | - uses: actions/checkout@v2 33 | - uses: actions/setup-java@v1 34 | with: 35 | java-version: '14.0.0' 36 | java-package: jdk 37 | architecture: x64 38 | - name: Download fat jar 39 | uses: actions/download-artifact@v1 40 | with: 41 | name: jars 42 | - name: Package jar as dmg installer 43 | run: "jpackage --name ${{ env.pkg-name }} -i jars --main-class example.Main --main-jar ${{ env.pkg-assembly }}" 44 | - name: Upload dmg 45 | uses: actions/upload-artifact@v1 46 | with: 47 | name: dmgs 48 | path: "${{ env.pkg-name }}-${{ env.pkg-version }}.dmg" 49 | windows: 50 | needs: fat 51 | runs-on: [windows-latest] 52 | steps: 53 | - uses: actions/checkout@v2 54 | - uses: actions/setup-java@v1 55 | with: 56 | java-version: '14.0.0' 57 | java-package: jdk 58 | architecture: x64 59 | - name: Download fat jar 60 | uses: actions/download-artifact@v1 61 | with: 62 | name: jars 63 | - name: Package jar as msi 64 | run: "jpackage --name ${{ env.pkg-name }} --type msi -i jars --main-class example.Main --main-jar ${{ env.pkg-assembly }} --win-dir-chooser" 65 | - name: View artifacts 66 | run: dir 67 | - name: Upload installer 68 | uses: actions/upload-artifact@v1 69 | with: 70 | name: msis 71 | path: "${{ env.pkg-name }}-${{ env.pkg-version }}.msi" 72 | linux: 73 | needs: fat 74 | runs-on: [ubuntu-latest] 75 | steps: 76 | - uses: actions/checkout@v2 77 | - uses: actions/setup-java@v1 78 | with: 79 | java-version: '14.0.0' 80 | java-package: jdk 81 | architecture: x64 82 | - name: Download fat jar 83 | uses: actions/download-artifact@v1 84 | with: 85 | name: jars 86 | - name: Package jar as debian package 87 | run: "jpackage --name ${{ env.pkg-name }} --type deb -i jars --main-class example.Main --main-jar ${{ env.pkg-assembly }}" 88 | - name: Upload deb 89 | uses: actions/upload-artifact@v1 90 | with: 91 | name: debs 92 | path: "${{ env.pkg-name }}-${{ env.pkg-version }}.deb" 93 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build master manually 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | logLevel: 7 | description: 'Log level' 8 | required: true 9 | default: 'warning' 10 | tags: 11 | description: 'Test master build' 12 | 13 | env: 14 | pkg-assembly: 'swingtest-assembly-1.1.jar' 15 | pkg-name: 'SwingTest' 16 | pkg-name-debian: 'swingtest' 17 | pkg-version: '1.1' 18 | 19 | jobs: 20 | fat: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Set up JDK 1.8 25 | uses: actions/setup-java@v1 26 | with: 27 | java-version: 1.8 28 | - name: Build fat jar 29 | run: sbt assembly 30 | - name: Upload fat jar 31 | uses: actions/upload-artifact@v1 32 | with: 33 | name: jars 34 | path: "target/scala-2.13/${{ env.pkg-assembly }}" 35 | osx: 36 | needs: fat 37 | runs-on: [macos-latest] 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: actions/setup-java@v1 41 | with: 42 | java-version: '14.0.0' 43 | java-package: jdk 44 | architecture: x64 45 | - name: Download fat jar 46 | uses: actions/download-artifact@v1 47 | with: 48 | name: jars 49 | - name: Package jar as dmg installer 50 | run: "jpackage --app-version ${{ env.pkg-version }} --name ${{ env.pkg-name }} -i jars --main-class example.Main --main-jar ${{ env.pkg-assembly }}" 51 | - name: View artifacts 52 | run: "ls -l" 53 | - name: Upload dmg 54 | uses: actions/upload-artifact@v1 55 | with: 56 | name: dmgs 57 | path: "${{ env.pkg-name }}-${{ env.pkg-version }}.dmg" 58 | windows: 59 | needs: fat 60 | runs-on: [windows-latest] 61 | steps: 62 | - uses: actions/checkout@v2 63 | - uses: actions/setup-java@v1 64 | with: 65 | java-version: '14.0.0' 66 | java-package: jdk 67 | architecture: x64 68 | - name: Download fat jar 69 | uses: actions/download-artifact@v1 70 | with: 71 | name: jars 72 | - name: Package jar as msi 73 | run: "jpackage --app-version ${{ env.pkg-version }} --name ${{ env.pkg-name }} --type msi -i jars --main-class example.Main --main-jar ${{ env.pkg-assembly }} --win-dir-chooser" 74 | - name: View artifacts 75 | run: dir 76 | - name: Upload installer 77 | uses: actions/upload-artifact@v1 78 | with: 79 | name: msis 80 | path: "${{ env.pkg-name }}-${{ env.pkg-version }}.msi" 81 | linux: 82 | needs: fat 83 | runs-on: [ubuntu-latest] 84 | steps: 85 | - uses: actions/checkout@v2 86 | - uses: actions/setup-java@v1 87 | with: 88 | java-version: '14.0.0' 89 | java-package: jdk 90 | architecture: x64 91 | - name: Download fat jar 92 | uses: actions/download-artifact@v1 93 | with: 94 | name: jars 95 | - name: Package jar as debian package 96 | run: "jpackage --app-version ${{ env.pkg-version }} --name ${{ env.pkg-name }} --type deb -i jars --main-class example.Main --main-jar ${{ env.pkg-assembly }}" 97 | - name: View artifacts 98 | run: dir 99 | - name: Upload deb 100 | uses: actions/upload-artifact@v1 101 | with: 102 | name: debs 103 | path: "${{ env.pkg-name-debian }}_${{ env.pkg-version }}-1_amd64.deb" 104 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Cover Java 2 | *.class 3 | 4 | 5 | # Cover SBT 6 | .cache/ 7 | .history/ 8 | .lib/ 9 | dist/* 10 | target/ 11 | lib_managed/ 12 | src_managed/ 13 | project/boot/ 14 | project/plugins/project/ 15 | 16 | 17 | # Cover IntelliJ 18 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 19 | 20 | # User-specific stuff 21 | .idea/**/workspace.xml 22 | .idea/**/tasks.xml 23 | .idea/**/usage.statistics.xml 24 | .idea/**/dictionaries 25 | .idea/**/shelf 26 | 27 | # Generated files 28 | .idea/**/contentModel.xml 29 | 30 | # Sensitive or high-churn files 31 | .idea/**/dataSources/ 32 | .idea/**/dataSources.ids 33 | .idea/**/dataSources.local.xml 34 | .idea/**/sqlDataSources.xml 35 | .idea/**/dynamic.xml 36 | .idea/**/uiDesigner.xml 37 | .idea/**/dbnavigator.xml 38 | 39 | # Gradle 40 | .idea/**/gradle.xml 41 | .idea/**/libraries 42 | 43 | # Gradle and Maven with auto-import 44 | # When using Gradle or Maven with auto-import, you should exclude module files, 45 | # since they will be recreated, and may cause churn. Uncomment if using 46 | # auto-import. 47 | .idea/modules.xml 48 | .idea/*.iml 49 | .idea/modules 50 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Phillip Lenhardt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Demonstrate jpackage for Scala apps for OS X and Windows using GitHub workflows 2 | 3 | This repo demonstrates the integration of Scala and Java 14's jpackage using GitHub workflows to create installable apps for both Windows and OS X. 4 | 5 | ## Usage 6 | 7 | Commits to any branch named `release/*` should result in triggering the workflow actions that: 8 | * build a fat jar using sbt-assembly 9 | * build an OS X dmg file 10 | * build a Windows msi file 11 | 12 | The built artifacts (jars, msis, dmgs) can be found under the repo's Action tab. 13 | 14 | The dmg should be installable on OS X, adding an app named `SwingTest`. 15 | 16 | The msi should be installable on Windows, adding an app named `SwingTest`. 17 | 18 | ## Limitations 19 | 20 | These apps are not signed, so both OS X and Windows installs will result in security warnings. 21 | 22 | For some reason, the dmg file is not created with the usual Applications symlink and arrow icons. 23 | 24 | ## Tooling 25 | 26 | * [Scala](https://www.scala-lang.org) 27 | * [sbt-assembly](https://github.com/sbt/sbt-assembly) 28 | * [Java 14](https://openjdk.java.net/projects/jdk/14/) 29 | * [GitHub Workflows](https://help.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow) 30 | 31 | ## Author 32 | 33 | **Phillip Lenhardt** - [philen](https://github.com/philen) 34 | 35 | ## License 36 | 37 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 38 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "swingtest" 2 | organization := "example" 3 | version := "1.1" 4 | scalaVersion := "2.13.8" 5 | 6 | lazy val app = (project in file(".")) 7 | .settings( 8 | test in assembly := {}, 9 | mainClass in assembly := Some("example.Main"), 10 | assemblyMergeStrategy in assembly := { 11 | case PathList("META-INF", xs @ _*) => 12 | MergeStrategy.discard 13 | case other => 14 | MergeStrategy.defaultMergeStrategy(other) 15 | } 16 | ) 17 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.7.1 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0") 2 | -------------------------------------------------------------------------------- /src/main/scala/example/Main.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import java.awt.{BorderLayout, Dimension} 4 | import javax.swing.{JFrame, JScrollPane, JTextArea, WindowConstants} 5 | 6 | 7 | object Main extends App { 8 | val textArea = new JTextArea 9 | textArea.setText( 10 | """Lorem ipsum dolor sit amet, consectetur adipiscing elit, 11 | | sed do eiusmod tempor incididunt ut labore et dolore 12 | | magna aliqua. Ut enim ad minim veniam, quis nostrud 13 | | exercitation ullamco laboris nisi ut aliquip ex ea 14 | | commodo consequat. Duis aute irure dolor in 15 | | reprehenderit in voluptate velit esse cillum dolore eu 16 | | fugiat nulla pariatur. Excepteur sint occaecat 17 | | cupidatat non proident, sunt in culpa qui officia 18 | | deserunt mollit anim id est laborum.""".stripMargin) 19 | val scrollPane = new JScrollPane(textArea) 20 | 21 | val frame = new JFrame("Hello, Swing") 22 | frame.getContentPane.add(scrollPane, BorderLayout.CENTER) 23 | frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE) 24 | frame.setSize(new Dimension(600, 400)) 25 | frame.setLocationRelativeTo(null) 26 | frame.setVisible(true) 27 | } --------------------------------------------------------------------------------