Créer des packages d'installation macOS prêts pour l'ID développeur

189

Remarque: Ceci concerne uniquement les packages du programme d' installation OS X , les packages à soumettre au Mac App Store suivent des règles différentes.

À cause du Gatekeeper de Mountain Lion, j'ai finalement dû prendre mon script de construction PackageMaker derrière la grange et le tirer. PackageMaker a déjà été supprimé de Xcode et déplacé dans "Auxiliary Tools for Xcode", donc j'espère qu'il sera bientôt oublié.

La question est de savoir comment puis-je utiliser pkgbuild, productbuildet pkgutilde le remplacer?

catlan
la source
donc je suppose que le problème avec packagemaker est l'incapacité de signer correctement les fichiers pkg à utiliser avec le garde-porte sur Mountain Lion?
JasonZ
1
C'est possible, mais PackageMaker a toujours été bogué et a été abandonné avec Mac OS X 10.6 Snow Leopard. Cela vous fera gagner du temps à long terme pour simplement vous familiariser avec les nouveaux outils.
catlan
@catlan: Avez-vous un lien officiel qui indique que Packagemaker est obsolète sur 10.6?
Carl
2
@carleeto: Il n'a jamais été annoncé comme étant obsolète, il a juste été retiré de Xcode et a finalement "disparu" comme un manifestant birman.
bug du
5
Notes de publication de Xcode 4.6: abandon de Package Maker adcdownload.apple.com/Developer_Tools/xcode_4.6/…
catlan

Réponses:

344

Notre exemple de projet a deux cibles de construction: HelloWorld.app et Helper.app. Nous créons un package de composants pour chacun et les combinons dans une archive produit .

Un package de composants contient une charge utile à installer par le programme d'installation d'OS X. Bien qu'un package de composants puisse être installé seul, il est généralement incorporé dans une archive de produit .

Nos outils: PKGBUILD , productbuild et pkgutil

Après un "Build and Archive" réussi, ouvrez $ BUILT_PRODUCTS_DIR dans le Terminal.

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

Cela nous donne le composant-plist, vous trouvez la description de la valeur dans la section "Liste des propriétés des composants" . pkgbuild -root génère les packages de composants , si vous n'avez pas besoin de modifier l'une des propriétés par défaut, vous pouvez omettre le paramètre --component-plist dans la commande suivante.

productbuild --synthesize donne une définition de distribution .

$ 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 

Dans le fichier Distribution.xml, vous pouvez modifier des éléments tels que le titre, l'arrière-plan, la bienvenue, le fichier Lisez-moi, la licence, etc. Vous transformez vos packages de composants et votre définition de distribution avec cette commande en une archive de produit :

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

Je recommande de jeter un œil à iTunes Installers Distribution.xml pour voir ce qui est possible. Vous pouvez extraire "Install iTunes.pkg" avec:

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

Permet de le mettre ensemble

J'ai généralement un dossier nommé Package dans mon projet qui comprend des éléments tels que Distribution.xml, des listes de composants, des ressources et des scripts.

Ajoutez une phase de génération de script d'exécution nommée "Générer le package", qui est définie sur Exécuter le script uniquement lors de l'installation :

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 vous n'avez pas à modifier le package après sa génération avec productbuild, vous pouvez supprimer les étapes pkgutil --expandet pkgutil --flatten. Vous pouvez également utiliser le --sign paramenter sur productbuild au lieu de courir productsign .

Signer un programme d'installation OS X

Les packages sont signés avec le certificat Developer ID Installer que vous pouvez télécharger à partir de Developer Certificate Utility .

La signature se fait avec le --sign "Developer ID Installer: John Doe"paramètre pkgbuild , productbuild ou productsign .

Notez que si vous allez créer une archive de produit signée à l' aide de productbuild, il n'y a aucune raison de signer les packages de composants .

Utilitaire de certificat de développeur

Tout le chemin: Copiez le package dans Xcode Archive

Pour copier quelque chose dans l'archive Xcode, nous ne pouvons pas utiliser la phase de construction du script d'exécution . Pour cela, nous devons utiliser une action de schéma.

Modifiez le schéma et développez Archive. Cliquez ensuite sur post-actions et ajoutez une nouvelle action d'exécution de script :

Dans 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

Dans Xcode 5, utilisez PKGplutôt cette valeur pour :

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

Dans le cas où votre contrôle de version ne stocke pas les informations Xcode Scheme, je suggère de l'ajouter en tant que script shell à votre projet afin que vous puissiez simplement restaurer l'action en faisant glisser le script de l'espace de travail vers la post-action.

Scripting

Il existe deux types de scripts différents: JavaScript dans les fichiers de définition de distribution et les scripts Shell.

La meilleure documentation sur les scripts Shell que j'ai trouvée dans WhiteBox - PackageMaker How-to , mais lisez-la avec prudence car elle fait référence à l'ancien format de package.

Lecture supplémentaire

Problèmes connus et solutions de contournement

Volet de sélection de destination

L'utilisateur se voit proposer l'option de sélection de destination avec un seul choix - «Installer pour tous les utilisateurs de cet ordinateur». L'option apparaît visuellement sélectionnée, mais l'utilisateur doit cliquer dessus afin de procéder à l'installation, ce qui crée une certaine confusion.

Exemple montrant le bogue du programme d'installation

La documentation d'Apples recommande d'utiliser, <domains enable_anywhere ... />mais cela déclenche le nouveau volet de sélection de destination plus bogué qu'Apple n'utilise dans aucun de ses packages.

L'utilisation de la version obsolète <options rootVolumeOnly="true" />vous donne l'ancien volet de sélection de destination. Exemple montrant l'ancien volet de sélection de destination


Vous souhaitez installer des éléments dans le dossier d'accueil de l'utilisateur actuel.

Réponse courte: N'ESSAYEZ PAS!

Réponse longue: VRAIMENT; N'ESSAYEZ PAS! Lisez les problèmes et solutions du programme d'installation . Vous savez ce que j'ai fait même après avoir lu ceci? J'ai été assez stupide pour l'essayer. Me disant que je suis sûr qu'ils ont résolu les problèmes dans 10.7 ou 10.8.

Tout d'abord, j'ai vu de temps en temps le bogue du volet de sélection de destination mentionné ci-dessus. Cela aurait dû m'arrêter, mais je l'ai ignoré. Si vous ne voulez pas passer la semaine après la sortie de votre logiciel à répondre aux e-mails de support, ils doivent cliquer une fois sur la belle sélection bleue, NE PAS l'utiliser.

Vous pensez maintenant que vos utilisateurs sont assez intelligents pour comprendre le panneau, n'est-ce pas? Eh bien, voici une autre chose à propos de l'installation du dossier personnel, ILS NE FONCTIONNENT PAS!

Je l'ai testé pendant deux semaines sur environ 10 machines différentes avec différentes versions d'OS et autres, et cela n'a jamais échoué. Alors je l'ai expédié. Moins d'une heure après la sortie, je suis ravi des utilisateurs qui ne pouvaient tout simplement pas l'installer. Les journaux suggèrent des problèmes d'autorisation que vous ne pourrez pas résoudre.

Répétons-le donc encore une fois: nous n'utilisons pas le programme d'installation pour les installations du dossier personnel!


RTFD pour Bienvenue, Lisez-moi, Licence et Conclusion n'est pas accepté par productbuild.

Programme d'installation pris en charge depuis le début des fichiers RTFD pour créer de jolis écrans de bienvenue avec des images, mais productbuild ne les accepte pas.

Solutions de contournement: utilisez un fichier rtf factice et remplacez-le dans le package par une fois productbuildterminé.

Remarque: vous pouvez également avoir des images Retina dans le fichier RTFD. Utilisez des fichiers TIFF multi-images pour ceci: tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif. Plus de détails .


Démarrage d'une application lorsque l'installation est effectuée avec 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

Il est important d'exécuter l'application en tant qu'utilisateur connecté et non en tant qu'utilisateur installateur. Ceci est fait avec le chemin uid de launchctl asuser . De plus, nous ne l'exécutons que lorsqu'il ne s'agit pas d'une installation en ligne de commande, effectuée avec l' outil d' installation ou Apple Remote Desktop .


catlan
la source
9
C'est un excellent tutoriel, mais suppose l'existence de faisceaux préfabriqués. Si je devais, par exemple, installer un seul fichier dans /tmppour être traité ultérieurement dans un script postflight, comment structurer la liste des composants? Toute la documentation disponible semble supposer que le développeur l'a générée avec --analyze, au moins initialement.
bug du
1
Si vous n'avez rien à modifier dans le, Component Property Listvous n'avez pas besoin de courir --analyze. Pour les fichiers de post-traitement, je suggère de les mettre tous dans un pkg et de définir cet emplacement d'installation de pkg sur /tmp. Mais peut-être ai-je mal compris votre question. Si c'est le cas, publiez-le dans une version plus détaillée sur SO.
catlan
2
Veuillez noter que cela n'a absolument aucun sens de créer des packages via la ligne de commande, en essayant d'échapper à tous les bogues dans l'application de packaging. Voir plutôt mon commentaire ci-dessous sur l'utilisation de l'application "Packages" de Stéphane Sudre qui résout tous les problèmes pour vous!
Bram de Jong
5
@BramdeJong "n'a aucun sens". Je ne suis pas d'accord. Apple gère les outils de ligne de commande. Packages est une application tierce qui n'est pas prise en charge par la communauté et peut être interrompue à l'avenir si Apple change quelque chose de radical. Pour moi, je préfère connaître la technique de la ligne de commande pour que, si Apple change quelque chose de radical, je puisse continuer à fonctionner.
Volomike du
5
$ pkgbuild --root ./HelloWorld.app est faux (en supposant que .app est un ensemble d'applications réel). pkgbuild fonctionne sur une racine de destination: c'est-à-dire: un dossier qui CONTIENT un bundle généré par la chaîne d'outils xcode. Donc, l'argument de pkgbuild est le chemin vers le dossier contenant le bundle que nous voulons empaqueter. Si vous ne parvenez pas à obtenir ce résultat, un package ne contient que le dossier de contenu de l'application. Il ne s'installe pas en tant qu'ensemble d'applications réel. Le cadeau est dans la liste des composants. Si cela ne contient pas d'entrée RootRelativeBundlePath spécifiant le bundle d'applications, vous vous êtes trompé.
Jonathan Mitchell
185

Il existe une application très intéressante de Stéphane Sudre qui fait tout cela pour vous, est scriptable / prend en charge la construction à partir de la ligne de commande, a une interface graphique super sympa et est GRATUITE. Ce qui est triste, c'est qu'il s'appelle "Packages", ce qui rend impossible la recherche sur Google.

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

J'aurais aimé le savoir avant de commencer à créer mes propres scripts.

Capture d'écran de l'application Packages

Bram de Jong
la source
11
Je ne peux pas croire que ce post n'ait pas plus de sens. Ce logiciel est incroyable et prend en charge la construction à partir de la ligne de commande.
Cesar Mendoza
1
Quelqu'un a essayé de signer un package avec cet outil? Je ne parviens pas à activer l'élément de menu "Définir le certificat" ...
GTAE86
2
@ user283182: Bosse très tardive, vous l'avez sûrement déjà compris, mais peut-être que cela aidera les autres - je pense que le problème auquel vous êtes confronté est détaillé dans les [Directives d'examen du Mac App Store] ( developer.apple.com/app- store / review / guidelines / mac /… ), règle 2.14: "Les applications doivent être emballées et soumises à l'aide des technologies d'emballage d'Apple incluses dans Xcode - aucun installateur tiers n'est autorisé."
aîné aîné
4
Je souhaite que ce soit une application payante et que le développeur met constamment à jour et corrige. Packages.app est génial, surtout si vous souhaitez déployer rapidement votre application. Il m'a fallu au total 3 minutes pour installer, lire la vue d'ensemble, configurer mon projet et créer le package installable. Bravo à Stéphane.
Nikolay Christov
1
Cette application est géniale!
Spencer Müller Diniz
3

Pour info, pour ceux qui essaient de créer un programme d'installation de package pour un bundle ou un plugin, c'est facile:

pkgbuild --component "Color Lists.colorPicker" --install-location ~/Library/ColorPickers ColorLists.pkg
gngrwzrd
la source
2
Pour info, il y a une différence entre la création d'un .pkg et la création d'un véritable installateur avec écran de bienvenue, licence, etc.
catlan
oui je suis conscient, j'ai mis ceci ici parce que je n'ai pas trouvé de référence à la création d'un programme d'installation pkg pour un plugin.
gngrwzrd
Cela m'a fait rouler. Juste le minimum pour donner la terre.
uchuugaka
3

Un +1 à la réponse acceptée:

Sélection de la destination dans l'installateur

Si la sélection du domaine (aka destination) est souhaitée entre le domaine utilisateur et le domaine système, alors plutôt que d'essayer d' <domains enable_anywhere="true">utiliser ce qui suit:

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

enable_currentUserHome installe l'application d'application sous ~/Applications/et enable_localSystempermet à l'application d'être installée sous/Application

J'ai essayé cela dans El Capitan 10.11.6 (15G1217) et cela semble fonctionner parfaitement dans 1 machine de développement et 2 machines virtuelles différentes que j'ai essayées.

PnotNP
la source
Cela fonctionne bien, mais avec un GOT'CHA: Si vous installez d'abord par utilisateur, puis installez par machine, l'installation se fera dans le répertoire utilisateur, pas dans le répertoire machine, mais avec les droits sudo. Le contraire n'est pas le cas: vous pouvez installer par machine, puis par utilisateur par la suite et l'avoir aux deux endroits.
Terje Dahl
@TerjeDahl oui c'est parce que, après l'installation, le bundle est déplacé vers l'emplacement où le même identifiant de bundle a été installé précédemment par l'installateur (et l'installateur le sait). Cela peut être évité par certains paramètres du fichier manifeste dont je ne me souviens pas pour le moment.
PnotNP
@ PnotNP Ah. Si vous aviez la gentillesse de revenir avec ces paramètres si vous vous en souvenez, ce serait génial!
Terje Dahl
2

Voici un script de construction qui crée un package d'installation signé à partir d'une racine de construction.

#!/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
la source
1
Oui, pkgbuild crée un programme d'installation .pkg.
Doug Richardson