Skip to content

Instantly share code, notes, and snippets.

@talkingmoose
Last active June 26, 2023 05:37
Show Gist options
  • Save talkingmoose/e9ed319226c6da30dd633725e48a97b0 to your computer and use it in GitHub Desktop.
Save talkingmoose/e9ed319226c6da30dd633725e48a97b0 to your computer and use it in GitHub Desktop.
The pkgbuild binary and Jamf Composer don't support adding single files of 8 GB or more to a package. Some apps like Install macOS Big Sur.app include files larger than 8 GB in their bundles. This script will create a deployable PKG file from apps whose bundles include those large files.
#!/bin/zsh
:<<ABOUT_THIS_SCRIPT
-------------------------------------------------------------------------------
Written by:William Smith
Professional Services Engineer
Jamf
bill@talkingmoose.net
https://gist.github.com/e9ed319226c6da30dd633725e48a97b0
Originally posted: October 15, 2020
Updated: December 2, 2020
Updated: December 11, 2020
Purpose: Creates a deployable PKG file from an app whose app bundle
contains one or more files larger than the 8 GB limit supported by pkgbuild
or Jamf Composer.
Instructions:
1. Save the script to a file named MegaPKGr.zsh on your Mac.
2. If necessary, run 'chmod +x /path/to/MegaPKGr.zsh' to make it execu-
table.
3. To run the script, open Terminal and enter:
/path/to/MegaPKGr.zsh /path/to/large.app
Except where otherwise noted, this work is licensed under
http://creativecommons.org/licenses/by/4.0/
"If you try to save wisdom until the world is wise, Father,
the world will never have it."
-------------------------------------------------------------------------------
ABOUT_THIS_SCRIPT
function logmessage() {
echo -ne "$1\r"
/bin/sleep 1
}
function logresult() {
if [ $? = 0 ] ; then
echo "$1"
/bin/date "+%Y-%m-%d %H:%M:%S $1" >> "$logFile"
/bin/sleep 1
else
echo "$2"
/bin/date "+%Y-%m-%d %H:%M:%S $2" >> "$logFile"
echo "Aborting script"
/bin/date "+%Y-%m-%d %H:%M:%S Aborting script" >> "$logFile"
cleanup
exit 1
fi
}
function cleanup() {
logmessage "Removing temporary items..."
/bin/rm -Rf "$tempDirectory"
logresult "Succeeded removing temporary items" "Failed removing temporary items"
}
# need to run this script with elevated privileges
if [ $EUID -ne 0 ]; then
echo
echo "This script requires elevated privileges. Run it again with \"sudo\"."
echo
exit 0
fi
# define log file location
logFile="/Library/Logs/MegaPKGr.log"
# choose app for packaging
appPath="$1"
while [[ "$appPath" = "" ]]
do
echo
echo "Drag the app for packaging into this Terminal window and press return."
echo "To abort this script, press Control-C."
echo
read -p "App path: " appPath
done
app=$( /usr/bin/basename "$appPath" )
echo
# the time right now
startTime=$( /bin/date '+%s' )
# prompt for folder for the final package
theCommand='set chooseFolder to POSIX path of (choose folder with prompt "Save package to this folder...")'
chooseDirectory=$( /usr/bin/osascript -e "$theCommand" )
# verify chosen directory is writable
logmessage "Verifying chosen folder..."
/usr/bin/touch "$chooseDirectory/.test"
logresult "Chosen folder is writable" "Chosen folder is not writable"
/bin/rm "$chooseDirectory/.test"
# verify disk space needed to package app
appSize=$( /usr/bin/du -md 0 "$appPath" | /usr/bin/awk '{ print $1 }' )
logmessage "Size of \"$app\" in MB: $appSize"
availableSpace=$( /bin/df -g /System/Volumes/Data | /usr/bin/grep dev | /usr/bin/awk '{ print $4 }' )
logmessage "Available disk space: $availableSpace GB "
neededSpace=$(( appSize * 3 / 1024 ))
logmessage "Disk space required to build package: $neededSpace GB "
[ $availableSpace -gt $neededSpace ]
logresult "Disk has enough free space to build \"$app.pkg\"" "Disk does not have enough free space to build \"$app.pkg\""
echo "Log file is located in /Library/Logs/MegaPKGr.log"
echo
# create temporary working directory
logmessage "Creating temporary working directory..."
tempDirectory=$( /usr/bin/mktemp -d "/private/tmp/MegaPKGr.XXXXXX" )
logresult "Succeeded creating temporary working directory" "Failed creating temporary working directory"
# create dmg from the app to create a single file
logmessage "Creating disk image (DMG) archive of \"$app\"..."
/usr/bin/hdiutil create -quiet "$tempDirectory/$app.dmg" -ov -volname MegaPKGr -fs APFS -srcfolder "$appPath"
logresult "Succeeded creating DMG archive of $app" "Failed creating DMG archive of $app"
# create folder structure for pkgbuild
logmessage "Ceating pkgbuild ROOT directory..."
/bin/mkdir -p "$tempDirectory/ROOT/private/tmp/MegaPKGrFileParts" && cd "$tempDirectory/ROOT/private/tmp/MegaPKGrFileParts"
logresult "Succeeded creating pkgbuild ROOT directory" "Failed creating pkgbuild ROOT directory"
logmessage "Creating pkgbuild Scripts directory"
/bin/mkdir -p "$tempDirectory/Scripts"
logresult "Succeeded creating pkgbuild Scripts directory" "Failed creating pkgbuild Scripts directory"
# split archive file into 500 MB file chunks
logmessage "Creating 500 MB file chunks from archive file..."
/usr/bin/split -b 500m "$tempDirectory/$app.dmg"
logresult "Succeeded creating 500 MB file chunks from archive file" "Failed creating 500 MB file chunks from archive file"
# create postinstall script for pkgbuild
logmessage "Creating pkgbuild postinstall script..."
/bin/cat << 'EOF' > "$tempDirectory/Scripts/postinstall"
#!/bin/bash
function logresult() {
if [ $? = 0 ] ; then
echo "$1"
else
echo "$2"
exit 1
fi
}
function cleanup() {
/bin/rm -Rf "$tempDirectory/" "/private/tmp/MegaPKGr"*
logresult "[RESULT: $?] Succeeded removing temporary items" "[ERROR: $?] Failed removing temporary items"
}
# create temporary working directory
echo "Creating temporary working directory"
tempDirectory=$( /usr/bin/mktemp -d "/private/tmp/MegaPKGr.XXXXXX" )
logresult "[RESULT: $?] Succeeded creating temporary working directory" "[ERROR: $?] Failed creating temporary working directory"
# change directory to temporary working directory
echo "Changing directory to working directory '$tempDirectory'"
cd "$tempDirectory"
logresult "[RESULT: $?] Succeeded changing to temporary working directory" "[ERROR: $?] Failed changing to temporary working directory"
# recombine file parts
echo "Recombining file parts"
/bin/cat /private/tmp/MegaPKGrFileParts/x* > "$tempDirectory/MegaPKGrFileParts.dmg"
logresult "[RESULT: $?] Succeeded recombining MegaPKGrFileParts file parts" "[ERROR: $?] Failed recombining MegaPKGrFileParts file parts"
# copy the application to original location
echo "Extracting archived file"
# mounting recombined DMG
/usr/bin/hdiutil attach -nobrowse "$tempDirectory/MegaPKGrFileParts.dmg"
logresult "[RESULT: $?] Succeeded mounting DMG" "[ERROR: $?] Failed mounting DMG"
# dittoing DMG contents to local disk
/usr/bin/ditto -rsrc "/Volumes/MegaPKGr/" /Applications
logresult "[RESULT: $?] Succeeded dittoing files to specified location" "[ERROR: $?] Failed dittoing files to specified location"
# unmounting recombined DMG
/sbin/umount "/Volumes/MegaPKGr"
logresult "[RESULT: $?] Succeeded unmounting DMG" "[ERROR: $?] Failed unmounting DMG"
# delete temporary files
echo "Deleting temporary items"
cleanup
exit 0
EOF
/bin/chmod +x "$tempDirectory/Scripts/postinstall"
logresult "Succeeded creating pkgbuild postinstall script" "Failed creating pkgbuild postinstall script"
# create installer package
logmessage "Creating package \"$app.pkg\"..."
/usr/bin/pkgbuild --quiet --identifier net.talkingmoose.MegaPKGr --version "1.0.0" --scripts "$tempDirectory/Scripts" --root "$tempDirectory/ROOT" "$chooseDirectory/$app.pkg"
logresult "Succeeded creating package \"$app.pkg\" in $chooseDirectory" "Failed creating package \"$app.pkg\" in \"$chooseDirectory\""
# remove temporary files
logmessage "Removing temporary items..."
cleanup
echo
# calculate and report script run time
stopTime=$( /bin/date '+%s' )
seconds=$(( $stopTime-$startTime ))
formattedTime=$( /bin/date -j -f "%s" "$seconds" '+%M minutes and %S seconds' )
logresult "Packaging time: $formattedTime"
echo
exit 0
@Kuldeep-Github
Copy link

Kuldeep-Github commented May 26, 2021

@talkingmoose

thank you writing this script, this really helps us.
My question, can we put the .app under /private/var ? instead /Application directory.
if yes, then where we need to make change in script.
Actually, I am trying to deploy the package via Jamf and want to cache the package under /private/var/ ,so that, its not visible for users to do any thing.

@dev-yeet
Copy link

dev-yeet commented May 26, 2021

@Kuldeep-Github

replace the only reference to "/Applications" with the new destination; i just tested with /private/var/tmp and it placed the installer there for me instead.

@Kuldeep-Github
Copy link

Thank you, @dev-yeet.
Yes it drops the location where we want by just replacing "/Application" to new_path

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment