Przygotowywanie pakietów instalatora macOS, które są gotowe na ID dewelopera

189

Uwaga: dotyczy tylko pakietów Instalatora OS X , pakiety do przesłania do Mac App Store podlegają innym regułom.

Ze względu Mountain Lion jest Gatekeeper końcu miałem wziąć mój PackageMaker kompilacji skryptu za stodołę i strzelać. PackageMaker został już usunięty z Xcode i przeniesiony do „Narzędzi pomocniczych dla Xcode”, więc mam nadzieję, że wkrótce zostanie zapomniany.

Powstaje pytanie, w jaki sposób mogę korzystać pkgbuild, productbuildi pkgutilaby go zastąpić?

catlan
źródło
więc zakładam, że problemem z packagemaker jest niemożność prawidłowego podpisania plików pkg do użytku z gatekeeper na Mountain Lion?
JasonZ
1
Jest to możliwe, ale PackageMaker zawsze był buggy, ma piekło i przestał działać w systemie Mac OS X 10.6 Snow Leopard. Na dłuższą metę zaoszczędzisz czas na zapoznanie się z nowymi narzędziami.
catlan
@catlan: Czy masz oficjalny link z informacją, że program packagemaker został wycofany z wersji 10.6?
Carl
2
@carleeto: Nigdy nie został ogłoszony jako przestarzały, po prostu usunięty z Xcode i ostatecznie „zniknął” jak birmański protestujący.
błąd
5
Informacje o wersji Xcode 4.6: Wycofanie
catlan 29.01.2013

Odpowiedzi:

344

Nasz przykładowy projekt ma dwa cele kompilacji: HelloWorld.app i Helper.app. Dla każdego tworzymy pakiet komponentów i łączymy je w archiwum produktów .

Pakiet komponent zawiera ładowność być zainstalowany przez OS X Installer. Chociaż pakiet składników można zainstalować samodzielnie, zazwyczaj jest on dołączany do archiwum produktów .

Nasze narzędzia: pkgbuild , productbuild i pkgutil

Po udanym „budowaniu i archiwizowaniu” otwórz $ BUILT_PRODUCTS_DIR w terminalu.

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

To daje nam listę komponentów, opis wartości znajdziesz w sekcji „Lista właściwości komponentów” . pkgbuild -root generuje pakiety składników , jeśli nie musisz zmieniać żadnych domyślnych właściwości, możesz pominąć parametr --component-plist w następującym poleceniu.

budowanie produktu - synteza wyników w definicji rozkładu .

$ 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 

W pliku Distribution.xml możesz zmieniać takie elementy, jak tytuł, tło, powitanie, plik readme, licencja i tak dalej. Za pomocą tego polecenia zamieniasz pakiety komponentów i definicję dystrybucji w archiwum produktu :

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

Polecam zajrzeć do iTunes Installers Distribution.xml, aby zobaczyć, co jest możliwe. Możesz wyodrębnić „Zainstaluj iTunes.pkg” za pomocą:

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

Złóżmy to razem

Zwykle w moim projekcie jest folder o nazwie Pakiet, który zawiera między innymi Distribution.xml, listy komponentów, zasoby i skrypty.

Dodaj fazę kompilacji skryptu uruchamiania o nazwie „Generuj pakiet”, która jest ustawiona na Uruchom skrypt tylko podczas instalacji :

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

Jeśli nie musisz zmieniać pakietu po jego wygenerowaniu za pomocą buildbuild, możesz pozbyć się kroków pkgutil --expandi pkgutil --flattenkroków. Również można użyć --sign paramenter na productbuild zamiast prowadzenia productsign .

Podpisz instalatora OS X

Pakiety są podpisane certyfikatem instalatora ID dewelopera, który można pobrać z Narzędzia certyfikatu programisty .

Podpisywanie odbywa się za pomocą --sign "Developer ID Installer: John Doe"parametru pkgbuild , build produktu lub znak produktu .

Pamiętaj, że jeśli zamierzasz utworzyć podpisane archiwum produktów przy użyciu programu buildbuild, nie ma powodu, aby podpisywać pakiety komponentów .

Narzędzie certyfikatu programisty

Całą drogę: Skopiuj pakiet do archiwum Xcode

Aby skopiować coś do Archiwum Xcode, nie możemy użyć fazy kompilacji skryptu Run . W tym celu musimy użyć akcji programu.

Edytuj schemat i rozwiń Archiwum. Następnie kliknij czynności po i dodaj nową akcję Uruchom skrypt :

W 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

W Xcode 5 użyj tej wartości PKGzamiast:

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

W przypadku, gdy twoja kontrola wersji nie przechowuje informacji o Xcode Scheme, sugeruję dodać to jako skrypt powłoki do twojego projektu, abyś mógł łatwo przywrócić akcję, przeciągając skrypt z obszaru roboczego do post-action.

Skrypty

Istnieją dwa różne rodzaje skryptów: JavaScript w plikach definicji dystrybucji i skrypty powłoki.

Najlepsza dokumentacja dotycząca skryptów powłoki, którą znalazłam w WhiteBox - PackageMaker - instrukcje , ale przeczytaj ją ostrożnie, ponieważ odnosi się do starego formatu pakietu.

Dodatkowe czytanie

Znane problemy i obejścia

Miejsce docelowe Wybierz okienko

Użytkownik otrzymuje opcję wyboru miejsca docelowego z jednym wyborem - „Zainstaluj dla wszystkich użytkowników tego komputera”. Opcja pojawia się wizualnie wybrana, ale użytkownik musi ją kliknąć, aby kontynuować instalację, powodując pewne zamieszanie.

Przykład pokazujący błąd instalatora

Dokumentacja jabłek zaleca użycie, <domains enable_anywhere ... />ale powoduje to pojawienie się nowego, bardziej błędnego okienka wyboru miejsca docelowego, którego Apple nie używa w żadnym ze swoich pakietów.

Używając przestarzałych, <options rootVolumeOnly="true" />otrzymujesz stare okienko wyboru miejsca docelowego. Przykład pokazujący stary panel Wybierz miejsce docelowe


Chcesz zainstalować elementy w folderze domowym bieżącego użytkownika.

Krótka odpowiedź: NIE WYPRÓBUJ GO!

Długa odpowiedź: NAPRAWDĘ; NIE WYPRÓBUJ GO! Przeczytaj Problemy i rozwiązania instalatora . Wiesz co zrobiłem nawet po przeczytaniu tego? Byłem na tyle głupi, żeby tego spróbować. Mówiąc sobie, jestem pewien, że rozwiązali problemy w wersji 10.7 lub 10.8.

Przede wszystkim widziałem od czasu do czasu wspomniany wyżej błąd okienka wyboru miejsca docelowego. To powinno mnie zatrzymać, ale zignorowałem to. Jeśli nie chcesz spędzić tygodnia po wydaniu oprogramowania, odpowiadając na e-maile dotyczące pomocy technicznej, które muszą kliknąć raz po ładnym niebieskim zaznaczeniu NIE używaj tego.

Myślisz teraz, że Twoi użytkownicy są wystarczająco inteligentni, aby wymyślić panel, prawda? Cóż, oto kolejna rzecz dotycząca instalacji folderów domowych, NIE DZIAŁAJĄ!

Testowałem go przez dwa tygodnie na około 10 różnych maszynach z różnymi wersjami systemu operacyjnego, a co nie, i nigdy nie zawiodło. Więc wysłałem to. W ciągu godziny od premiery przesyłam serdecznie użytkownikom, którzy po prostu nie mogli go zainstalować. Dzienniki wskazywały na problemy z uprawnieniami, których nie będziesz w stanie naprawić.

Powtórzmy to jeszcze raz: Nie używamy Instalatora do instalacji folderów domowych!


RTFD for Welcome, Read-me, License and Concellation nie jest akceptowane przez productbuild.

Instalator od początku obsługiwał pliki RTFD, aby tworzyć ładne ekrany powitalne z obrazami, ale buildbuild produktu ich nie akceptuje.

Obejścia: Użyj fikcyjnego pliku rtf i zamień go w pakiecie po zakończeniu productbuild.

Uwaga: W pliku RTFD można również umieszczać obrazy Retina. Użyj tiff wielu obrazów na to: tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif. Więcej szczegółów .


Uruchamianie aplikacji po zakończeniu instalacji za pomocą skryptu 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

Ważne jest, aby uruchomić aplikację jako użytkownik zalogowany, a nie jako użytkownik instalatora. Odbywa się to za pomocą ścieżki identyfikatora użytkownika launchctl asuser . Ponadto uruchamiamy go tylko wtedy, gdy nie jest to instalacja z poziomu wiersza poleceń, wykonana za pomocą narzędzia instalacyjnego lub Apple Remote Desktop .


catlan
źródło
9
Jest to doskonały samouczek, ale zakłada istnienie pakietów prefabrykowanych. Gdybym miał na przykład zainstalować pojedynczy plik /tmpdo przetworzenia później w skrypcie postflight, w jaki sposób uporządkować listę komponentów? Wydaje się, że cała dostępna dokumentacja zakłada, że ​​programista ją wygenerował --analyze, przynajmniej początkowo.
błąd
1
Jeśli nie musisz nic zmieniać Component Property List, nie musisz biegać --analyze. W przypadku plików postprocessing proponuję umieścić je wszystkie w pkg i ustawić lokalizację instalacji pkg na /tmp. Ale może źle zrozumiałem twoje pytanie. Jeśli tak, opublikuj go w bardziej szczegółowej wersji na SO.
catlan
2
Zauważ, że absolutnie nie ma sensu tworzyć pakietów za pomocą wiersza poleceń, próbując uniknąć wszystkich błędów w aplikacji do pakowania. Zamiast tego zobacz mój komentarz na temat korzystania z aplikacji „Pakiety” Stéphane Sudre, która rozwiązuje wszystkie problemy dla Ciebie!
Bram de Jong,
5
@BramdeJong „nie ma sensu”. Nie zgadzam się. Apple utrzymuje narzędzia wiersza poleceń. Pakiety to aplikacja innych firm, która nie jest obsługiwana przez społeczność i może ulec awarii w przyszłości, jeśli Apple zmieni coś drastycznego. Dla mnie wolałbym znać technikę wiersza poleceń, aby jeśli Apple zmieniło coś drastycznego, mógłbym kontynuować.
Volomike
5
$ pkgbuild --root ./HelloWorld.app jest niepoprawny (zakładając, że .app jest rzeczywistym pakietem aplikacji). pkgbuild działa na docelowym katalogu głównym: tj. folderze, który ZAWIERA pakiet wygenerowany przez łańcuch narzędzi xcode. Argumentem pkgbuild jest więc ścieżka do folderu zawierającego pakiet, który chcemy spakować. Niepoprawne uzyskanie tego skutkuje pakietem zawierającym tylko folder zawartości aplikacji. Nie zostanie zainstalowany jako rzeczywisty pakiet aplikacji. Podarunek znajduje się w plist komponentów. Jeśli to nie zawiera wpisu RootRelativeBundlePath określającego pakiet aplikacji, to masz problem.
Jonathan Mitchell,
185

Jest jedna bardzo interesująca aplikacja Stéphane Sudre, która robi to wszystko za Ciebie, jest skryptowalna / obsługuje budowanie z wiersza poleceń, ma bardzo ładny interfejs GUI i jest BEZPŁATNA. Smutne jest to, że nazywa się to „Pakietami”, co uniemożliwia znalezienie google.

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

Żałowałem, że nie wiedziałem o tym, zanim zacząłem ręcznie tworzyć własne skrypty.

Zrzut ekranu aplikacji pakietu

Bram de Jong
źródło
11
Nie mogę uwierzyć, że ten post nie ma więcej sensu. To oprogramowanie jest niesamowite i obsługuje budowanie z wiersza poleceń.
Cesar Mendoza
1
Ktoś próbował podpisać paczkę za pomocą tego narzędzia? Nie mogę uzyskać pozycji menu „Ustaw certyfikat” w celu aktywacji ....
GTAE86
2
@ user283182: Bardzo późny guz, na pewno już to zrozumiałeś, ale być może pomoże to innym - myślę, że problem, z którym się zmierzasz, jest szczegółowo opisany w [Wytyczne recenzji Mac App Store] ( developer.apple.com/app- store / review / Guidelines / mac /… ), reguła 2.14: „Aplikacje muszą być pakowane i przesyłane przy użyciu technologii pakowania Apple zawartych w Xcode - nie są dozwolone instalatory innych firm”.
starszy starszy
4
Chciałbym, aby była to aplikacja płatna, a programista stale aktualizuje i łata. Packages.app jest niesamowity, szczególnie jeśli chcesz szybko wdrożyć swoją aplikację. Instalacja zajęła mi 3 minuty. Przeczytaj omówienie, skonfiguruj mój projekt i utwórz instalowalny pakiet. Wielkie uznanie dla Stéphane'a.
Nikolay Christov
1
Ta aplikacja jest niesamowita!
Spencer Müller Diniz
3

Dla tych, którzy próbują utworzyć instalator pakietów dla pakietu lub wtyczki, jest to łatwe:

pkgbuild --component "Color Lists.colorPicker" --install-location ~/Library/ColorPickers ColorLists.pkg
gngrwzrd
źródło
2
Do Twojej wiadomości, istnieje różnica między tworzeniem pliku .pkg a tworzeniem prawdziwego instalatora z ekranem powitalnym, licencją itd.
catlan
tak, wiem, umieściłem to tutaj, ponieważ nie mogłem znaleźć odniesienia do tworzenia instalatora pkg dla wtyczki.
gngrwzrd
To mnie zmusiło. Tylko minimum, aby dać uziemienie.
uchuugaka,
3

+1 do zaakceptowanej odpowiedzi:

Wybór miejsca docelowego w instalatorze

Jeśli pożądany jest wybór domeny (inaczej miejsca docelowego) między domeną użytkownika a domeną systemową, zamiast próbować <domains enable_anywhere="true">wykonać następujące czynności:

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

enable_currentUserHome instaluje aplikację pod ~/Applications/i enable_localSystemzezwala na instalację aplikacji pod/Application

Próbowałem tego w El Capitan 10.11.6 (15G1217) i wydaje się, że działa idealnie dobrze na 1 maszynie deweloperskiej i 2 różnych maszynach wirtualnych, które wypróbowałem.

PnotNP
źródło
Działa to dobrze, ale z GOT'CHA: Jeśli najpierw instalujesz na użytkownika, a następnie instalujesz na maszynie, instalacja będzie w katalogu użytkownik-katalog, nie katalog-maszyna, ale z prawami sudo. Nie jest odwrotnie: możesz zainstalować na maszynie, a następnie na użytkownika i mieć ją w obu miejscach.
Terje Dahl
@TerjeDahl tak, ponieważ po instalacji pakiet jest przenoszony do lokalizacji, w której ten sam identyfikator pakietu został wcześniej zainstalowany przez instalatora (i instalator o tym wie). Można temu zapobiec dzięki niektórym ustawieniom w pliku manifestu, których teraz nie pamiętam.
PnotNP
@ PnotNP Ah. Jeśli byłbyś tak miły, aby powrócić z tymi ustawieniami, jeśli pamiętasz, to byłoby świetnie!
Terje Dahl
2

Oto skrypt kompilacji, który tworzy podpisany pakiet instalatora z katalogu głównego kompilacji.

#!/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
źródło
1
Tak, pkgbuild tworzy instalator .pkg.
Doug Richardson