Skip to content

Commit

Permalink
Standalone Executable (#660)
Browse files Browse the repository at this point in the history
Co-authored-by: Armin Samii <armin.samii@gmail.com>
Co-authored-by: HEdingfield <hylton@groupagree.com>
  • Loading branch information
3 people authored Jun 14, 2023
1 parent 64608c6 commit 477994d
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 31 deletions.
38 changes: 38 additions & 0 deletions .github/actions/gradle-and-sha/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: "Run Gradle command and create SHA512"
description: "Runs a Gradle command and creates SHA512 hashes"

inputs:
gradle-command:
description: "The Gradle command to run"
required: true
intermediate-filepath:
description: "The initial path of the output file"
required: true
final-filepath:
description: "The desired final path of the output file"
required: true

runs:
using: "composite"
steps:
- name: "Run Gradle command"
shell: bash
run: |
./gradlew ${{ inputs.gradle-command }}
- name: "Rename output file"
shell: bash
run: mv ${{ inputs.intermediate-filepath }} ${{ inputs.final-filepath }}

- name: "Generate SHA512 for Linux"
shell: bash
if: runner.os == 'Linux'
run: sha512sum ${{ inputs.final-filepath }} > ${{ inputs.final-filepath }}.sha512
- name: "Generate SHA512 for Windows"
shell: bash
if: runner.os == 'Windows'
run: certutil -hashfile ${{ inputs.final-filepath }} SHA512 >> ${{ inputs.final-filepath }}.sha512
- name: "Generate SHA512 for OSX"
shell: bash
if: runner.os == 'macOS'
run: openssl dgst -sha512 > ${{ inputs.final-filepath }}.sha512
124 changes: 101 additions & 23 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
name: Build Releases
name: "Generate Releases"

on:
release:
types: [published]
# To test this workflow without creating a release, uncomment the following and add a branch name:
# push:
# branches:
# - 'branch-name'

jobs:
release:
Expand All @@ -11,10 +15,48 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: "Create filename"
id: fn
- name: "Create base filename for all artifacts"
id: basefn
shell: bash
run: echo "FILENAME=build/rctab_${{ github.ref_name }}_${{ runner.os }}.zip" >> $GITHUB_OUTPUT
run: |
FILEPATH=$(echo rctab_${{ github.ref_name }}_${{ runner.os }} | sed -e 's/\//_/g')
echo "FILEPATH=$FILEPATH" >> $GITHUB_OUTPUT
# Normalize platform-specific filepaths generated by gradle
- name: "Create .zip filename"
id: zipfn
shell: bash
run: echo "FILEPATH=build/${{ steps.basefn.outputs.FILEPATH }}.zip" >> $GITHUB_OUTPUT
- name: "Get extension"
id: ext
shell: bash
run: |
if [ ${{ runner.os }} == 'Windows' ]; then
echo "EXT=.exe" >> $GITHUB_OUTPUT
elif [ ${{ runner.os }} == 'Linux' ]; then
echo "EXT=.deb" >> $GITHUB_OUTPUT
else
echo "EXT=.dmg" >> $GITHUB_OUTPUT
fi
- name: "Get jpackage output filepath"
id: jpackagefn
shell: bash
run: |
# TODO Sync version number with Main.java and build.gradle (github.com/BrightSpots/rcv/issues/662)
# The version numbers are hardcoded because the files below include the version number in them,
# and while we could use some regex to figure out the version number automatically, it seems cleaner
# to know the expected version number upfront.
if [ ${{ runner.os }} == 'Windows' ]; then
echo "FILEPATH=build/jpackage/RCTab-1.4.0-alpha.exe" >> $GITHUB_OUTPUT
elif [ ${{ runner.os }} == 'Linux' ]; then
echo "FILEPATH=build/jpackage/rctab_1.4.0-alpha-1_amd64.deb" >> $GITHUB_OUTPUT
else
echo "FILEPATH=build/jpackage/RCTab-1.4.0-alpha.dmg" >> $GITHUB_OUTPUT
fi
- name: "Create executable filename"
id: exefn
shell: bash
run: echo "FILEPATH=build/${{ steps.basefn.outputs.FILEPATH }}${{ steps.ext.outputs.EXT }}" >> $GITHUB_OUTPUT

- uses: actions/checkout@v3

Expand All @@ -27,40 +69,76 @@ jobs:
- name: "Validate Gradle wrapper"
uses: gradle/wrapper-validation-action@ccb4328a959376b642e027874838f60f8e596de3

- name: "Create zip with Gradle"
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
- name: "Create zip with jlinkZip"
uses: ./.github/actions/gradle-and-sha
with:
arguments: jlinkZip
gradle-command: jlinkZip
intermediate-filepath: build/rcv.zip
final-filepath: ${{ steps.zipfn.outputs.FILEPATH }}

- name: "Rename zip file"
run: cp build/rcv.zip ${{ steps.fn.outputs.FILENAME }}
- name: "Prepare keychain"
if: matrix.os == 'macOS-latest'
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
run: |
export TEMP_PWD=temporary-password-to-avoid-GUI-prompt
echo "Decode Base64 certificates"
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
echo "Create and unlock keychain"
security create-keychain -p $TEMP_PWD build.keychain
security unlock-keychain -p $TEMP_PWD build.keychain
echo "Import certificates into keychain"
# Note: in the next command, the -A should not be used outside of github actions.
# It allows any application to read the keychain, which is fine in an ephemeral environment,
# but not fine if you run this on your own machine.
security import certificate.p12 -k build.keychain -P $MACOS_CERTIFICATE_PWD -A -T /usr/bin/codesign -T /usr/bin/productbuild -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $TEMP_PWD build.keychain
- name: "Generate SHA512 for Linux"
if: runner.os == 'Linux'
run: sha512sum ${{ steps.fn.outputs.FILENAME }} > ${{ steps.fn.outputs.FILENAME }}.sha512
- name: "Generate SHA512 for Windows"
if: runner.os == 'Windows'
shell: bash
run: certutil -hashfile ${{ steps.fn.outputs.FILENAME }} SHA512 >> ${{ steps.fn.outputs.FILENAME }}.sha512
- name: "Generate SHA512 for OSX"
if: runner.os == 'macOS'
run: openssl dgst -sha512 > ${{ steps.fn.outputs.FILENAME }}.sha512
- name: "Create executable with jpackage (and sign, on MacOS)"
uses: ./.github/actions/gradle-and-sha
with:
gradle-command: jpackage
intermediate-filepath: ${{ steps.jpackagefn.outputs.FILEPATH }}
final-filepath: ${{ steps.exefn.outputs.FILEPATH }}

- name: "Notarize app bundle"
if: matrix.os == 'macOS-latest'
env:
MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
MACOS_NOTARIZATION_PWD: ${{ secrets.MACOS_NOTARIZATION_PWD }}
IDENTITY_PUBLIC_KEY: A257HB4NS4
run: |
echo "Unlock keychain"
security unlock-keychain -p temporary-password-to-avoid-GUI-prompt build.keychain
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k temporary-password-to-avoid-GUI-prompt build.keychain
echo "Create keychain profile"
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$MACOS_NOTARIZATION_APPLE_ID" --team-id "$MACOS_NOTARIZATION_TEAM_ID" --password "$MACOS_NOTARIZATION_PWD"
echo "Creating temp notarization archive"
ditto -c -k --sequesterRsrc --keepParent ${{ steps.exefn.outputs.FILEPATH }} "notarization.zip"
echo "Notarize app -- this may take a few minutes"
xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait
echo "Attach staple"
xcrun stapler staple ${{ steps.exefn.outputs.FILEPATH }}
- uses: actions/upload-artifact@v3
with:
name: Package
if-no-files-found: error
path: |
${{ github.workspace }}/${{ steps.fn.outputs.FILENAME }}
${{ github.workspace }}/${{ steps.fn.outputs.FILENAME }}.sha512
${{ github.workspace }}/${{ steps.zipfn.outputs.FILEPATH }}
${{ github.workspace }}/${{ steps.zipfn.outputs.FILEPATH }}.sha512
${{ github.workspace }}/${{ steps.exefn.outputs.FILEPATH }}
${{ github.workspace }}/${{ steps.exefn.outputs.FILEPATH }}.sha512
retention-days: 90

- name: "Upload binaries to release"
uses: svenstaro/upload-release-action@v2
if: ${{ github.event_name }} == 'release'
if: github.event_name == 'release'
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ github.workspace }}/${{ steps.fn.outputs.FILENAME }}*
file: build/${{ steps.basefn.outputs.FILEPATH }}*
tag: ${{ github.ref_name }}
overwrite: true
file_glob: true
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run Build, Lint, and Test
name: "Build, Lint, and Test"

on: [push]

Expand All @@ -10,10 +10,10 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up JDK 17
- name: "Set up JDK 17"
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'temurin'
- name: Test with Gradle
- name: "Test with Gradle"
run: ./gradlew check
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ The Tabulator produces the following as output:

1. Download the pre-compiled Tabulator for your OS from the GitHub [releases page](https://github.com/BrightSpots/rcv/releases).

**Note**: this download should be a "jlink image", which means you don't even need to have Java installed on your machine to run it!
**Note**: this download is a "jlink package", which means you don't even need to have Java installed on your machine to run it!

2. Unzip the file, navigate to the `bin` directory, and launch the RCV Tabulator GUI by running the `rcv` script if using MacOS or Linux, or `rcv.bat` if using Windows.

On Linux, you may install the .deb file, then run `/opt/rcv/bin/RCTab` to launch the tabulator GUI.

#### Method 2 (Less Easy): Compile and Run Using Gradle

1. Install [JDK 17 or higher](https://jdk.java.net/), and make sure your Java path is picking it up properly by verifying that the following command returns the expected version:
Expand Down
64 changes: 61 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,78 @@ javafx {
}

def JLINK_DIR = "$buildDir/rcv"

// Comma-separated list of modules
// Necessary to explicitly include due to https://github.com/BrightSpots/rcv/issues/625
def MODULES_TO_ADD = "com.ctc.wstx"

// ### jlink plugin settings
jlink {
imageDir = file(JLINK_DIR)
imageZip = file(JLINK_DIR + ".zip")
addOptions '--add-modules', MODULES_TO_ADD, '--strip-debug', '--compress', '2',
'--no-header-files', '--no-man-pages'
addOptions '--add-modules', MODULES_TO_ADD,
'--strip-debug', '--compress', '2',
'--no-header-files', '--no-man-pages'
mergedModule {
requires "java.xml"
}
launcher {
name = "rcv"
// TODO Sync version number with release.yml and Main.java (github.com/BrightSpots/rcv/issues/662)
name = "RCTab"
version = "1.4.0-alpha"
}

jpackage {
installerOptions += [
'--verbose',
'--vendor', 'Ranked Choice Voting Resource Center'
]
if (org.gradle.internal.os.OperatingSystem.current().isLinux()) {
imageOptions += [
'--icon', 'src/main/resources/network/brightspots/rcv/launcher.png',
]

installerOptions += [
'--linux-package-name', 'rctab',
'--linux-deb-maintainer', 'info@rcvresources.org',
'--linux-shortcut'
]
}
if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
imageOptions += [
'--icon', 'src/main/resources/network/brightspots/rcv/launcher.ico',
]

installerOptions += [
'--win-per-user-install',
'--win-dir-chooser',
'--win-menu',
'--win-shortcut'
]
}
if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) {
// .App is signed with the Application certificate
imageOptions += [
'--icon', 'src/main/resources/network/brightspots/rcv/launcher.icns',
'--resource-dir', 'src/main/resources/network/brightspots/rcv/',
'--mac-sign',
'--mac-signing-key-user-name', 'Developer ID Application: Election Administration Resource Center (A257HB4NS4',
'--mac-signing-keychain', 'build.keychain'
]

// DMG is signed with the Installer certificate
installerOptions += [
'--type', 'dmg',
'--icon', 'src/main/resources/network/brightspots/rcv/launcher.icns',
'--resource-dir', 'src/main/resources/network/brightspots/rcv/',
'--mac-sign',
'--mac-signing-key-user-name', 'Developer ID Installer: Election Administration Resource Center (A257HB4NS4)',
'--mac-signing-keychain', 'build.keychain',
'--mac-package-name', 'RCTab',
'--mac-package-identifier', 'network.brightspots.rcv',
'--mac-package-signing-prefix', 'network.brightspots.rcv'
]
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/network/brightspots/rcv/GuiApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Screen;
import javafx.stage.Stage;

Expand All @@ -38,10 +39,14 @@ public void start(Stage window) {
context.setMainWindow(window);

String resourcePath = "/network/brightspots/rcv/GuiConfigLayout.fxml";
String iconPath = "/network/brightspots/rcv/launcher.png";
try {
Parent root = FXMLLoader.load(getClass().getResource(resourcePath));
window.setTitle(Main.APP_NAME);
window.setScene(new Scene(root));

Image icon = new Image(getClass().getResourceAsStream(iconPath));
window.getIcons().add(icon);
} catch (IOException exception) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/network/brightspots/rcv/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
public class Main extends GuiApplication {

public static final String APP_NAME = "RCTab";
public static final String APP_VERSION = "1.4.0.alpha";

// TODO Sync version number with release.yml and build.gradle:
// github.com/BrightSpots/rcv/issues/662
public static final String APP_VERSION = "1.4.0-alpha";

/**
* Main entry point to RCTab.
Expand Down
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 477994d

Please sign in to comment.