Preparando pacotes do instalador para o macOS prontos para o ID do desenvolvedor

188

Nota: Isso é apenas para pacotes do OS X Installer ; os pacotes para envio à Mac App Store seguem regras diferentes.

Por causa do Porteiro do Leão da Montanha, finalmente tive que pegar meu script de compilação PackageMaker atrás do celeiro e atirar nele. O PackageMaker já foi removido do Xcode e movido para "Ferramentas Auxiliares para o Xcode", portanto esperamos que seja esquecido em breve.

A questão é como eu uso pkgbuild, productbuilde pkgutilsubstituí-lo?

Catlan
fonte
então suponho que o problema com o fabricante de pacotes seja a incapacidade de assinar corretamente os arquivos pkg para uso com o gatekeeper no Mountain Lion?
JasonZ
1
É possível, mas o PackageMaker sempre foi um buggy, e foi reprovado com o Mac OS X 10.6 Snow Leopard. Você economizará tempo a longo prazo para se familiarizar com as novas ferramentas.
catlan
@catlan: Você tem um link oficial que diz que o fabricante de pacotes foi preterido na versão 10.6?
Carl
2
@carleeto: nunca foi anunciado como obsoleto, apenas foi removido do Xcode e acabou "desaparecendo" como um manifestante birmanês.
bug
5
Notas de versão do Xcode 4.6: Descontinuação
catlan

Respostas:

343

Nosso projeto de exemplo tem dois destinos de construção: HelloWorld.app e Helper.app. Criamos um pacote de componentes para cada um e os combinamos em um arquivo de produtos .

Um pacote de componentes contém carga útil a ser instalada pelo OS X Installer. Embora um pacote de componentes possa ser instalado por si só, ele geralmente é incorporado a um arquivo do produto .

Nossas ferramentas: pkgbuild , productbuild e pkgutil

Após um "Build and Archive" bem-sucedido, abra $ BUILT_PRODUCTS_DIR no Terminal.

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

Isso nos fornece o componente, você encontra a descrição do valor na seção "Lista de propriedades do componente" . O pkgbuild -root gera os pacotes de componentes , se você não precisar alterar nenhuma das propriedades padrão, poderá omitir o parâmetro --component-plist no comando a seguir.

productbuild - sintetiza resultados em uma definição de distribuição .

$ 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 

No Distribution.xml, você pode alterar itens como título, plano de fundo, bem-vindo, leia-me, licença e assim por diante. Você transforma seus pacotes de componentes e definição de distribuição com este comando em um archive do produto :

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

Eu recomendo dar uma olhada no iTunes Installers Distribution.xml para ver o que é possível. Você pode extrair "Instalar o iTunes.pkg" com:

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

Vamos colocá-lo juntos

Normalmente, tenho uma pasta chamada Package no meu projeto, que inclui coisas como Distribution.xml, lista de componentes, recursos e scripts.

Adicione uma Fase de Criação de Script de Execução denominada "Gerar Pacote", configurada como Executar script apenas ao 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}"

Se você não precisar alterar o pacote depois que ele for gerado com o productbuild, poderá se livrar das etapas pkgutil --expande pkgutil --flatten. Além disso, você pode usar o parâmetro --sign no productbuild em vez de executar o productsign .

Assine um instalador do OS X

Os pacotes são assinados com o certificado do Developer ID Installer , que você pode baixar no Developer Certificate Utility .

A assinatura é feita com o --sign "Developer ID Installer: John Doe"parâmetro pkgbuild , productbuild ou productsign .

Observe que, se você deseja criar um arquivo de produtos assinado usando o productbuild, não há razão para assinar os pacotes de componentes .

Utilitário de certificado do desenvolvedor

Todo o caminho: Copie o pacote no arquivo Xcode

Para copiar algo no arquivo Xcode, não podemos usar a fase de criação de script de execução . Para isso, precisamos usar uma ação de esquema.

Edite Esquema e expanda Arquivar. Em seguida, clique em pós-ações e adicione uma nova ação de script de execução :

No 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

No Xcode 5, use este valor para PKG:

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

Caso seu controle de versão não armazene informações sobre o Esquema Xcode, sugiro adicioná-lo como shell script ao seu projeto para que você possa restaurar a ação com facilidade, arrastando o script da área de trabalho para a pós-ação.

Script

Existem dois tipos diferentes de script: JavaScript em arquivos de definição de distribuição e scripts de shell.

A melhor documentação sobre scripts de shell que encontrei no WhiteBox - PackageMaker How-to , mas leia isso com cautela, pois se refere ao formato antigo do pacote.

Leitura Adicional

Problemas conhecidos e soluções alternativas

Painel de Seleção de Destino

É apresentada ao usuário a opção de seleção de destino com apenas uma opção: "Instalar para todos os usuários deste computador". A opção aparece visualmente selecionada, mas o usuário precisa clicar nela para prosseguir com a instalação, causando alguma confusão.

Exemplo mostrando o bug do instalador

A Documentação da Apple recomenda o uso, <domains enable_anywhere ... />mas isso aciona o novo Painel de Seleção de Destino com mais bugs, que a Apple não usa em nenhum de seus Pacotes.

Usando o descontinuado, <options rootVolumeOnly="true" />você obtém o antigo Painel de Seleção de Destino. Exemplo mostrando o Painel de Seleção de Destino antigo


Você deseja instalar itens na pasta inicial do usuário atual.

Resposta curta: NÃO TENTE!

Resposta longa: REALMENTE; NÃO TENTE! Leia Problemas e soluções do instalador . Você sabe o que eu fiz, mesmo depois de ler isso? Eu fui estúpido o suficiente para tentar. Dizendo a mim mesmo que tenho certeza de que eles resolveram os problemas em 10.7 ou 10.8.

Antes de tudo, vi de tempos em tempos o Bug do Painel de Seleção de Destino acima mencionado. Isso deveria ter me parado, mas eu ignorei. Se você não deseja passar a semana após o lançamento do software, respondendo a e-mails de suporte nos quais eles precisam clicar uma vez que a bela seleção azul NÃO use isso.

Agora você está pensando que seus usuários são inteligentes o suficiente para descobrir o painel, não é? Bem, aqui está outra coisa sobre a instalação da pasta pessoal: ELES NÃO FUNCIONAM!

Testei-o por duas semanas em cerca de 10 máquinas diferentes com versões diferentes do sistema operacional e quais não, e nunca falhou. Então eu enviei. Dentro de uma hora do lançamento, recebo respostas de usuários que simplesmente não conseguiam instalá-lo. Os logs sugerem problemas de permissão que você não poderá corrigir.

Então, vamos repetir mais uma vez: Não usamos o Installer para instalações de pastas pessoais!


RTFD para Boas-vindas, Leia-me, Licença e Conclusão não é aceito por productbuild.

O instalador é suportado desde o início dos arquivos RTFD para criar lindas telas de boas-vindas com imagens, mas o productbuild não as aceita.

Soluções alternativas: use um arquivo rtf fictício e substitua-o no pacote depois que productbuildterminar.

Nota: Você também pode ter imagens Retina dentro do arquivo RTFD. Use arquivos TIFF multi-imagem para isso: tiffutil -cat Welcome.tif Welcome_2x.tif -out FinalWelcome.tif. Mais detalhes .


Iniciando um aplicativo quando a instalação é concluída com um 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 executar o aplicativo como usuário conectado, não como usuário instalador. Isso é feito com o caminho launchctl asuser uid . Também o executamos apenas quando não é uma instalação de linha de comando, feita com a ferramenta de instalação ou o Apple Remote Desktop .


catlan
fonte
9
Este é um excelente tutorial, mas assume a existência de pacotes configuráveis ​​pré-fabricados. Se eu fosse, por exemplo, instalar um único arquivo /tmppara ser processado posteriormente em um script pós-voo, como estruturar a lista de componentes? Toda a documentação disponível parece assumir que o desenvolvedor a gerou --analyze, pelo menos inicialmente.
bug
1
Se você não precisar alterar nada Component Property List, não precisará executar --analyze. Para arquivos de pós-processamento, sugiro colocá-los todos em um pkg e definir esse local de instalação para o pkg /tmp. Mas talvez eu entenda mal sua pergunta. Em caso afirmativo, publique-o em uma versão mais detalhada no SO.
catlan 21/10
2
Observe que não faz absolutamente nenhum sentido criar pacotes através da linha de comando, tentando escapar de todos os erros no aplicativo de empacotamento. Em vez disso, veja meu comentário abaixo sobre o uso do aplicativo "Packages" da Stéphane Sudre, que resolve todos os problemas para você!
Bram de Jong
5
@BramdeJong "não faz sentido". Discordo. A Apple mantém as ferramentas de linha de comando. Packages é um aplicativo de terceiros que não é suportado pela comunidade e pode ser interrompido no futuro se a Apple mudar algo drástico. Para mim, eu prefiro conhecer a técnica da linha de comando para que, se a Apple mudar algo drástico, eu possa continuar correndo.
Volomike
5
$ pkgbuild --root ./HelloWorld.app está errado (assumindo que .app é um pacote de aplicativos real). O pkgbuild opera em uma raiz de destino: ou seja: uma pasta que CONTÉM um pacote gerado pela cadeia de ferramentas xcode. Portanto, o argumento para pkgbuild é o caminho para a pasta que contém o pacote que queremos empacotar. Deixar de fazer isso direito resulta em um pacote que contém apenas a pasta de conteúdo do aplicativo. Não será instalado como um pacote de aplicativos real. A oferta está no componente principal. Se isso não contiver uma entrada RootRelativeBundlePath especificando o pacote de aplicativos, você errou.
22616 Jonathan Mitchell
185

Existe uma aplicação muito interessante da Stéphane Sudre que faz tudo isso para você, é programável / suporta construção a partir da linha de comando, tem uma interface gráfica super agradável e é GRÁTIS. O triste é que se chama "Pacotes", o que torna impossível encontrar no google.

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

Eu gostaria de saber sobre isso antes de começar a criar meus próprios scripts.

Captura de tela do aplicativo de pacotes

Bram de Jong
fonte
11
Não acredito que este post não tenha mais sentido. Esse software é incrível e suporta a construção a partir da linha de comando.
César Mendoza
1
Alguém tentou assinar um pacote com esta ferramenta? Eu não posso obter o item de menu "Set Certificado" para activar ....
GTAE86
2
@ user283182: Solavanco muito tarde, certamente você já descobriu, mas talvez isso ajude outras pessoas - acho que o problema que você está enfrentando está detalhado nas [Diretrizes de revisão da Mac App Store] ( developer.apple.com/app- store / review / guidelines / mac /… ), regra 2.14: "Os aplicativos devem ser empacotados e enviados usando as tecnologias de empacotamento da Apple incluídas no Xcode - não são permitidos instaladores de terceiros".
ancião ancião
4
Desejo que este seja um aplicativo pago e o desenvolvedor esteja atualizando e corrigindo constantemente. Packages.app é incrível, especialmente se você deseja implantar rapidamente seu aplicativo. Levei um total de 3 minutos para instalar, ler a Visão geral, configurar meu projeto e criar o pacote instalável. Muitos elogios para Stéphane.
Nikolay Christov
1
Este aplicativo é incrível!
Spencer Müller Diniz
3

Para sua informação, para aqueles que estão tentando criar um instalador de pacote para um pacote ou plugin, é fácil:

pkgbuild --component "Color Lists.colorPicker" --install-location ~/Library/ColorPickers ColorLists.pkg
gngrwzrd
fonte
2
Para sua informação, existe uma diferença entre criar um .pkg e criar um instalador real com tela de boas-vindas, licença e assim por diante.
catlan
sim, eu sei, coloquei isso aqui porque não consegui encontrar referência para criar um instalador do pkg para um plugin.
Gngrwzrd 11/08
Isso me fez rolar. Apenas o mínimo para fundamentar.
uchuugaka
3

Resposta +1 à resposta aceita:

Seleção de destino no instalador

Se a seleção de domínio (também conhecida como destino) for desejada entre o domínio do usuário e o domínio do sistema, em vez de tentar <domains enable_anywhere="true">usar o seguinte:

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

enable_currentUserHome instala o aplicativo em ~/Applications/e enable_localSystempermite que o aplicativo seja instalado em/Application

Eu tentei isso no El Capitan 10.11.6 (15G1217) e parece estar funcionando perfeitamente em 1 máquina de desenvolvimento e 2 VMs diferentes que tentei.

PnotNP
fonte
Isso funciona bem, mas com um GOT'CHA: Se você primeiro instalar por usuário e depois instalar por máquina, a instalação estará no diretório do usuário, não no diretório da máquina, mas com direitos sudo. O contrário não é o caso: você pode instalar por máquina e depois por usuário e instalá-lo nos dois lugares.
Terje Dahl
@TerjeDahl sim, isso ocorre porque, após a instalação, o pacote configurável é movido para o local em que o mesmo ID do pacote configurável foi instalado anteriormente pelo instalador (e o instalador sabe disso). Isso pode ser evitado por algumas configurações no arquivo de manifesto das quais não me lembro agora.
precisa saber é o seguinte
@ PnotNP Ah. Se você tivesse a gentileza de voltar com as configurações, se pudesse se lembrar, isso seria ótimo!
Terje Dahl
2

Aqui está um script de construção que cria um pacote de instalação assinado a partir de uma raiz de construção.

#!/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
fonte
1
Sim, o pkgbuild cria um instalador .pkg.
Doug Richardson