feat(bundler) dmg bundle (#551)

* feat(bundler) dmg bundle

* feat(bundler) fix dmg bundle support scripts, add license option

* chore(bundler) add "forked from" notice

* fix(tests) remove dmg bundling from the template test
This commit is contained in:
Lucas Fernandes Nogueira 2020-04-07 11:18:37 -03:00 committed by GitHub
parent d54c3b4ce5
commit db7d10308f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 761 additions and 204 deletions

View File

@ -22,17 +22,35 @@ use std::path::PathBuf;
pub fn bundle_project(settings: Settings) -> crate::Result<Vec<PathBuf>> {
let mut paths = Vec::new();
for package_type in settings.package_types()? {
paths.append(&mut match package_type {
PackageType::OsxBundle => osx_bundle::bundle_project(&settings)?,
let package_types = settings.package_types()?;
for package_type in &package_types {
let mut bundle_paths = match package_type {
PackageType::OsxBundle => {
if package_types.clone().iter().any(|&t| t == PackageType::Dmg) {
vec![]
} else {
osx_bundle::bundle_project(&settings)?
}
}
PackageType::IosBundle => ios_bundle::bundle_project(&settings)?,
#[cfg(target_os = "windows")]
PackageType::WindowsMsi => msi_bundle::bundle_project(&settings)?,
PackageType::Deb => deb_bundle::bundle_project(&settings)?,
PackageType::Deb => {
if package_types
.clone()
.iter()
.any(|&t| t == PackageType::AppImage)
{
vec![]
} else {
deb_bundle::bundle_project(&settings)?
}
}
PackageType::Rpm => rpm_bundle::bundle_project(&settings)?,
PackageType::AppImage => appimage_bundle::bundle_project(&settings)?,
PackageType::Dmg => dmg_bundle::bundle_project(&settings)?,
});
};
paths.append(&mut bundle_paths);
}
settings.copy_resources(settings.project_out_directory())?;

View File

@ -90,5 +90,5 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
.spawn()
.expect("Failed to execute shell script");
Ok(vec![appimage_path])
Ok(vec![package_dir, appimage_path])
}

View File

@ -2,82 +2,93 @@ use super::common;
use super::osx_bundle;
use crate::Settings;
use handlebars::Handlebars;
use lazy_static::lazy_static;
use std::collections::BTreeMap;
use std::fs::{write, File};
use std::io::Write;
use std::fs::{self, write};
use std::path::PathBuf;
use std::process::{Command, Stdio};
// Create handlebars template for shell scripts
lazy_static! {
static ref HANDLEBARS: Handlebars<'static> = {
let mut handlebars = Handlebars::new();
handlebars
.register_template_string("bundle_dmg", include_str!("templates/bundle_dmg"))
.expect("Failed to setup handlebars template");
handlebars
};
}
use crate::ResultExt;
// create script files to bundle project and execute bundle_script.
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
// generate the app.app folder
osx_bundle::bundle_project(settings)?;
// get uppercase string of app name
let upcase = settings.binary_name().to_uppercase();
// generate BTreeMap for templates
let mut sh_map = BTreeMap::new();
sh_map.insert("app_name", settings.binary_name());
sh_map.insert("app_name_upcase", &upcase);
let bundle_temp = HANDLEBARS
.render("bundle_dmg", &sh_map)
.or_else(|e| Err(e.to_string()))?;
let app_name = settings.bundle_name();
// get the target path
let output_path = settings.project_out_directory();
let output_path = settings.project_out_directory().join("bundle/dmg");
let dmg_name = format!("{}.dmg", app_name.clone());
let dmg_path = output_path.join(&dmg_name.clone());
let bundle_name = &format!("{}.app", app_name);
let bundle_dir = settings.project_out_directory().join("bundle/osx");
let bundle_path = bundle_dir.join(&bundle_name.clone());
let support_directory_path = output_path.join("support");
if output_path.exists() {
fs::remove_dir_all(&output_path).chain_err(|| format!("Failed to remove old {}", dmg_name))?;
}
fs::create_dir_all(&support_directory_path).chain_err(|| {
format!(
"Failed to create output directory at {:?}",
support_directory_path
)
})?;
// create paths for script
let bundle_sh = output_path.join("bundle_dmg.sh");
let bundle_script_path = output_path.join("bundle_dmg.sh");
let license_script_path = support_directory_path.join("dmg-license.py");
common::print_bundling(format!("{:?}", &output_path.join(format!("{}.dmg", &upcase))).as_str())?;
common::print_bundling(format!("{:?}", &dmg_path.clone()).as_str())?;
// write the scripts
write(&bundle_sh, bundle_temp).or_else(|e| Err(e.to_string()))?;
// copy seticon binary
let seticon = include_bytes!("templates/seticon");
let seticon_out = &output_path.join("seticon");
let mut seticon_buffer = File::create(seticon_out).or_else(|e| Err(e.to_string()))?;
seticon_buffer
.write_all(seticon)
.or_else(|e| Err(e.to_string()))?;
write(&bundle_script_path, include_str!("templates/dmg/bundle_dmg")).or_else(|e| Err(e.to_string()))?;
write(support_directory_path.join("template.applescript"), include_str!("templates/dmg/template.applescript"))?;
write(&license_script_path, include_str!("templates/dmg/dmg-license.py"))?;
// chmod script for execution
Command::new("chmod")
.arg("777")
.arg(&bundle_sh)
.arg(&seticon_out)
.current_dir(output_path)
.arg(&bundle_script_path)
.arg(&license_script_path)
.current_dir(output_path.clone())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.expect("Failed to chmod script");
let mut args = vec![
"--volname",
&app_name,
"--volicon",
"../../../../icons/icon.icns",
"--icon",
&bundle_name, "180", "170",
"--app-drop-link",
"480", "170",
"--window-size",
"660", "400",
"--hide-extension",
&bundle_name,
];
if let Some(license_path) = settings.osx_license() {
args.push("--eula");
args.push(license_path);
}
// execute the bundle script
Command::new(&bundle_sh)
.current_dir(output_path)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
let status = Command::new(&bundle_script_path)
.current_dir(bundle_dir.clone())
.args(args)
.args(vec![dmg_name.as_str(), bundle_name.as_str()])
.status()
.expect("Failed to execute shell script");
Ok(vec![bundle_sh])
if !status.success() {
Err(crate::Error::from("error running bundle_dmg.sh"))
} else {
fs::rename(bundle_dir.join(dmg_name.clone()), dmg_path.clone())?;
Ok(vec![bundle_path, dmg_path])
}
}

View File

@ -97,6 +97,7 @@ struct BundleSettings {
deb_depends: Option<Vec<String>>,
osx_frameworks: Option<Vec<String>>,
osx_minimum_system_version: Option<String>,
osx_license: Option<String>,
// Bundles for other binaries/examples:
bin: Option<HashMap<String, BundleSettings>>,
example: Option<HashMap<String, BundleSettings>>,
@ -563,6 +564,14 @@ impl Settings {
.as_ref()
.map(String::as_str)
}
pub fn osx_license(&self) -> Option<&str> {
self
.bundle_settings
.osx_license
.as_ref()
.map(String::as_str)
}
}
fn bundle_settings_from_table(
@ -634,6 +643,7 @@ fn merge_settings(
config.osx.minimum_system_version,
bundle_settings.osx_minimum_system_version,
),
osx_license: options_value(config.osx.license, bundle_settings.osx_license),
external_bin: options_value(config.external_bin, bundle_settings.external_bin),
exception_domain: options_value(
config.osx.exception_domain,

View File

@ -11,11 +11,12 @@ pub struct DebConfig {
}
#[derive(PartialEq, Deserialize, Clone, Debug, Default)]
#[serde(tag = "deb", rename_all = "camelCase")]
#[serde(tag = "osx", rename_all = "camelCase")]
pub struct OsxConfig {
pub frameworks: Option<Vec<String>>,
pub minimum_system_version: Option<String>,
pub exception_domain: Option<String>,
pub license: Option<String>,
}
#[derive(PartialEq, Deserialize, Clone, Debug, Default)]

View File

@ -1,144 +0,0 @@
#!/usr/bin/env sh
# using sh because modern MacOS ships with zsh not bash
# some patterns "lifted" from:
# - https://github.com/andreyvit/create-dmg/blob/master/create-dmg
# - https://stackoverflow.com/a/97025
# - https://github.com/sindresorhus/create-dmg/blob/master/cli.js
set -e
MACOS_APP_NAME="{{app_name_upcase}}"
MACOS_APP_DIR="bundle/osx/{{app_name}}.app"
ICNS_FILE="app.app/Contents/Resources/icon.icns"
DMG_TEMP_NAME=/tmp/temp.dmg
MOUNT_DIR="/Volumes/${MACOS_APP_NAME}"
# Create the image
echo "Creating disk image..."
test -f "${DMG_TEMP_NAME}" && rm -f "${DMG_TEMP_NAME}"
test -f "${MACOS_APP_NAME}.dmg" && rm -f "${MACOS_APP_NAME}.dmg"
hdiutil create ${DMG_TEMP_NAME} -srcfolder bundle/osx -volname "${MACOS_APP_NAME}" -ov -format UDRW -size 10000k -fs HFS+ -fsargs "-c c=64,a=16,e=16" -anyowners -nospotlight
# mount it
echo "Mounting disk image..."
# try unmount dmg if it was mounted previously (e.g. developer mounted dmg, installed app and forgot to unmount it)
DEV_NAME=$(hdiutil info | egrep --color=never '^/dev/' | sed 1q | awk '{print $1}')
test -d "${MOUNT_DIR}" && hdiutil detach "${DEV_NAME}"
device=$(hdiutil attach -readwrite -noverify -noautoopen "${DMG_TEMP_NAME}" | \
egrep '^/dev/' | sed 1q | awk '{print $1}')
sleep 5
bless --folder "${MOUNT_DIR}" -label "${MACOS_APP_NAME}"
test -d "${MOUNT_DIR}/.background" || mkdir "${MOUNT_DIR}/.background"
test -f ../../icons/bg.png && cp ../../icons/bg.png "${MOUNT_DIR}"/.background/bg.png
# I couldn't get DeRez and Rez to work. Leaving it here in case its helpful in the future.
### DeRez -only icns "${MOUNT_DIR}/.VolumeIcon.icns" > "${MOUNT_DIR}/.VolumeIcon.rsrc"
### Rez -append "${MOUNT_DIR}/.VolumeIcon.rsrc" -o my_app.dmg
# This Works
cp '../../icons/icon.icns' "${MOUNT_DIR}/.VolumeIcon.icns"
VERSION=$(sw_vers -productVersion | cut -d'.' -f2)
if [ "$VERSION" -gt 12 ]; then
echo "Using SIPS v10.13+"
sips -i "${MOUNT_DIR}/.VolumeIcon.icns" # 10.13+
else
echo "Using SIPS v10.12-"
sips --addIcon "${MOUNT_DIR}/.VolumeIcon.icns"
fi
SetFile -c icnC "${MOUNT_DIR}/.VolumeIcon.icns"
SetFile -a C "${MOUNT_DIR}"
# Doesn't stick after the renaming
### ./seticon "${MOUNT_DIR}/${ICNS_FILE}" "${DMG_TEMP_NAME}"
# This does not work
### echo "read 'icns' (-16455) \"${MOUNT_DIR}/.VolumeIcon.icns\";" >> Icon.rsrc
### Rez -a Icon.rsrc -o FileName.ext
### DeRez -only icns "${MOUNT_DIR}/.VolumeIcon.icns" > "${MOUNT_DIR}/.Icon.rsrc"
### Rez -a Icon.rsrc -o "${MOUNT_DIR}/Icon$'\r'"
### SetFile -c icnC "${MOUNT_DIR}/.Icon.rsrc"
### SetFile -a C "${MOUNT_DIR}"
### SetFile -a "${MOUNT_DIR}/Icon$'\r'"
# this is from
# https://stackoverflow.com/questions/8371790/how-to-set-icon-on-file-or-directory-using-cli-on-os-x#8375093
### iconSource='../../icons/icon.icns'
### iconDestination="${MOUNT_DIR}"
### icon=/tmp/$(basename $iconSource)
### rsrc=/tmp/icon.rsrc
# Create icon from the iconSource
### cp $iconSource $icon
# Add icon to image file, meaning use itself as the icon
### sips -i $icon
# Take that icon and put it into a rsrc file
### DeRez -only icns $icon > $rsrc
# Apply the rsrc file to
### SetFile -a C "${iconDestination}"
# Create the magical Icon\r file
### touch "${iconDestination}/Icon$'\r'"
### Rez -append $rsrc -o "${iconDestination}/Icon?"
### SetFile -a V "${iconDestination}/Icon$'\r'"
echo '
tell application "Finder"
tell disk "'${MACOS_APP_NAME}'"
open
set current view of container window to icon view
set toolbar visible of container window to false
set statusbar visible of container window to false
set the bounds of container window to {400, 100, 885, 430}
set theViewOptions to the icon view options of container window
set arrangement of theViewOptions to not arranged
set icon size of theViewOptions to 110
set background picture of theViewOptions to file ".background:'bg.png'"
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
set position of item "'app.app'" of container window to {100, 100}
set position of item "Applications" of container window to {375, 100}
update without registering applications
delay 5
close
end tell
end tell
' | osascript
chmod -Rf go-w /Volumes/"${MACOS_APP_NAME}"
sync
sync
hdiutil detach ${device}
# Some variations on format:
## UDZO Compressed image (default)
## UDRO Read-only image
## UDBZ Better compressed image
## UDRW Read/Write image
hdiutil convert "${DMG_TEMP_NAME}" -format UDBZ -imagekey zlib-level=9 -o "${MACOS_APP_NAME}"
./seticon ../../icons/icon.icns "${MACOS_APP_NAME}.dmg"
rm -f ${DMG_TEMP_NAME}
# This seems not to work as well at maintaining the icons :(
zip -r "${MACOS_APP_NAME}.dmg.zip" "${MACOS_APP_NAME}.dmg"
# FUTURE WORK
# SIGNER=$(security find-identity -v -p codesigning)
# codesign --sign 'Mac Developer' "${DMG_TEMP_NAME}"
# Add a license with Rez
# http://www.owsiak.org/adding-license-to-a-dmg-file-in-5-minutes-or-less/

View File

@ -0,0 +1,394 @@
#!/usr/bin/env bash
# Create a read-only disk image of the contents of a folder
# forked from https://github.com/andreyvit/create-dmg
set -e;
function pure_version() {
echo '1.0.0.6'
}
function version() {
echo "create-dmg $(pure_version)"
}
function usage() {
version
echo "Creates a fancy DMG file."
echo "Usage: $(basename $0) [options] <output_name.dmg> <source_folder>"
echo "All contents of source_folder will be copied into the disk image."
echo "Options:"
echo " --volname name"
echo " set volume name (displayed in the Finder sidebar and window title)"
echo " --volicon icon.icns"
echo " set volume icon"
echo " --background pic.png"
echo " set folder background image (provide png, gif, jpg)"
echo " --window-pos x y"
echo " set position the folder window"
echo " --window-size width height"
echo " set size of the folder window"
echo " --text-size text_size"
echo " set window text size (10-16)"
echo " --icon-size icon_size"
echo " set window icons size (up to 128)"
echo " --icon file_name x y"
echo " set position of the file's icon"
echo " --hide-extension file_name"
echo " hide the extension of file"
echo " --custom-icon file_name custom_icon_or_sample_file x y"
echo " set position and custom icon"
echo " --app-drop-link x y"
echo " make a drop link to Applications, at location x,y"
echo " --ql-drop-link x y"
echo " make a drop link to user QuickLook install dir, at location x,y"
echo " --eula eula_file"
echo " attach a license file to the dmg"
echo " --no-internet-enable"
echo " disable automatic mount&copy"
echo " --format"
echo " specify the final image format (default is UDZO)"
echo " --add-file target_name file|folder x y"
echo " add additional file or folder (can be used multiple times)"
echo " --disk-image-size x"
echo " set the disk image size manually to x MB"
echo " --hdiutil-verbose"
echo " execute hdiutil in verbose mode"
echo " --hdiutil-quiet"
echo " execute hdiutil in quiet mode"
echo " --sandbox-safe"
echo " execute hdiutil with sandbox compatibility and do not bless"
echo " --rez rez_path"
echo " use custom path to Rez tool"
echo " --version show tool version number"
echo " -h, --help display this help"
exit 0
}
WINX=10
WINY=60
WINW=500
WINH=350
ICON_SIZE=128
TEXT_SIZE=16
FORMAT="UDZO"
ADD_FILE_SOURCES=()
ADD_FILE_TARGETS=()
IMAGEKEY=""
HDIUTIL_VERBOSITY=""
SANDBOX_SAFE=0
SKIP_JENKINS=0
while test "${1:0:1}" = "-"; do
case $1 in
--volname)
VOLUME_NAME="$2"
shift; shift;;
--volicon)
VOLUME_ICON_FILE="$2"
shift; shift;;
--background)
BACKGROUND_FILE="$2"
BACKGROUND_FILE_NAME="$(basename $BACKGROUND_FILE)"
BACKGROUND_CLAUSE="set background picture of opts to file \".background:$BACKGROUND_FILE_NAME\""
REPOSITION_HIDDEN_FILES_CLAUSE="set position of every item to {theBottomRightX + 100, 100}"
shift; shift;;
--icon-size)
ICON_SIZE="$2"
shift; shift;;
--text-size)
TEXT_SIZE="$2"
shift; shift;;
--window-pos)
WINX=$2; WINY=$3
shift; shift; shift;;
--window-size)
WINW=$2; WINH=$3
shift; shift; shift;;
--icon)
POSITION_CLAUSE="${POSITION_CLAUSE}set position of item \"$2\" to {$3, $4}
"
shift; shift; shift; shift;;
--hide-extension)
HIDING_CLAUSE="${HIDING_CLAUSE}set the extension hidden of item \"$2\" to true
"
shift; shift;;
--custom-icon)
shift; shift; shift; shift; shift;;
-h | --help)
usage;;
--version)
version; exit 0;;
--pure-version)
pure_version; exit 0;;
--ql-drop-link)
QL_LINK=$2
QL_CLAUSE="set position of item \"QuickLook\" to {$2, $3}
"
shift; shift; shift;;
--app-drop-link)
APPLICATION_LINK=$2
APPLICATION_CLAUSE="set position of item \"Applications\" to {$2, $3}
"
shift; shift; shift;;
--eula)
EULA_RSRC=$2
shift; shift;;
--no-internet-enable)
NOINTERNET=1
shift;;
--format)
FORMAT="$2"
shift; shift;;
--add-file | --add-folder)
ADD_FILE_TARGETS+=("$2")
ADD_FILE_SOURCES+=("$3")
POSITION_CLAUSE="${POSITION_CLAUSE}
set position of item \"$2\" to {$4, $5}
"
shift; shift; shift; shift; shift;;
--disk-image-size)
DISK_IMAGE_SIZE="$2"
shift; shift;;
--hdiutil-verbose)
HDIUTIL_VERBOSITY='-verbose'
shift;;
--hdiutil-quiet)
HDIUTIL_VERBOSITY='-quiet'
shift;;
--sandbox-safe)
SANDBOX_SAFE=1
shift;;
--rez)
REZ_PATH=$2
shift; shift;;
--skip-jenkins)
SKIP_JENKINS=1
shift;;
-*)
echo "Unknown option $1. Run with --help for help."
exit 1;;
esac
case $FORMAT in
UDZO)
IMAGEKEY="-imagekey zlib-level=9";;
UDBZ)
IMAGEKEY="-imagekey bzip2-level=9";;
esac
done
test -z "$2" && {
echo "Not enough arguments. Invoke with --help for help."
exit 1
}
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DMG_PATH="$1"
DMG_DIRNAME="$(dirname "$DMG_PATH")"
DMG_DIR="$(cd "$DMG_DIRNAME" > /dev/null; pwd)"
DMG_NAME="$(basename "$DMG_PATH")"
DMG_TEMP_NAME="$DMG_DIR/rw.${DMG_NAME}"
SRC_FOLDER="$(cd "$2" > /dev/null; pwd)"
test -z "$VOLUME_NAME" && VOLUME_NAME="$(basename "$DMG_PATH" .dmg)"
# brew formula will set this as 1 and embed the support scripts
BREW_INSTALL=0
AUX_PATH="$SCRIPT_DIR/support"
if [ $BREW_INSTALL -eq 0 ]; then
test -d "$AUX_PATH" || {
echo "Cannot find support directory: $AUX_PATH"
exit 1
}
fi
if [ -f "$SRC_FOLDER/.DS_Store" ]; then
echo "Deleting any .DS_Store in source folder"
rm "$SRC_FOLDER/.DS_Store"
fi
# Create the image
echo "Creating disk image..."
test -f "${DMG_TEMP_NAME}" && rm -f "${DMG_TEMP_NAME}"
# Using Megabytes since hdiutil fails with very large Byte numbers
function blocks_to_megabytes() {
# Add 1 extra MB, since there's no decimal retention here
MB_SIZE=$((($1 * 512 / 1000 / 1000) + 1))
echo $MB_SIZE
}
function get_size() {
# Get block size in disk
bytes_size=`du -s "$1" | sed -e 's/ .*//g'`
echo `blocks_to_megabytes $bytes_size`
}
# Create the DMG with the specified size or the hdiutil estimation
CUSTOM_SIZE=''
if ! test -z "$DISK_IMAGE_SIZE"; then
CUSTOM_SIZE="-size ${DISK_IMAGE_SIZE}m"
fi
if [ $SANDBOX_SAFE -eq 0 ]; then
hdiutil create ${HDIUTIL_VERBOSITY} -srcfolder "$SRC_FOLDER" -volname "${VOLUME_NAME}" -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW ${CUSTOM_SIZE} "${DMG_TEMP_NAME}"
else
hdiutil makehybrid ${HDIUTIL_VERBOSITY} -default-volume-name "${VOLUME_NAME}" -hfs -o "${DMG_TEMP_NAME}" "$SRC_FOLDER"
hdiutil convert -format UDRW -ov -o "${DMG_TEMP_NAME}" "${DMG_TEMP_NAME}"
DISK_IMAGE_SIZE_CUSTOM=$DISK_IMAGE_SIZE
fi
# Get the created DMG actual size
DISK_IMAGE_SIZE=`get_size "${DMG_TEMP_NAME}"`
# Use the custom size if bigger
if [ $SANDBOX_SAFE -eq 1 ] && [ ! -z "$DISK_IMAGE_SIZE_CUSTOM" ] && [ $DISK_IMAGE_SIZE_CUSTOM -gt $DISK_IMAGE_SIZE ]; then
DISK_IMAGE_SIZE=$DISK_IMAGE_SIZE_CUSTOM
fi
# Estimate the additional soruces size
if ! test -z "$ADD_FILE_SOURCES"; then
for i in "${!ADD_FILE_SOURCES[@]}"
do
SOURCE_SIZE=`get_size "${ADD_FILE_SOURCES[$i]}"`
DISK_IMAGE_SIZE=$(expr $DISK_IMAGE_SIZE + $SOURCE_SIZE)
done
fi
# Add extra space for additional resources
DISK_IMAGE_SIZE=$(expr $DISK_IMAGE_SIZE + 20)
# Resize the image for the extra stuff
hdiutil resize ${HDIUTIL_VERBOSITY} -size ${DISK_IMAGE_SIZE}m "${DMG_TEMP_NAME}"
# mount it
echo "Mounting disk image..."
MOUNT_DIR="/Volumes/${VOLUME_NAME}"
# try unmount dmg if it was mounted previously (e.g. developer mounted dmg, installed app and forgot to unmount it)
echo "Unmounting disk image..."
DEV_NAME=$(hdiutil info | egrep --color=never '^/dev/' | sed 1q | awk '{print $1}')
test -d "${MOUNT_DIR}" && hdiutil detach "${DEV_NAME}"
echo "Mount directory: $MOUNT_DIR"
DEV_NAME=$(hdiutil attach -readwrite -noverify -noautoopen "${DMG_TEMP_NAME}" | egrep --color=never '^/dev/' | sed 1q | awk '{print $1}')
echo "Device name: $DEV_NAME"
if ! test -z "$BACKGROUND_FILE"; then
echo "Copying background file..."
test -d "$MOUNT_DIR/.background" || mkdir "$MOUNT_DIR/.background"
cp "$BACKGROUND_FILE" "$MOUNT_DIR/.background/$BACKGROUND_FILE_NAME"
fi
if ! test -z "$APPLICATION_LINK"; then
echo "making link to Applications dir"
test -d "$MOUNT_DIR/Applications" || ln -s /Applications "$MOUNT_DIR/Applications"
fi
if ! test -z "$QL_LINK"; then
echo "making link to QuickLook install dir"
ln -s "/Library/QuickLook" "$MOUNT_DIR/QuickLook"
fi
if ! test -z "$VOLUME_ICON_FILE"; then
echo "Copying volume icon file '$VOLUME_ICON_FILE'..."
cp "$VOLUME_ICON_FILE" "$MOUNT_DIR/.VolumeIcon.icns"
SetFile -c icnC "$MOUNT_DIR/.VolumeIcon.icns"
fi
if ! test -z "$ADD_FILE_SOURCES"; then
echo "Copying custom files..."
for i in "${!ADD_FILE_SOURCES[@]}"
do
echo "${ADD_FILE_SOURCES[$i]}"
cp -a "${ADD_FILE_SOURCES[$i]}" "$MOUNT_DIR/${ADD_FILE_TARGETS[$i]}"
done
fi
# run applescript
APPLESCRIPT=$(mktemp -t createdmg.tmp.XXXXXXXXXX)
function applescript_source() {
if [ $BREW_INSTALL -eq 0 ]; then
cat "$AUX_PATH/template.applescript"
else
cat << 'EOS'
# BREW_INLINE_APPLESCRIPT_PLACEHOLDER
EOS
fi
}
if [ $SKIP_JENKINS -eq 0 ]; then
applescript_source | sed -e "s/WINX/$WINX/g" -e "s/WINY/$WINY/g" -e "s/WINW/$WINW/g" -e "s/WINH/$WINH/g" -e "s/BACKGROUND_CLAUSE/$BACKGROUND_CLAUSE/g" -e "s/REPOSITION_HIDDEN_FILES_CLAUSE/$REPOSITION_HIDDEN_FILES_CLAUSE/g" -e "s/ICON_SIZE/$ICON_SIZE/g" -e "s/TEXT_SIZE/$TEXT_SIZE/g" | perl -pe "s/POSITION_CLAUSE/$POSITION_CLAUSE/g" | perl -pe "s/QL_CLAUSE/$QL_CLAUSE/g" | perl -pe "s/APPLICATION_CLAUSE/$APPLICATION_CLAUSE/g" | perl -pe "s/HIDING_CLAUSE/$HIDING_CLAUSE/" >"$APPLESCRIPT"
sleep 2 # pause to workaround occasional "Cant get disk" (-1728) issues
echo "Running Applescript: /usr/bin/osascript \"${APPLESCRIPT}\" \"${VOLUME_NAME}\""
(/usr/bin/osascript "${APPLESCRIPT}" "${VOLUME_NAME}" || if [[ "$?" -ne 0 ]]; then echo "Failed running AppleScript"; hdiutil detach "${DEV_NAME}"; exit 64; fi)
echo "Done running the applescript..."
sleep 4
rm "$APPLESCRIPT"
fi
# make sure it's not world writeable
echo "Fixing permissions..."
chmod -Rf go-w "${MOUNT_DIR}" &> /dev/null || true
echo "Done fixing permissions."
# make the top window open itself on mount:
if [ $SANDBOX_SAFE -eq 0 ]; then
echo "Blessing started"
bless --folder "${MOUNT_DIR}" --openfolder "${MOUNT_DIR}"
echo "Blessing finished"
else
echo "Skipping blessing on sandbox"
fi
if ! test -z "$VOLUME_ICON_FILE"; then
# tell the volume that it has a special file attribute
SetFile -a C "$MOUNT_DIR"
fi
# unmount
echo "Unmounting disk image..."
hdiutil detach "${DEV_NAME}"
# compress image
echo "Compressing disk image..."
hdiutil convert ${HDIUTIL_VERBOSITY} "${DMG_TEMP_NAME}" -format ${FORMAT} ${IMAGEKEY} -o "${DMG_DIR}/${DMG_NAME}"
rm -f "${DMG_TEMP_NAME}"
# adding EULA resources
if [ ! -z "${EULA_RSRC}" -a "${EULA_RSRC}" != "-null-" ]; then
echo "adding EULA resources"
REZ_ARG=""
if [ ! -z "${REZ_PATH}" -a "${REZ_PATH}" != "-null-" ]; then
REZ_ARG="--rez ${REZ_PATH}"
fi
if [ $BREW_INSTALL -eq 0 ]; then
"${AUX_PATH}/dmg-license.py" "${DMG_DIR}/${DMG_NAME}" "${EULA_RSRC}" ${REZ_ARG}
else
python - "${DMG_DIR}/${DMG_NAME}" "${EULA_RSRC}" ${REZ_ARG} <<-'EOS'
# BREW_INLINE_LICENSE_PLACEHOLDER
EOS
fi
fi
if [ ! -z "${NOINTERNET}" -a "${NOINTERNET}" == 1 ]; then
echo "not setting 'internet-enable' on the dmg"
else
# check if hdiutil supports internet-enable
# support was removed in macOS 10.15
# https://github.com/andreyvit/create-dmg/issues/76
if hdiutil internet-enable -help >/dev/null 2>/dev/null
then
hdiutil internet-enable -yes "${DMG_DIR}/${DMG_NAME}"
else
echo "hdiutil does not support internet-enable. Note it was removed in macOS 10.15."
fi
fi
echo "Disk image done"
exit 0

View File

@ -0,0 +1,163 @@
#!/usr/bin/env python
"""
This script adds a license file to a DMG. Requires Xcode and a plain ascii text
license file.
Obviously only runs on a Mac.
Copyright (C) 2011-2013 Jared Hobbs
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:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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.
"""
from __future__ import print_function
import os
import sys
import tempfile
import optparse
class Path(str):
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
os.unlink(self)
def mktemp(dir=None, suffix=''):
(fd, filename) = tempfile.mkstemp(dir=dir, suffix=suffix)
os.close(fd)
return Path(filename)
def main(options, args):
dmgFile, license = args
with mktemp('.') as tmpFile:
with open(tmpFile, 'w') as f:
f.write("""data 'TMPL' (128, "LPic") {
$"1344 6566 6175 6C74 204C 616E 6775 6167"
$"6520 4944 4457 5244 0543 6F75 6E74 4F43"
$"4E54 042A 2A2A 2A4C 5354 430B 7379 7320"
$"6C61 6E67 2049 4444 5752 441E 6C6F 6361"
$"6C20 7265 7320 4944 2028 6F66 6673 6574"
$"2066 726F 6D20 3530 3030 4457 5244 1032"
$"2D62 7974 6520 6C61 6E67 7561 6765 3F44"
$"5752 4404 2A2A 2A2A 4C53 5445"
};
data 'LPic' (5000) {
$"0000 0002 0000 0000 0000 0000 0004 0000"
};
data 'STR#' (5000, "English buttons") {
$"0006 0D45 6E67 6C69 7368 2074 6573 7431"
$"0541 6772 6565 0844 6973 6167 7265 6505"
$"5072 696E 7407 5361 7665 2E2E 2E7A 4966"
$"2079 6F75 2061 6772 6565 2077 6974 6820"
$"7468 6520 7465 726D 7320 6F66 2074 6869"
$"7320 6C69 6365 6E73 652C 2063 6C69 636B"
$"2022 4167 7265 6522 2074 6F20 6163 6365"
$"7373 2074 6865 2073 6F66 7477 6172 652E"
$"2020 4966 2079 6F75 2064 6F20 6E6F 7420"
$"6167 7265 652C 2070 7265 7373 2022 4469"
$"7361 6772 6565 2E22"
};
data 'STR#' (5002, "English") {
$"0006 0745 6E67 6C69 7368 0541 6772 6565"
$"0844 6973 6167 7265 6505 5072 696E 7407"
$"5361 7665 2E2E 2E7B 4966 2079 6F75 2061"
$"6772 6565 2077 6974 6820 7468 6520 7465"
$"726D 7320 6F66 2074 6869 7320 6C69 6365"
$"6E73 652C 2070 7265 7373 2022 4167 7265"
$"6522 2074 6F20 696E 7374 616C 6C20 7468"
$"6520 736F 6674 7761 7265 2E20 2049 6620"
$"796F 7520 646F 206E 6F74 2061 6772 6565"
$"2C20 7072 6573 7320 2244 6973 6167 7265"
$"6522 2E"
};\n\n""")
with open(license, 'r') as l:
kind = 'RTF ' if license.lower().endswith('.rtf') else 'TEXT'
f.write('data \'%s\' (5000, "English") {\n' % kind)
def escape(s):
return s.strip().replace('\\', '\\\\').replace('"', '\\"').replace('\0', '')
for line in l:
line = escape(line)
for liner in [line[i:i+1000] for i in range(0, len(line), 1000)]:
f.write(' "' + liner + '"\n')
f.write(' "' + '\\n"\n')
f.write('};\n\n')
f.write("""data 'styl' (5000, "English") {
$"0003 0000 0000 000C 0009 0014 0000 0000"
$"0000 0000 0000 0000 0027 000C 0009 0014"
$"0100 0000 0000 0000 0000 0000 002A 000C"
$"0009 0014 0000 0000 0000 0000 0000"
};\n""")
os.system('hdiutil unflatten -quiet "%s"' % dmgFile)
ret = os.system('%s -a %s -o "%s"' %
(options.rez, tmpFile, dmgFile))
os.system('hdiutil flatten -quiet "%s"' % dmgFile)
if options.compression is not None:
os.system('cp %s %s.temp.dmg' % (dmgFile, dmgFile))
os.remove(dmgFile)
if options.compression == "bz2":
os.system('hdiutil convert %s.temp.dmg -format UDBZ -o %s' %
(dmgFile, dmgFile))
elif options.compression == "gz":
os.system('hdiutil convert %s.temp.dmg -format ' % dmgFile +
'UDZO -imagekey zlib-devel=9 -o %s' % dmgFile)
os.remove('%s.temp.dmg' % dmgFile)
if ret == 0:
print("Successfully added license to '%s'" % dmgFile)
else:
print("Failed to add license to '%s'" % dmgFile)
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.set_usage("""%prog <dmgFile> <licenseFile> [OPTIONS]
This program adds a software license agreement to a DMG file.
It requires Xcode and either a plain ascii text <licenseFile>
or a <licenseFile.rtf> with the RTF contents.
See --help for more details.""")
parser.add_option(
'--rez',
'-r',
action='store',
default='/Applications/Xcode.app/Contents/Developer/Tools/Rez',
help='The path to the Rez tool. Defaults to %default'
)
parser.add_option(
'--compression',
'-c',
action='store',
choices=['bz2', 'gz'],
default=None,
help='Optionally compress dmg using specified compression type. '
'Choices are bz2 and gz.'
)
options, args = parser.parse_args()
cond = len(args) != 2
if not os.path.exists(options.rez):
print('Failed to find Rez at "%s"!\n' % options.rez)
cond = True
if cond:
parser.print_usage()
sys.exit(1)
main(options, args)

View File

@ -0,0 +1,74 @@
on run (volumeName)
tell application "Finder"
tell disk (volumeName as string)
open
set theXOrigin to WINX
set theYOrigin to WINY
set theWidth to WINW
set theHeight to WINH
set theBottomRightX to (theXOrigin + theWidth)
set theBottomRightY to (theYOrigin + theHeight)
set dsStore to "\"" & "/Volumes/" & volumeName & "/" & ".DS_STORE\""
tell container window
set current view to icon view
set toolbar visible to false
set statusbar visible to false
set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY}
set statusbar visible to false
REPOSITION_HIDDEN_FILES_CLAUSE
end tell
set opts to the icon view options of container window
tell opts
set icon size to ICON_SIZE
set text size to TEXT_SIZE
set arrangement to not arranged
end tell
BACKGROUND_CLAUSE
-- Positioning
POSITION_CLAUSE
-- Hiding
HIDING_CLAUSE
-- Application and QL Link Clauses
APPLICATION_CLAUSE
QL_CLAUSE
close
open
-- Force saving of the size
delay 1
tell container window
set statusbar visible to false
set the bounds to {theXOrigin, theYOrigin, theBottomRightX - 10, theBottomRightY - 10}
end tell
end tell
delay 1
tell disk (volumeName as string)
tell container window
set statusbar visible to false
set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY}
end tell
end tell
--give the finder some time to write the .DS_Store file
delay 3
set waitTime to 0
set ejectMe to false
repeat while ejectMe is false
delay 1
set waitTime to waitTime + 1
if (do shell script "[ -f " & dsStore & " ]; echo $?") = "0" then set ejectMe to true
end repeat
log "waited " & waitTime & " seconds for .DS_STORE to be created."
end tell
end run

View File

@ -58,11 +58,34 @@ const getTauriConfig = (cfg: Partial<TauriConfig>): TauriConfig => {
const runningDevServer = config.build.devPath && config.build.devPath.startsWith('http')
if (!runningDevServer) {
config.build.devPath = appPaths.resolve.tauri(config.build.devPath)
process.env.TAURI_DIST_DIR = appPaths.resolve.app(config.build.devPath)
process.env.TAURI_DIST_DIR = config.build.devPath
}
if (config.build.distDir) {
config.build.distDir = appPaths.resolve.tauri(config.build.distDir)
process.env.TAURI_DIST_DIR = appPaths.resolve.app(config.build.distDir)
process.env.TAURI_DIST_DIR = config.build.distDir
}
// bundle configuration
if (config.tauri.bundle) {
// OSX
if (config.tauri.bundle.osx) {
const license = config.tauri.bundle.osx.license
if (typeof license === 'string') {
config.tauri.bundle.osx.license = appPaths.resolve.tauri(license)
} else if (license !== null) {
const licensePath = appPaths.resolve.app('LICENSE')
if (existsSync(licensePath)) {
config.tauri.bundle.osx.license = licensePath
}
}
}
// targets
if (Array.isArray(config.tauri.bundle.targets)) {
if (process.platform !== 'win32') {
config.tauri.bundle.targets = config.tauri.bundle.targets.filter(t => t !== 'msi')
}
}
}
if (!process.env.TAURI_DIST_DIR) {

View File

@ -39,6 +39,7 @@ export interface TauriConfig {
osx?: {
frameworks?: string[]
minimumSystemVersion?: string
license?: string
}
exceptionDomain?: string
}

View File

@ -30,7 +30,13 @@ describe('[CLI] tauri.js template', () => {
}
const build = require('api/build')
build().promise.then(() => {
build({
tauri: {
bundle: {
targets: ['deb', 'osx', 'msi', 'appimage'] // we can't bundle dmg on CI so we remove it here
}
}
}).promise.then(() => {
process.chdir(cwd)
done()
}).catch(done)