PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/LanguagesManager/Commands/CreatePull.php

https://github.com/CodeYellowBV/piwik
PHP | 219 lines | 161 code | 46 blank | 12 comment | 14 complexity | b2c002c77514accbce127faa9e0751e7 MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Plugins\LanguagesManager\Commands;
  10. use Piwik\Plugin\ConsoleCommand;
  11. use Piwik\Plugins\LanguagesManager\API;
  12. use Symfony\Component\Console\Input\ArrayInput;
  13. use Symfony\Component\Console\Input\InputInterface;
  14. use Symfony\Component\Console\Input\InputOption;
  15. use Symfony\Component\Console\Output\OutputInterface;
  16. /**
  17. */
  18. class CreatePull extends ConsoleCommand
  19. {
  20. protected function configure()
  21. {
  22. $this->setName('translations:createpull')
  23. ->setDescription('Updates translation files')
  24. ->addOption('username', 'u', InputOption::VALUE_OPTIONAL, 'oTrance username')
  25. ->addOption('password', 'p', InputOption::VALUE_OPTIONAL, 'oTrance password')
  26. ->addOption('plugin', 'P', InputOption::VALUE_OPTIONAL, 'optional name of plugin to update translations for');
  27. }
  28. protected function execute(InputInterface $input, OutputInterface $output)
  29. {
  30. $changes = shell_exec('git status --porcelain -uno');
  31. if (!empty($changes)) {
  32. $output->writeln("You have uncommited changes. Creating pull request is only available with a clean working directory");
  33. return;
  34. }
  35. $unpushedCommits = shell_exec('git log origin/master..HEAD');
  36. if (!empty($unpushedCommits)) {
  37. $output->writeln("You have unpushed commits. Creating pull request is only available with a clean working directory");
  38. return;
  39. }
  40. chdir(PIWIK_DOCUMENT_ROOT);
  41. shell_exec('
  42. git checkout master > /dev/null 2>&1
  43. git pull > /dev/null 2>&1
  44. git submodule init > /dev/null 2>&1
  45. git submodule update > /dev/null 2>&1
  46. ');
  47. $plugin = $input->getOption('plugin');
  48. if (!empty($plugin)) {
  49. chdir(PIWIK_DOCUMENT_ROOT.DIRECTORY_SEPARATOR.'plugins'.DIRECTORY_SEPARATOR.$plugin);
  50. shell_exec('
  51. git checkout master > /dev/null 2>&1
  52. git pull > /dev/null 2>&1
  53. ');
  54. }
  55. // check if branch exists localy and track it if not
  56. $branch = shell_exec('git branch | grep translationupdates');
  57. if (empty($branch)) {
  58. shell_exec('git checkout -b translationupdates origin/translationupdates');
  59. }
  60. // switch to branch and update it to latest master
  61. shell_exec('
  62. git checkout translationupdates > /dev/null 2>&1
  63. git merge master > /dev/null 2>&1
  64. git push origin translationupdates > /dev/null 2>&1
  65. ');
  66. // update translation files
  67. $command = $this->getApplication()->find('translations:update');
  68. $arguments = array(
  69. 'command' => 'translations:update',
  70. '--username' => $input->getOption('username'),
  71. '--password' => $input->getOption('password'),
  72. '--plugin' => $plugin
  73. );
  74. $inputObject = new ArrayInput($arguments);
  75. $inputObject->setInteractive($input->isInteractive());
  76. $command->run($inputObject, $output);
  77. shell_exec('git add lang/. > /dev/null 2>&1');
  78. if (empty($plugin)) {
  79. foreach (Update::getPluginsInCore() AS $pluginName) {
  80. shell_exec(sprintf('git add plugins/%s/lang/. > /dev/null 2>&1', $pluginName));
  81. }
  82. }
  83. $changes = shell_exec('git status --porcelain -uno');
  84. if (empty($changes)) {
  85. $output->writeln("Nothing changed. Everything is already up to date.");
  86. shell_exec('git checkout master > /dev/null 2>&1');
  87. return;
  88. }
  89. API::unsetInstance(); // reset languagemanager api (to force refresh of data)
  90. $stats = shell_exec('git diff --numstat HEAD');
  91. preg_match_all('/([0-9]+)\t([0-9]+)\t[a-zA-Z\/]*lang\/([a-z]{2,3})\.json/', $stats, $lineChanges);
  92. $addedLinesSum = 0;
  93. if (!empty($lineChanges[1])) {
  94. $addedLinesSum = array_sum($lineChanges[1]);
  95. }
  96. $linesSumByLang = array();
  97. for($i=0; $i<count($lineChanges[0]); $i++) {
  98. @$linesSumByLang[$lineChanges[3][$i]] += $lineChanges[1][$i];
  99. }
  100. preg_match_all('/M [a-zA-Z\/]*lang\/([a-z]{2,3})\.json/', $changes, $modifiedFiles);
  101. preg_match_all('/A [a-zA-Z\/]*lang\/([a-z]{2,3})\.json/', $changes, $addedFiles);
  102. $messages = array();
  103. $languageCodesTouched = array();
  104. if (!empty($addedFiles[1])) {
  105. foreach ($addedFiles[1] AS $addedFile) {
  106. $languageInfo = $this->getLanguageInfoByIsoCode($addedFile);
  107. $messages[$addedFile] = sprintf('- Added %s (%s changes / %s translated)\n', $languageInfo['english_name'], $linesSumByLang[$addedFile], $languageInfo['percentage_complete']);
  108. }
  109. $languageCodesTouched = array_merge($languageCodesTouched, $addedFiles[1]);
  110. }
  111. if (!empty($modifiedFiles[1])) {
  112. foreach ($modifiedFiles[1] AS $modifiedFile) {
  113. $languageInfo = $this->getLanguageInfoByIsoCode($modifiedFile);
  114. $messages[$modifiedFile] = sprintf('- Updated %s (%s changes / %s translated)\n', $languageInfo['english_name'], $linesSumByLang[$modifiedFile], $languageInfo['percentage_complete']);
  115. }
  116. $languageCodesTouched = $modifiedFiles[1];
  117. }
  118. $message = implode('', $messages);
  119. $languageCodesTouched = array_unique($languageCodesTouched, SORT_REGULAR);
  120. $title = sprintf(
  121. 'Updated %s strings in %u languages (%s)',
  122. $addedLinesSum,
  123. count($languageCodesTouched),
  124. implode(', ', $languageCodesTouched)
  125. );
  126. shell_exec('git commit -m "language update ${pluginName} refs #3430"');
  127. shell_exec('git push');
  128. shell_exec('git checkout master > /dev/null 2>&1');
  129. $this->createPullRequest($output, $title, $message);
  130. }
  131. private function getLanguageInfoByIsoCode($isoCode)
  132. {
  133. $languages = API::getInstance()->getAvailableLanguagesInfo();
  134. foreach ($languages AS $languageInfo) {
  135. if ($languageInfo['code'] == $isoCode) {
  136. return $languageInfo;
  137. }
  138. }
  139. return array();
  140. }
  141. private function createPullRequest(OutputInterface $output, $title, $message)
  142. {
  143. $dialog = $this->getHelperSet()->get('dialog');
  144. while (true) {
  145. $username = $dialog->ask($output, 'Please provide your github username (to create a pull request using Github API): ');
  146. $returnCode = shell_exec('curl \
  147. -X POST \
  148. -k \
  149. --silent \
  150. --write-out %{http_code} \
  151. --stderr /dev/null \
  152. -o /dev/null \
  153. -u '.$username.' \
  154. --data "{\"title\":\"[automatic translation update] '.$title.'\",\"body\":\"'.$message.'\",\"head\":\"translationupdates\",\"base\":\"master\"}" \
  155. -H "Accept: application/json" \
  156. https://api.github.com/repos/piwik/piwik/pulls');
  157. switch ($returnCode) {
  158. case 401:
  159. $output->writeln("Pull request failed. Bad credentials... Please try again");
  160. continue;
  161. case 422:
  162. $output->writeln("Pull request failed. Unprocessable Entity. Maybe a pull request was already created before.");
  163. return;
  164. case 201:
  165. case 200:
  166. $output->writeln("Pull request successfully created.");
  167. return;
  168. default:
  169. $output->writeln("Pull request failed... Please try again");
  170. }
  171. }
  172. }
  173. }