<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>[admin@blog] #</title>
	<atom:link href="http://adminblog.foucry.net/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://adminblog.foucry.net</link>
	<description></description>
	<lastBuildDate>Fri, 27 Apr 2012 15:10:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>GitHub, ssh et les clefs</title>
		<link>http://adminblog.foucry.net/?p=1218</link>
		<comments>http://adminblog.foucry.net/?p=1218#comments</comments>
		<pubDate>Fri, 13 Apr 2012 09:11:41 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Trucs à deux balles]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=1218</guid>
		<description><![CDATA[Aujourd&#8217;hui j&#8217;ai décidé de déposer le script xcodebuild-wrapper.py dans GitHub afin de le partager avec la communauté. Les échanges avec GitHub se font en passant par SSH afin de chiffrer les communication est assurer un minimum de sécurité. Dans la mise en place du compte GitHub explique comment créer une paire de clefs RSA, comme [...]]]></description>
			<content:encoded><![CDATA[<p>Aujourd&#8217;hui j&#8217;ai décidé de déposer le script xcodebuild-wrapper.py dans <a href="http://www.github.com">GitHub</a> afin de le partager avec la communauté.</p>
<p>Les échanges avec GitHub se font en passant par SSH afin de chiffrer les communication est assurer un minimum de sécurité.</p>
<p>Dans la mise en place du compte GitHub explique comment créer une paire de clefs RSA, comme mettre la clef publique sur dans les paramètres du compte GitHub.</p>
<p>Tout cela est très bien, mais me pose (et peux vous poser) un problème. J&#8217;ai déjà des paires de clefs SSH dans mon <code>$HOME/.ssh</code>. Comment faire pour avoir une clef spécifique et utiliser celle là au moment des push/pull ?</p>
<p><span id="more-1218"></span></p>
<h2>Génération de la paire de clefs</h2>
<p>La première entorse aux instructions de GitHub se fait dès la création de la paire de clef. Au lieu de laisser faire <code>ssh-keygen</code>, je donne le nom du fichier que je veux pour ma clef privée, ici <code>github.rsa</code></p>
<pre class="brush: bash; title: ; notranslate"> ssh-keygen -t rsa -C &quot;github@example.com&quot;
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa): /home/user/.ssh/github.rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/user/.ssh/github.rsa.
Your public key has been saved in /home/user/.ssh/github.rsa.pub.
The key fingerprint is:
8f:12:12:1e:76:3f:b9:4a:2c:60:fd:62:f3:da:36:b4 github@example.com</pre>
<p>Il faut ensuite déposer la clef publique (<code>~/.ssh/github.ras.pub</code>) sur le serveur en suivant les instructions de GitHub&#8230; Encore une fois pas jusqu&#8217;au bout.</p>
<p>Pour testes la connectivité nous devons modifier la commande proposée :</p>
<pre class="brush: bash; title: ; notranslate"># ssh -T git@github.com -i $HOME/.ssh/github.rsa
Enter passphrase for key '/home/jfoucry/.ssh/github.rsa':
Hi jfoucry! You've successfully authenticated, but GitHub does not provide shell access.</pre>
<h2>Macédoine d&#8217;instructions</h2>
<p>GitHub propose de configurer de façon globale (mot clef global dans les commandes git). Personnellement, ça me dérange, j&#8217;utilise git avec d&#8217;autres dépôts et j&#8217;aimerais laisser la configuration global tel quelle est.</p>
<p>Avant la série d&#8217;instruction qui consiste à mettre le nom du développeur et le token GitHub dans la configuration GLOBAL de git, nous allons créer le répertoire local qui contiens nos sources.</p>
<pre class="brush: bash; title: ; notranslate"># mkdir ~/Documents/Sources/projets/MonProjet
# cd ~/Documents/Sources/projets/MonProjet
# git init </pre>
<p>Notre dépôt local de référence est créé, c&#8217;est maintenant que nous allons reprendre les instructions de GitHub, avec une petite différence, nous allons retirer le mot clef global :</p>
<pre class="brush: bash; title: ; notranslate"># git config github.user userName
# git config github.token MonToken
# git config user.name &quot;Prenom Nom&quot;
# git config user.email &quot;github@example.com&quot;</pre>
<h2>SSh, le retour de la vegeance du petit fils</h2>
<p>Si nous continuons les instructions de GitHub, nous allons avoir un problème. La commande git ne va pas trouver la bonne clef privée pour dialoguer avec le serveur GitHub. Nous devons ruser en utilisant une des fonctionnalités de OpenSSH, j&#8217;ai nommé, le fichier <code>$HOME/.ssh/config</code>.</p>
<p>Dans ce fichier nous allons indiquer comment joindre le serveur GitHub. Mais voyez plutôt :</p>
<pre class="brush: bash; title: ; notranslate">host github
	Hostname github.com
	user git
	IdentityFile /home/jfoucry/.ssh/github.rsa</pre>
<p>La première ligne est un petit nom pour le serveur, le seconde indique l&#8217;adresse ou le nom du serveur, user DOIT être git dans cette configuration, et IdentityFile est le chemin vers la clef privée.</p>
<p>Ré-essayons la commande que GitHub propose pour tester une fois le fichier sauvegardé. Cette fois, j&#8217;utilise le petit nom de ma configuration.</p>
<pre class="brush: bash; title: ; notranslate">ssh -T github
Enter passphrase for key '/home/jfoucry/.ssh/github.rsa':
Enter passphrase for key '/home/jfoucry/.ssh/github.rsa':
Hi jfoucry! You've successfully authenticated, but GitHub does not provide shell access.</pre>
<h2>Finissons-en maintenant</h2>
<p>En effet, nous pouvons maintenant reprendre la documentation de GitHub (avec encore quelques modification mineures).</p>
<pre class="brush: bash; title: ; notranslate"># touch README
# git add README
# git commit -m &quot;First commit&quot;
# git remote add origin github:jfoucry/MonProjet.git
# git push -u origin master</pre>
<p>J&#8217;utilise le petit de ma configuration SSH.</p>
<p>Pour cloner, on utiliser à nouveau le petit nom :</p>
<pre class="brush: bash; title: ; notranslate"># git clone github:jfoucry/MonProjet.git
Cloning into 'MonProjet'...
Enter passphrase for key '/home/jfoucry/.ssh/github.rsa':
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Receiving objects: 100% (3/3), 211 bytes, done.</pre>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=1218</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Compiler pour iOS/OSX depuis Linux 2/x</title>
		<link>http://adminblog.foucry.net/?p=1085</link>
		<comments>http://adminblog.foucry.net/?p=1085#comments</comments>
		<pubDate>Wed, 11 Apr 2012 20:00:29 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[Apple]]></category>
		<category><![CDATA[Cocoaheads]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=1085</guid>
		<description><![CDATA[Résumé des épisodes précédents Nous avons vu comment installer Java, Jenkins et faire un premier flux Jenkins pour compiler une application OSX. Un flux pour iOS sur Simulateur Cette fois-ci, nous allons voir comment faire une application iOS destinée au simulateur. En ligne de commande depuis une machine sous Linux l&#8217;intérêt est très limité. Mais [...]]]></description>
			<content:encoded><![CDATA[<h2>Résumé des épisodes précédents</h2>
<p>Nous avons vu comment installer Java, Jenkins et faire un premier flux Jenkins pour compiler une application OSX.<br />
<span id="more-1085"></span></p>
<h2>Un flux pour iOS sur Simulateur</h2>
<p>Cette fois-ci, nous allons voir comment faire une application iOS destinée au simulateur. En ligne de commande depuis une machine sous Linux l&#8217;intérêt est très limité. Mais ce deuxième flux va nous permettre d&#8217;avoir un squelette pour notre but ultime, la compilation d&#8217;une application iOS pour un périphérique et sa diffusion &laquo;&nbsp;Over The Air&nbsp;&raquo; (OTA).</p>
<p>Nous allons donc faire un nouveau job Jenkins. Appelons le iOS_Build.</p>
<p>Comme pour le précédent nous allons désigner un dépôt Git pour un projet iOS.</p>
<p>La commande de compilation est toujours &laquo;&nbsp;Execute Shell&nbsp;&raquo; avec la commande suivante :</p>
<pre class="brush: bash; title: ; notranslate">xcodebuild -target MonProjet -configuration Debug -sdk iphonesimulator5.0</pre>
<ul>
<li>target doit être une target connue dans votre projet. Si vous n&#8217;avez pas en tête la liste des target de votre projet, vous pouvez utiliser certains paramètres de xcodebuild</li>
</ul>
<pre class="brush: bash; title: ; notranslate">xcodebuild -list -project /Users/Shared/Jenkins/Home/jobs/iOS_Build/workspace/MonProjet.xcodeproj
Information about project &quot;MonProjet&quot;:
    Targets:
        MonProjet
        MonProjetTests

    Build Configurations:
        Debug
        Release

    If no build configuration is specified and -scheme is not passed then &quot;Release&quot; is used.

    Schemes:
        MonProjet</pre>
<ul>
<li>Configuration correspond à l&#8217;une des configurations de build de votre projet. Vous avez dû noter que la ligne de commande précédente vous donnait aussi les configurations de build</li>
<li>sdk indique le SDK à utiliser ; ici il s&#8217;agit du SDK 5.0 pour le simulateur. Pour connaître la liste des SDK disponibles sur votre machine, utilisons encore une fois xcodebuild</li>
</ul>
<pre class="brush: bash; title: ; notranslate">xcodebuild -showsdks
Mac OS X SDKs:
        Mac OS X 10.6                   -sdk macosx10.6
        Mac OS X 10.7                   -sdk macosx10.7

iOS SDKs:
        iOS 5.0                         -sdk iphoneos5.0

iOS Simulator SDKs:
        Simulator - iOS 5.0             -sdk iphonesimulator5.0</pre>
<p>Si vous avez de la chance, votre build va fonctionner rapidement. Si ce n&#8217;est pas le cas regardez les traces dans Jenkins pour savoir ce qui ne fonctionne pas.</p>
<h2>Un flux pour iOS sur iDevice</h2>
<p>Et pour une compilation iOS, comment faire ? Partons du même projet, en le dupliquant (New Job et choisissez Copy existing job et donnez le nom de votre flux précédent).</p>
<p>Ce qui va changer dans ce nouveau, c&#8217;est une fois de plus la ligne de commande qui exécute xcodebuild.</p>
<p>Le premier changement est le SDK. iphonesimulator5.0 devient iphoneos5.0. Si vous décidez de tenter une compilation immédiatement, celle-ci va échouer.</p>
<pre class="brush: bash; title: ; notranslate">Code Sign error: There are no valid certificate/private key pairs in the default keychain</pre>
<p>Il faut indiquer à xcodebuild le certificat de signature de code à utiliser.</p>
<p>Note : Vous aurez besoin de la clef privée, de votre certificat de développeur et du provising profile de votre projet. Le plus simple est de passer par l&#8217;application Keychain Access (Trousseau d&#8217;Accès) sur votre machine de développement).</p>
<h3>Jouons avec la keychain et les certificats</h3>
<p>.</p>
<h4>Export avec l&#8217;interface graphique du Trousseau d&#8217;accès</h4>
<p>Dans les certificats, sélectionnez celui qui correspond à celui généré par Apple pour votre compte développeur et exportez-le. La clef privée est comprise dans cet export. Choisissez une extension .p12 (format pkcs12).</p>
<h4>Export à la ligne de commande</h4>
<p>Note à l&#8217;attention des chevelus qui nous lisent : ici, commence la vieillesse et la calvitie. Samson, si tu tiens à ta crinière fuit loin des keychain..<br />
Toutefois, il est intéressant de le faire à ligne de commande pour rester dans le cadre de ce tutoriel.</p>
<p>Donc, sur la machine de développement, nous allons extraire les informations qui nous intéressent.</p>
<pre class="brush: bash; title: ; notranslate">% security export -k login.keychain -t identities -f pkcs12 -o $HOME/identities.p12 -P &quot;passphrase&quot;</pre>
<p>La passphrase est celle de l&#8217;ouverture de votre keychain.</p>
<p>Transférez le fichier résultant sur votre machine de build. J&#8217;ai déposé le mien dans /Users/Shared.</p>
<p>Passons maintenant sur la machine de build. Pour apprendre des tas de choses (et parce que je n&#8217;ais pas d&#8217;écran branché sur cette machine) je vais manipuler la keychain à la ligne de commande.</p>
<p>Commençons pas obtenir la liste des keychain existantes :</p>
<pre class="brush: bash; title: ; notranslate">% security list-keychains
    &quot;/Users/jacques/Library/Keychains/login.keychain&quot;
    &quot;/Library/Keychains/System.keychain&quot;</pre>
<p>Nous pouvons également connaître le contenu des keychains avec <code>security dump-keychain</code> ce qui va nous permettre de savoir si nous avons les éléments nécessaires à la compilation des applications iPhone.<br />
Nous avons besoin du certificat racine d&#8217;Apple, du certificat du développeur, de sa clef privée et de sa clef publique.</p>
<p>Recherchons le certificat racine d&#8217;Apple :</p>
<pre class="brush: bash; title: ; notranslate">% security dump-keychain | grep -i &quot;Apple Code Signing Certification Authority&quot;
    &quot;alis&quot;&lt;blob&gt;=&quot;Apple Code Signing Certification Authority&quot;
    &quot;labl&quot;&lt;blob&gt;=&quot;Apple Code Signing Certification Authority&quot;
    &quot;subj&quot;&lt;blob&gt;=0x307F310B300906035504061302555331133011060355040A130A4150504C4520494E432E31263024060355040B131D4150504C452043455254494649434154494F4E20415554484F52495459313330310603550403132A4150504C4520434F4445205349474E494E472043455254494649434154494F4E20415554484F52495459  &quot;0\1771&#92;&#48;130&#92;&#48;11&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;06&#92;&#48;23&#92;&#48;02US1&#92;&#48;230&#92;&#48;21&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;12&#92;&#48;23&#92;&#48;12APPLE INC.1&amp;0$&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;13&#92;&#48;23&#92;&#48;35APPLE CERTIFICATION AUTHORITY1301&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;03&#92;&#48;23*APPLE CODE SIGNING CERTIFICATION AUTHORITY&quot;
    &quot;issu&quot;&lt;blob&gt;=0x307F310B300906035504061302555331133011060355040A130A4150504C4520494E432E31263024060355040B131D4150504C452043455254494649434154494F4E20415554484F52495459313330310603550403132A4150504C4520434F4445205349474E494E472043455254494649434154494F4E20415554484F52495459  &quot;0\1771&#92;&#48;130&#92;&#48;11&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;06&#92;&#48;23&#92;&#48;02US1&#92;&#48;230&#92;&#48;21&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;12&#92;&#48;23&#92;&#48;12APPLE INC.1&amp;0$&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;13&#92;&#48;23&#92;&#48;35APPLE CERTIFICATION AUTHORITY1301&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;03&#92;&#48;23*APPLE CODE SIGNING CERTIFICATION AUTHORITY&quot;</pre>
<p>Il est présent, tout va bien.</p>
<p>Cherchons à présent les informations sur le développeur (iPhone Developper) :</p>
<pre class="brush: bash; title: ; notranslate">%  security dump-keychain | grep -i &quot;iPhone Developer&quot;</pre>
<p>Je n&#8217;ai aucune réponse sur ma machine, preuve que je n&#8217;ai pas ces informations dans une keychain. Il va donc falloir importer les éléments dans une keychain. Et tout d&#8217;abord créer une nouvelle keychain.</p>
<p>Attention, il est nécessaire de faire cette keychain pour l&#8217;utilisateur qui fait tourner Jenkins, il faut donc devenir Jenkins</p>
<pre class="brush: bash; title: ; notranslate">% su - jenkins</pre>
<p>Nous allons commencer par nous assurer que jenkins accède aux certificats racine d&#8217;Apple. C&#8217;est normalement le cas, ces certificats étant dans la keychain System.</p>
<pre class="brush: bash; title: ; notranslate">% security dump-keychain | grep -i &quot;Apple Code Signing Certification Authority&quot;
    &quot;alis&quot;&lt;blob&gt;=&quot;Apple Code Signing Certification Authority&quot;
    &quot;labl&quot;&lt;blob&gt;=&quot;Apple Code Signing Certification Authority&quot;
    &quot;subj&quot;&lt;blob&gt;=0x307F310B300906035504061302555331133011060355040A130A4150504C4520494E432E31263024060355040B131D4150504C452043455254494649434154494F4E20415554484F52495459313330310603550403132A4150504C4520434F4445205349474E494E472043455254494649434154494F4E20415554484F52495459  &quot;0\1771&#92;&#48;130&#92;&#48;11&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;06&#92;&#48;23&#92;&#48;02US1&#92;&#48;230&#92;&#48;21&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;12&#92;&#48;23&#92;&#48;12APPLE INC.1&amp;0$&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;13&#92;&#48;23&#92;&#48;35APPLE CERTIFICATION AUTHORITY1301&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;03&#92;&#48;23*APPLE CODE SIGNING CERTIFICATION AUTHORITY&quot;
    &quot;issu&quot;&lt;blob&gt;=0x307F310B300906035504061302555331133011060355040A130A4150504C4520494E432E31263024060355040B131D4150504C452043455254494649434154494F4E20415554484F52495459313330310603550403132A4150504C4520434F4445205349474E494E472043455254494649434154494F4E20415554484F52495459  &quot;0\1771&#92;&#48;130&#92;&#48;11&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;06&#92;&#48;23&#92;&#48;02US1&#92;&#48;230&#92;&#48;21&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;12&#92;&#48;23&#92;&#48;12APPLE INC.1&amp;0$&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;13&#92;&#48;23&#92;&#48;35APPLE CERTIFICATION AUTHORITY1301&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;03&#92;&#48;23*APPLE CODE SIGNING CERTIFICATION AUTHORITY&quot;</pre>
<p>Apparemment, tout va bien. </p>
<p>Créons notre keychain, que nous appellerons Jenkins.keychain pour des raisons de commodité, mais que vous pouvez nommer comme vous le désirez.</p>
<pre class="brush: bash; title: ; notranslate">% security create-keychain jenkins.keychain
password for new keychain:
retype password for new keychain:</pre>
<pre class="brush: bash; title: ; notranslate">% security list-keychains
    &quot;/Users/Shared/Jenkins/Home/Library/Keychains/jenkins.keychain&quot;
    &quot;/Library/Keychains/System.keychain&quot;</pre>
<p>Parfait, notre keychain est créée. Il faut maintenant importer les éléments issus de l&#8217;export de la machine de developpement.</p>
<pre class="brush: bash; title: ; notranslate">% security list-keychains
    &quot;/Users/Shared/Jenkins/Home/Library/Keychains/jenkins.keychain&quot;
    &quot;/Library/Keychains/System.keychain&quot;
macmini:~ jenkins$ security import /Users/Shared/FoucryJacques.p12 -k jenkins.keychain -t cert -f pkcs12 -T /usr/bin/codesign -T /usr/bin/xcodebuild -P motDePasse
1 identity imported.</pre>
<ul>
<li> le chemin vers le fichier .p12</li>
<li>-t cert, nous importons un certificat</li>
<li>-f pkcs12, il s&#8217;agit du format dans lequel se trouvent les données</li>
<li>-T xxxx, les applications qui ont accès à l&#8217;identité, ici <code>xcodebuild</code> et <code>codesign</code></li>
<li>-P le mot de passe qui verrouille le fichier p12. Ce mot de passe a été renseigné au moment de l&#8217;export sur la machine de développement</li>
</ul>
<p>Histoire d&#8217;être certains du succès de l&#8217;opération, vérifions que la keychian est bien renseignée :</p>
<pre class="brush: bash; title: ; notranslate">% security dump-keychain | grep -i &quot;iPhone Developer&quot;
    &quot;alis&quot;&lt;blob&gt;=&quot;iPhone Developer: Jacques Foucry (K45L6X42KM)&quot;
    &quot;labl&quot;&lt;blob&gt;=&quot;iPhone Developer: Jacques Foucry (K45L6X42KM)&quot;
    &quot;subj&quot;&lt;blob&gt;=0x308193311A3018060A0992268993F22C6401010C0A3454354E4D52535036593136303406035504030C2D6950686F6E6520446576656C6F7065723A204A61637175657320466F7563727920284B34354C365834324B4D2931173015060355040B0C0E4A61637175657320466F7563727931173015060355040A0C0E4A61637175657320466F75637279310B3009060355040613024652  &quot;0\201\2231&#92;&#48;320&#92;&#48;30&#92;&#48;06&#92;&#48;12&#92;&#48;11\222&amp;\211\223\362,d&#92;&#48;01&#92;&#48;01&#92;&#48;14&#92;&#48;124T5NMRSP6Y1604&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;03&#92;&#48;14-iPhone Developer: Jacques Foucry (K45L6X42KM)1&#92;&#48;270&#92;&#48;25&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;13&#92;&#48;14&#92;&#48;16Jacques Foucry1&#92;&#48;270&#92;&#48;25&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;12&#92;&#48;14&#92;&#48;16Jacques Foucry1&#92;&#48;130&#92;&#48;11&#92;&#48;06&#92;&#48;03U&#92;&#48;04&#92;&#48;06&#92;&#48;23&#92;&#48;02FR&quot;</pre>
<p>Nous avons besoin de mettre le Provisioning Profiles dans un répertoire <code>~/Library/MobileDevice/Provisioning Profiles</code> </p>
<p>Avant de reprendre la configuration de Jenkins, nous allons tenter de faire une compilation du produit en utilisant la même ligne de commande que celle que nous allons mettre dans le flux. Il faut bien sûr d&#8217;abord récupérer les sources avec une commande git clone.</p>
<p>Une fois les sources sur un coin de votre disque, rendez-vous dans le répertoire du projet et tentez la commande suivante :</p>
<pre class="brush: bash; title: ; notranslate">% xcodebuild -sdk iphoneos5.0 -project PillStock.xcodeproj -target MonProjet -configuration &quot;Release&quot;
...
/tmp/MonProjet/build/Release-iphoneos/PillStock.app: User interaction is not allowed.
Command /usr/bin/codesign failed with exit code 1

** BUILD FAILED **</pre>
<p>Oh Oh, ça ne fonctionne pas&#8230; Le point c&#8217;est important est un peut plus haut : User interaction is not allowed.</p>
<p>En fait, notre keychain est fermé, il est nécessaire de l&#8217;ouvrir pour permettre l´accès aux données qu&#8217;elle contient.</p>
<pre class="brush: bash; title: ; notranslate">% security unlock-keychain jenkins.keychain
% xcodebuild -sdk iphoneos5.0 -project PillStock.xcodeproj -target MonProjet -configuration &quot;Release&quot;
...
** BUILD SUCCEEDED **</pre>
<p>Pas mal non ?</p>
<h2>La même dans Jenkins</h2>
<p>Sur ce coup-là, nous allons allez très vite. Un nouveau job, le dépôt git, la ligne de commande dans Execute Shell, test et PAF le chien !</p>
<p>La compilation échoue et les traces nous indiquent que xcodebuild ne peux pas signer les sources. Sans doute parce qu&#8217;il ne trouve pas le certificat dans la keychain par défaut.</p>
<p>Je vais vous épargner du temps. La keychain par défaut, c&#8217;est System.keychain. Parfais, mettons le certificat et la clef privée dans la keychain System&#8230; Ne marchera pas non plus, j&#8217;ai essayé.</p>
<p>Il semble en fait que la jvm et donc jenkins ne dialogue pas au même process pour ouvrir les keychain et les activer. Il en résulte que les processus de compilation ne peuvent pas accéder aux keychains ouverte par d&#8217;autres processus.</p>
<p>Notre problème n&#8217;est pas forcément insoluble, mais risque de nous demander de déployer une énergie assez importante. Comment passer outre ?</p>
<p>J&#8217;ai tenté pour vous l&#8217;idée du script qui déverrouille la keychain et fait la compilation. Pas de résultat positif non plus.</p>
<p>Alors, que faire ?</p>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=1085</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Le beaujolais nouveau est arrivé</title>
		<link>http://adminblog.foucry.net/?p=1174</link>
		<comments>http://adminblog.foucry.net/?p=1174#comments</comments>
		<pubDate>Sun, 01 Apr 2012 16:12:50 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[10.6]]></category>
		<category><![CDATA[Administration]]></category>
		<category><![CDATA[Livre]]></category>
		<category><![CDATA[Mac OS X Server à votre service]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=1174</guid>
		<description><![CDATA[Vous l&#8217;attendiez depuis fort longtemps, mais aujourd&#8217;hui, 1er avril 2012, &#171;&#160;Mac OS X Server à votre service, troisième édition, Snow Leopard&#160;&#187; est disponible en version PDF. Comme vous le savez sans doute si vous lisez le blog, j&#8217;écris mes livres en utilisant Docbook. C&#8217;est à partir des sources Docbook et d&#8217;une xslt adéquate que je [...]]]></description>
			<content:encoded><![CDATA[<p>Vous l&#8217;attendiez depuis fort longtemps, mais aujourd&#8217;hui, 1er avril 2012, &laquo;&nbsp;Mac OS X Server à votre service, troisième édition, Snow Leopard&nbsp;&raquo; est disponible en version PDF.</p>
<p>Comme vous le savez sans doute si vous lisez le blog, j&#8217;écris mes livres en utilisant Docbook.</p>
<p>C&#8217;est à partir des sources Docbook et d&#8217;une xslt adéquate que je génère le PDF destiné à l&#8217;impression.</p>
<p>C&#8217;est à partir des mêmes sources et d&#8217;une autre XSLT que je génère un PDF personnalisé pour chacun des acheteurs.</p>
<p>Et c&#8217;est avec les mêmes sources et une troisième XSLT (plus une CSS et des scripts python) que je peux créer une version EPUB.</p>
<p>Ainsi donc, les acheteurs de &laquo;&nbsp;Mac OS X Server à votre service, troisième édition, Snow Lepoard&nbsp;&raquo; se verront livrer une version PDF et une version EPUB.</p>
<p>Nous nous retrouvons donc avec la liste des prix suivante :</p>
<table>
<tr>
<td>Mac OS X Server à votre service, troisième édition, Snow Leopard, version électronique</td>
<td align="right">24,99&nbsp;€</td>
</tr>
<tr>
<td>Mac OS X Server à votre service, troisième édition, Snow Leopard, version électronique + Mac OS X Server à votre service, deuxième édition, Lepoard version PDF</td>
<td align="right">29,99&nbsp;€</td>
</tr>
<tr>
<td>Mac OS X Server à votre service, deuxième édition, Leopard version PDF</td>
<td align="right">19&nbsp;€</td>
</tr>
<tr>
<td>Mac OS X Server à votre service, deuxième édition, Leopard version PDF + Mac OS X Server à votre service, première édition, Tiger version PDF</td>
<td align="right"> 25&nbsp;€</td>
</tr>
<tr>
<td>Mac OS X Server à votre service, première édition, Tiger version PDF</td>
<td align="right"> 15&nbsp;€</td>
</tr>
</table>
<p>Rendez-vous sur la page <a href="http://adminblog.foucry.net/?page_id=550" title="Boutique">boutique</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=1174</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Compiler pour iOS/OSX depuis Linux 3/x</title>
		<link>http://adminblog.foucry.net/?p=1077</link>
		<comments>http://adminblog.foucry.net/?p=1077#comments</comments>
		<pubDate>Mon, 12 Mar 2012 20:00:13 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[Apple]]></category>
		<category><![CDATA[Cocoaheads]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=1077</guid>
		<description><![CDATA[Résumé des épisodes précédents Dans notre premier épisode nous avons vu comment installer Java, Jenkins, et compiler, sans trop de difficulté une application OSX. Dans le deuxième épisode, nous avons joué avec la keychain et nous nous sommes rendu compte que même avec un script nous avions des problèmes. À qui la faute ? Après [...]]]></description>
			<content:encoded><![CDATA[<h2>Résumé des épisodes précédents</h2>
<p>Dans notre premier épisode nous avons vu comment installer Java, Jenkins, et compiler, sans trop de difficulté une application OSX. Dans le deuxième épisode, nous avons joué avec la keychain et nous nous sommes rendu compte que même avec un script nous avions des problèmes.<br />
<span id="more-1077"></span></p>
<h3>À qui la faute ?</h3>
<p>Après avoir longtemps cherché, la faute en est à la keychain. Pour faire une compilation destinée à un iDevice, le code doit être signé avec le certificat développeur. Dans la keychain on trouve l&#8217;identité du développeur. Même, ne déveouillant la keychain contenant cette identité et en la rendant keychain par défaut, le flux Jenkins s&#8217;obstine à chercher les éléments dans la keychain System. La solution, bien que sale, semble de mettre les éléments dans cette Keychain. Mais non, là encore, l&#8217;identité n&#8217;est pas trouvée.</p>
<h3>Alors, qu&#8217;est-ce qu&#8217;on fait ?</h3>
<p>On pourrait laisser tomber&#8230; ou s&#8217;obstiner&#8230; ou partir sur une autre idée. J&#8217;ai choisi la troisième option et ait décidé de faire un script python qui va tout faire pour moi !</p>
<h3>Pourquoi Python ?</h3>
<p>Parce que&#8230; J&#8217;ai envie d&#8217;apprendre Python depuis longtemps et j&#8217;ai l’occasion de m&#8217;y mettre avec ce script. Python dispose d&#8217;une foultitude de modules dont certain qui peuvent nous simplifier la vie dans ce cas.</p>
<h3>Quelle version ?</h3>
<p>Bien que la version 3.x de Python soit disponible depuis un petit bout de temps maintenant, nous allons rester sur la dernière des versions 2.x, la 2.7.x. En effet, de nombreux modules n&#8217;ont pas encore été portés pour Python3.</p>
<h2>Synopsis de notre script</h2>
<p>Dans les grandes lignes notre script doit appeler xcodebuild pour compiler notre projet, appeler xcrun pour transformer le .app issue de la compilation en .ipa, créer un fichier manifest.plist utilisé pour la distribution OTA et envoyer le tout vers la machine de distribution.</p>
<p>Nous verrons bien vite qu&#8217;il va falloir ajouter des fonctionnalités pour permettre à ces quatre étapes d&#8217;opérer correctement.</p>
<h3>Découper le script en fonction</h3>
<p>Nous allons découper notre script en fonction. C&#8217;est plus facile à tester, plus facile à comprendre et moins long à écrire.</p>
<div class="su-box" style="border:1px solid #0909b0">
<div class="su-box-title" style="background-color:#0b0bdc;border-top:1px solid #9d9df1;text-shadow:1px 1px 0 #030342">Note</div>
<div class="su-box-content">Je ne suis pas un expert de Python, il est fort possible que mon script ne soit pas parfait et aurait pu être plus efficacement. Toutefois il permet d&#8217;arriver au but fixé et c&#8217;est déjà pas mal </div>
</div>
<h3>xcodebuild, la ligne de commande</h3>
<p>Commençons par la ligne de commande qui permet la compilation de notre projet.</p>
<pre class="brush: bash; title: ; notranslate">% xcodebuild -project MonPrpject.xcodeproj -target maTarget -sdk iphoneos5.0 --configuration Release</pre>
<p>Si vous renseignez correctement les variables et que votre projet compile sur votre machine de développement, il est fort probable que cette compilation fonctionne.</p>
<p>Intégrons cela dans une fonction :</p>
<pre class="brush: python; title: ; notranslate">def compileApp(SDK, project, configuration, target):
	cmd_array=([&quot;/usr/bin/xcodebuild&quot;,&quot;-sdk&quot;,SDK,&quot;-project &quot;,project,&quot;-configuration&quot;,configuration, &quot;-target&quot;, target])
	try:
		subprocess.check_call(cmd_array)
	except OSError as e:
		logger.debug(&quot;Error in %s. Exiting&quot;% &quot; &quot;.join(cmd_array))
		writeToSTDERR(&quot;Error, please see the log file&quot;)
		sys.exit(1)</pre>
<div class="su-box" style="border:1px solid #0909b0">
<div class="su-box-title" style="background-color:#0b0bdc;border-top:1px solid #9d9df1;text-shadow:1px 1px 0 #030342">Note</div>
<div class="su-box-content">La plupart des fonctions font être faites sur ce modèle. La ligne de commande est créée dans une variable, exécutée par subprocess.check_call avec une traque des exceptions</div>
</div>
<p>Cette fonction ne marche pas telle quelle. En effet, elle utilise subprocess.check_call qui fait partie du module subprocess, d&#8217;un logger pour mettre des traces dans un fichier externe. Nous allons aussi avoir besoin de récupérer, de la ligne de commande qui lance notre script, des paramètres.</p>
<h3>Écriture du script</h3>
<pre class="brush: python; title: ; notranslate">#!/usr/bin/python2.7
# -*-coding:utf-8 -*-</pre>
<p>Tout d&#8217;abord le chemin vers l&#8217;interpréteur de notre script, ici python 2.7. Ajoutons une ligne qui indique le codage utilisé pour le script.</p>
<pre class="brush: python; title: ; notranslate">import logging
import os
import sys
import subprocess
import argparse</pre>
<p>Import des modules dont nous aurons besoin. os et sys sont importants ils permettent de dialoguer avec l&#8217;OS. Les deux autres logging et subprocess sont ceux dont nous avons besoin dans nos fonctions. Enfin, argparse est celui qui va nous permettre de récupérer les arguments de la ligne de commande.</p>
<p>Au cours de notre développement, nous aurons besoin d&#8217;autres modules, il faudra ajouter ici la ligne d&#8217;importation nécessaire.</p>
<h3>Récupération des paramètres</h3>
<p>Pour récupérer les arguments passés en paramètres, nous avons besoin d&#8217;un objet de type ArgumentParser. Nous allons ensuite ajouter à cet objet les arguments que nous acceptons, avec un court message d&#8217;aide et le nom de la variable qui accueillera la valeur transmise.<br />
Enfin, nous allons copier les valeurs dans des variables plus facilement manipulables. Nous allons également ajouter un paramètre pour régler le niveau de trace que nous voulons dans notre fichier de trace.</p>
<p>Les paramètres sont stockés dans un tableau (parser.parse-args). Nous copions ce tableau et de cette copie nous extrayons les valeurs.</p>
<pre class="brush: python; title: ; notranslate"># --- Parse arguments with argparse

parser = argparse.ArgumentParser(description=xcodebuild wrapper parameters') parser.add_argument('-P', '--projectPath', action=&quot;store&quot;, required=True, dest=&quot;project&quot;, help=&quot;Path to the project file&quot;)
parser.add_argument('-s', '--sdk', action=&quot;store&quot;, required=True, dest=&quot;sdk&quot;, help=&quot;SDK&quot;)
parser.add_argument('-c', '--configuration', action=&quot;store&quot;, required=True, dest=&quot;config&quot;, help=&quot;Configuration&quot;)
parser.add_argument('-t', '--target', action=&quot;store&quot;, required=True, dest=&quot;target&quot;, help=&quot;Path to projet's file&quot;)

parser.add_argument('--log', action=&quot;store&quot;, dest=&quot;logLevel&quot;, help=&quot;LogLevel, could be DEBUG | INFO | WARNING | ERROR | CRITICAL. Default value is INFO&quot;, default=&quot;INFO&quot;)

args = parser.parse-args()

project = args.project
SDK = args.sdk configuration = args.configuration target = args.target logLevel = args.logLevel</pre>
<p>Avant de continuer, mettons en place le logger.</p>
<pre class="brush: python; title: ; notranslate"># --- Logger Initialization
 logger = logging.getLogger('xcodebuild-wrapper')
 logHandler = logging.FileHandler('/tmp/xcodebuild-wrapper.log')
 formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
 logHandler.setFormatter(formatter)
 logger.addHandler(logHandler)

# --- Define LogLevel

if logLevel==&quot;DEBUG&quot;:
  logger.setLevel(logging.DEBUG)
 elif logLevel==&quot;INFO&quot;:
  logger.setLevel(logging.INFO)
 elif logLevel==&quot;WARNING&quot;:
  logger.setLevel(logging.WARNING)
 elif logLevel==&quot;ERROR&quot;:
  logger.setLevel(logging.ERROR)
 elif logLevel==&quot;CRITICAL&quot;:
  logger.setLevel(logging.CRITICAL)
 else:  logger.info('Unkown loglevel '+ debugLevel +', using INFO')</pre>
<p>Voilà, nous avons tous les éléments nécessaires pour notre première fonction. Celle-ci doit être définie AVANT son appel.<br />
Pour l&#8217;appeler, il suffit de mettre son nom avec entre parenthèses les paramètres qu&#8217;elle attend.</p>
<pre class="brush: python; title: ; notranslate">compileApp(SDK, project, configuration,target)</pre>
<h3>Transformer l&#8217;application .app en .ipa</h3>
<p>La fonction compileAPP prend votre projet et tous les éléments dont il a besoin pour en faire un bundle .app. Or, sur un iDevice ce ne sont pas des app qui tournent, mais des .ipa (qui sont en fait des fichiers zippés). Il faut donc transformer notre .app en .ipa. cela se fait avec <code>xcrun</code>. <code>xcrun</code> ne fait rien en lui même. Il appelle un autre outil de développement <code>PackageApplication</code>.</p>
<p>Voici la fonction createIPA qui réalise cette transformation:</p>
<pre class="brush: python; title: ; notranslate">def createIPA(GSDK, workspace, configuration, target, DeveloperName, ProvisioningProfile,targetFolder,APPPath):
  cmd_array=([&quot;/usr/bin/xcrun&quot;,&quot;-sdk&quot;,GSDK, &quot;PackageApplication&quot;, &quot;-v&quot;,&quot; %s/%s.app&quot;% (APPPath,target),&quot;-o&quot;,&quot;%s/%s.ipa&quot;%(targetFolder,target),&quot;-sign&quot;,&quot;&quot;%s&quot;&quot;%(DeveloperName), &quot;-embed&quot;,&quot;&quot;%s&quot;&quot;%(ProvisioningProfile)])

  try:
   subprocess.check_call(cmd_array)
  except OSError as e:
   logger.debug(&quot;Error in %s. Exiting&quot;% &quot; &quot;.join(cmd_array))
   logger.info(&quot; Error String == %s Error Num == %d&quot;% (e.strerror, e.errno))
   writeToSTDERR(&quot;Error, please see the log file&quot;)
sys.exit(1)</pre>
<p>Le principe est le même que précédemment, la ligne de commande est décomposée et mise dans un tableau. Tout de suite nous voyons que nous avons besoin d&#8217;autres paramètres. Il va donc falloir ajouter des lignes à la réception des paramètres.</p>
<p>Les paramètres qui manquent sont :</p>
<ul>
<li>GSDK</li>
<li>workspace</li>
<li>DeveloperName</li>
<li>ProvisioningProfile</li>
<li>APPPath</li>
<li>targetFolder</li>
</ul>
<h4>Définition des paramètres</h4>
<p>Certains de ces paramètres (GDSK, APPPath et workspace) sont issus de variables déjà connues. GSDk est SDK sans le numéro de releases (iphoneos5.0 devient iphoneos), workspace est le répertoire qui contient le projet, APPPath est le répertoire dans lequel on trouve le .app résultat de la compilation. Plaçons la définition de ces variables avant l&#8217;appel au compileAPP.</p>
<pre class="brush: python; title: ; notranslate">workspace = os.path.dirname(project)
GSDK = SDK[:-3]
APPPath=&quot;%s/build/%s-%s&quot;%(workspace,configuration,GSDK)</pre>
<p>targetFolder est le répertoire dans lequel nous allons placer le fichier .ipa (et d&#8217;autres choses). Ce répertoire doit être différent pour chaque compilation. Afin qu&#8217;il soit unique nous allons le nommer du nom de la target plus le timestamp courant. Une fois son nom défini, nous allons le créer à l&#8217;aide d&#8217;une fonction. La définition est à placer juste après celles de nos autres variables, la fonction doit être avant compileAPP et l&#8217;appel doit lui aussi se trouver avant l&#8217;appel à compileAPP.</p>
<p>La définition:</p>
<pre class="brush: python; title: ; notranslate">timestamp = int(time.time())
 targetFolder=&quot;/tmp/%s-%d&quot;%(target,timestamp)
 logger.debug(&quot;targetFolder == %s&quot;%(targetFolder))</pre>
<p>La fonction :</p>
<pre class="brush: python; title: ; notranslate">def createTargetFolder(folder):
  try:
   subprocess.call([&quot;/bin/mkdir&quot;, folder])
  except OSError as e:
   if os.path.exists(folder):
    logger.info(&quot;%s folder already exist, Exiting&quot;)
    logger.debug(&quot;ErrorString == %s ErrorNum == %d&quot;% (e.strerror,e.errno))
    pass
   else:
    raise Exception(&quot;Error in creating %s folder&quot;% (folder))
    writeToSTDERR(&quot;Error, please see the log file&quot;)
  sys.exit(1)</pre>
<div class="su-box" style="border:1px solid #0909b0">
<div class="su-box-title" style="background-color:#0b0bdc;border-top:1px solid #9d9df1;text-shadow:1px 1px 0 #030342">Note</div>
<div class="su-box-content">time est déclaré dans un module, il va falloir l&#8217;ajouter dans les imports !!!</div>
</div>
<pre class="brush: python; title: ; notranslate">import time</pre>
<h4>Ajout dans la gestion des paramètres</h4>
<p>Pour ce qui est de la récupération des paramètres, il faut ajouter le ProvisioningProfile et le DeveloperName. D&#8217;abord sous forme de add_argument :</p>
<pre class="brush: python; title: ; notranslate">parser.add_argument('-n', '--developerName', action=&quot;store&quot;, required=True, dest=&quot;devname&quot;, help=&quot;Developer name. This information is in the provisionign profile&quot;)
parser.add_argument('-P', '--provisioningProfile', action=&quot;store&quot;, required=True, dest=&quot;ProvisioningProfile&quot;, help=&quot;Provisioning Profile path&quot;)</pre>
<p>Puis sous forme de récupération de valeur :</p>
<pre class="brush: python; title: ; notranslate">DeveloperName = args.devname
ProvisioningProfile = args.ProvisioningProfile</pre>
<h3>Et maintenant ?</h3>
<p>Maintenant, cela a peu de chance de fonctionner. Il manque ce pour quoi nous nous sommes lancés dans cette aventure, la gestion de la keychain.<br />
Nous avons besoin de :</p>
<ul>
<li>la keychain</li>
<li>son mot de passe</li>
</ul>
<p>Deviner quoi ? Nous allons ajouter ces deux éléments à la liste de ceux qui sont attendus dans la ligne de commande.</p>
<div class="su-box" style="border:1px solid #b00909">
<div class="su-box-title" style="background-color:#dc0b0b;border-top:1px solid #f19d9d;text-shadow:1px 1px 0 #420303">Attention</div>
<div class="su-box-content">Le mot de passe est en clair, il peut être intercepté par une commande <code>ps</code> au bon moment</div>
</div>
<h4>Gestion des paramètres</h4>
<pre class="brush: python; title: ; notranslate">parser.add_argument('-k', '--keychain', action=&quot;store&quot;, required=True, dest=&quot;keychain&quot;, help=&quot;Path to the keychain file&quot;)
parser.add_argument('-K', '--keychainPassword', action=&quot;store&quot;, required=True, dest=&quot;keychainPassword&quot;, help=&quot;keychain's password&quot;)
keychain = args.keychain password = args.keychainPassword</pre>
<p>Et la fonction qui ouvre et passe la keychain en keychain par défaut.</p>
<pre class="brush: python; title: ; notranslate"> def openKeychain(password, keychain):
  cmd_array=([&quot;/usr/bin/security&quot;,&quot;unlock-keychain&quot;, &quot;-p&quot;, password, keychain])
  try:   subprocess.check_call(cmd_array)
  except OSError as e:
   logger.debug(&quot;Error in %s, exiting&quot;% &quot; &quot;.join(cmd_string))
   writeToSTDERR(&quot;Error, please see the log file&quot;)
  sys.exit(1)

  cmd_array=([&quot;/usr/bin/security&quot;, &quot;default-keychain&quot;,&quot;-d&quot;,&quot;user&quot;, &quot;-s&quot;, keychain])
  try:   subprocess.check_call(cmd_array)
  except OSError as e:
   logger.debug(&quot;Error in %s, exiting&quot;% &quot; &quot;.join(cmd_array))</pre>
<p>Cette définition est à mettre avant les appels aux fonctions et l&#8217;appel lui-même avant l&#8217;appel à compileAPP.</p>
<h3>La suite, la suite, la suite</h3>
<p>Avant de la passer à la suite&#8230; faisons un bilan, histoire de voir les plus lents recoller au peloton.<br />
Notre script se compose ainsi :</p>
<ul>
<li>Import des modules</li>
<li>gestion des paramètres de la ligne de commande</li>
<li>définition du logger</li>
<li>fonction d&#8217;ouverture de la keychain</li>
<li>fonction de création du dossier de destination</li>
<li>fonction de compilation .app</li>
<li>fonction de transformation .app en .ipa</li>
<li>définition de certaines variables</li>
<li>appel de la fonction de création du dossier de destination</li>
<li>appel de la fonction d&#8217;ouverture de la keycahin</li>
<li>appel de la fonction de compilation .app</li>
<li>appel de la fonction de transformation .app en .ipa</li>
</ul>
<h3>Que manque t&#8217;il ?</h3>
<p>Pour être distribué notre .ipa a besoin de :</p>
<ul>
<li>un fichier manifest.plist qui décrit la méthode de distribution et ses paramètres</li>
<li>un fichier html qui contient le lien de téléchargement</li>
</ul>
<h4>Commençons par le fichier manifest.plist</h4>
<p>Celui-ci peut être généré depuis notre script python en prenant les paramètres dans notre .ipa grâce au module plistlib. Le fichier .ipa étant un fichier zip, il est nécessaire d&#8217;avoir aussi le module zipfile. Enfin, pour réaliser l&#8217;extraction nous utilisons le module shtuil. Trois lignes de plus dans notre section import.</p>
<pre class="brush: python; title: ; notranslate">import zipfile
import plistlib
import shutil</pre>
<p>Pour créer ce fichier, deux fonctions sont nécessaires. La première va extraire du fichier .ipa le fichier Info.plist (une plist binaire). La deuxième va transformer cette plist binaire en un plist texte et en extraire les informations dont nous avons besoin.</p>
<p>Extraction du fichier Info.plist</p>
<pre class="brush: python; title: ; notranslate">def retrieveInfo(targetFolder,target):
  zin=zipfile.ZipFile(&quot;/%s/%s.ipa&quot;%(targetFolder,target)) # definiton du fichier zip
  logger.debug(zin)

  for item in zin.namelist(): # parcours des fichiers contenus dans le zip
    if fnmatch.fnmatch(item, '*/Info.plist'): # recherche du fichier Info.plist
    filename = os.path.basename(item)
    filename = &quot;/%s/%s&quot;%(targetFolder,filename)
    inFile = zin.open(item)
    outFile = file(filename, &quot;wb&quot;)

    shutil.copyfileobj(inFile,outFile) # extraction du fichier
    inFile.close()
    outFile.close()
  return filename</pre>
<p>Création du fichier manifest.plist avec les éléments extraits d&#8217;Info.plist</p>
<pre class="brush: python; title: ; notranslate">def createManifest(info,target,targetFolder,deployment_address):
  xmlfile=&quot;/%s/%s.xml&quot;%(targetFolder,target)

# we need to convert Info.plist into a xml file (by default it's a binary plist)
  cmd_array=([&quot;/usr/bin/plutil&quot;, &quot;-convert&quot;, &quot;xml1&quot;, &quot;-o&quot;, xmlfile, info])
  subprocess.Popen(cmd_array).wait()
  infoPlistFile = open(xmlfile, 'r')
  app_plist = plistlib.readPlist(infoPlistFile)
  os.remove(xmlfile)
  manifestFilename='/%s/manifest.plist'%(targetFolder)
  manifest_plist= {
   'items' : [    {
           'assets' : [
               {
                   'kind' : 'software-package',
                   'url' : urlparse.urljoin(deployment_address, target + '.ipa'),
               }
            ],
            'metadata' : {
                'bundle-identifier' : app_plist['CFBundleIdentifier'],
                'bundle-version' : app_plist['CFBundleVersion'],
                'kind' : 'software',
                'title' : app_plist['CFBundleName'],
            }
      }
   ]
}
plistlib.writePlist(manifest_plist, manifestFilename)
return manifestFilename</pre>
<h4>Le fichier html</h4>
<p>Dans le fichier html nous devons indiquer l&#8217;url de distribution de notre application. Il va falloir ajouter ce paramètre à la liste de ceux que nous lisons déjà de la ligne de commande.</p>
<pre class="brush: python; title: ; notranslate"> parser.add_argument('-d', '--deploymentAddress', action=&quot;store&quot;, required=True, dest=&quot;deploy&quot;, help=&quot;Deployment Address, used in manifest.plist file&quot;)
...
deployment_address = args.deploy</pre>
<p>Et créer une fonction pour générer le fichier html (index.html sera sont nom). Nous allons faire un modèle et remplir certains éléments avec le contenu de variables. Une autre fonction appelle celle de création du fichier html avec les bons paramètres.</p>
<pre class="brush: python; title: ; notranslate">def fillHTML(app_name,manifest):  manifestPath=&quot;%s%s&quot;%(deployment_address,manifest) # définition du chemin vers le fichier manifest SUR LA MACHINE DE DÉPLOYEMENT
template_html=&quot;&quot;&quot;
&lt;!DOCTYPE html PUBLIC &quot;-//WC3/DTD HTML 1.0 Transitional/EN&quot; &quot;http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;

&lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&gt;

&lt;head&gt;

&lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot; /&gt;

&lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0&amp;quot;&gt;

&lt;title&gt;[BETA_NAME] - Beta Release&lt;/title&gt;

&lt;style type=&amp;quot;text/css&amp;quot;&gt;

body {background:#fff;margin:0;padding:0;font-family:arial,helvetica,sans-serif;text-align:center;padding:10px;color:#333;font-size:16px;}

#container {width:300px;margin:0 auto;border:2px solid #000;border-radius:15px; box-shadow:4px 4px 4px gray;}

h1 {margin:0;padding:0;font-size:14px;}

p {font-size:13px;}

.link {background:#ecf5ff;border-top:1px solid #fff;border:1px solid #dfebf8;margin-top:.5em;padding:.3em;}

.link a {text-decoration:none;font-size:15px;display:block;color:#069;}

&lt;/style&gt;

&lt;/head&gt;

&lt;body&gt;

&lt;div id=&amp;quot;container&amp;quot;&gt;

&lt;h1&gt;Dear testers&lt;/h1&gt;

&lt;div class=&amp;quot;link&amp;quot;&gt;&lt;a href=&amp;quot;itms-services://?action=download-manifest&amp;amp;url=[DEPLOYMENT_PATH]&amp;quot;&gt;Tap here to install&lt;br /&gt;[BETA_NAME]&lt;br /&gt;On Your Device&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Link didn't work?&lt;/strong&gt;&lt;br /&gt;

Make sure you're visiting this page on your device, not your computer.&lt;/p&gt;

&lt;/body&gt;

&lt;/html&gt;
&quot;&quot;&quot;
  TEMPLATE_PLACEHOLDER_NAME = '[BETA_NAME]'
  TEMPLATE_PLACEHOLDER_DEPLOYMENT_PATH = '[DEPLOYMENT_PATH]'
  template_html = string.replace(template_html, TEMPLATE_PLACEHOLDER_NAME, app_name)
  template_html = string.replace(template_html, TEMPLATE_PLACEHOLDER_DEPLOYMENT_PATH, manifestPath)
  return template_html

def createIndexHTML(targetFolder,target,manifest):
  indexFilename = '/%s/index.html'%(targetFolder)
  manifest=os.path.basename(manifest)
  indexFile = open(indexFilename, 'w')
  indexFile.write(fillHTML(target,manifest))
  return indexFilename</pre>
<p>Les fonctions createManifest et createIndexHTML sont appelées après la transformation .ipa.</p>
<h3>Quoi d&#8217;autre</h3>
<p>Il nous manque une chose importante pour que notre script soit complet, la copie des fichiers .ipa, index.html et manifest.plist sur la machine de distribution.</p>
<p>Nous allons faire cela par SSH en supposant que la connexion sur la machine de distribution est possible pour l&#8217;utilisateur jenkins (souvenez-vous, c&#8217;est lui qui va faire tourner le script de build).</p>
<div class="su-box" style="border:1px solid #b00909">
<div class="su-box-title" style="background-color:#dc0b0b;border-top:1px solid #f19d9d;text-shadow:1px 1px 0 #420303">Attention</div>
<div class="su-box-content"> Pour faciliter la copie, l&#8217;utilisateur jenkins peut se connecter sur la machine de distribution sans mot de passe. Assurez-vous toutefois qu&#8217;il n&#8217;ait AUCUN droit d&#8217;administration sur la machine cible (il n&#8217;est pas sudoers) et que cette machine n&#8217;accepte que les connexions SSH par échange de clef EXCLUSIVEMENT</div>
</div>
<p>Il existe plusieurs modules qui permettent de faire du SSH avec Python. Je n&#8217;ai toutefois pas réussi a les faires fonctionner. C&#8217;est pourquoi je me suis rabattu sur cmd_array et subprocess.check_call.</p>
<pre class="brush: python; title: ; notranslate">def distribution(server, user, password,distantFolder,sourceFolder):
  cmd_array=([&quot;/usr/bin/scp&quot;, &quot;-r&quot;, &quot;%s/*&quot;%(targetFolder),  &quot;%s@%s:%s&quot;%(user,server,distantFolder)])
  try:
   subprocess.check_call(cmd_array)
  except OSError as e:
   logger.debug(&quot;Error in %s. Exiting&quot;% &quot; &quot;.join(cmd_array))
    logger.debug(&quot;ErrorString == %s ErrorNum == %d&quot;% (e.strerror,e.errno))</pre>
<h3>Pour finir ce long billet</h3>
<p>Reprenons l&#8217;architecture de notre script et voyons ce que nous y avons ajouté :</p>
<ul>
<li>Import des modules</li>
<li>gestion des paramètres de la ligne de commande</li>
<li>définition du logger</li>
<li>fonction d&#8217;ouverture de la keychain</li>
<li>fonction de création du dossier de destination</li>
<li>fonction de compilation .app</li>
<li>fonction de transformation .app en .ipa</li>
<li>définition de certaines variables</li>
<li>appel de la fonction de création du dossier de destination</li>
<li>appel de la fonction d&#8217;ouverture de la keychain</li>
<li>appel de la fonction de compilation .app</li>
<li>appel de la fonction de transformation .app en .ipa</li>
<li>création du manifest.plist
<ul>
<li>Extraction du fichier Info.plist du .ipa</li>
</ul>
</li>
<li>création du fichier index.html</li>
<li>copie des fichiers sur la machine cible</li>
</ul>
<p>Notre script est désormais terminé. Dans un prochain billet, nous verrons comment y introduire deux ou trois fonctionnalités supplémentaires.</p>
<p>Vous pouvez <a href="http://adminblog.foucry.net/wp-content/uploads/2012/03/xcodebuild-wrapper.zip">télécharger</a> ce script.</p>
<p><a href="http://creativecommons.org/licenses/by-sa/3.0/" rel="license"><img style="border-width: 0;" src="http://i.creativecommons.org/l/by-sa/3.0/88x31.png" alt="Licence Creative Commons" /></a><br />
<span>xcodebuild-wrapper.py</span> de <a href="http://adminblog.foucry.net/?p=1077" rel="cc:attributionURL">Jacques Foucry</a> est mis à disposition selon les termes de la <a href="http://creativecommons.org/licenses/by-sa/3.0/" rel="license">licence Creative Commons Paternité &#8211; Partage à l&#8217;Identique 3.0 non transposé</a>.</p>
<p>Vous pouvez également retrouver ce script sur <a href="https://github.com/jfoucry/xcodebuild-wrapper">GitHub</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=1077</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Compiler pour iOS/OSX depuis Linux 1/x</title>
		<link>http://adminblog.foucry.net/?p=1034</link>
		<comments>http://adminblog.foucry.net/?p=1034#comments</comments>
		<pubDate>Mon, 27 Feb 2012 16:33:12 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[Développement]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=1034</guid>
		<description><![CDATA[Bon d&#8217;accord le titre est super accrocheur, mais c&#8217;est de la triche. Je me sers de mon Linux pour écrire du code, mais j&#8217;ai besoin d&#8217;un Mac pour faire la compilation. Toutefois nous allons utiliser la ligne de commande au maximum. Nous allons aussi faire de l&#8217;intégration continue. À chaque commit notre Mac OS X [...]]]></description>
			<content:encoded><![CDATA[<p>Bon d&#8217;accord le titre est super accrocheur, mais c&#8217;est de la triche. Je me sers de mon Linux pour écrire du code, mais j&#8217;ai besoin d&#8217;un Mac pour faire la compilation. Toutefois nous allons utiliser la ligne de commande au maximum.</p>
<p>Nous allons aussi faire de l&#8217;intégration continue. À chaque commit notre Mac OS X va récupérer les sources, les compiler et rendre le résultat disponible pour une installation sur un iDevice.<br />
<span id="more-1034"></span><br />
Maintes commandes de cette installation réclament des droits d&#8217;administration. Il est donc nécessaire de faire ces manipulations avec un utilisateur ayant les droits d&#8217;administration sur le Mac cible.</p>
<h2>Java</h2>
<p>Jenkins utilise Java. Par défaut Java n&#8217;est pas installé sur Lion. Nous devons donc l&#8217;installer par nous même. Heureusement Apple nous propose une image.</p>
<pre class="brush: bash; title: ; notranslate"># curl -O http://supportdownload.apple.com/download.info.apple.com/Apple_Support_Area/Apple_Software_Updates/Mac_OS_X/downloads/041-1941.20111108.6wtg7/JavaForMacOSX10.7.dmg</pre>
<p>Une fois le téléchargement terminé nous allons monter l&#8217;image disque téléchargée</p>
<pre class="brush: bash; title: ; notranslate"># hdiutil attach JavaForMacOSX10.7.dmg
 Checksumming Driver Descriptor Map (DDM : 0)…
 Driver Descriptor Map (DDM : 0): verified CRC32 $148850C9
 Checksumming (Apple_Free : 1)…
 (Apple_Free : 1): verified CRC32 $00000000
 Checksumming Apple (Apple_partition_map : 2)…
 Apple (Apple_partition_map : 2): verified CRC32 $4460C6D3
 Checksumming Macintosh (Apple_Driver_ATAPI : 3)…
 Macintosh (Apple_Driver_ATAPI : 3): verified CRC32 $F1E8BA9E
 Checksumming (Apple_Free : 4)…
 (Apple_Free : 4): verified CRC32 $00000000
 Checksumming disk image (Apple_HFS : 5)…
 ...................................................................................................................
 disk image (Apple_HFS : 5): verified CRC32 $2796E2B9
 Checksumming (Apple_Free : 6)…
 (Apple_Free : 6): verified CRC32 $00000000
 verified CRC32 $5A6B6640
 /dev/disk2 Apple_partition_scheme
 /dev/disk2s1 Apple_partition_map
 /dev/disk2s2 Apple_Driver_ATAPI
 /dev/disk2s3 Apple_HFS /Volumes/Java for Mac OS X 10.7</pre>
<p>C&#8217;est dans <code>/Volumes/Java for Mac OS X 10.7</code> que nous trouverons le paquet à installer.</p>
<pre class="brush: bash; title: ; notranslate"># cd /Volumes/Java For Mac OS X 10.7
# ls
JavaForMacOSX10.7.pkg</pre>
<p>Il faut utiliser la commande installer pour réaliser l&#8217;installation</p>
<pre class="brush: bash; title: ; notranslate"># sudo installer -pkg JavaForMacOSX10.7.pkg -target /</pre>
<h2>L&#8217;intégration continue</h2>
<p>Au même titre que le développement Agile, l&#8217;intégration continue est achement à la mode. Le principe est simple. Un robot (en l&#8217;occurrence jenkins) tourne sur une machine d&#8217;intégration. Il récupère les sources depuis un gestionnaire de source, réalise les compilations du produit. Le but est de vérifier que le dernier commit n&#8217;a pas cassé la compilation. En cas de problème le développeur responsable de l&#8217;erreur est alerté du problème.</p>
<p>Il est donc nécessaire d&#8217;avoir un système de gestion de source, un mac, un accès SSH sur ce mac et les outils développeurs installés sur ce mac. Nous auront aussi besoin d&#8217;Apache, le serveur Web. Ce dernier est installé par défaut sur Mac OS X, nous n&#8217;aurons que la configuration du produit à faire.</p>
<h3>Installation de Jenkins</h3>
<p>L&#8217;installation de Jenkins est plus que simple. Il suffit de récupérer sur le site le paquet conçu pour Mac OS X&#8230;</p>
<pre class="brush: bash; title: ; notranslate">$ curl -O http://jenkins.mirror.isppower.de/osx/jenkins-1.451.pkg</pre>
<p>et de l&#8217;installer avec la commande éponyme :</p>
<pre class="brush: bash; title: ; notranslate">$ sudo installer -pkg jenkins-1.451.pkg -target /</pre>
<h3>Création de l&#8217;utilisateur Jenkins</h3>
<p>Pour amoindrir le risque de trou de sécurité, Jenkins ne doit pas tourner avec un utilisateur ayant de trop grands privilèges. C&#8217;est pourquoi nous devons créer un utilisateur spécifique pour cette tâche.</p>
<pre class="brush: bash; title: ; notranslate">sudo dscl . create /Users/jenkins
sudo dscl . create /Users/jenkins PrimaryGroupID 1
sudo dscl . create /Users/jenkins UniqueID 300
sudo dscl . create /Users/jenkins UserShell /bin/bash
sudo dscl . create /Users/jenkins home /Users/Shared/Jenkins/Home/
sudo dscl . create /Users/jenkins NFSHomeDirectory /Users/Shared/Jenkins/Home/
sudo dscl . passwd /Users/jenkins</pre>
<p>Il faut prévenir jenkins qu&#8217;il possède maintenant un utilisateur pour fonctionner. Cela se fait dans le fichier <code>/Library/LauchDaemons/org/jenkins-ci.plist</code>. Ce fichier provient du paquet que nous avons installé.<br />
À la fin du fichier se trouve la définition de l&#8217;utilisateur responsable de jenkins :</p>
<pre class="brush: bash; title: ; notranslate">&lt;UserName&gt;
&lt;daemon&gt;</pre>
<p>Changez simplement daemon par jenkins avec votre éditeur de texte préféré.</p>
<p>Changeons également le propriétaire du répertoire Jenkins :</p>
<pre class="brush: bash; title: ; notranslate"># sudo chown -R jenkins /Users/Shared/Jenkins</pre>
<p>Il est temps de relancer le service avec les nouveaux paramètres.</p>
<p>Nous allons utiliser les mécanismes prévus par Apple et configurés par Jenkins (et nous), launchd</p>
<pre class="brush: bash; title: ; notranslate">sudo launchctl unload -w /Library/LaunchDaemons/org.jenkins-ci.plist
sudo launchctl load -w /Library/LaunchDaemons/org.jenkins-ci.plist</pre>
<p>Si tout va bien, vous devriez trouver dans <code>/var/log/system.log</code> la ligne suivante :</p>
<pre class="brush: bash; title: ; notranslate">Feb 27 11:53:32 macmini org.jenkins-ci[37647]: INFO: Jenkins is fully up and running</pre>
<p>Signifiant ainsi que votre jenkins est opérationnel.</p>
<p>Reste à tester la connexion vers la console Jenkins. Pointez votre navigateur vers l&#8217;adresse IP de votre serveur, sur le port 8080. Vous devriez obtenir quelque chose qui ressemble à cela :</p>
<p><a href="http://adminblog.foucry.net/wp-content/uploads/2012/02/Jenkins1.png"><img class="aligncenter size-medium wp-image-1037" title="Jenkins, la page d'accueil" src="http://adminblog.foucry.net/wp-content/uploads/2012/02/Jenkins1-300x166.png" alt="Jenkins, la page d'accueil" width="600" /></a></p>
<h3>Jenkins, SSH et Git</h3>
<p>Pour utiliser git avec jenkins, il est nécessaire d&#8217;avoir une clef SSH. Il faut donc générer une clef SSH (en fait une paire de clefs, un privée que vous devez garder secrète et une publique qui sera envoyée sur le serveur git).</p>
<h4>SSH</h4>
<p>Cette clef doit appartenir à jenkins, il faut donc être jenkins pour générer cette paire de clefs :</p>
<pre class="brush: bash; title: ; notranslate"># su - jenkins
 # ssh-keygen -t dsa
 Generating public/private dsa key pair.
 Enter file in which to save the key (/Users/Shared/Jenkins/Home//.ssh/id_dsa):
 Created directory '/Users/Shared/Jenkins/Home//.ssh'.
 Enter passphrase (empty for no passphrase):
 Enter same passphrase again:
 Your identification has been saved in /Users/Shared/Jenkins/Home//.ssh/id_dsa.
 Your public key has been saved in /Users/Shared/Jenkins/Home//.ssh/id_dsa.pub.
 The key fingerprint is:
 48:e0:09:7a:da:9e:67:e0:8c:25:fe:bd:9c:de:2f:e4 jenkins@macmini.local
 The key's randomart image is:
 +--[ DSA 1024]----+
 | . . |
 | . o o |
 |. . o . |
 | + . . |
 |o + . S |
 |.B o . |
 |..= o o |
 | .oo oE |
 | ..*..o. |
 +-----------------+</pre>
<p>Il n&#8217;est pas prudent du tout d&#8217;avoir une clef sans passphrase. Toutefois, dans le cas d&#8217;un service qui doit tourner indépendamment, c&#8217;est le seul moyen de faire.</p>
<p>Pour que plus tard nous puissions récupérer les sources via ssh et git il est nécessaire de préparer le terrain.</p>
<p>Toujours en étant jenkins, il faut que nous tentions de nous connecter à la machine qui contient nos sources.</p>
<p>Pour des raisons de sécurité, il est préférable d&#8217;avoir un utilisateur dédié sur la machine qui contient les sources. En fonction de sa nature la commande pour créer cet utilisateur peut être différente. Sur un linux il y a de grande chose que ce soit <code>useradd</code>.</p>
<pre class="brush: bash; title: ; notranslate">% sudo useradd -m jenkins</pre>
<p>Donnez un mot de passe à cet utilisateur :</p>
<pre class="brush: bash; title: ; notranslate">% sudo passwd jenkins
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully</pre>
<p>Tant que vous êtes connectés sur la machine Linux, profitez-en pour devenir <strong>Jenkins</strong> afin de créer dans le répertoire de cet utilisateur le dossier
<pre class="brush: bash; title: ; notranslate">.ssh</pre>
<p>.</p>
<pre class="brush: bash; title: ; notranslate">% su - jenkins
Password:
$ pwd
/home/jenkins
$ mkdir .ssh
$ exit
logout</pre>
<p>Il faut maintenant mettre la clef publique de jenkins dans les fichiers de configuration SSH de l&#8217;utilisateur jenkins SUR LA MACHINE cible :</p>
<pre class="brush: bash; title: ; notranslate"># cat .ssh/id_dsa.pub | ssh jenkins@source.example.com &quot;cat - &amp;gt;&amp;gt; /home/jenkins/.ssh/authorized_keys2&quot;</pre>
<p>Vous devez connaitre le mot de passe de l&#8217;utilisateur sur cette machine.</p>
<p>Ensuite vous devriez pouvoir vous connecter directement sur cette machine</p>
<pre class="brush: bash; title: ; notranslate"># ssh jenkins@source.example.com</pre>
<h4>Git</h4>
<p>Nous devons installer un greffon pour utiliser git avec jenkins.<br />
Depuis l&#8217;écran d&#8217;accueil, sélectionnez<strong> Manage Jenkins &gt; Manage Plugins</strong> et choisissez l&#8217;onglet <strong>Available</strong>. La liste est très longue (vous pourrez explorer la liste des greffons une fois que votre installation de départ fonctionnera). Dans rubrique &laquo;&nbsp;Source Code Management&nbsp;&raquo; vous devriez trouver &laquo;&nbsp;Git Plugin&nbsp;&raquo;. Cochez la case et cliquez sur &laquo;&nbsp;Downlaod now and install after restart&nbsp;&raquo;.</p>
<p>Fastoche non ?</p>
<h3>Créer un flux pour XCode</h3>
<p>Notre but est de créer un flux de compilation pour XCode. C&#8217;est donc ce que nous allons faire maintenant en commençant par préciser d&#8217;où viennnent les sources.</p>
<p>Depuis l&#8217;écran d&#8217;accueil, sélectionnez <strong>New Job</strong>. Nous allons donner un nom à notre flux et choisir de faire un <strong>free-style software project</strong></p>
<p><a href="http://adminblog.foucry.net/wp-content/uploads/2012/02/jenkins2.png"><img class="aligncenter size-medium wp-image-1038" title="Jenkins, création d'un flux" src="http://adminblog.foucry.net/wp-content/uploads/2012/02/jenkins2-300x158.png" alt="Jenkins, création d'un flux" width="600" /></a></p>
<p>Mettez une description si vous le désirez (cela peut être utile quand vous avez beaucoup de flux de compilation).</p>
<p>Commençons par indiquer à Jenkins où trouver les sources nécessaire à la compilation du projet. Dans la rubrique SCM, choisissez Git et donnez l&#8217;url de votre dépôt. Pensez à mettre le nom de l&#8217;utilisateur sur la machine cible, vers lequel vous avez installé la clef ssh de jenkins.</p>
<p>Passons maintenant à la rubrique <strong>Build</strong> qui, comme son nom l&#8217;Indique, va nous permettre de décrire comment doit être construit notre projet.</p>
<p>Dans le menu déroulant sélectionnez <strong>Execute Shell</strong>. Cela fait apparaitre un champ de saisie. Nous allons y mettre les lignes de commande a exécuter pour contruire notre projet xcode. Il s&#8217;agit de la commande xcodebuild.</p>
<p>Sauvez votre flux. Cela vous fait retourner à la page d&#8217;accueil.</p>
<p>Celle-ci vous indique que n&#8217;avez pas de workspace (espace de travail). Celui-ci sera crée avec le premier lancement d&#8217;un flux.</p>
<p>Il est donc temps de lancer la première compilation automatique en cliquant sur <strong>Run a build</strong>.</p>
<h2>Mise au point</h2>
<p>Forcément votre premier lancement devrait échouer. Il y a des nombreuses explications possibles. Voyons ce qu&#8217;il m&#8217;est arrivé pour que vous puissiez avoir quelques pistes.</p>
<p>En cliquant sur le numéro de build dans le cadre build history, vous aurez accès au fichier de trace.</p>
<ul>
<li>Impossible de cloner le projet.</li>
<ul>
<li>En étant jenkins (su &#8211; jenkins) essayer de faire ce clone à la main :</li>
</ul>
</ul>
<p><span class="Apple-style-span" style="font-family: Consolas, Monaco, monospace; font-size: 12px; line-height: 18px; white-space: pre;">git clone ssh://jacques@source.example.com/data/repositories/monprojet</span></p>
<ul>
<li>Error performing command: git tag -a -f -m Jenkins Build #6 jenkins-Test_XCode-6</li>
<ul>
<li>Votre git n&#8217;est pas complèment configuré sur la machine qui fait tourner jenkins, il faut au moins donner un nom et une adresse email</li>
</ul>
</ul>
<pre>git config --global user.email "you@example.com"
git config --global user.name "Your Name"</pre>
<ul>
<li>You have not agreed to the Xcode license agreements, please run xcodebuild standalone from within a Terminal window to review and agree to the Xcode license agreements.</li>
<ul>
<li>Toujours en étant jenkins, lancez <code>xcodebuild </code>sans argument et lisez le contrat xcode. Suivez les instructions pour accepter la licence.</li>
</ul>
</ul>
<p>Si tout se passe bien, vous devriez voir une pastille bleue dans le cadre build history</p>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=1034</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>À propos du nuage</title>
		<link>http://adminblog.foucry.net/?p=1025</link>
		<comments>http://adminblog.foucry.net/?p=1025#comments</comments>
		<pubDate>Fri, 20 Jan 2012 23:02:45 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[Non classé]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=1025</guid>
		<description><![CDATA[Depuis deux ans environ le nuage est à la mode. Apple propose son iCloud, Amazon sa solution, OVH vient se s&#8217;y mettre. Sur le papier, ça semble terrible&#8230; Pas besoin d&#8217;admin pour gérer des machines, simple à mettre en œuvre, extensible, peu cher (faut voir)&#8230; Seulement, voilà, la récente affaire de MegaUpload nous apprend qu&#8217;il [...]]]></description>
			<content:encoded><![CDATA[<p>Depuis deux ans environ le nuage est à la mode. Apple propose son iCloud, Amazon sa solution, OVH vient se s&#8217;y mettre.</p>
<p>Sur le papier, ça semble terrible&#8230; Pas besoin d&#8217;admin pour gérer des machines, simple à mettre en œuvre, extensible, peu cher (faut voir)&#8230;</p>
<p>Seulement, voilà, la récente affaire de MegaUpload nous apprend qu&#8217;il faut faire des sauvegardes, même de ce que l&#8217;on met dans le cloud. De nombreux utilisateurs honnêtes du site déjà cité se retrouvent le bec dans l&#8217;eau parce qu&#8217;ils ont trop fait confiance aux fournisseurs.</p>
<p>Même si OVH ou Amazon sont très loin de la réputation sulfureuse de MegaUpload, un malheur est vite arrivé (faillite, décision de justice&#8230;)</p>
<p>Pensez-y avant de sauter le pas. </p>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=1025</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lion Server et VPN</title>
		<link>http://adminblog.foucry.net/?p=1022</link>
		<comments>http://adminblog.foucry.net/?p=1022#comments</comments>
		<pubDate>Fri, 06 Jan 2012 09:33:36 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[OS]]></category>
		<category><![CDATA[Trucs à deux balles]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=1022</guid>
		<description><![CDATA[Je ne me suis pas penché sur le problème, mais je vais entièrement confiance à Yoann. Lion Server et le VPN PPTP ne font pas bon ménage, du moins dans l&#8217;interface de Server Admin. Yoann a creuser la documentation et crée un outil pour configurer le VPN sous Lion Server. Vous trouverez tous les détails [...]]]></description>
			<content:encoded><![CDATA[<p>Je ne me suis pas penché sur le problème, mais je vais entièrement confiance à Yoann. </p>
<p>Lion Server et le VPN PPTP ne font pas bon ménage, du moins dans l&#8217;interface de Server Admin. Yoann a creuser la documentation et crée un outil pour configurer le VPN sous Lion Server.</p>
<p>Vous trouverez tous les détails sur son <a href="http://blog.inig-services.com/archives/936" target="_blank">blog</a> </p>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=1022</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CocoaHeads Paris, c&#8217;est la rentrée</title>
		<link>http://adminblog.foucry.net/?p=1010</link>
		<comments>http://adminblog.foucry.net/?p=1010#comments</comments>
		<pubDate>Mon, 05 Sep 2011 12:46:41 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[Cocoaheads]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=1010</guid>
		<description><![CDATA[Les cartables sont prêts, les crayons de bois (J&#8217;aime beaucoup cette expression) sont taillés, les batteries des MacBook, MacBookAir et MacBook Pro sont chargées à bloc&#8230; C&#8217;est le moment de reprendre les bonnes habitudes du deuxième jeudi de chaque mois&#160;: CocoaHeads. Tous les détails, comme à l&#8217;accoutumé sont sur le site cocoaheads.fr. Et les retardataires [...]]]></description>
			<content:encoded><![CDATA[<p>Les cartables sont prêts, les crayons de bois (J&#8217;aime beaucoup cette expression) sont taillés, les batteries des MacBook, MacBookAir et MacBook Pro sont chargées à bloc&#8230;</p>
<p>C&#8217;est le moment de reprendre les bonnes habitudes du deuxième jeudi de chaque mois&nbsp;: <a href="http://cocoaheads.fr/2011/09/cocoaheads-paris-cest-reparti/">CocoaHeads</a>.</p>
<p>Tous les détails, comme à l&#8217;accoutumé sont sur le site <a href="http://cocoaheads.fr/">cocoaheads.fr</a>.</p>
<p>Et les retardataires iront directement dans le bureau du proviseur !</p>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=1010</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vim et conflit de raccourcis</title>
		<link>http://adminblog.foucry.net/?p=1005</link>
		<comments>http://adminblog.foucry.net/?p=1005#comments</comments>
		<pubDate>Fri, 19 Aug 2011 08:01:10 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[VI]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=1005</guid>
		<description><![CDATA[L&#8217;avez-vous noté, si vous avez installé comments.vim nous avons un conflit de raccourci clavier. Mais si, souvenez-vous. Je vous avez donné deux astuces pour incrémenter et décrémenter des chiffres dans un texte avec Vim et les raccourcis CTRL+x (pour incrémenter) et CTRL+a (pour décrémenter). comments.vim utilise aussi CTRL+x pour dé-commenter. Du coup on perd la [...]]]></description>
			<content:encoded><![CDATA[<p>L&#8217;avez-vous noté, si vous avez installé comments.vim nous avons un conflit de raccourci clavier.</p>
<p>Mais si, souvenez-vous. Je vous avez donné deux <a href="http://adminblog.foucry.net/?p=909">astuces</a> pour incrémenter et décrémenter des chiffres dans un texte avec Vim et les raccourcis CTRL+x (pour incrémenter) et CTRL+a (pour décrémenter).</p>
<p>comments.vim utilise aussi CTRL+x pour dé-commenter. Du coup on perd la fonctionnalité de base VIm. Il va falloir éditer le fichier de définition des raccourcis de comments.vim changer la combinaison de touche.</p>
<p><span id="more-1005"></span></p>
<p>Il faut éditer le fichier <code>~/.vim/plugins/vim.comments</code>. Pensez à en faire une sauvegarde avant&nbsp;!</p>
<p>Dans mon fichier, aux lignes 101 et 103 on trouve la définition de la combinaison de touches pour dé-commenter&nbsp;:</p>
<pre>" key-mappings for un-comment line in normal mode
noremap  &lt;silent&gt; &lt;C-X&gt; :call UnCommentLine()&lt;CR&gt;
" key-mappings for range un-comment lines in visual &lt;Shift-V&gt; mode
vnoremap &lt;silent&gt; &lt;C-X&gt; :call RangeUnCommentLine()&lt;CR&gt;</pre>
<p>Il suffit de changer le <code>X</code> par la lettre que vous désirer utiliser pour ce raccourci. J&#8217;ai choisi le <code>M</code>.</p>
<p>On sauvegarde, on relance Vim et hop, ça marche&nbsp;!</p>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=1005</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Plus loin avec Cocoa.vim</title>
		<link>http://adminblog.foucry.net/?p=987</link>
		<comments>http://adminblog.foucry.net/?p=987#comments</comments>
		<pubDate>Mon, 08 Aug 2011 09:30:29 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[VI]]></category>

		<guid isPermaLink="false">http://adminblog.foucry.net/?p=987</guid>
		<description><![CDATA[Je vous ai récemment parlé d&#8217;un greffon pour vim qui permet de gagner en confort pour écrire de l&#8217;Objective-C. En plus de la coloration syntaxique, ce greffon apporte d&#8217;autres fonctionnalités intéressantes. Création du squelette de l&#8217;implémentation À partir d&#8217;un fichier header .h cocoa.vim est capable de générer le squelette de l’implémentation dans le fichier .m. [...]]]></description>
			<content:encoded><![CDATA[<p>Je vous ai récemment parlé d&#8217;un <a href="http://adminblog.foucry.net/?p=973">greffon</a> pour vim qui permet de gagner en confort pour écrire de l&#8217;Objective-C.</p>
<p>En plus de la coloration syntaxique, ce greffon apporte d&#8217;autres fonctionnalités intéressantes.<br />
<span id="more-987"></span></p>
<h3>Création du squelette de l&#8217;implémentation</h3>
<p>À partir d&#8217;un fichier header <code>.h</code> cocoa.vim est capable de générer le squelette de l’implémentation dans le fichier <code>.m</code>.</p>
<p>Il est nécessaire que le fichier dans lequel vous allez utiliser cette fonctionnalité soit reconnu comme un fichier d&#8217;implémentation, soit donc un <code>.m</code>. Une fois cette formalité accomplie, passez en mode commande  et tapez <code>:BuildMethods</code>.</p>
<p>- Si votre fichier d&#8217;implémentation porte le même nom que votre fichier, header la génération est simple et immédiat &nbsp;;<br />
- si votre fichier d&#8217;implémentation ne porte pas le même nom que votre fichier header vous devez le préciser dans la commande&nbsp;:</p>
<pre>:BuildMethods monBeauFichier.h</pre>
<p>Magique non&nbsp;?</p>
<h3>Note</h3>
<p>Il est possible que malgré tout vous ayez du mal à vous faire respecter par Vim et ce greffon. Il faut s&#8217;assurer que les éléments qui le constituent sont bien lus au lancement de Vim. Pour ce faire on dispose du fichier <code>~/.vimrc</code>. Dans celui-ci on va forcer quelques paramètres. Voici le mien, très court en réalité&nbsp;:</p>
<pre>filetype plugin on
filetype on
source ~/.vim/ftplugin/objc_cocoa_mappings.vim</pre>
<p>- La première ligne indique qu’il faut utiliser la version greffon de filetype au lieu de celle par défaut&nbsp;;<br />
- la seconde active la détection du type de fichier en cours d&#8217;édition&nbsp;;<br />
- la troisième force le chargement des fonctions additionnelles de cocoa.vim.</p>
<p>Une fois le fichier créé, cocoa.vim devrait mieux fonctionner.</p>
<h3>Liste des méthodes et saut à l&#8217;une d&#8217;entre elles</h3>
<p>Un fichier d&#8217;implémentation comporte de nombreuses lignes de code et de nombreuses méthodes. En avoir la liste est important.</p>
<p>Passez en mode commande et tapez <code>:ListMethod</code>. La fenêtre se découpe alors, avec en bas la liste des méthodes. Déplacez votre curseur sur celle qui vous intéresse et faites un retour chariot. Magie encore, vous voici sur la première ligne de cette méthode.</p>
<h3>Commentaires</h3>
<p>Un autre greffon intéressant à mettre en place est <a href="http://www.vim.org/scripts/script.php?script_id=1528">vim.comments</a>. Il permet, grâce à deux raccourcis clavier (<strong>CTRL-C</strong> et<strong> CTRL-X</strong>) de commenter ou dé commenté des lignes de code. Ce greffon n&#8217;est pas limité à Objective-C, il peut être utilisé pour du shell script, du php, du html&#8230; En fait, il n&#8217;est pas conçu pour l&#8217;Objective-C, il va nous falloir le modifier quelque peux pour le faire fonctionner.</p>
<h4>L&#8217;installation</h4>
<p>Copiez simplement le fichier <code>comments.vim</code> dans <code>~/.vim/plugin</code>.</p>
<p>Avant d&#8217;aller plus loin, tester la présence de votre greffon en éditant un fichier source et en testant les raccourcis. Si vous éditez un fichier <code>.m</code> le commentaire sera un <code>#</code>. C&#8217;est normal, souvenez-vous que nous allons devoir personnaliser le script de ce greffon.</p>
<h4>Personnalisation</h4>
<p>Pour personnaliser le script, nous allons l&#8217;éditer, avec Vim par exemple&nbsp;:</p>
<pre>$ vim ~/.vim/plugin/vim.comments</pre>
<p>Nous allons ajouter la même chose en quatre endroits. L&#8217;ajout à faire est <code>|| file_name =~ '\/m$' </code>.<br />
Ligne 110&nbsp;:</p>
<pre>if file_name =~ '\.cpp$' || file_name =~ '\.hpp$' || file_name =~ '\.java$' || file_name =~ '\.php[2345]\?$' || file_name =~ '\.C$'</pre>
<p>devient </p>
<pre>if file_name =~ '\.cpp$' <em>|| file_name =~ '\m$'</em> || file_name =~ '\.hpp$' || file_name =~ '\.java$' || file_name =~ '\.php[2345]\?$' || file_name =~ '\.C$'</pre>
<p>La même modification est à faire sur les lignes 175, 220 et 285.</p>
<p>Les commentaires du fichier sont très clairs et doivent vous guider si vous êtes perdus.</p>
<p>Il est possible de commenter une ligne, mais aussi un bloc&#8230; Il suffit de sélectionner les lignes du bloc&#8230; Et la question qui suit doit être&nbsp;:&nbsp;&raquo;Et comment on sélectionne un bloc gros malin &nbsp;?&nbsp;&raquo;. Facile, avec le raccourci qui va bien.</p>
<p>Positionnez le curseur à l&#8217;emplacement de début ou de fin du bloc, utiliser le raccourci <strong>SHIFT-V</strong> et déplacez vous, vers le haut ou vers le bas jusqu&#8217;à l&#8217;autre borne de votre groupe et appliquez le raccourci de commentaire.</p>
<h3>Complétion</h3>
<p>Un atout majeur pour Xcode4, c&#8217;est la complétion, Et là, il faut dire qu&#8217;il va être très dur à battre. En effet, Xcode apprend votre code au fur et à mesure que vous le tapez pour augmenter son dictionnaire de complétion. Pas vraiment possible avec Vim. Toutefois avec le greffon cocoa.vim on trouve un fichier de <em>snippets</em>. Celui-ci se trouve dans <code>~./vim/snippets/objc.vim</code>. </p>
<p>En regardant ce fichier on se rend compte que <code>imp&lt;TAB&gt;</code> est remplacé par <code>#import "fichier.h"</code>.</p>
<p>Je vous laisse explorer ce fichier, le comprendre et pourquoi pas mettre vos propres snippets de complétion.</p>
<p>Attention, pour que la complétion fonctionne, il est nécessaire d&#8217;installer un autre greffon, <a href="http://www.vim.org/scripts/script.php?script_id=2540">snipMate</a>.<br />
.</p>
<h3>Deux derniers petits trucs (pour aujourd&#8217;hui)</h3>
<p>Vim (gvim, et MacVim aussi) supporte les onglets. Il suffit de le lancer avec l&#8217;option <code>-p</code>. Personnellement j&#8217;ai fais un alias dans on fichier <code>.bashrc</code>&nbsp;:</p>
<pre>alias vim='vim -p'
gvim ='gvim -p'
mvim='mvim -p'</pre>
<p>Vous êtes en train d&#8217;éditer un fichier <code>.m</code> et vous avez besoin d&#8217;ouvrir un fichier inclus (ou importé), il suffit de placer sur la ligne d&#8217;inclusion du fichier et d&#8217;utiliser le raccourci <code>CTRL-W gf</code>.</p>
<p><code>CTRL-W</code> ouvre un nouvel onglet et <code>gf</code> pour <code>g</code>o <code>f</code>ile.</p>
<h3>Conclusion</h3>
<p>Avec ces quelques greffons, extension de Vim, nous avons un environnement d&#8217;édition de fichier Objective-C qui n&#8217;a pas grand-chose à envier à Xcode4.</p>
]]></content:encoded>
			<wfw:commentRss>http://adminblog.foucry.net/?feed=rss2&#038;p=987</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

