=Start=
缘由:
前段时间我负责的一个终端应用推广在借助MDM进行分发的时候出了点问题(具体情况是在低于或等于10.15.7版本的macOS系统下发了但是没有正常安装),提供应用安装包的同学说手动安装没问题那肯定不是包的问题,负责MDM分发的同学说其它的安装包都可以正常下发肯定也不是MDM的问题,在这种情况下,为了能让应用推广正常进行下去,我只好自己去学习和尝试解决,这里简单记录一下macOS上pkg安装包的制作方法和注意事项(等后面有机会了我再深入了解一下MDM的相关知识),方便有需要的参考。
正文:
参考解答:
pkg安装包是什么?
pkg是macOS平台下一种常见的安装包格式(另一种常见的是dmg格式)。两者的简单介绍如下:
dmg文件(或磁盘镜像文件)用于安装软件,但其中可以包含任意类型的文件,而不仅仅是应用程序或安装文件。这种格式常见于从互联网下载的macOS软件安装包,比如微信Mac版,甚至是待会要介绍的Packages这个macOS上的软件安装包制作工具本身也是以dmg格式提供的下载。
pkg文件是macOS安装包,包含安装程序脚本和压缩的安装文件,用于在用户硬盘上安装Mac软件应用程序。
虽然在互联网上分发的macOS安装程序以dmg格式的居多,但是在企业内部推macOS应用的时候,一般是pkg格式的居多,因为DMG文件需要挂载为一个虚拟磁盘来推送应用,而PKG文件可以直接安装,并且通常具备一系列安装流程,引导用户完成安装过程。
如何制作pkg安装包?
因为我是新手,同时也只是为了快速制作一个pkg安装包用于测试和解决问题,所以会以手头上能找到的最简单方便的教程/工具为主。这里的教程是「【技术分享】Mac环境PKG安装包制作教程」,工具是「Packages」。
Packages是一款开源的安装包制作工具,能够指定文件的安装路径、定制安装流程、资源国际化、插件机制、执行安装脚本,基本能够满足绝大部分场景。
Packages的下载和安装就不赘述了,比较简单,下面只介绍一些基本的使用和特定的注意事项。
- 两种项目模版的选择:
Distribution: A Distribution project can include multiple packages and allows you to have a welcome message, custom background picture, etc.
Raw Package: A Raw Package project lets you install files at specific locations.
简而言之就是,如果安装包要实现的功能足够简单,可以选择Raw Package模式,可以支持你将安装文件放置到特定目录下。复杂的或者想实现多一点功能的就选Distribution模式(支持自定义背景图片/欢迎信息等功能)。
- 项目名称和位置
“Project Name”为项目名称可任意填写,需要注意的是最好将“Project Directory”选定为安装文件所在的目录,否则容易出现安装包运行失败的现象——实测发现主要和目录权限有关,这个问题其实不大。
- Project的几个设置
Settings #可以设置安装包名(也即编译出来的pkg文件名)、安装包输出位置、安装包格式等信息。
Presentation #设置安装过程中的呈现效果。标准的安装过程分为:Introduction, Read Me, License, Destination Select, Installation Type, Installation, Summary七个步骤。可以为每个步骤定制文本信息并选择国际化设置。
Requirements & Resources
Comments
- 具体package的设置
- Settings #设定标识符/版本号/安装完成的动作(是否重启等)/安装选项(安装是否需要输入密码等)
- Payload #指定安装的目标目录(默认是根目录,如果要修改,选中特定目录之后要点击Set按钮才行,对应目录图标上就会出现一个准星图标,表示文件将安装到这个位置)
- Scripts #pre/post安装前后的执行脚本(安装前的脚本一般用于关闭正在运行的app进程,删除原来的app;安装后的脚本主要做启动app的工作)
如上所有操作后打包选项设置完成。然后点击工具栏的Build或者Build and Run开始制作pkg安装包,两者的区别是前者只制作pkg包,而后者制作后立即运行。
- pkg包制作好之后的测试
第一次制作pkg安装包时,会有很多内容不清楚,这时最好的办法是每一步具体的操作参考原文先搞一个出来,然后对制作好的pkg包进行安装测试,多试几次你就会明白每一步操作都是起什么作用的,哪些步骤比较重要不能马虎,哪些步骤不重要可以不用关注。
实际操作一遍下来之后你就会发现这是个熟能生巧的活,没有太多的技术含量(但是第一个搞出这个的人还真的是挺有探索精神的)。
==
几个简单快速获取macOS硬件/环境信息的命令
date
hostname
system_profiler SPHardwareDataType | grep -E "Serial Number|Hardware UUID"
ifconfig| grep 'inet ' | grep -v '127.0.0.1'
curl --connect-timeout 1 ifconfig.me
Scripts中的 pre-installation 和 post-installation 脚本样例
==> preInstall.sh
#!/usr/bin/env bash
echo "[pre]start" >>/tmp/pkg_install.txt
date >>/tmp/pkg_install.txt
ps aux | grep -i "keyword" | grep -v "grep" >>/tmp/pkg_install.txt
if [[ $? -eq 0 ]]; then
#statements
echo "[pre]process already exist, do nothing!" >>/tmp/pkg_install.txt
else
#statements
[ ! -f name11.pkg ] && sudo installer -pkg ./name11.pkg -target / && echo "[pre]try to install!" >>/tmp/pkg_install.txt
curl -I "https://ixyzero.com/preInstall"
fi
ls -lt "/Library/Application Support/DoPkgTest/" >>/tmp/pkg_install.txt 2>&1
date >>/tmp/pkg_install.txt
echo "[pre]end" >>/tmp/pkg_install.txt
exit 0
==> postInstall.sh
#!/usr/bin/env bash
echo "[post]start" >>/tmp/pkg_install.txt
date >>/tmp/pkg_install.txt
ps aux | grep -i "keyword" | grep -v "grep" >>/tmp/pkg_install.txt
if [[ $? -eq 0 ]]; then
#statements
echo "[post]process already exist, do nothing!" >>/tmp/pkg_install.txt
else
#statements
echo 'sudo installer -pkg "/Library/Application Support/DoPkgTest/name11.pkg" -target /' >>/tmp/pkg_install.txt
sudo installer -pkg "/Library/Application Support/DoPkgTest/name11.pkg" -target /
host=$(hostname)
ip=$(ifconfig | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}')
sn=$(system_profiler SPHardwareDataType | grep "Serial Number" | awk '{print $NF}')
curl -I "https://ixyzero.com/host=$host&ip=$ip&sn=$sn"
echo "https://ixyzero.com/host=$host&ip=$ip&sn=$sn" >>/tmp/pkg_install.txt
fi
date >>/tmp/pkg_install.txt
echo "[post]end" >>/tmp/pkg_install.txt
参考链接:
【技术分享】Mac环境PKG安装包制作教程
https://mp.weixin.qq.com/s/rFfm1_oyZbd-4cVu6fvsaw
http://s.sudre.free.fr/Software/Packages/about.html
http://s.sudre.free.fr/Software/documentation/Packages/en_2017/index.html
Open PKG File
https://openpkgfile.com/
Difference between .pkg and .dmg file?
https://forums.macrumors.com/threads/difference-between-pkg-and-dmg-file.217052/
Apple Mac OS X: Difference between APP and DMG
https://www.askingbox.com/question/apple-mac-os-x-difference-between-app-and-dmg
How to convert DMG to PKG Mac App for distribution
https://www.hexnode.com/mobile-device-management/help/convert-dmg-to-pkg-mac/
Why are so many Mac downloads a .dmg just that contains a single .pkg?
https://apple.stackexchange.com/questions/30695/why-are-so-many-mac-downloads-a-dmg-just-that-contains-a-single-pkg
https://en.wikipedia.org/wiki/Apple_Disk_Image
https://en.wikipedia.org/wiki/Installer_(macOS)
=END=
《 “macOS上pkg安装包的制作” 》 有 7 条评论
一些经验整理/总结
`
因为安装的pkg包一般都比较大,所以需要提前传到CDN上,然后通过CDN的链接进行下载,以减少下载失败的可能;
刚想到一点就是最好也校验一下从CDN下载的pkg包的哈希,避免因为缓存投毒的原因被安装上了非预期的软件;
另外就是收集请求的域名一定要保证外网可达,否则可能会因为非必要原因导致数据分析存在困难和误差。
1. preInstall
* 检查目标进程/目录是否存在
* 如果进程不存在,则下载安装包;如果进程存在但版本较低,则下载更新包;如果进程存在且版本正常,则仅打印日志其它什么也不做
* 发送请求/打印日志,用来告诉服务端 preInstall 正常执行
2. install
* 检查目标进程/目录是否存在
* 执行安装/更新命令
* 发送请求/打印日志,用来告诉服务端 install 正常执行
3. postInstall
* 检查目标进程/目录是否存在(理论上应该是有的,因为前面一步刚执行了检查和安装)
* 如果不存在,则执行安装;如果已存在,则什么也不用做
* 清理前2步中的无用文件,然后采集相关必要信息用于数据分析
* 发送请求/打印日志,用来告诉服务端 postInstall 正常执行
`
今天经过多次实测发现,Payload页面仅仅用于指定要将文件(一般是可执行文件)放置的位置,该文件本身是不涉及到自动执行的,如果你想这么做,需要在post-installation那里用脚本显示执行才OK。
所以上面第2步的install功能要根据实际情况拆分到preInstall或postInstall阶段中。
Pro Terminal Commands: 10 Uses for ps on macOS
https://www.applegazette.com/mac/pro-terminal-commands-10-uses-for-ps-on-macos/
`
$ ps -e -o pid,user,pcpu,pmem,comm
# 仅获取特定进程的进程名
$ proc=$(ps -e -o comm | grep -i “cge” | awk -F/ ‘{print $NF}’ | tr ‘\n’ ‘+’)
`
macOS上如何获取当前的登录用户名称
macos get current logged in user
https://stackoverflow.com/questions/1104972/how-do-i-get-the-name-of-the-active-user-via-the-command-line-in-os-x/1104976#1104976
`
stat -f “%Su” /dev/console
`
Getting the current user in macOS – Update
https://scriptingosx.com/2020/02/getting-the-current-user-in-macos-update/
`
# method 1
loggedInUser=$( echo “show State:/Users/ConsoleUser” | scutil | awk ‘/Name :/ && ! /loginwindow/ { print $3 }’ )
# method 2
loggedInUser=$(stat -f %Su /dev/console)
`
[…] 之前在学习macOS上pkg安装包制作的时候整理了一篇文章《macOS上pkg安装包的制作》,简单说了一下如何使用 Packages 应用生成pkg安装包,这里简单记录一下后来碰到的一些知识点,方便有需要的时候参考。 […]
Using Script2Pkg to create payload-free installer packages
https://derflounder.wordpress.com/2023/05/02/using-script2pkg-to-create-payload-free-installer-packages/
`
使用 Script2Pkg 创建无负载程序安装包
对于那些不熟悉这个概念的人来说,无负载程序安装包是**仅用于运行脚本的程序安装包**。他们不安装任何文件,这些文件将被称为程序安装包的有效负载。由于这些程序安装包中不包含有效负载,因此使用此工具构建的程序安装包被称为无有效负载。
Script2Pkg包括以下功能:
1. 构建未签名的无负载程序安装包
2. 构建签名的无负载程序安装包
3. 构建签名和公证的无负载程序安装包
4. 验证任何程序安装包的签名和公证状态
`
Proper Packaging Principles
https://n8felton.wordpress.com/2023/04/28/proper-packaging-principles/
https://github.com/n8felton/proper-packaging-principles
`
一些打包原则
1. Build native macOS packages
2. Version numbers go up
3. Naming conventions are necessary and helpful
4. Downloading packages should be easy
5. Do not assume that your package will be installed interactively via the GUI (Installer.app)
6. Software configuration and licensing should be done separately
macOS Software Packaging Tools
macOS Built-in
* pkgbuild
* productbuild
* installer
* pkgutil
Third-party Package Creation
* munki-pkg
* The Luggage
* Packages
Third-party Utilities
* Suspicious Package
* Pacifist
* Apparency
`