source: spip-zone/_outils_/smart_paquets/inc_empaqueteur.php @ 43982

Last change on this file since 43982 was 43982, checked in by esj@…, 10 years ago

smart_paquets. Mettre dans un fichier à part, et le charger automatiquement, ce qui est spécifique à la production de archives.xml pour plugin.xml, afin de pouvoir à terme basculer automatiquement sur une autre fonction pour paquet.xml. Et blinder un peu les cas d'erreur.

File size: 11.6 KB
Line 
1<?php
2
3define('_TRACE',true);
4define('_SLEEP_BETWEEN',200000);
5// on force une mise a jour de tous les paquets une fois pas jour,
6// entre minuit et 1H
7define('_FORCE_UPDATE',date('H')<1);
8#error_reporting(E_ALL);
9
10function empaqueteur($url, $dir_repo, $dir_paq, $src, $dest, $xml, $nom_vcs, $mail_to, $mail_from)
11{
12        global $erreurs;
13
14        $producteur =  'empaqueteur_' . basename($xml, '.xml');
15        require($producteur . '.php');
16
17        $erreurs = array();
18        $url = trim($url);
19        $dir_repo = rtrim($dir_repo ? $dir_repo : basename($url),'/') .'/';
20        $dir_paq = rtrim($dir_paq ? $dir_paq : '.','/') .'/';
21       
22        list($depot, $zips) = empaqueteur_zips($url, $dir_repo, $dir_paq, $src, $nom_vcs, $xml);
23        if (!$erreurs) {
24                // ne pas nettoyer le fichier archives.xml !
25                $old = array_keys($zips);
26                foreach(is_array($dest) ? $dest : array($dest) as $nom_dest)
27                        $old[] = $nom_dest . '.xml';
28                nettoyer_vieux_paquets($old, $dir_paq . $dir_repo, $dir_tmp);
29        } elseif ($mail_to) empaqueteur_mail($erreurs, $mail_to, $mail_from);
30
31        $all = $producteur($depot, $zips);
32        if ($all)
33                empaqueteur_xml_archives($all, $dir_paq, $dir_repo);
34        else    echo_trace("Aucun Zip produit");
35}
36
37// Fonction creant le fichier archives.xml final.
38// Ne pas le reecrire si rien de neuf, c'est + efficace et ca permet
39// au detecteur de nouvelle version d'utiliser If-Modified-Since.
40// Attention, file_put_contents fait ce qu'il veut de certaines lignes vides
41// http://fr2.php.net/manual/fr/function.file-put-contents.php
42
43function empaqueteur_xml_archives($all, $dir_paq, $dir_repo)
44{
45        $xml = strlen($all);
46        $f = $dir_paq . $dir_repo . 'archives.xml';
47        $old = (file_exists($f)) ? trim(file_get_contents($f)) : '';
48        if ($old != $all) {
49                echo_trace("Nouveau $f de taille $xml");
50                file_put_contents($f, $all);
51                return;
52        }
53        echo_trace("$f intact (taille: $xml)");
54}
55
56function empaqueteur_zips($url, $dir_repo, $dir_paq, $src, $nom_vcs, $xml)
57{
58        $dir_tmp = $dir_paq.'tmp/';
59        $dir_paq .= $dir_repo;
60
61        if (!function_exists($vcs = 'empaqueteur_exec_' . $nom_vcs)) {
62                echo_trace("VCS non disponible: '$nom_vcs'");
63                $vcs = 'explode'; // i.e. ne fait rien de ses arguments.
64        }
65
66        // Creation des repertoires de travail et import initial
67        if (!file_exists($dir_repo)){
68                preparer_chemin(dirname(rtrim($dir_repo,'/')));
69                if ($url) $vcs("checkout", "$url $dir_repo");
70        }
71        if (!file_exists($dir_paq))
72                preparer_chemin($dir_paq);
73        if (!file_exists($dir_tmp))
74                preparer_chemin($dir_tmp);
75
76        // Si le repo est en file:// on fait un svnsync dessus,
77        // le up est fait par un hook post-commit sur ce qui a change uniquement
78        // sauf une fois par jour
79        if (preg_match(',^file://,',$url)) {
80                $vcs("sync", $url);
81                if (_FORCE_UPDATE)
82                        $vcs("up",rtrim($dir_repo,'/'));
83        }
84        elseif ($url) {
85                $vcs("up", rtrim($dir_repo,'/'));
86        }
87
88        list($depot, $paquets) = liste_paquets($dir_repo . $src);
89        $zips = array();
90        foreach($paquets as $paquet){
91                if ($paquet['revision']=='HEAD' AND
92                    $r = empaqueteur_zip($paquet, $dir_repo, $dir_paq, $dir_tmp, $vcs, $xml)) {
93                        $zips[$paquet['nom'] .".zip"] = $r;
94                        if (intval(_SLEEP_BETWEEN)) usleep(_SLEEP_BETWEEN);
95                }
96        }
97        echo_trace(count($zips) . " trouves");
98        return array($depot, $zips);
99}
100       
101function empaqueteur_zip($paquet, $dir_repo, $dir_paq, $dir_tmp, $vcs, $xml)
102{
103        $dsource = $dir_repo. $paquet['source'];
104        if (!file_exists($dsource)){
105                echo_trace("Erreur : $dsource inexistant");
106                return false;
107        }
108        // ajouter le fichier svn.revision
109        $rev = $dsource . "/svn.revision";
110        $vcs("info", "$dsource > $rev");
111        $info = renseigner_revision_paquet($rev);
112        $zip = $paquet['nom'] .".zip";
113        $zippath = $dir_paq.$zip;
114        if (!paqueter($dsource, $zip, $zippath, $paquet['nom_dossier'], $info, $dir_tmp))
115                return false;
116        // copier le svn.revision de stable/spip.zip qui permet de connaitre la derniere version stable
117        if ($zip=="stable/spip.zip") {
118                // necessaire de remettre le fichier a la date actuelle
119                // car il a la date du dernier commit sur ce paquet
120                # touch($dsource."/svn.revision");
121                copie_update($rev,$dir_paq.dirname($zip)."/svn.revision");
122        }
123        // supprimer le fichier info revision cree ci-dessus
124        @unlink($rev);
125        // Recuperer le xml qui decrit le plugin
126        $f = $dsource . '/' . $xml ;
127        if (!file_exists($f)) {
128                echo_trace("(info) Paquet $zip sans $xml");
129                $desc = '';
130        } else {
131                $re = ",<"."\?xml[^>]*\?".">,Uims";
132                $desc = trim(preg_replace($re,'',file_get_contents($f)));
133        }
134
135        return array(
136                filesize($zippath),
137                filemtime($zippath),
138                $paquet['source'],
139                $info[1],
140                $desc);
141}
142
143function empaqueteur_mail($erreurs, $mail_to, $mail_from)
144{
145        $headers =
146                "From: ".($mail_from ?$mail_from : $mail_to)."\r\n"
147                ."Reply-To: ".($mail_from ?$mail_from : $mail_to)."\r\n";
148        mail($mail_to,"empaqueteur",implode("",$erreurs),$headers);
149}
150
151
152/**
153 * Echo avec un horodatage
154 */
155function echo_trace($out){
156        $outh = @date("[Y-m-d H:i:s] ",time()).$out."\n";
157        if (_TRACE) echo $outh;
158
159        if (strncmp($out,"Erreur :",8)==0)
160                $GLOBALS['erreurs'][]=$outh;
161
162        return $outh;
163}
164
165/**
166 * exec avec une trace de la commande et de son resultat
167 */
168function exec_trace($commande){
169        $output = array();
170        echo_trace($commande);
171        $out = exec("$commande 2>&1", $output);
172        if ($output!==null) {
173                $out = end($output);
174        }
175        if (strlen(trim($out)))
176                array_map('echo_trace',$output);
177        return $out;
178}
179
180/**
181 * exec de Subversion
182 */
183
184function empaqueteur_exec_svn($cmd, $args)
185{
186        $bin = ($cmd == 'sync') ? 'svnsync' : 'svn';
187        return exec_trace("$bin $cmd $args");
188}
189
190/**
191 * preparer une arborescence/vide/vers/un/nom/de/dossier
192 */
193function preparer_chemin($dir){
194        if (strlen($dir)){
195                if ($parent = dirname($dir) AND !is_dir($parent))
196                        preparer_chemin($parent);
197                if (!is_dir($dir))
198                        mkdir($dir);
199        }
200}
201
202/**
203 * supprimer une arborescence/vide/vers/un/nom/de/dossier
204 * a partir d'un repertoire $base que l'on conserve
205 */
206function supprimer_chemin($base,$dir){
207        if (strlen($dir) AND $dir!='.'){
208                if (file_exists($base .$dir))
209                        rmdir($base .$dir);
210                supprimer_chemin($base,dirname($dir));
211        }
212}
213
214/**
215 * Copier le paquet si il a change
216 */
217function copie_update($source,$dest){
218        if (file_exists($source)){
219                $ts = filemtime($source);
220                if (file_exists($dest))
221                        unlink($dest);
222                else {
223                        // si l'archive doit etre mise dans un sous repertoire
224                        // creer l'arbo la premiere fois
225                        preparer_chemin(dirname($dest));
226                }
227                rename($source,$dest);
228                touch($dest,$ts);
229        }
230        else
231                echo_trace("Erreur : fichier $source non trouve");
232}
233
234function renseigner_revision_paquet($file){
235        $revision = $date_commit = 0;
236        /*
237                Creation du fichier svn.revision
238                Ce fichier est utilise par SPIP pour afficher son numero de version dans l'interface privee
239                a l'aide de la fonction version_svn_courante()
240                http://doc.spip.org/@version_svn_courante
241        */
242               
243
244        $infos = @file($file);
245        if (!$infos) return array('', 0);
246        $xml_props = $txt_props = array();
247        foreach($infos as $line) {
248                if (preg_match('/^(Last Changed Rev|R.vision de la derni.re modification)\s*: (?<revision>\d*)$/',$line,$matches))
249                        $xml_props['revision'] = $txt_props['Revision'] = $matches['revision'];
250                if (preg_match('/^URL\s*: (?<url>.*)$/',$line,$matches)) 
251                        $xml_props['origine'] = $txt_props['Origine'] = $matches['url'];
252                if (preg_match('/^(Last Changed Date|Date de la derni.re modification\s*): (?<date_commit>[^(]*)($|\()/',$line,$matches)) 
253                        $xml_props['commit'] = $txt_props['Dernier commit'] = $matches['date_commit'];
254        }
255       
256        $svn_revision = "<svn_revision>\n<text_version>";
257        foreach($txt_props as $prop => $val) $svn_revision .= "\n$prop: $val";
258        $svn_revision .= "\n</text_version>";
259        foreach($xml_props as $prop => $val) $svn_revision .= "\n<$prop>$val</$prop>";
260        $svn_revision .= "\n</svn_revision>";
261       
262        if ($fp = @fopen($file,"w")) {
263                fwrite($fp, $svn_revision);
264                @fclose($fp);
265        } else echo_trace("Erreur: impossible d'ecrire dans $file");
266        // mettre la date du fichier a celle du dernier commit
267        // pour ne pas fausser la date du paquet (qui est celle du plus recent fichier)
268        touch($file,strtotime($xml_props['commit']));
269
270        $date_commit = date('Y-m-d H:i:s',strtotime($xml_props['commit']));
271        //echo_trace("$file, Revision $revision, dernier commit : $date_commit");
272        return array($xml_props['revision'],$date_commit);
273}
274
275/**
276 * Creer un paquet a partir du
277 * $source : sous repertoire de $dir
278 * $zip : nom du fichier zip (avec l'extension)
279 * $nom_dossier : arborescence dans le zip qui sera cree lors du dezippage
280 *
281 */
282function paqueter($source, $zip, $zippath, $nom_dossier, $rev, $dir_tmp){
283        $zipfile = basename($zip);
284        list($revision,$date_commit) = $rev;
285        $date_paquet = file_exists($zippath) ? filemtime($zippath) : 0;
286        // tester si le paquet est a jour
287        if (strtotime($date_commit)<$date_paquet AND !_FORCE_UPDATE) {
288                echo_trace("$zip OK : du ".date('Y-m-d H:i:s',$date_paquet)." / dernier commit du $date_commit");
289                return true;
290        }
291        else {
292                $tmp = $dir_tmp.$nom_dossier;
293                preparer_chemin($tmp);
294                if (!rename($source,$tmp)) {
295                        echo_trace("Erreur : $source --> $tmp");
296                        return false;
297                } else {
298                        $d = getcwd();
299                        chdir($dir_tmp);
300                        $base_dir = reset(explode('/',$nom_dossier));
301                        // zipper en prenant la date du fichier le plus recent
302                        // comme date du paquet
303                        exec_trace("zip -roq $zipfile $base_dir -x \*/.svn\*");
304                        chdir($d);
305
306                        $date_paquet = filemtime($dir_tmp.$zipfile);
307                        // cas ou le dernier commit consiste en la suppression de fichiers
308                        // du coup le zip est plus ancien que le dernier commit
309                        // on corrige manuellement
310                        if ($date_paquet<strtotime($date_commit)) {
311                                touch($dir_tmp.$zipfile,strtotime($date_commit)+1);
312                        }
313                        rename($tmp,$source);
314                }
315                supprimer_chemin($dir_tmp,$nom_dossier);
316                copie_update($dir_tmp.$zipfile,$zippath);
317                return true;
318        }
319}
320
321/**
322 * Supprimer les vieux paquets obsoletes
323 * @param <type> $paquets_a_jour
324 */
325function nettoyer_vieux_paquets($paquets_a_jour, $dir_paq, $dir_tmp){
326        $maxfiles = 10000; // securite
327        if (@is_dir($dir_paq) AND is_readable($dir_paq) AND $d = @opendir($dir_paq)) {
328                while (($f = readdir($d)) !== false && ($nbfiles<$maxfiles)) {
329                        if ($f[0] != '.' # ignorer . .. .svn etc
330                        AND $f != 'CVS'
331                        AND $f != 'remove.txt'
332                        AND is_readable($g = $dir_paq.$f)) {
333                                if (is_file($g)) {
334                                        if (!in_array($f,$paquets_a_jour)){
335                                                echo_trace("Suppression du vieux paquet $f");
336                                                unlink($g);
337                                                @unlink($dir_tmp.$f); // securite, on vire aussi le vieux paquet tmp eventuel
338                                        }
339                                }
340                        }
341                }
342                closedir($d);
343        }
344}
345
346
347/**
348 * Lister les paquets demandes dans le fichier archivelist a la racine
349 * @param <type> $archivelist
350 * @return <type>
351 */
352function liste_paquets($archivelist){
353        echo_trace("chargement de $archivelist");
354        if (!$archivefile=file($archivelist)){
355                echo_trace("Erreur : Impossible de lire $archivelist");
356                return array();
357        }
358
359        $depot = array();
360        $paquets = array();
361        foreach($archivefile as $ligne=>$lignepaquet){
362                $lignepaquet=rtrim($lignepaquet);//on vire le retour ligne de la fin
363                if (strlen($lignepaquet)) {
364                        if (substr($lignepaquet,0,1)!="#") {
365                                // C'est une ligne de definition d'un paquet :
366                                // - on separe les parametres
367                                // - et on fixe ceux manquants
368                                $a = explode(";",$lignepaquet);
369                                $b = preg_split("/:/",$a[0]);
370                                $source = $b[0];
371                                $svn_version = empty($b[1]) ?'HEAD' : $b[1];           
372                                $nom_paquet = empty($a[1]) ? basename($source) : $a[1];
373                                $nom_dossier = empty($a[2]) ? $nom_paquet : $a[2];
374                                // Ajout au tableau des paquets a construire
375                                $paquets[] = array('source'=>rtrim($source,'/'),
376                                                   'nom'=>$nom_paquet,
377                                                   'nom_dossier'=>$nom_dossier,
378                                                   'revision'=>$svn_version);
379                        }
380                        else if (preg_match('#@([^=\s]+)\s*=(.+)$#', substr($lignepaquet, 1), $matches)){
381                                // C'est une ligne d'information sur le depot
382                                // - on stocke le parametre trouve
383                                $depot[trim($matches[1])] = trim($matches[2]);
384                        }
385                }
386        }
387
388        echo_trace(count($depot)." informations de depot definies");
389        echo_trace(count($paquets)." paquets definis");
390        return array($depot, $paquets);
391}
392
393?>
Note: See TracBrowser for help on using the repository browser.