Preparare i pacchetti di installazione di macOS che sono pronti per l'ID sviluppatore


189

Nota: questo è solo per i pacchetti di installazione di OS X , i pacchetti per l'invio al Mac App Store seguono regole diverse.

A causa di Mountain Lion's Gatekeeper alla fine ho dovuto prendere il mio script di costruzione PackageMaker dietro il fienile e girarlo. PackageMaker è già stato rimosso da Xcode e spostato in "Strumenti ausiliari per Xcode", quindi si spera che venga presto dimenticato.

La domanda è: come si usa pkgbuild, productbuilde pkgutilsostituirlo?


quindi presumo che il problema con il packagemaker sia l'impossibilità di firmare correttamente i file pkg da utilizzare con gatekeeper su Mountain Lion?
JasonZ,

1
È possibile, ma PackageMaker ha sempre avuto problemi con l'inferno e si è deprecato con Mac OS X 10.6 Snow Leopard. A lungo termine ti farà risparmiare tempo per familiarizzare con i nuovi strumenti.
catlan,

@catlan: hai un link ufficiale che dice che il pacchetto è stato deprecato il 10.6?
Carl,

2
@carleeto: non è mai stato annunciato come deprecato, appena rimosso da Xcode e alla fine "scomparso" come un manifestante birmano.
bug

5
Note di rilascio di Xcode 4.6: Deprecazione
catlan

Risposte:


344

Il nostro progetto di esempio ha due target di build: HelloWorld.app e Helper.app. Realizziamo un pacchetto di componenti per ciascuno e li combiniamo in un archivio prodotti .

Un pacchetto di componenti contiene payload che deve essere installato dal programma di installazione di OS X. Sebbene un pacchetto di componenti possa essere installato da solo, in genere è incorporato in un archivio prodotti .

I nostri strumenti: pkgbuild , productbuild e pkgutil

Dopo un "Build and Archive" riuscito apri $ BUILT_PRODUCTS_DIR nel Terminale.

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

Questo ci dà la lista dei componenti, puoi trovare la descrizione del valore nella sezione "Elenco delle proprietà dei componenti" . pkgbuild -root genera i pacchetti dei componenti , se non è necessario modificare nessuna delle proprietà predefinite è possibile omettere il parametro --component-plist nel comando seguente.

productbuild --synthesize risulta in una definizione di distribuzione .

$ 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 

In Distribution.xml puoi cambiare cose come titolo, sfondo, benvenuto, readme, licenza e così via. Trasforma i pacchetti dei componenti e la definizione di distribuzione con questo comando in un archivio prodotti :

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

Consiglio di dare un'occhiata a iTunes Installers Distribution.xml per vedere cosa è possibile. Puoi estrarre "Installa iTunes.pkg" con:

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

Mettiamolo insieme

Di solito ho una cartella chiamata Package nel mio progetto che include cose come Distribution.xml, componenti, risorse e script.

Aggiungi una fase di costruzione dello script Run denominata "Genera pacchetto", che è impostata su Esegui script solo durante l'installazione :

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}"

Se non è necessario modificare il pacchetto dopo che è stato generato con productbuild, è possibile eliminare i passaggi pkgutil --expande pkgutil --flatten. Inoltre è possibile utilizzare il --sign paramenter su productbuild invece di correre productsign .

Firma un programma di installazione di OS X.

I pacchetti sono firmati con il certificato Installer ID sviluppatore che è possibile scaricare dall'Utilità certificato sviluppatore .

La firma viene eseguita con il --sign "Developer ID Installer: John Doe"parametro di pkgbuild , productbuild o productsign .

Si noti che se si intende creare un archivio prodotti firmato utilizzando productbuild, non vi è alcun motivo per firmare i pacchetti dei componenti .

Utilità certificato sviluppatore

Completamente: copia il pacchetto in Xcode Archive

Per copiare qualcosa nell'archivio Xcode non possiamo usare la fase di costruzione Run Script . Per questo abbiamo bisogno di usare un'azione Scheme.

Modifica schema ed espandi Archivio. Quindi fare clic su post-azioni e aggiungere una nuova azione Script di esecuzione :

In 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

In Xcode 5, utilizzare invece questo valore per PKG:

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

Nel caso in cui il controllo della versione non memorizzi le informazioni sullo schema Xcode, suggerisco di aggiungerlo come script shell al progetto in modo da poter ripristinare semplicemente l'azione trascinando lo script dallo spazio di lavoro in post-azione.

Scripting

Esistono due diversi tipi di script: JavaScript nei file di definizione della distribuzione e Script di shell.

La migliore documentazione sugli script Shell che ho trovato in WhiteBox - Come fare PackageMaker , ma leggi questo con cautela perché si riferisce al vecchio formato del pacchetto.

Letture aggiuntive

Problemi noti e soluzioni alternative

Riquadro di selezione della destinazione

All'utente viene presentata l'opzione di selezione della destinazione con una sola scelta: "Installa per tutti gli utenti di questo computer". L'opzione appare visivamente selezionata, ma l'utente deve fare clic su di essa per procedere con l'installazione, creando confusione.

Esempio che mostra il bug del programma di installazione

La documentazione sulle mele consiglia di utilizzare, <domains enable_anywhere ... />ma ciò innesca il nuovo riquadro di selezione della destinazione più difettoso che Apple non utilizza in nessuno dei loro pacchetti.

L'utilizzo del deprecato <options rootVolumeOnly="true" />fornisce il riquadro di selezione della destinazione precedente. Esempio che mostra il riquadro di selezione della destinazione precedente


Si desidera installare elementi nella cartella principale dell'utente corrente.

Risposta breve: NON PROVARLO!

Risposta lunga: DAVVERO; NON PROVARLO! Leggi i problemi e le soluzioni dell'installatore . Sai cosa ho fatto anche dopo aver letto questo? Sono stato abbastanza stupido da provarlo. Mi dico che sono sicuro che hanno risolto i problemi in 10.7 o 10.8.

Prima di tutto ho visto di tanto in tanto il suddetto errore nel riquadro di selezione della destinazione. Ciò avrebbe dovuto fermarmi, ma l'ho ignorato. Se non vuoi passare la settimana dopo aver rilasciato il tuo software rispondendo alle e-mail di supporto che devono fare clic una volta che la bella selezione blu NON usa questo.

Ora stai pensando che i tuoi utenti siano abbastanza intelligenti da capire il pannello, vero? Bene, ecco un'altra cosa sull'installazione della cartella home, NON FUNZIONANO!

L'ho provato per due settimane su circa 10 macchine diverse con diverse versioni del sistema operativo e cosa no, e non ha mai fallito. Quindi l'ho spedito. Entro un'ora dal rilascio rincuoro gli utenti che non sono riusciti a installarlo. I registri suggeriscono problemi di autorizzazione che non sarai in grado di risolvere.

Ripetiamolo ancora una volta: non utilizziamo l'Installer per le installazioni di cartelle home!


RTFD per Benvenuto, Leggimi, Licenza e Conclusione non è accettato da productbuild.

Programma di installazione supportato sin dall'inizio file RTFD per creare schermate di benvenuto con immagini, ma productbuild non le accetta.

Soluzioni alternative: utilizzare un file rtf fittizio e sostituirlo nel pacchetto dopo aver terminato productbuild.

Nota: è anche possibile avere immagini Retina all'interno del file RTFD. Utilizzare file tiff multi-immagine per questo: tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif. Maggiori dettagli .


Avvio di un'applicazione al termine dell'installazione con uno 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

È importante eseguire l'app come utente connesso, non come utente del programma di installazione. Questo viene fatto con launchctl asuser uid path . Inoltre lo eseguiamo solo quando non è un'installazione da riga di comando, eseguita con lo strumento di installazione o Apple Remote Desktop .



9
Questo è un tutorial eccellente, ma presuppone l'esistenza di fasci prefabbricati. Se, ad esempio, dovessi installare un singolo file /tmpper essere elaborato successivamente in uno script postflight, come strutturare l'elenco dei componenti? Tutta la documentazione disponibile sembra presumere che lo sviluppatore l'abbia generata --analyze, almeno inizialmente.
bug

1
Se non è necessario modificare nulla Component Property List, non è necessario eseguire--analyze . Per i file di postelaborazione, suggerisco di inserirli tutti in un pkg e impostare il percorso di installazione di pkg su /tmp. Ma forse ho frainteso la tua domanda. In tal caso, pubblicalo in una versione più dettagliata su SO.
catlan,

2
Si noti che non ha assolutamente senso creare pacchetti tramite la riga di comando, cercando di sfuggire a tutti i bug nell'app di packaging. Piuttosto vedi il mio commento qui sotto sull'uso dell'applicazione "Pacchetti" di Stéphane Sudre che risolve tutti i problemi per te!
Bram de Jong,

5
@BramdeJong "non ha senso". Non sono d'accordo. Apple mantiene gli strumenti da riga di comando. Pacchetti è un'app di terze parti che non è supportata dalla comunità e potrebbe rompersi in futuro se Apple cambia qualcosa di drastico. Per me, preferirei conoscere la tecnica della riga di comando in modo che, se Apple cambia qualcosa di drastico, allora posso continuare a correre.
Volomike,

5
$ pkgbuild --root ./HelloWorld.app è errato (supponendo che .app sia un vero e proprio pacchetto di app). pkgbuild opera su una radice di destinazione: ovvero: una cartella che CONTIENE un bundle generato dalla catena di strumenti xcode. Quindi l'argomento di pkgbuild è il percorso della cartella contenente il pacchetto che vogliamo impacchettare. Se non si ottiene questo risultato corretto si ottiene un pacchetto che contiene solo la cartella del contenuto dell'app. Non verrà installato come un vero e proprio pacchetto di app. L'omaggio è nella lista componenti. Se questo non contiene una voce RootRelativeBundlePath che specifica il bundle dell'app, hai sbagliato.
Jonathan Mitchell

185

Esiste un'applicazione molto interessante di Stéphane Sudre che fa tutto questo per te, è programmabile / supporta la costruzione dalla riga di comando, ha una superba interfaccia grafica ed è GRATUITA. La cosa triste è: si chiama "Pacchetti" che rende impossibile trovare su Google.

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

Avrei voluto saperlo prima di iniziare a creare a mano i miei script.

Schermata dell'applicazione Pacchetti


11
Non posso credere che questo post non abbia più senso. Quel software è fantastico e supporta la costruzione dalla riga di comando.
Cesar Mendoza,

1
Qualcuno ha provato a firmare un pacchetto con questo strumento? Non riesco ad attivare la voce di menu "Imposta certificato" ....
GTAE86

2
@ user283182: Urto molto tardi, sicuramente l'hai già capito, ma forse questo aiuterà gli altri - Penso che il problema che stai affrontando sia dettagliato nelle [Linee guida per la revisione del Mac App Store] ( developer.apple.com/app- store / review / linee guida / mac / ... ), regola 2.14: "Le app devono essere impacchettate e inviate utilizzando le tecnologie di packaging di Apple incluse in Xcode - non sono ammessi installatori di terze parti".
Elder Elder,

4
Vorrei che fosse un'app a pagamento e lo sviluppatore aggiorna e corregge costantemente. Packages.app è fantastico soprattutto se vuoi distribuire rapidamente la tua app. Mi ci sono voluti 3 minuti per l'installazione leggere la panoramica, impostare il mio progetto e creare il pacchetto installabile. Grandi complimenti a Stéphane.
Nikolay Christov,

1
Questa app è fantastica!
Spencer Müller Diniz,

3

Cordiali saluti per coloro che stanno cercando di creare un programma di installazione di pacchetti per un pacchetto o un plug-in, è facile:

pkgbuild --component "Color Lists.colorPicker" --install-location ~/Library/ColorPickers ColorLists.pkg

2
Cordiali saluti, c'è una differenza tra la creazione di un .pkg e la creazione di un vero programma di installazione con schermata di benvenuto, licenza e così via.
catlan,

sì, lo so, l'ho messo qui perché non sono riuscito a trovare riferimenti alla creazione di un programma di installazione pkg per un plugin.
gngrwzrd,

Questo mi ha fatto rotolare. Solo il minimo per dare la messa a terra.
Uchuugaka,

3

Un +1 alla risposta accettata:

Selezione destinazione nell'installatore

Se si desidera la selezione del dominio (aka destinazione) tra dominio utente e dominio di sistema, anziché provare a <domains enable_anywhere="true">utilizzare quanto segue:

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

enable_currentUserHome installa l'applicazione in ~/Applications/e enable_localSystemconsente di installare l'applicazione in/Application

Ho provato questo in El Capitan 10.11.6 (15G1217) e sembra funzionare perfettamente su 1 macchina di sviluppo e 2 macchine virtuali diverse che ho provato.


Funziona bene, ma con un GOT'CHA: se si installa prima per utente, quindi si installa per macchina, l'installazione sarà nella directory utente, non nella directory macchina, ma con diritti sudo. Il contrario non è il caso: è possibile installare per computer, quindi per utente in seguito e averlo in entrambi i punti.
Terje Dahl,

@TerjeDahl sì, perché dopo l'installazione il pacchetto viene spostato nella posizione lo stesso ID pacchetto è stato installato in precedenza dall'installer (e l'installer lo sa). Questo può essere impedito da alcune impostazioni nel file manifest che non ricordo in questo momento.
PnotNP,

@ PnotNP Ah. Se tu fossi così gentile da tornare con quelle ambientazioni da ricordare, sarebbe fantastico!
Terje Dahl,

2

Ecco uno script di build che crea un pacchetto di installazione firmato da una radice di build.

#!/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

1
Sì, pkgbuild crea un programma di installazione .pkg.
Doug Richardson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.