| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 | 
							- <?php
 
- /**
 
-  * Script to get changes between feature branch and the mainline
 
-  *
 
-  * @category   dev
 
-  * @package    build
 
-  * Copyright © Magento, Inc. All rights reserved.
 
-  * See COPYING.txt for license details.
 
-  */
 
- define(
 
-     'USAGE',
 
-     <<<USAGE
 
-         php -f get_github_changes.php --
 
-     --output-file="<output_file>"
 
-     --base-path="<base_path>"
 
-     --repo="<main_repo>"
 
-     --branch="<branch>"
 
-     [--file-extensions="<comma_separated_list_of_formats>"]
 
- USAGE
 
- );
 
- $options = getopt('', ['output-file:', 'base-path:', 'repo:', 'file-extensions:', 'branch:']);
 
- $requiredOptions = ['output-file', 'base-path', 'repo', 'branch'];
 
- if (!validateInput($options, $requiredOptions)) {
 
-     echo USAGE;
 
-     exit(1);
 
- }
 
- $fileExtensions = explode(',', isset($options['file-extensions']) ? $options['file-extensions'] : 'php');
 
- include_once __DIR__ . '/framework/autoload.php';
 
- $mainline = 'mainline_' . (string)rand(0, 9999);
 
- $repo = getRepo($options, $mainline);
 
- $branches = $repo->getBranches('--remotes');
 
- generateBranchesList($options['output-file'], $branches, $options['branch']);
 
- $changes = retrieveChangesAcrossForks($mainline, $repo, $options['branch']);
 
- $changedFiles = getChangedFiles($changes, $fileExtensions);
 
- generateChangedFilesList($options['output-file'], $changedFiles);
 
- saveChangedFileContent($repo);
 
- $additions = retrieveNewFilesAcrossForks($mainline, $repo, $options['branch']);
 
- $addedFiles = getChangedFiles($additions, $fileExtensions);
 
- $additionsFile = pathinfo($options['output-file']);
 
- $additionsFile = $additionsFile['dirname']
 
-     . DIRECTORY_SEPARATOR
 
-     . $additionsFile['filename']
 
-     . '.added.'
 
-     . $additionsFile['extension'];
 
- generateChangedFilesList($additionsFile, $addedFiles);
 
- cleanup($repo, $mainline);
 
- /**
 
-  * Save changed file content.
 
-  *
 
-  * @param GitRepo $repo
 
-  * @return void
 
-  */
 
- function saveChangedFileContent(GitRepo $repo)
 
- {
 
-     $changedFilesContentFileName = BP . Magento\TestFramework\Utility\ChangedFiles::CHANGED_FILES_CONTENT_FILE;
 
-     foreach ($repo->getChangedContentFiles() as $key => $changedContentFile) {
 
-         $filePath = sprintf($changedFilesContentFileName, $key);
 
-         $oldContent = file_exists($filePath) ? file_get_contents($filePath) : '{}';
 
-         $oldData = json_decode($oldContent, true);
 
-         $data = array_merge($oldData, $changedContentFile);
 
-         file_put_contents($filePath, json_encode($data));
 
-     }
 
- }
 
- /**
 
-  * Generates a file containing changed files
 
-  *
 
-  * @param string $outputFile
 
-  * @param array $changedFiles
 
-  * @return void
 
-  */
 
- function generateChangedFilesList($outputFile, $changedFiles)
 
- {
 
-     $changedFilesList = fopen($outputFile, 'w');
 
-     foreach ($changedFiles as $file) {
 
-         fwrite($changedFilesList, $file . PHP_EOL);
 
-     }
 
-     fclose($changedFilesList);
 
- }
 
- /**
 
-  * Generates a file containing origin branches
 
-  *
 
-  * @param string $outputFile
 
-  * @param array $branches
 
-  * @param string $branchName
 
-  * @return void
 
-  */
 
- function generateBranchesList($outputFile, $branches, $branchName)
 
- {
 
-     $branchOutputFile = str_replace('changed_files', 'branches', $outputFile);
 
-     $branchesList = fopen($branchOutputFile, 'w');
 
-     fwrite($branchesList, $branchName . PHP_EOL);
 
-     foreach ($branches as $branch) {
 
-         fwrite($branchesList, substr(strrchr($branch, '/'), 1) . PHP_EOL);
 
-     }
 
-     fclose($branchesList);
 
- }
 
- /**
 
-  * Gets list of changed files
 
-  *
 
-  * @param array $changes
 
-  * @param array $fileExtensions
 
-  * @return array
 
-  */
 
- function getChangedFiles(array $changes, array $fileExtensions)
 
- {
 
-     $files = [];
 
-     foreach ($changes as $fileName) {
 
-         foreach ($fileExtensions as $extensions) {
 
-             $isFileExension = strpos($fileName, '.' . $extensions);
 
-             if ($isFileExension) {
 
-                 $files[] = $fileName;
 
-             }
 
-         }
 
-     }
 
-     return $files;
 
- }
 
- /**
 
-  * Retrieves changes across forks
 
-  *
 
-  * @param array $options
 
-  * @param string $mainline
 
-  * @return GitRepo
 
-  * @throws Exception
 
-  */
 
- function getRepo($options, $mainline)
 
- {
 
-     $repo = new GitRepo($options['base-path']);
 
-     $repo->addRemote($mainline, $options['repo']);
 
-     $repo->fetch($mainline);
 
-     return $repo;
 
- }
 
- /**
 
-  * Combine list of changed files based on comparison between forks.
 
-  *
 
-  * @param string $mainline
 
-  * @param GitRepo $repo
 
-  * @param string $branchName
 
-  * @return array
 
-  */
 
- function retrieveChangesAcrossForks($mainline, GitRepo $repo, $branchName)
 
- {
 
-     return $repo->compareChanges($mainline, $branchName, GitRepo::CHANGE_TYPE_ALL);
 
- }
 
- /**
 
-  * Combine list of new files based on comparison between forks.
 
-  *
 
-  * @param string $mainline
 
-  * @param GitRepo $repo
 
-  * @param string $branchName
 
-  * @return array
 
-  */
 
- function retrieveNewFilesAcrossForks($mainline, GitRepo $repo, $branchName)
 
- {
 
-     return $repo->compareChanges($mainline, $branchName, GitRepo::CHANGE_TYPE_ADDED);
 
- }
 
- /**
 
-  * Deletes temporary "base" repo
 
-  *
 
-  * @param GitRepo $repo
 
-  * @param string $mainline
 
-  */
 
- function cleanup($repo, $mainline)
 
- {
 
-     $repo->removeRemote($mainline);
 
- }
 
- /**
 
-  * Validates input options based on required options
 
-  *
 
-  * @param array $options
 
-  * @param array $requiredOptions
 
-  * @return bool
 
-  */
 
- function validateInput(array $options, array $requiredOptions)
 
- {
 
-     foreach ($requiredOptions as $requiredOption) {
 
-         if (!isset($options[$requiredOption]) || empty($options[$requiredOption])) {
 
-             return false;
 
-         }
 
-     }
 
-     return true;
 
- }
 
- //@codingStandardsIgnoreStart
 
- class GitRepo
 
- // @codingStandardsIgnoreEnd
 
- {
 
-     const CHANGE_TYPE_ADDED = 1;
 
-     const CHANGE_TYPE_MODIFIED = 2;
 
-     const CHANGE_TYPE_ALL = 3;
 
-     /**
 
-      * Absolute path to git project
 
-      *
 
-      * @var string
 
-      */
 
-     private $workTree;
 
-     /**
 
-      * @var array
 
-      */
 
-     private $remoteList = [];
 
-     /**
 
-      * Array of changed content files.
 
-      *
 
-      * Example:
 
-      *         'extension' =>
 
-      *                      'path_to_file/filename'  => 'Content that was edited',
 
-      *                      'path_to_file/filename2' => 'Content that was edited',
 
-      *
 
-      * @var array
 
-      */
 
-     private $changedContentFiles = [];
 
-     /**
 
-      * @param string $workTree absolute path to git project
 
-      */
 
-     public function __construct($workTree)
 
-     {
 
-         if (empty($workTree) || !is_dir($workTree)) {
 
-             throw new UnexpectedValueException('Working tree should be a valid path to directory');
 
-         }
 
-         $this->workTree = $workTree;
 
-     }
 
-     /**
 
-      * Adds remote
 
-      *
 
-      * @param string $alias
 
-      * @param string $url
 
-      */
 
-     public function addRemote($alias, $url)
 
-     {
 
-         if (isset($this->remoteList[$alias])) {
 
-             return;
 
-         }
 
-         $this->remoteList[$alias] = $url;
 
-         $this->call(sprintf('remote add %s %s', $alias, $url));
 
-     }
 
-     /**
 
-      * Remove remote
 
-      *
 
-      * @param string $alias
 
-      */
 
-     public function removeRemote($alias)
 
-     {
 
-         if (isset($this->remoteList[$alias])) {
 
-             $this->call(sprintf('remote rm %s', $alias));
 
-             unset($this->remoteList[$alias]);
 
-         }
 
-     }
 
-     /**
 
-      * Fetches remote
 
-      *
 
-      * @param string $remoteAlias
 
-      */
 
-     public function fetch($remoteAlias)
 
-     {
 
-         if (!isset($this->remoteList[$remoteAlias])) {
 
-             throw new LogicException('Alias "' . $remoteAlias . '" is not defined');
 
-         }
 
-         $this->call(sprintf('fetch %s', $remoteAlias));
 
-     }
 
-     /**
 
-      * Returns branches
 
-      *
 
-      * @param string $source
 
-      * @return array|mixed
 
-      */
 
-     public function getBranches($source = '--all')
 
-     {
 
-         $result = $this->call(sprintf('branch ' . $source));
 
-         return is_array($result) ? $result : [];
 
-     }
 
-     /**
 
-      * Returns files changes between branch and HEAD
 
-      *
 
-      * @param string $remoteAlias
 
-      * @param string $remoteBranch
 
-      * @param int $changesType
 
-      * @return array
 
-      */
 
-     public function compareChanges($remoteAlias, $remoteBranch, $changesType = self::CHANGE_TYPE_ALL)
 
-     {
 
-         if (!isset($this->remoteList[$remoteAlias])) {
 
-             throw new LogicException('Alias "' . $remoteAlias . '" is not defined');
 
-         }
 
-         $result = $this->call(sprintf('log %s/%s..HEAD  --name-status --oneline', $remoteAlias, $remoteBranch));
 
-         return is_array($result)
 
-             ? $this->filterChangedFiles(
 
-                 $result,
 
-                 $remoteAlias,
 
-                 $remoteBranch,
 
-                 $changesType
 
-             )
 
-             : [];
 
-     }
 
-     /**
 
-      * Makes a diff of file for specified remote/branch and filters only those have real changes
 
-      *
 
-      * @param array $changes
 
-      * @param string $remoteAlias
 
-      * @param string $remoteBranch
 
-      * @param int $changesType
 
-      * @return array
 
-      */
 
-     protected function filterChangedFiles(
 
-         array $changes,
 
-         $remoteAlias,
 
-         $remoteBranch,
 
-         $changesType = self::CHANGE_TYPE_ALL
 
-     ) {
 
-         $countScannedFiles = 0;
 
-         $changedFilesMasks = $this->buildChangedFilesMask($changesType);
 
-         $filteredChanges = [];
 
-         foreach ($changes as $fileName) {
 
-             $countScannedFiles++;
 
-             if (($countScannedFiles % 5000) == 0) {
 
-                 echo $countScannedFiles . " files scanned so far\n";
 
-             }
 
-             $changeTypeMask = $this->detectChangeTypeMask($fileName, $changedFilesMasks);
 
-             if (null === $changeTypeMask) {
 
-                 continue;
 
-             }
 
-             $fileName = trim(substr($fileName, strlen($changeTypeMask)));
 
-             if (in_array($fileName, $filteredChanges)) {
 
-                 continue;
 
-             }
 
-             $fileChanges = $this->getFileChangeDetails($fileName, $remoteAlias, $remoteBranch);
 
-             if (empty($fileChanges)) {
 
-                 continue;
 
-             }
 
-             if (!(isset($this->changedContentFiles[$fileName]))) {
 
-                 $this->setChangedContentFile($fileChanges, $fileName);
 
-             }
 
-             $filteredChanges[] = $fileName;
 
-         }
 
-         echo $countScannedFiles . " files scanned\n";
 
-         return $filteredChanges;
 
-     }
 
-     /**
 
-      * Build mask of git diff report
 
-      *
 
-      * @param int $changesType
 
-      * @return array
 
-      */
 
-     private function buildChangedFilesMask(int $changesType): array
 
-     {
 
-         $changedFilesMasks = [];
 
-         foreach ([
 
-             self::CHANGE_TYPE_ADDED => "A\t",
 
-             self::CHANGE_TYPE_MODIFIED => "M\t",
 
-         ] as $changeType => $changedFilesMask) {
 
-             if ($changeType & $changesType) {
 
-                 $changedFilesMasks[] = $changedFilesMask;
 
-             }
 
-         }
 
-         return $changedFilesMasks;
 
-     }
 
-     /**
 
-      * Find one of the allowed modification mask returned by git diff.
 
-      *
 
-      * Example of change record: "A path/to/added_file"
 
-      *
 
-      * @param string $changeRecord
 
-      * @param array $allowedMasks
 
-      * @return string|null
 
-      */
 
-     private function detectChangeTypeMask(string $changeRecord, array $allowedMasks)
 
-     {
 
-         foreach ($allowedMasks as $mask) {
 
-             if (strpos($changeRecord, $mask) === 0) {
 
-                 return $mask;
 
-             }
 
-         }
 
-         return null;
 
-     }
 
-     /**
 
-      * Read detailed information about changes in a file
 
-      *
 
-      * @param string $fileName
 
-      * @param string $remoteAlias
 
-      * @param string $remoteBranch
 
-      * @return array
 
-      */
 
-     private function getFileChangeDetails(string $fileName, string $remoteAlias, string $remoteBranch): array
 
-     {
 
-         if (!is_file($this->workTree . '/' . $fileName)) {
 
-             return [];
 
-         }
 
-         $result = $this->call(
 
-             sprintf(
 
-                 'diff HEAD %s/%s -- %s',
 
-                 $remoteAlias,
 
-                 $remoteBranch,
 
-                 $fileName
 
-             )
 
-         );
 
-         return $result;
 
-     }
 
-     /**
 
-      * Set changed content for file.
 
-      *
 
-      * @param array $content
 
-      * @param string $fileName
 
-      * @return void
 
-      */
 
-     private function setChangedContentFile(array $content, $fileName)
 
-     {
 
-         $changedContent = '';
 
-         $extension = Magento\TestFramework\Utility\ChangedFiles::getFileExtension($fileName);
 
-         foreach ($content as $item) {
 
-             if (strpos($item, '---') !== 0 && strpos($item, '-') === 0 && $line = ltrim($item, '-')) {
 
-                 $changedContent .= $line . "\n";
 
-             }
 
-         }
 
-         if ($changedContent !== '') {
 
-             $this->changedContentFiles[$extension][$fileName] = $changedContent;
 
-         }
 
-     }
 
-     /**
 
-      * Get changed content files collection.
 
-      *
 
-      * @return array
 
-      */
 
-     public function getChangedContentFiles()
 
-     {
 
-         return $this->changedContentFiles;
 
-     }
 
-     /**
 
-      * Makes call ro git cli
 
-      *
 
-      * @param string $command
 
-      * @return mixed
 
-      */
 
-     private function call($command)
 
-     {
 
-         $gitCmd = sprintf(
 
-             'git --git-dir %s --work-tree %s',
 
-             escapeshellarg("{$this->workTree}/.git"),
 
-             escapeshellarg($this->workTree)
 
-         );
 
-         $tmp = sprintf('%s %s', $gitCmd, $command);
 
-         exec($tmp, $output);
 
-         return $output;
 
-     }
 
- }
 
 
  |