diff --git a/VERSIONS_WIP/142668 b/VERSIONS_WIP/142668 new file mode 100644 index 0000000000000000000000000000000000000000..795ec07f8ed099b1fb45ea129959f382a0377788 --- /dev/null +++ b/VERSIONS_WIP/142668 @@ -0,0 +1,2 @@ + - ticket #142668 : Administration : ajout d'un script permettant une mise à jour casi instantanée de la charte graphique + diff --git a/application/modules/admin/controllers/IndexController.php b/application/modules/admin/controllers/IndexController.php index 4ade7edbdb2d90c8773563869da0c1d92e0e53b8..d9ac1a0a5f25ff50d5ad68dca570d2464052c25d 100644 --- a/application/modules/admin/controllers/IndexController.php +++ b/application/modules/admin/controllers/IndexController.php @@ -141,13 +141,15 @@ class Admin_IndexController extends ZendAfi_Controller_Action { $this->view->titre = $this->_('Mise à jour de la charte graphique'); $reader = new Class_Profil_SkinUpdateReader(); $this->view->reader = $reader; + if ($id = $this->_getParam('id')) + $reader->forSkinOnly($id); $git = $this->_getParam('git'); if ($git === 'pull') $this->_askGitPull($reader); if ($git === 'clone') - $this->_askGitClone($reader, $this->_getParam('url')); + $this->_askGitClone($reader, $id ? $id : $this->_getParam('url')); } diff --git a/library/Class/Profil.php b/library/Class/Profil.php index d2de26f92119b636c36f2f93d4420a7c030ea8ec..35a2147759f271419694bb6767000708daa962c8 100644 --- a/library/Class/Profil.php +++ b/library/Class/Profil.php @@ -2371,6 +2371,11 @@ class Class_Profil extends Storm_Model_Abstract { } + public function getSkinPath() { + return $this->_getSkin()->getPath(); + } + + public function topProfilsDoAndSave($closure) { $profils = $this->getLoader()->findAllBy(['where'=> 'parent_id is null']); foreach ($profils as $profil) { diff --git a/library/Class/Profil/SkinUpdateReader.php b/library/Class/Profil/SkinUpdateReader.php index e2f6ec6c920a533c244bea836680fc38c4062994..d8bb4618ada790a2632f13a4f6afb7471c3dcdac 100644 --- a/library/Class/Profil/SkinUpdateReader.php +++ b/library/Class/Profil/SkinUpdateReader.php @@ -35,6 +35,7 @@ class Class_Profil_SkinUpdateReader { $skins_clone_log = 'skins_clone_log.json', $command_clone = 'git clone'; + protected $_for_skin_only; /** @category testing */ public static function setRealtime($value) { @@ -49,7 +50,7 @@ class Class_Profil_SkinUpdateReader { } - public static function getUpdateLogPath() { + public function getUpdateLogPath() { return PATH_TEMP . static::$skins_update_log; } @@ -59,15 +60,29 @@ class Class_Profil_SkinUpdateReader { } - public static function getSkinsPath($folder) { - return ROOT_PATH . (new Class_Profil())->getExtraPath() . $folder; + public static function getSkinPath() { + return (new Class_Profil())->getSkinPath(); + } + + + public function forSkinOnly($skin) { + $this->_for_skin_only = $skin; + return $this; } public function askGitPull() { - $data = []; - foreach($this->getUpdatableSkins() as $skin) + $data = $this->getUpdateJson(); + + foreach($this->getUpdatableSkins() as $skin) { + if ($this->_for_skin_only && ($skin != $this->_for_skin_only)) { + $data[$skin]['skin'] = $skin; + $data[$skin]['action'] = "done"; + continue; + } $data[$skin] = $this->_askOneGitPull($skin); + } + return $this->_writeInUpdateLog($data, !$this->_isRealtime()); } @@ -78,7 +93,10 @@ class Class_Profil_SkinUpdateReader { ? $this->_runGitPullIn($label)->getMessage() : $this->_('En attente depuis le %s', static::getCurrentDateTime()); - return ['Status' => $status]; + return ['skin' => $label, + 'skin_path' => static::getSkinPath(), + 'status' => $status, + 'action' => 'update']; } @@ -89,7 +107,7 @@ class Class_Profil_SkinUpdateReader { $data = []; foreach($this->getUpdatableSkins() as $skin) - $data[$skin] = ['Status' => $this->_runGitPullIn($skin)->getMessage()]; + $data[$skin] = ['status' => $this->_runGitPullIn($skin)->getMessage()]; return $this->_writeInUpdateLog($data, false); } @@ -105,6 +123,8 @@ class Class_Profil_SkinUpdateReader { public function askGitClone($label) { + if (!$label) + return $this; $url = GIT_SKINS . '/' . $label . '.git'; if ($this->_isRealtime()) @@ -113,8 +133,11 @@ class Class_Profil_SkinUpdateReader { if (!$datas = $this->getCloneJson()) $datas = []; - $datas[$label] = ['status' => $this->_('En attente depuis le %s', static::getCurrentDateTime()), - 'url' => $url]; + $datas[$label] = [ + 'skin' => $label, + 'status' => $this->_('En attente depuis le %s', static::getCurrentDateTime()), + 'url' => $url, + 'action' => 'update']; return $this->_writeInCloneLog($datas, true); } @@ -127,7 +150,7 @@ class Class_Profil_SkinUpdateReader { return $message; $update_datas = $this->getUpdateJson(); - $update_datas[$label] = ['Status' => $message]; + $update_datas[$label] = ['status' => $message]; $this->_writeInUpdateLog($update_datas, null); return $message; @@ -178,7 +201,9 @@ class Class_Profil_SkinUpdateReader { public function getUpdateJson() { - return json_decode($this->getFileWriter()->getContents(static::getUpdateLogPath()), true); + if (!$file = $this->getFileWriter()->getContents($this->getUpdateLogPath())) + $file = $this->getFileWriter()->getContents($this->getUpdateLogPath().'.done'); + return json_decode($file, true); } @@ -189,7 +214,35 @@ class Class_Profil_SkinUpdateReader { if (!isset($json[$folder])) return ''; - return $json[$folder]['Status']; + return isset($json[$folder]['status']) + ? $json[$folder]['status'] + : $json[$folder]['Status']; + } + + + public function getUpdateLog($folder) { + if (!$folder || !($json = $this->getUpdateJson())) + return ''; + + if (!isset($json[$folder])) + return ''; + + return isset($json[$folder]['log']) + ? $json[$folder]['log'] + : ''; + } + + + public function getCloneLog($folder) { + if (!$folder || !($json = $this->getCloneJson())) + return ''; + + if (!isset($json[$folder])) + return ''; + + return isset($json[$folder]['log']) + ? $json[$folder]['log'] + : ''; } @@ -200,10 +253,11 @@ class Class_Profil_SkinUpdateReader { public function getUpdatableSkinsCollection() { $skins = array_map(function($label) - { - return new Class_Profil_SkinUpdateReader_Skin($label, - $this->getStatus($label)); - }, + { + return new Class_Profil_SkinUpdateReader_Skin($label, + $this->getStatus($label), + $this->getUpdateLog($label)); + }, $this->getUpdatableSkins()); return new Storm_Collection($skins); @@ -211,17 +265,19 @@ class Class_Profil_SkinUpdateReader { public function getWaitingClones() { + if (!$datas = $this->getCloneJson()) return []; $clones = array_map(function($data, $label) - { - return is_array($data) - ? new Class_Profil_SkinUpdateReader_Clone($label, - $data['status'], - $data['url']) - : null; - }, + { + return is_array($data) + ? new Class_Profil_SkinUpdateReader_Clone($label, + $data['status'], + $data['url'], + $this->getCloneLog($label)) + : null; + }, $datas, array_keys($datas)); @@ -230,13 +286,16 @@ class Class_Profil_SkinUpdateReader { public function getCloneJson() { - return json_decode($this->getFileWriter()->getContents(static::getCloneLogPath()), true); + if (!$file = $this->getFileWriter()->getContents($this->getCloneLogPath())) + $file = $this->getFileWriter()->getContents($this->getCloneLogPath().'.done'); + return json_decode($file, true); } protected function _runAndLogCommands($commands) { $command = implode(' && ', $commands); $runner = $this->getCommand(); + $runner->exec($command); $output = '<br>' . implode('<br>', $runner->getOutput()); @@ -267,23 +326,25 @@ class Class_Profil_SkinUpdateReader { } - protected function _writeInLog($data, $log_path, $should_run) { - if (is_bool($should_run)) - $data['should_run'] = $should_run; + protected function _writeInCloneDoneLog($data, $should_run) { + return $this->_writeInLog($data, static::getCloneLogPath().'done', $should_run); + } + + protected function _writeInLog($data, $log_path, $should_run) { return static::getFileWriter()->putContents($log_path, json_encode($data)); } protected function _shouldRunUpdate() { $json = $this->getUpdateJson(); - return isset($json['should_run']) && $json['should_run']; + return isset($json['should_run']) && ($json['should_run']); } protected function _shouldRunClone() { $json = $this->getCloneJson(); - return isset($json['should_run']) && $json['should_run']; + return isset($json['should_run']) && ($json['should_run']); } } @@ -303,7 +364,7 @@ class Class_Profil_SkinUpdateReader_DataState { public function cloneBecomesUpdatable($label, $message) { - $this->_update[$label] = ['Status' => $message]; + $this->_update[$label] = ['status' => $message]; unset($this->_clones[$label]); $this->_should_write_update = true; return $this; @@ -363,11 +424,18 @@ class Class_Profil_SkinUpdateReader_Skin { protected $_label, - $_status; + $_status, + $_log; - public function __construct($label, $status) { + public function __construct($label, $status, $log) { $this->_label = $label; $this->_status = $status; + $this->_log = $log; + } + + + public function getId() { + return $this->_label; } @@ -379,6 +447,11 @@ class Class_Profil_SkinUpdateReader_Skin { public function getStatus() { return $this->_status; } + + + public function getLog() { + return str_replace("\n",'<br>',$this->_log); + } } @@ -389,11 +462,13 @@ class Class_Profil_SkinUpdateReader_Clone { protected $_skin, - $_url; + $_url, + $_log; - public function __construct($label, $status, $url) { - $this->_skin = new Class_Profil_SkinUpdateReader_Skin($label, $status); + public function __construct($label, $status, $url, $log) { + $this->_skin = new Class_Profil_SkinUpdateReader_Skin($label, $status, ''); $this->_url = $url; + $this->_log = $log; } @@ -402,6 +477,11 @@ class Class_Profil_SkinUpdateReader_Clone { } + public function getId() { + return $this->_skin->getLabel(); + } + + public function getStatus() { return $this->_skin->getStatus(); } @@ -410,4 +490,9 @@ class Class_Profil_SkinUpdateReader_Clone { public function getUrl() { return $this->_url; } + + + public function getLog() { + return str_replace("\n",'<br>',$this->_log); + } } diff --git a/library/Class/TableDescription/CloneSkins.php b/library/Class/TableDescription/CloneSkins.php index 13b637a29e9295fd617a91b4c6e112a4b26f3632..ba914c065e3506582b0ae72734293edb6c08ce9b 100644 --- a/library/Class/TableDescription/CloneSkins.php +++ b/library/Class/TableDescription/CloneSkins.php @@ -22,7 +22,18 @@ class Class_TableDescription_CloneSkins extends Class_TableDescription_UpdateSkins { public function init() { - parent::init(); + + $this->addColumn($this->_('Thème'), 'label') + ->addColumn($this->_('Statut'), 'status') + ->addColumn($this->_('Log'), 'log') + ->addRowAction(['url' => ['module' => 'admin', + 'controller' => 'index', + 'action' => 'update-skin', + 'git' => 'clone', + 'id' => '%s'], + 'label' => 'Demander la mise à jour', + 'icon' => 'batch'] ); + $this->addColumn($this->_('Url'), 'url') ->addRowAction(['url' => ['action' => 'clone-skin-cancel', 'id' => '%s'], diff --git a/library/Class/TableDescription/UpdateSkins.php b/library/Class/TableDescription/UpdateSkins.php index 7c7835be2cccf0348d43275ddc2b4a297073bcb2..5fca8c746f528feea2f6829a72ec32f8c9a03bc0 100644 --- a/library/Class/TableDescription/UpdateSkins.php +++ b/library/Class/TableDescription/UpdateSkins.php @@ -23,6 +23,15 @@ class Class_TableDescription_UpdateSkins extends Class_TableDescription { public function init() { $this->addColumn($this->_('Thème'), 'label') - ->addColumn($this->_('Statut'), 'status'); + ->addColumn($this->_('Statut'), 'status') + ->addColumn($this->_('Log'), 'log') + ->addRowAction(['url' => ['module' => 'admin', + 'controller' => 'index', + 'action' => 'update-skin', + 'git' => 'pull', + 'id' => '%s'], + 'label' => 'Demander la mise à jour', + 'icon' => 'batch'] ); + } } diff --git a/scripts/update_skins.php b/scripts/update_skins.php index 5ca6cb701de0d0ede27b8f853018a7ade8e03315..ea0f584281432e19e2c8971b06e0c2007b2f0c82 100644 --- a/scripts/update_skins.php +++ b/scripts/update_skins.php @@ -1,5 +1,5 @@ <?php require('console.php'); -$updater = new Class_Profil_SkinUpdateReader; -$updater->runGitPull(); -$updater->runGitClone(); +//$updater = new Class_Profil_SkinUpdateReader; +//$updater->runGitPull(); +//$updater->runGitClone(); diff --git a/scripts/update_skins.sh b/scripts/update_skins.sh new file mode 100644 index 0000000000000000000000000000000000000000..6fcd42e1a4602ff20bda4b6aac0219bc4e194145 --- /dev/null +++ b/scripts/update_skins.sh @@ -0,0 +1,159 @@ +#!/bin/bash +LOGFILE="/tmp/logupdate" +## Update or clone git skin +## Usage: bash update_skins.sh update|clone|clean + +usage='Usage: bash update_skins.sh update|clone' +if [ $# -lt 1 ]; then + printf "$0: not enough arguments\n%s\n" "$usage" >&2 + exit 2 +fi + +# echo "Delete old skins_update_log.json.lock" + + +function gitpull() { + local skin=$1 + local filename=$2 + + cd "skins/"${skin} && \ + git reset --hard HEAD && git pull --rebase > $LOGFILE 2>&1 + cd - + updateFile $skin $filename 'action' "done" + updateFile $skin $filename 'log' "`cat ${LOGFILE}`" +} + + +function gitclone() { + local skin=$1 + local filename=$2 + local url=`jq -r ".[\"${skin}\"].url" $filename".lock"` + echo "git clone ${url} ${skin}" + cd "skins/" && \ + git clone ${url} ${skin} > $LOGFILE 2>&1 + cd - + updateFile $skin $filename 'action' "done" + updateFile $skin $filename 'log' "`cat ${LOGFILE}`" +} + + +function updateFile() { + local skin=$1 + local filename=$2 + local key=$3 + local value=$4 + local lockfile=$filename".lock" + local donefile=$filename".done" + jq ".[\"${skin}\"].${key}=\"${value}\"" $lockfile >$donefile + cp $donefile $lockfile +} + + +function shouldUpdateSkin() { + local skin=$1 + local filename=$2 + jq -rC ".[\"${skin}\"].action == \"update\" |@sh" $filename".lock" +} + + +function writeUpdateLog() { + local skin=$1 + local filename=$2 + local mydate=$(date +%m/%d/%Y' '%T) + updateFile $skin $filename 'status' "Mis a jour le ${mydate}" +} + + +function skinsUpdate() { + for filename in `find ./temp/ -name "skins_update_log.json.lock" -mmin +15 |xargs -n1` + do + rm $filename + done + + if [ -e './temp/skins_update_log.json.lock' ]; then + echo "can't do anything , skins_update_log.json.lock exists" + exit + fi + + for filename in `find ./temp/ -name "skins_update_log.json" |xargs -n1` + do + mv $filename $filename".lock" + + jq -Cr '.[].skin' $filename".lock" | + while read -r skin; do \ + output=$(shouldUpdateSkin $skin $filename) + if [ $output != 'true' ]; then + continue + fi + + writeUpdateLog $skin $filename + gitpull $skin $filename + done + + if [ -e $filename".done" ]; then + rm $filename".lock" + else + mv $filename".lock" $filename".done" + fi + + done +} + + +function skinsClone(){ + for filename in `find ./temp/ -name "skins_clone_log.json.lock" -mmin +15 |xargs -n1` + do + rm $filename + done + + if [ -e './temp/skins_clone_log.json.lock' ]; then + echo "can't do anything , skins_clone_log.json.lock exists" + exit + fi + + + for filename in `find ./temp/ -name "skins_clone_log.json" |xargs -n1` + do + mv $filename $filename".lock" + jq -Cr '.[].skin' $filename".lock" | + while read -r skin ; do \ + output=$(shouldUpdateSkin $skin $filename) + + if [ $output == 'false' ]; then \ + continue + fi + writeUpdateLog $skin $filename + gitclone $skin $filename + + done + + if [ -e $filename".done" ]; then + rm $filename".lock" + else + mv $filename".lock" $filename".done" + fi + + + done +} + + +if [ $1 == 'update' ]; then + skinsUpdate +fi + +if [ $1 == 'clean' ]; then + if [ -e $filename".done" ]; then + rm $filename".done" + fi + if [ -e $filename".lock" ]; then + rm $filename".lock" + fi + +fi + + +if [ $1 == 'clone' ]; then + skinsClone +fi + diff --git a/tests/application/modules/admin/controllers/IndexControllerUpdateSkinTest.php b/tests/application/modules/admin/controllers/IndexControllerUpdateSkinTest.php index a443297099fa136b0f051195e82691f5ec473b24..9f221789169b4d5ade8dd7dc7b5858c4ab45281f 100644 --- a/tests/application/modules/admin/controllers/IndexControllerUpdateSkinTest.php +++ b/tests/application/modules/admin/controllers/IndexControllerUpdateSkinTest.php @@ -42,9 +42,9 @@ abstract class IndexControllerUpdateSkinTestCase extends Admin_AbstractControlle $this->_file_writer = $this->mock(); Class_Profil_SkinUpdateReader::setFileWriter($this->_file_writer); - $this->_update_log_content = json_encode(['Valence' => ['Status' => '25/04/2016 15:01:37']]); + $this->_update_log_content = json_encode(['Valence' => ['Status' => '25/04/2016 15:01:37', 'action' => 'done', 'log' => 'error on last update']]); $this->_clone_log_content = json_encode(['my-super-skin' => ['status' => 'En attente depuis longtemps...', - 'url' => 'git@git.afi-sa.net:opac-skins/my-super-skin.git'], + 'url' => 'git@git.afi-sa.net:opac-skins/my-super-skin.git', 'log' => 'error on last clone'], 'should_run' => false]); $this->_file_writer @@ -118,6 +118,18 @@ class IndexControllerUpdateSkinDispatchTest extends IndexControllerUpdateSkinTes } + /** @test */ + public function listShouldContainsUpdateLogForValence() { + $this->assertXPathContentContains('//td', 'error on last update'); + } + + + /** @test */ + public function listShouldContainsCloneLogForValence() { + $this->assertXPathContentContains('//td', 'error on last clone'); + } + + /** @test */ public function valenceShouldhaveBeenUpdatedOn25Slash04slash2016() { $this->assertXPathContentContains('//table[@id="skins"]//td', '25/04/2016 15:01:37'); @@ -126,7 +138,7 @@ class IndexControllerUpdateSkinDispatchTest extends IndexControllerUpdateSkinTes /** @test */ public function buttonUpdateSkinShouldBePresent() { - $this->assertXPathContentContains('//button[@data-url="/admin/index/update-skin/git/pull"]', + $this->assertXPathContentContains('//a[@href="/admin/index/update-skin/git/pull/id/Valence"]', 'Demander la mise à jour'); } @@ -188,17 +200,18 @@ class IndexControllerUpdateSkinGitPullTest extends IndexControllerUpdateSkinTest /** @test */ public function logShouldBeMarkedAsRunnable() { - $this->assertTrue($this->_update_log_data['should_run']); + $this->assertEquals('update', $this->_update_log_data['Valence']['action']); } /** @test */ public function valenceShouldBeWaiting() { $this->assertEquals('En attente depuis le 2016-05-02 12:30:00', - $this->_update_log_data['Valence']['Status']); + $this->_update_log_data['Valence']['status']); } + /** @test */ public function updateAskedMessageShouldBeDisplay() { $this->assertFlashMessengerContentContains('La demande de mise à jour a été envoyée au serveur'); @@ -231,7 +244,7 @@ class IndexControllerUpdateSkinGitCloneTest extends IndexControllerUpdateSkinTes /** @test */ public function waitingClonesShouldBeRunnable() { - $this->assertTrue($this->_clone_log_data['should_run']); + $this->assertEquals('update', $this->_clone_log_data['my-super-skin']['action']); } diff --git a/tests/library/Class/Profil/SkinUpdateReaderTest.php b/tests/library/Class/Profil/SkinUpdateReaderTest.php index b95db2f96c2f980f8869cb9cf9f16caa01e3861f..45321bd2c80d2022d9e912a2926860c26ee89e97 100644 --- a/tests/library/Class/Profil/SkinUpdateReaderTest.php +++ b/tests/library/Class/Profil/SkinUpdateReaderTest.php @@ -65,11 +65,13 @@ abstract class Class_SkinUpdateReaderTestCase extends ModelTestCase { ->beStrict(); - $this->_update_log_content = json_encode(['Valence' => ['Status' => 'En attente depuis le 2016-05-02 12:30:00'], + $this->_update_log_content = json_encode(['Valence' => ['Status' => 'En attente depuis le 2016-05-02 12:30:00', + 'action' => 'update'], 'should_run' => true]); $this->_clone_log_content = json_encode(['Iep38' => ['Status' => 'En attente depuis le 2016-05-02 12:30:00', - 'url' => 'git@git.afi-sa.net:opac-skins/Iep38.git'], + 'url' => 'git@git.afi-sa.net:opac-skins/Iep38.git', + 'action' => 'update'], 'should_run' => true]); $this->_file_writer