Files
JIMRI/scripts/diskimage-sign.sh
2026-06-17 14:00:51 +02:00

313 lines
10 KiB
Bash
Executable File

#!/bin/bash
#
# Create a signed and notarized JMRI macOS disk image (from diskimage.sh) from an unsigned one
# This is structured to use the Mac OS X tools when run on a Mac, and native Linux tools when run on a Linux box,
# but only the OS X version is being actively developed because it relies on some Apple-specific
# tools for notarization.
#
# arguments are:
# Release version string e.g. "4.5.5", e.g. "${release.version-string}" from Ant
# Output DMG file pathname e.g. dist/release/JMRI.4.5.5.dmg, "${dist.release}/JMRI.${release.version-string}.dmg" in Ant
# Input DMG file pathname e.g. dist/release/JMRI.4.5.5-unsigned.dmg,
# ID of signing certificate, i.e. "Developer ID Application: My Name"
# Apple ID for notarization
# Application-specific password on that Apple ID (see appleid.apple.com)
# Location of keychain file containing that certificate
# Password to unlock that keychain file
#
# Although it might be consuming additional space on the final disk image,
# we do the signing and jar-updating there to ensure this doesn't
# cause issues for other uses of the raw as-built files
#
# Copyright 2007, 2011, 2016, 2019 Bob Jacobsen, david d zuhn
#
set -e # bail on errors
set -x # show our work
whoami
REL_VER=$1
OUTPUT=$2
INPUTIMAGEFILE=$3
CERTIFICATE=$4
AC_USER=$5
AC_PASSWORD=$6
KEYCHAIN_FILE=$7
JAR=/usr/bin/jar
# -----------------------------------------
function trapExitHandler {
trap - 1 2 3 15
cp /tmp/php-temp-out-file.dmg /tmp/test.dmg
umount "$tmpimage2" && echo "Unmounted tmpimage2"
umount "$tmpindir" && echo "Unmounted tempindir"
rm -rf "$INPUTIMAGEFILE" && echo "Deleted input image file"
umount "$tmpoutdir" && echo "Unmounted tmpoutdir image"
rm -rf "$tmpimage1" "$tmpimage2" "$tmpoutdir" "$tmpindir" >/dev/null 2>&1
}
# -----------------------------------------
# Retry a command up to a specific number of times until it exits successfully.
# Waits 1 minute between retries
#
# $ retry 5 echo Hello
# Hello
#
#
function retry {
local retries=$1
shift
local count=0
until "$@"; do
exit=$?
wait=60
count=$(($count + 1))
if [ $count -lt $retries ]; then
echo "Retry $count/$retries exited $exit, retrying in $wait seconds..."
sleep $wait
else
echo "Retry $count/$retries exited $exit, no more retries left."
return $exit
fi
done
return 0
}
# -----------------------------------------
# Sign a file (2nd arg) with a jar file (1st arg) in place
#
function signJarMember {
local jar=$1
local file=$2
if [ -f "$jar" ]
then
export JAVA_HOME=`/usr/libexec/java_home -v 1.8`
$JAR xf $jar $file
signFile $file
$JAR uvf $jar $file
rm $file
fi
return 0
}
# -----------------------------------------
# Sign a single file (1st arg)
#
function signFile {
local file=$1
echo sign $file
if [ -e "$file" ]
then
xattr -lr $file
xattr -cr $file
sudo -u jake codesign -v -s "$CERTIFICATE" --force --keychain "$KEYCHAIN_FILE" --deep $file
fi
return 0
}
# -----------------------------------------
if [ "$REL_VER" = "" -o "$OUTPUT" = "" -o "$INPUTIMAGEFILE" = "" ]
then
echo "usage: $0 VERSION OUTPUTIMAGEFILE INPUTIMAGEFILE" 1>&2
exit 1
fi
# switch to a directory with known-good permissions
if ! workdir=`mktemp -d -t workdir`
then
echo "Cannot create temporary working directory"
exit 1
fi
cd $workdir
echo 'workdir' $workdir
if [ -x /usr/bin/hdiutil ]
then
# if a Linux box were to have hdiutil, I think that would be the preferable route to follow
# although it's pretty unlikely
SYSTEM=MACOSX
export JAVA_HOME=`/usr/libexec/java_home -v 1.8`
java -version
else
SYSTEM=LINUX
fi
if ! tmpoutdir=`mktemp -d -t JMRI.output`
then
echo "Cannot create output temporary directory"
exit 1
fi
echo "tmpoutdir" $tmpoutdir
if ! tmpindir=`mktemp -d -t JMRI.input`
then
echo "Cannot create input temporary directory"
exit 1
fi
echo 'tmpindir' $tmpindir
if ! tmpimage1=`mktemp -t JMRI.tmp.image.1`
then
echo "Cannot create temp image 1"
exit
fi
echo 'tempimage1' $tmpimage1
if ! tmpimage2=`mktemp -t JMRI.tmp.image.2`
then
echo "Cannot create temp image 1"
exit 1
fi
echo 'tmpimage2' $tmpimage2
# handle cleanup on exit
trap trapExitHandler 0
# handle error signals by aborting
trap 'exit 2' 1 2 3 15
# shouldn't be anything left over, but if so, clean up
rm -f "$tmpimage1" "$tmpimage2"
# mount input image
sync
hdiutil attach "$INPUTIMAGEFILE" -mountpoint "$tmpindir" -nobrowse
echo "INPUTIMAGEFILE" $INPUTIMAGEFILE
# create disk image and mount
jmrisize=`du -ms "$tmpindir" | awk '{print $1}'`
imagesize=`expr $jmrisize + 80`
if [ "$SYSTEM" = "MACOSX" ]
then
hdiutil create -size ${imagesize}MB -fs HFS+ -layout SPUD -volname "JMRI ${REL_VER}" "$tmpimage2"
hdiutil attach ${tmpimage2}.dmg -mountpoint "$tmpoutdir" -nobrowse
else
dd if=/dev/zero of="$tmpimage2" bs=1M count=${imagesize}
mkfs.hfsplus -v "JMRI ${REL_VER}" "${tmpimage2}"
sudo mount -t hfsplus -o loop,rw,uid=$UID "$tmpimage2" $tmpoutdir
fi
# wait for the mountpoint to settle down...
# I don't think we need this on macOS
#sleep 10
if [ -w "$tmpoutdir" ]
then
SUDO=
else
SUDO=sudo
fi
# copy contents of the Mac OS X distribution to the newly mounted filesysten
tar -C "$tmpindir" -cf - JMRI | $SUDO tar -C "$tmpoutdir" -xf -
# unlock login keychain (if needed, which is when?)
#security unlock-keychain -p password /Users/jake/Library/Keychains/login.keychain-db
# display debug info for the keychain containing the certification
#security list-keychains
#security -v default-keychain
#security -v login-keychain
#security -v show-keychain-info "$KEYCHAIN_FILE"
#security -v show-keychain-info /Users/jake/Library/Keychains/login.keychain-db
#security -v find-certificate -c "$CERTIFICATE" "$KEYCHAIN_FILE"
# sign the app files in output
signFile $tmpoutdir/JMRI/PanelPro.app
signFile $tmpoutdir/JMRI/DecoderPro.app
signFile $tmpoutdir/JMRI/SoundPro.app
signFile $tmpoutdir/JMRI/LccPro.app
# clear attributes in lib
xattr -cr $tmpoutdir/JMRI/lib
# sign the individual library files
signFile $tmpoutdir/JMRI/lib/macosx/libgluegen-rt.jnilib
signFile $tmpoutdir/JMRI/lib/macosx/libjinput-osx.jnilib
signFile $tmpoutdir/JMRI/lib/macosx/libjoal.jnilib
signFile $tmpoutdir/JMRI/lib/macosx/libgluegen_rt.dylib
signFile $tmpoutdir/JMRI/lib/macosx/libjoal.dylib
signFile $tmpoutdir/JMRI/lib/macosx/libopenal.1.15.1.dylib
signFile $tmpoutdir/JMRI/lib/macosx/libopenal.1.dylib
signFile $tmpoutdir/JMRI/lib/macosx/libopenal.dylib
# sign libraries inside jar files
signJarMember $tmpoutdir/JMRI/lib/libusb4java-1.3.0-darwin-x86-64.jar org/usb4java/darwin-x86-64/libusb4java.dylib
signJarMember $tmpoutdir/JMRI/lib/libusb4java-1.3.0-darwin-aarch64.jar org/usb4java/darwin-aarch64/libusb4java.dylib
signJarMember $tmpoutdir/JMRI/lib/libusb4java-1.3.0-darwin-aarch64.jar org/usb4java/darwin-x86-64/libusb4java.dylib
signJarMember $tmpoutdir/JMRI/lib/bluecove-2.1.1-SNAPSHOT.jar libbluecove.jnilib
signJarMember $tmpoutdir/JMRI/lib/jna-4.4.0.jar com/sun/jna/darwin/libjnidispatch.jnilib
signJarMember $tmpoutdir/JMRI/lib/jna-5.9.0.jar com/sun/jna/darwin-x86-64/libjnidispatch.jnilib
signJarMember $tmpoutdir/JMRI/lib/jna-5.9.0.jar com/sun/jna/darwin-aarch64/libjnidispatch.jnilib
signJarMember $tmpoutdir/JMRI/lib/hid4java-0.5.0.jar darwin/libhidapi.dylib
# signJarMember $tmpoutdir/JMRI/lib/selenium-server-standalone-3.6.0.jar com/sun/jna/darwin/libjnidispatch.jnilib # OMITTED DUE TO TOC ISSUE
signJarMember $tmpoutdir/JMRI/lib/jython-standalone-2.7.2.jar META-INF/native/osx/libjansi.jnilib
signJarMember $tmpoutdir/JMRI/lib/jython-standalone-2.7.2.jar jni/Darwin/libjffi-1.2.jnilib
# added 2024-08-29
signJarMember $tmpoutdir/JMRI/lib/jinput-2.0.9-natives-all.jar libjinput-osx.jnilib
signJarMember $tmpoutdir/JMRI/lib/jna-5.13.0.jar com/sun/jna/darwin-aarch64/libjnidispatch.jnilib
signJarMember $tmpoutdir/JMRI/lib/jna-5.13.0.jar com/sun/jna/darwin-x86-64/libjnidispatch.jnilib
signJarMember $tmpoutdir/JMRI/lib/jSerialComm-2.10.4.jar OSX/x86/libjSerialComm.jnilib
signJarMember $tmpoutdir/JMRI/lib/jSerialComm-2.10.4.jar OSX/aarch64/libjSerialComm.jnilib
signJarMember $tmpoutdir/JMRI/lib/jSerialComm-2.10.4.jar OSX/x86_64/libjSerialComm.jnilib
signJarMember $tmpoutdir/JMRI/lib/jython-standalone-2.7.2.jar jni/Darwin/libjffi-1.2.jnilib
# added 2024-09-03
signJarMember $tmpoutdir/JMRI/lib/jython-standalone-2.7.4.jar META-INF/native/osx/libjansi.jnilib
signJarMember $tmpoutdir/JMRI/lib/jython-standalone-2.7.4.jar jni/Darwin/libjffi-1.2.jnilib
# added 2025-03-09 for DarkLAF
signJarMember $tmpoutdir/JMRI/lib/darklaf-macos-3.0.2.jar com/github/weisj/darklaf/platform/darklaf-macos/libdarklaf-macos-x86-64.dylib
signJarMember $tmpoutdir/JMRI/lib/darklaf-macos-3.0.2.jar com/github/weisj/darklaf/platform/darklaf-macos/libdarklaf-macos-arm64.dylib
# add an Applications icon
$SUDO ln -s /Applications "$tmpoutdir"
# now, how do we make a nice background picture in the folder with directions on how to drag'n'drop to Applications?
# eject the mounted disk image
if [ "$SYSTEM" = "MACOSX" ]
then
hdiutil detach "$tmpoutdir" && EJECTED=1
else
sudo umount "$tmpoutdir" && EJECTED=1
fi
# pack into a smaller disk image for distribution
if [ "$SYSTEM" = "MACOSX" ]
then
rm -f "$OUTPUT"
hdiutil convert ${tmpimage2}.dmg -format UDZO -imagekey zlib-level=9 -o "$OUTPUT"
else
# this relies on the 'dmg' tool from https://github.com/erwint/libdmg-hfsplus
dmg dmg "$tmpimage2" "$OUTPUT"
fi
# sign image file
signFile "$OUTPUT"
# notarize distribution: start by uploading
# xcrun altool --notarize-app --primary-bundle-id "org.jmri" --username "$AC_USER" --password "$AC_PASSWORD" --file "$OUTPUT"
xcrun notarytool submit /tmp/php-temp-out-file.dmg --apple-id "$AC_USER" --password "$AC_PASSWORD" --team-id V2UXGA8SJW --wait
# stapling result will temporarily fail while the notatization is still happening at Apple
# --wait in new (Nov 2022) notarytool operation above should make this redundant
sleep 30
retry 20 xcrun stapler staple "$OUTPUT"
# clean up
hdiutil detach $tmpindir
# this should also be handled by the trap, but it doesn't hurt...
rm -rf $tmpimage1 $tmpimage2 $tmpoutdir