Hacer que los paquetes de instalador de macOS estén listos para ID de desarrollador

189

Nota: Esto es solo para los paquetes del instalador de OS X , los paquetes para enviar a la Mac App Store siguen diferentes reglas.

Debido al Guardián de la Puerta del León de Montaña, finalmente tuve que tomar mi script de compilación PackageMaker detrás del granero y dispararlo. PackageMaker ya se eliminó de Xcode y se trasladó a "Herramientas auxiliares para Xcode", por lo que esperamos que pronto se olvide.

La pregunta es ¿cómo se utiliza pkgbuild, productbuildy pkgutilpara reemplazarlo?

catlan
fuente
¿así que supongo que el problema con el fabricante de paquetes es la incapacidad de firmar correctamente los archivos pkg para usarlos con gatekeeper en Mountain Lion?
JasonZ
1
Es posible, pero PackageMaker siempre tuvo errores, y quedó obsoleto con Mac OS X 10.6 Snow Leopard. A la larga, le ahorrará tiempo familiarizarse con las nuevas herramientas.
catlan
@catlan: ¿Tiene un enlace oficial que dice que el fabricante de paquetes ha quedado en desuso en 10.6?
Carl
2
@carleeto: nunca se anunció como obsoleto, simplemente se eliminó de Xcode y finalmente "desapareció" como un manifestante birmano.
error
55
Notas de la versión de Xcode 4.6: Desuso de Package Maker adcdownload.apple.com/Developer_Tools/xcode_4.6/…
catlan

Respuestas:

344

Nuestro proyecto de ejemplo tiene dos objetivos de compilación: HelloWorld.app y Helper.app. Creamos un paquete de componentes para cada uno y los combinamos en un archivo de producto .

Un paquete de componentes contiene carga útil que debe instalar el instalador de OS X. Aunque un paquete de componentes se puede instalar solo, normalmente se incorpora a un archivo de producto .

Nuestras herramientas: pkgbuild , productbuild y pkgutil

Después de un exitoso "Build and Archive", abra $ BUILT_PRODUCTS_DIR en la Terminal.

$ cd ~/Library/Developer/Xcode/DerivedData/.../InstallationBuildProductsLocation
$ pkgbuild --analyze --root ./HelloWorld.app HelloWorldAppComponents.plist
$ pkgbuild --analyze --root ./Helper.app HelperAppComponents.plist

Esto nos da la lista de componentes, puede encontrar la descripción del valor en la sección "Lista de propiedades de componentes" . pkgbuild -root genera el paquetes de componentes , si no necesita cambiar ninguna de las propiedades predeterminadas, puede omitir el parámetro --component-plist en el siguiente comando.

productbuild: sintetizar resultados en una definición de distribución .

$ pkgbuild --root ./HelloWorld.app \
    --component-plist HelloWorldAppComponents.plist \
    HelloWorld.pkg
$ pkgbuild --root ./Helper.app \
    --component-plist HelperAppComponents.plist \
    Helper.pkg
$ productbuild --synthesize \
    --package HelloWorld.pkg --package Helper.pkg \
    Distribution.xml 

En Distribution.xml puede cambiar cosas como título, fondo, bienvenida, archivo Léame, licencia, etc. Convierte tus paquetes de componentes y la definición de distribución con este comando en un archivo de producto :

$ productbuild --distribution ./Distribution.xml \
    --package-path . \
    ./Installer.pkg

Recomiendo echar un vistazo a iTunes Installers Distribution.xml para ver qué es posible. Puede extraer "Instalar iTunes.pkg" con:

$ pkgutil --expand "Install iTunes.pkg" "Install iTunes"

Vamos a armarlo

Por lo general, tengo una carpeta llamada Paquete en mi proyecto que incluye elementos como Distribution.xml, componentes-listas, recursos y scripts.

Agregue una Fase de compilación de script de ejecución denominada "Generar paquete", que se establece en Ejecutar script solo al instalar :

VERSION=$(defaults read "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}/Contents/Info" CFBundleVersion)

PACKAGE_NAME=`echo "$PRODUCT_NAME" | sed "s/ /_/g"`
TMP1_ARCHIVE="${BUILT_PRODUCTS_DIR}/$PACKAGE_NAME-tmp1.pkg"
TMP2_ARCHIVE="${BUILT_PRODUCTS_DIR}/$PACKAGE_NAME-tmp2"
TMP3_ARCHIVE="${BUILT_PRODUCTS_DIR}/$PACKAGE_NAME-tmp3.pkg"
ARCHIVE_FILENAME="${BUILT_PRODUCTS_DIR}/${PACKAGE_NAME}.pkg"

pkgbuild --root "${INSTALL_ROOT}" \
    --component-plist "./Package/HelloWorldAppComponents.plist" \
    --scripts "./Package/Scripts" \
    --identifier "com.test.pkg.HelloWorld" \
    --version "$VERSION" \
    --install-location "/" \
    "${BUILT_PRODUCTS_DIR}/HelloWorld.pkg"
pkgbuild --root "${BUILT_PRODUCTS_DIR}/Helper.app" \
    --component-plist "./Package/HelperAppComponents.plist" \
    --identifier "com.test.pkg.Helper" \
    --version "$VERSION" \
    --install-location "/" \
    "${BUILT_PRODUCTS_DIR}/Helper.pkg"
productbuild --distribution "./Package/Distribution.xml"  \
    --package-path "${BUILT_PRODUCTS_DIR}" \
    --resources "./Package/Resources" \
    "${TMP1_ARCHIVE}"

pkgutil --expand "${TMP1_ARCHIVE}" "${TMP2_ARCHIVE}"

# Patches and Workarounds

pkgutil --flatten "${TMP2_ARCHIVE}" "${TMP3_ARCHIVE}"

productsign --sign "Developer ID Installer: John Doe" \
    "${TMP3_ARCHIVE}" "${ARCHIVE_FILENAME}"

Si no tiene que cambiar el paquete después de que se genera con productbuild , puede deshacerse de los pasos pkgutil --expandy pkgutil --flatten. También se puede utilizar el --sign paramenter en productbuild en lugar de correr productsign .

Firmar un instalador de OS X

Los paquetes se firman con el certificado de instalador de ID de desarrollador que puede descargar desde Developer Certificate Utility .

La firma se realiza con el --sign "Developer ID Installer: John Doe"parámetro pkgbuild , productbuild o productsign .

Tenga en cuenta que si va a crear un archivo de producto firmado usando productbuild, no hay razón para firmar los paquetes de componentes .

Utilidad de certificado de desarrollador

Todo el camino: Copie el paquete en Xcode Archive

Para copiar algo en el Archivo Xcode no podemos usar la Fase de Ejecución de Script Script . Para esto necesitamos usar una acción de esquema.

Editar esquema y expandir archivo. A continuación, haga clic en acciones posteriores y agregue una nueva acción Ejecutar script :

En Xcode 6:

#!/bin/bash

PACKAGES="${ARCHIVE_PATH}/Packages"

PACKAGE_NAME=`echo "$PRODUCT_NAME" | sed "s/ /_/g"`
ARCHIVE_FILENAME="$PACKAGE_NAME.pkg"
PKG="${OBJROOT}/../BuildProductsPath/${CONFIGURATION}/${ARCHIVE_FILENAME}"

if [ -f "${PKG}" ]; then
    mkdir "${PACKAGES}"
    cp -r "${PKG}" "${PACKAGES}"
fi

En Xcode 5, use este valor para PKG:

PKG="${OBJROOT}/ArchiveIntermediates/${TARGET_NAME}/BuildProductsPath/${CONFIGURATION}/${ARCHIVE_FILENAME}"

En caso de que su control de versiones no almacene la información del Esquema Xcode, sugiero agregar esto como script de shell a su proyecto para que pueda restaurar la acción simplemente arrastrando el script desde el espacio de trabajo a la acción posterior.

Scripting

Hay dos tipos diferentes de secuencias de comandos: JavaScript en los archivos de definición de distribución y las secuencias de comandos de Shell.

La mejor documentación sobre los scripts de Shell la encontré en WhiteBox - PackageMaker How-to , pero lea esto con precaución porque se refiere al formato de paquete anterior.

Lectura adicional

Problemas conocidos y soluciones alternativas

Panel de selección de destino

Al usuario se le presenta la opción de selección de destino con una sola opción: "Instalar para todos los usuarios de esta computadora". La opción aparece seleccionada visualmente, pero el usuario debe hacer clic en ella para continuar con la instalación, lo que genera cierta confusión.

Ejemplo que muestra el error del instalador

Apples Documentation recomienda usar, <domains enable_anywhere ... />pero esto desencadena el nuevo panel de selección de destino con más errores que Apple no usa en ninguno de sus paquetes.

El uso de la opción obsoleta le <options rootVolumeOnly="true" />proporciona el Panel de selección de destino anterior. Ejemplo que muestra el panel de selección de destino anterior


Desea instalar elementos en la carpeta de inicio del usuario actual.

Respuesta corta: ¡NO LO INTENTES!

Respuesta larga: REALMENTE; ¡NO LO INTENTES! Leer problemas y soluciones del instalador . ¿Sabes lo que hice incluso después de leer esto? Fui lo suficientemente estúpido como para intentarlo. Diciéndome a mí mismo que estoy seguro de que solucionaron los problemas en 10.7 o 10.8.

En primer lugar, vi de vez en cuando el error de panel de selección de destino mencionado anteriormente. Eso debería haberme detenido, pero lo ignoré. Si no desea pasar la semana posterior a la publicación de su software respondiendo correos electrónicos de soporte que deben hacer clic una vez que la buena selección azul NO use esto.

Ahora está pensando que sus usuarios son lo suficientemente inteligentes como para descubrir el panel, ¿no es así? Bueno, aquí hay otra cosa sobre la instalación de la carpeta de inicio, ¡NO FUNCIONAN!

Lo probé durante dos semanas en alrededor de 10 máquinas diferentes con diferentes versiones de sistema operativo y lo que no, y nunca falló. Entonces lo envié. Una hora después del lanzamiento, me alegro de los usuarios que simplemente no pudieron instalarlo. Los registros insinuaron problemas de permisos que no podrá solucionar.

Así que repitámoslo una vez más: ¡No utilizamos el instalador para las instalaciones de la carpeta de inicio!


RTFD para Bienvenido, Léame, Licencia y Conclusión no es aceptado por productbuild.

El instalador admite desde el principio los archivos RTFD para crear bonitas pantallas de bienvenida con imágenes, pero productbuild no las acepta.

Soluciones: use un archivo ficticio rtf y reemplácelo en el paquete una vez que productbuildhaya terminado.

Nota: También puede tener imágenes Retina dentro del archivo RTFD. Utilizar archivos TIFF multi-imagen para esto: tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif. Más detalles .


Iniciar una aplicación cuando la instalación se realiza con un script BundlePostInstallScriptPath :

#!/bin/bash

LOGGED_IN_USER_ID=`id -u "${USER}"`

if [ "${COMMAND_LINE_INSTALL}" = "" ]
then
    /bin/launchctl asuser "${LOGGED_IN_USER_ID}" /usr/bin/open -g PATH_OR_BUNDLE_ID
fi

exit 0

Es importante ejecutar la aplicación como usuario conectado, no como usuario instalador. Esto se hace con launchctl asuser uid path . Además, solo lo ejecutamos cuando no es una instalación de línea de comandos, realizada con la herramienta de instalación o Apple Remote Desktop .


catlan
fuente
9
Este es un excelente tutorial, pero supone la existencia de paquetes prefabricados. Si /tmptuviera que, por ejemplo, instalar un solo archivo para procesarlo más tarde en un script posterior al vuelo, ¿cómo estructuro la lista de componentes? Toda la documentación disponible parece suponer que el desarrollador la ha generado --analyze, al menos inicialmente.
error
1
Si no necesita cambiar nada Component Property List, no necesita ejecutar --analyze. Para los archivos de postproceso, sugiero ponerlos todos en un paquete y establecer esa ubicación de instalación de paquete en /tmp. Pero tal vez no entiendo tu pregunta. Si es así, publíquelo en una versión más detallada sobre SO.
catlan
2
Tenga en cuenta que no tiene ningún sentido hacer paquetes a través de la línea de comandos, tratando de escapar de todos los errores en la aplicación de empaquetado. ¡Más bien vea mi comentario a continuación sobre el uso de la aplicación "Paquetes" de Stéphane Sudre que resuelve todos los problemas para usted!
Bram de Jong
55
@BramdeJong "no tiene sentido". Estoy en desacuerdo. Apple mantiene las herramientas de línea de comando. Packages es una aplicación de terceros que no es compatible con la comunidad y puede romperse en el futuro si Apple cambia algo drástico. Para mí, prefiero conocer la técnica de la línea de comandos para que, si Apple cambia algo drástico, pueda seguir corriendo.
Volomike
55
$ pkgbuild --root ./HelloWorld.app está mal (suponiendo que .app es un paquete de aplicaciones real). pkgbuild opera en una raíz de destino: es decir, una carpeta que CONTIENE un paquete generado por la cadena de herramientas xcode. Entonces, el argumento para pkgbuild es la ruta a la carpeta que contiene el paquete que queremos empaquetar. Si no se consigue esto correctamente, se genera un paquete que contiene solo la carpeta de contenido de la aplicación. No se instalará como un paquete de aplicaciones real. El sorteo está en la lista de componentes. Si eso no contiene una entrada RootRelativeBundlePath que especifique el paquete de la aplicación, entonces la has jodido.
Jonathan Mitchell
185

Hay una aplicación muy interesante de Stéphane Sudre que hace todo esto por usted, es programable / admite la construcción desde la línea de comandos, tiene una GUI súper agradable y es GRATIS. Lo triste es: se llama "Paquetes", lo que hace que sea imposible de encontrar en google.

http://s.sudre.free.fr/Software/Packages/about.html

Desearía haberlo sabido antes de comenzar a elaborar mis propios guiones.

Captura de pantalla de la aplicación de paquetes

Bram de Jong
fuente
11
No puedo creer que esta publicación no tenga más sentido. Ese software es sorprendente y admite la construcción desde la línea de comandos.
Cesar Mendoza
1
¿Alguien intentó firmar un paquete con esta herramienta? No puedo activar el elemento de menú "Establecer certificado" ....
GTAE86
2
@ user283182: Muy tarde, seguramente ya lo has resuelto, pero tal vez esto ayude a otros. Creo que el problema que enfrentas se detalla en las [Pautas de revisión de la tienda de aplicaciones de Mac] ( developer.apple.com/app- store / review / Guidelines / mac / ... ), regla 2.14: "Las aplicaciones deben empaquetarse y enviarse utilizando las tecnologías de empaque de Apple incluidas en Xcode; no se permiten instaladores de terceros".
elder elder
44
Desearía que esta fuera una aplicación paga y el desarrollador está constantemente actualizando y parcheando. Packages.app es increíble, especialmente si desea implementar rápidamente su aplicación. Me llevó un total de 3 minutos para instalar, leer la Descripción general, configurar mi proyecto y crear el paquete instalable. Grandes felicitaciones a Stéphane.
Nikolay Christov
1
¡Esta aplicación es increíble!
Spencer Müller Diniz
3

Para su información para aquellos que están tratando de crear un instalador de paquetes para un paquete o complemento, es fácil:

pkgbuild --component "Color Lists.colorPicker" --install-location ~/Library/ColorPickers ColorLists.pkg
gngrwzrd
fuente
2
Para su información, hay una diferencia entre crear un .pkg y crear un instalador real con pantalla de bienvenida, licencia, etc.
catlan
Sí, lo sé, puse esto aquí porque no pude encontrar referencias para crear un instalador de paquetes para un complemento.
gngrwzrd
Esto me puso en marcha. Solo lo mínimo para dar a tierra.
uchuugaka
3

A +1 a la respuesta aceptada:

Selección de destino en el instalador

Si se desea la selección del dominio (también conocido como destino) entre el dominio del usuario y el dominio del sistema, en lugar de intentar <domains enable_anywhere="true">usar lo siguiente:

<domains enable_currentUserHome="true" enable_localSystem="true"/>

enable_currentUserHome instala la aplicación de la aplicación bajo ~/Applications/yenable_localSystem permite que la aplicación se instale en/Application

He intentado esto en El Capitan 10.11.6 (15G1217) y parece estar funcionando perfectamente bien en 1 máquina de desarrollo y 2 máquinas virtuales diferentes que probé.

PnotNP
fuente
Esto funciona bien, pero con un GOT'CHA: si primero instala por usuario, luego instala por máquina, la instalación se realizará en el directorio de usuarios, no en el directorio de máquinas, sino con derechos de sudo. Lo contrario no es el caso: puede instalar por máquina, y luego por usuario y tenerlo en ambos lugares.
Terje Dahl el
@TerjeDahl sí, esto se debe a que después de la instalación, el paquete se mueve a la ubicación que el instalador instaló previamente la misma ID de paquete (y el instalador lo sabe). Esto puede evitarse mediante algunas configuraciones en el archivo de manifiesto que no recuerdo en este momento.
PnotNP
@ PnotNP Ah. Si fuera tan amable de volver con esos ajustes si pudiera recordar, ¡eso sería genial!
Terje Dahl el
2

Aquí hay un script de compilación que crea un paquete de instalador firmado desde una raíz de compilación.

#!/bin/bash
# TRIMCheck build script
# Copyright Doug Richardson 2015
# Usage: build.sh
#
# The result is a disk image that contains the TRIMCheck installer.
#

DSTROOT=/tmp/trimcheck.dst
SRCROOT=/tmp/trimcheck.src

INSTALLER_PATH=/tmp/trimcheck
INSTALLER_PKG="TRIMCheck.pkg"
INSTALLER="$INSTALLER_PATH/$INSTALLER_PKG"

#
# Clean out anything that doesn't belong.
#
echo Going to clean out build directories
rm -rf build $DSTROOT $SRCROOT $INSTALLER_PATH
echo Build directories cleaned out


#
# Build
#
echo ------------------
echo Installing Sources
echo ------------------
xcodebuild -project TRIMCheck.xcodeproj installsrc SRCROOT=$SRCROOT || exit 1

echo ----------------
echo Building Project
echo ----------------
pushd $SRCROOT
xcodebuild -project TRIMCheck.xcodeproj -target trimcheck -configuration Release install || exit 1
popd

echo ------------------
echo Building Installer
echo ------------------
mkdir -p "$INSTALLER_PATH" || exit 1

echo "Runing pkgbuild. Note you must be connected to Internet for this to work as it"
echo "has to contact a time server in order to generate a trusted timestamp. See"
echo "man pkgbuild for more info under SIGNED PACKAGES."
pkgbuild --identifier "com.delicioussafari.TRIMCheck" \
    --sign "Developer ID Installer: Douglas Richardson (4L84QT8KA9)" \
    --root "$DSTROOT" \
    "$INSTALLER" || exit 1


echo Successfully built TRIMCheck
open "$INSTALLER_PATH"

exit 0
Doug Richardson
fuente
1
Sí, pkgbuild crea un instalador .pkg.
Doug Richardson