Source of file GitApi.php
Size: 13,764 Bytes - Last Modified: 2021-12-23T10:42:13+00:00
/var/www/docs.ssmods.com/process/src/src/Api/GitApi.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 | <?php namespace Sunnysideup\ModuleChecks\Api; use Exception; use GitWrapper\Exception\GitException; use GitWrapper\GitWrapper; use SilverStripe\Control\Director; use SilverStripe\Core\Config\Config; use Sunnysideup\ModuleChecks\BaseObject; use Sunnysideup\ModuleChecks\Model\Check; use Sunnysideup\ModuleChecks\Model\Module; use Sunnysideup\ModuleChecks\Model\ModuleCheck; use Sunnysideup\ModuleChecks\Tasks\UpdateModules; use Sunnysideup\Flush\FlushNow; class GitApi extends BaseObject { use FlushNow; protected $gitApiWrapper = null; protected $commsWrapper = null; private $repo = null; private $latestTag = null; public function __construct(Module $repo, ?bool $forceNew = false) { $this->repo = $repo; //check if one has been created already... if (! $this->gitApiWrapper) { //basic check if ($this->repo->ModuleName === '') { user_error('ModuleName element must be set before using git repository commands'); } //create comms $this->commsWrapper = new GitWrapper(); // Stream output of subsequent Git commands in real time to STDOUT and STDERR. if (Director::is_cli()) { $this->commsWrapper->streamOutput(); } $pathToPrivateKey = BaseObject::absolute_path_to_private_key(); // Optionally specify a private key other than one of the defaults. $this->commsWrapper->setPrivateKey($pathToPrivateKey); $this->commsWrapper->setEnvVar('HOME', dirname($pathToPrivateKey)); //if directory exists, return existing repo, //otherwise clone it.... if ($this->IsDirGitRepo($this->repo->Directory())) { if ($forceNew) { $this->repo->RemoveClone(); //do again! $this->__construct($this->repo, false); } $this->gitApiWrapper = $this->commsWrapper->workingCopy($this->repo->Directory()); } else { FlushNow::do_flush('cloning ... ' . $this->repo->FullGitURL(), 'created'); $this->gitApiWrapper = null; $cloneAttempts = 0; while (! $this->gitApiWrapper) { $cloneAttempts ++; if ($cloneAttempts === 4) { $message = 'Failed to clone module ' . $this->repo->LongModuleName() . ' after ' . ($cloneAttempts - 1) . ' attempts.'; //UpdateModules::$unsolvedItems[$this->ModuleName()] = 'Failed to clone modules'; ModuleCheck::log_error($message); USER_ERROR($message); return; } try { $this->commsWrapper->setTimeout(240); //Big modules need a longer timeout $this->gitApiWrapper = $this->commsWrapper->cloneRepository( $this->repo->FullGitURL(), $this->repo->Directory() ); $this->pull(); $this->commsWrapper->setTimeout(60); } catch (Exception $e) { if (strpos($e->getMessage(), 'already exists and is not an empty directory') !== false) { user_error($e->getMessage(), E_USER_ERROR); } FlushNow::do_flush('Failed to clone repository: ' . $e->getMessage()); FlushNow::do_flush('Waiting 8 seconds to try again ...:'); $this->repo->removeClone(); sleep(8); } } } $this->gitApiWrapper->config('push.default', 'simple'); $this->gitApiWrapper->config('user.name', Config::inst()->get(BaseObject::class, 'github_user_name')); $this->gitApiWrapper->config('user.email', Config::inst()->get(BaseObject::class, 'github_user_email')); $this->commsWrapper->git('config -l'); } } /** * pulls a git repo * * @return bool */ public function pull() { if ($this->gitApiWrapper) { try { $this->gitApiWrapper->pull(); } catch (GitException $e) { print_r($e); throw $e; return false; } return true; //FlushNow::do_flush($git->getOutput()); } return false; } /** * commits a git repo * * @param string $message * * @return bool */ public function commit($message = 'PATCH: module clean-up'): bool { if ($this->gitApiWrapper) { try { $this->gitApiWrapper->commit($message); } catch (Exception $e) { $errStr = $e->getMessage(); if (stripos($errStr, 'nothing to commit') === false) { print_r($e); throw $e; } FlushNow::do_flush('No changes to commit'); return false; } //FlushNow::do_flush($git->getOutput()); return true; } return false; } /** * adds all files to a git repo * @return bool */ public function add(): bool { FlushNow::do_flush('Adding new files to ' . $this->repo->ModuleName . ' ... ', 'created'); if ($this->gitApiWrapper) { try { $this->gitApiWrapper->add('.'); } catch (GitException $e) { $errStr = $e->getMessage(); if (stripos($errStr, 'did not match any files') === false) { print_r($e); throw $e; } FlushNow::do_flush('No new files to add to $module. '); return false; } //FlushNow::do_flush($git->getOutput()); return true; } return false; } /** * adds all files to a git repo * * @return bool */ public function push(): bool { FlushNow::do_flush('Pushing files to ' . $this->repo->ModuleName . ' ... ', 'created'); if ($this->gitApiWrapper) { $pushed = false; $pushAttempts = 0; while (! $pushed) { $pushAttempts ++; try { $this->gitApiWrapper->push(); $pushed = true; } catch (Exception $e) { if ($pushAttempts === 3) { $this->gitApiWrapper->getOutput(); print_r($e); throw $e; } FlushNow::do_flush('Failed to push repository: ' . $e->getMessage() . ''); FlushNow::do_flush('Waiting 8 seconds to try again ...:'); sleep(8); return false; } } return true; } return false; } public function getLatestCommitTime() { // equivalent to git log -1 --format=%cd . if ($this->gitApiWrapper) { $options = [ 'format' => '%cd', '1' => true, ]; try { $result = $this->gitApiWrapper->log($options); } catch (Exception $e) { $errStr = $e->getMessage(); if (stripos($errStr, 'does not have any commits') === false) { print_r($e); throw $e; } return false; } if ($result) { return strtotime($result); } return false; } return false; } public function getLatestTag() { if ($this->latestTag === null) { if ($this->gitApiWrapper) { $options = [ 'tags' => true, 'simplify-by-decoration' => true, 'pretty' => 'format:%ai %d', ]; $cwd = getcwd(); chdir($this->repo->Directory()); try { $result = $this->gitApiWrapper->log($options); } catch (Exception $e) { $errStr = $e->getMessage(); if (stripos($errStr, 'does not have any commits') === false) { print_r($e); throw $e; } FlushNow::do_flush('Unable to get tag because there are no commits to the repository'); return false; } chdir($cwd); $resultLines = explode("\n", $result->getOutput()); // 2016-10-14 12:29:08 +1300 (HEAD -> master, tag: 2.3.0, tag: 2.2.0, tag: 2.1.0, origin/master, origin/HEAD)\ // or // 2016-08-29 17:18:22 +1200 (tag: 2.0.0) //print_r($resultLines); if (count($resultLines) === 0) { return false; } $latestTimeStamp = 0; $latestTag = false; foreach ($resultLines as $line) { $isTagInLine = (strpos($line, 'tag') !== false); if ($isTagInLine) { $tagStr = trim(substr($line, 25)); $dateStr = trim(substr($line, 0, 26)); //extract tag numbers from $tagStr $matches = []; // print_r ("original!!! " . $tagStr); $result = preg_match_all('/tag: \d{1,3}.\d{1,3}.\d{1,3}/', $tagStr, $matches); if ($result === false) { continue; } elseif ($result > 1) { $tagStr = $matches[0][0]; } //print_r ($matches); $tagStr = str_replace('(', '', $tagStr); $tagStr = str_replace(')', '', $tagStr); $timeStamp = strtotime($dateStr); if ($latestTimeStamp < $timeStamp) { $latestTimeStamp = $timeStamp; $latestTag = $tagStr; } } } if ($latestTag) { $latestTag = str_replace('tag:', '', $latestTag); $tagParts = explode('.', $latestTag); if (count($tagParts) !== 3) { return false; } $this->latestTag = [ 'tagstring' => $latestTag, 'tagparts' => $tagParts, 'timestamp' => $latestTimeStamp, ]; } else { $this->latestTag = false; } } } return $this->latestTag; } /** * git command used: //git log 0.0.1..HEAD --oneline * return @string (major | minor | patch) */ public function getChangeTypeSinceLastTag() { $latestTag = trim($this->getLatestTag()['tagstring']); if ($this->gitApiWrapper) { //var_dump ($git); //die(); $options = [ 'oneline' => true, ]; $cwd = getcwd(); chdir($this->repo->Directory()); try { $result = $this->gitApiWrapper->log($latestTag . '..HEAD', $options); // print_r($latestTag); // print_r($result); if (! is_array($result)) { $result = explode("\n", $result); } // print_r ($result); } catch (Exception $e) { $errStr = $e->getMessage(); FlushNow::do_flush('Unable to get next tag type (getChangeTypeSinceLastTag): ' . $errStr); return false; } chdir($cwd); $returnLine = 'PATCH'; foreach ($result as $line) { if (stripos($line, 'MAJOR:') !== false) { $returnLine = 'MAJOR'; break; } if (stripos($line, 'MINOR:') !== false) { $returnLine = 'MINOR'; } } return $returnLine; } } public function createTag($tagCommandOptions): bool { $this->gitApiWrapper->tag($tagCommandOptions); return $this->gitApiWrapper->push(['tags' => true]); } public function findNextTag(array $tag, string $changeType): string { switch ($changeType) { case 'MAJOR': $tag['tagparts'][0] = intval($tag['tagparts'][0]) + 1; $tag['tagparts'][1] = 0; $tag['tagparts'][2] = 0; break; case 'MINOR': $tag['tagparts'][1] = intval($tag['tagparts'][1]) + 1; $tag['tagparts'][2] = 0; break; default: case 'PATCH': $tag['tagparts'][2] = intval($tag['tagparts'][2]) + 1; break; } return trim(implode('.', $tag['tagparts'])); } protected function IsDirGitRepo($directory): bool { return file_exists($directory . '/.git'); } } |