Select Git revision
RepositoryModel.php

Roger W Feese authored
RepositoryModel.php 13.11 KiB
<?php
class Bulletin_RepositoryModel
{
static protected $_instance;
static public function getInstance()
{
if (!self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
protected $_repoPath;
protected $_lockFile;
// Array of options loaded from application.ini
protected $_options;
public function getFileList()
{
foreach (glob($this->_repoPath . '/data/20*') as $yearDirectory) {
$relativePath = substr($yearDirectory, strlen($this->_repoPath));
$year = basename($relativePath);
foreach (glob($yearDirectory . '/majors/*') as $majorFile) {
$files[$year]['majors'][basename($majorFile)] = basename($majorFile, '.xhtml');
}
foreach (glob($yearDirectory . '/colleges/*') as $collegeFile) {
$files[$year]['colleges'][basename($collegeFile)] = basename($collegeFile, '.xhtml');
}
foreach (glob($yearDirectory . '/other/*') as $otherFile) {
$files[$year]['other'][basename($otherFile)] = basename($otherFile, '.xhtml');
}
}
return $files;
}
public function getFileContents($path, $branch = 'master')
{
$this->_checkout($branch);
$contents = file_get_contents($this->_repoPath . '/' . $path);
$this->_checkout('master');
return $contents;
}
public function saveFileContents($path, $branch, Auth_UserModel $user, $contents, $message = 'Saving Changes')
{
if ($branch == 'master') {
throw new Exception('Saving to the master branch is not allowed.');
}
$this->_checkout($branch);
file_put_contents($this->_repoPath . '/' . $path, $contents);
$author = $user->getFirstName() . ' ' . $user->getLastName() . ' <' . $user->getEmail() . '>';
$this->_git_exec('commit --allow-empty -a -m ' . escapeshellarg($message) . ' --author=' . escapeshellarg($author));
$this->_checkout('master');
$this->push();
}
public function getFileNotes($path, $branch = 'master')
{
$object = $note = '';
$filepath = $this->_repoPath . '/' . $path;
$this->_checkout($branch);
if(file_exists($filepath)){
$this->_git_exec('hash-object ' . escapeshellarg($filepath), $object);
$this->_git_exec('notes --ref=creq show ' . escapeshellarg($object), $note);
}
$this->_checkout('master');
return $note;
}
public function saveFileNotes($path, $branch, $contents)
{
$tmpPath = tempnam(sys_get_temp_dir(), 'creq_');
file_put_contents($tmpPath, $contents);
if ($branch == 'master') {
throw new Exception('Saving to the master branch is not allowed');
}
$this->_checkout($branch);
$this->_git_exec('hash-object ' . escapeshellarg($this->_repoPath . '/' . $path), $object);
$this->_git_exec(
'notes --ref=creq ' .
' add ' . escapeshellarg($object) .
' -f -F ' . escapeshellarg($tmpPath)
);
$this->_checkout('master');
unlink($tmpPath);
}
public function moveFile($current_file_path, $new_file_path, $branch, Auth_UserModel $user, $message = 'Renaming File')
{
if ($branch == 'master') {
throw new Exception('Saving to the master branch is not allowed.');
}
$this->_checkout($branch);
$this->_git_exec("mv " . escapeshellarg($current_file_path) . " " . escapeshellarg($new_file_path));
$author = $user->getFirstName() . ' ' . $user->getLastName() . ' <' . $user->getEmail() . '>';
$this->_git_exec('commit --allow-empty -a -m ' . escapeshellarg($message) . ' --author=' . escapeshellarg($author));
$this->_checkout('master');
}
public function deleteFile($file_path, $branch, Auth_UserModel $user, $message = 'Removing File')
{
if ($branch == 'master') {
throw new Exception('Saving to the master branch is not allowed.');
}
$this->_checkout($branch);
$this->_git_exec("rm " . escapeshellarg($file_path));
$author = $user->getFirstName() . ' ' . $user->getLastName() . ' <' . $user->getEmail() . '>';
$this->_git_exec('commit --allow-empty -a -m ' . escapeshellarg($message) . ' --author=' . escapeshellarg($author));
$this->_checkout('master');
}
public function mergeBranchToMaster($branch)
{
if ($branch == 'master') {
throw new Exception('Supplied branch name cannot be master.');
}
$this->_checkout('master');
$this->_git_exec("merge " . $branch);
$this->push();
}
protected function __construct()
{
$this->_repoPath = APPLICATION_PATH . '/bulletin.git';
$front = Zend_Controller_Front::getInstance();
$options = $front->getParam('bootstrap')->getOptions();
$this->_options = (isset($options['bulletin']) ? $options['bulletin'] : array());
if (!file_exists($this->_repoPath) || !is_dir($this->_repoPath)) {
throw new Exception('The repository directory does not exist.');
}
if (!is_writable($this->_repoPath)) {
throw new Exception('The repository is not writable by the web user.');
}
if (!file_exists($this->_repoPath . '/.git')) {
$originParts = parse_url($this->_options['github']['origin']);
$origin = $originParts['scheme'] . '://'
. urlencode($this->_options['github']['username']) . ':'
. urlencode($this->_options['github']['password']) . '@'
. $originParts['host'] . $originParts['path'];
$this->_git_exec('clone ' . escapeshellarg($origin) . ' .');
$this->_git_exec('remote add upstream ' . escapeshellarg($this->_options['github']['upstream']));
$this->_git_exec('config --add remote.origin.fetch "refs/notes/*:refs/notes/*"');
$this->_git_exec('config --add remote.origin.push refs/notes/creq');
$this->_git_exec('fetch');
file_put_contents($this->_repoPath . '/.git/info/exclude', '*.xhtml.diff' . PHP_EOL);
}
$this->lockFile = fopen($this->_repoPath . '/.lock', 'a');
flock($this->lockFile, LOCK_EX);
$this->_checkout('master');
}
protected function __destruct()
{
flock($this->lockFile, LOCK_UN);
}
protected function _git_exec($command, &$output = array()) {
$cwd = getcwd();
chdir($this->_repoPath);
$return = NULL;
$output = array();
exec('git ' . $command, $output, $return);
$output = implode(PHP_EOL, $output);
if ($return != 0) {
throw new Exception('An error occurred while running a git command.', $return);
}
chdir($cwd);
return $return;
}
protected function _checkout($branch)
{
try {
$this->_git_exec('checkout ' . escapeshellarg($branch));
} catch (Exception $e) {
$this->_git_exec('checkout -b ' . escapeshellarg($branch) . ' master');
}
}
public function pull()
{
$this->_checkout('master');
$this->_git_exec('fetch');
$this->_git_exec('fetch upstream');
$this->_git_exec('merge --ff-only upstream/master');
}
public function push()
{
$this->_git_exec('push origin --all');
}
public function pullRequest($branch, $title, $body = '', &$pullRequestNumber = -1)
{
$remotePath = explode('/', parse_url($this->_options['github']['upstream'], PHP_URL_PATH));
$remoteHost = parse_url($this->_options['github']['upstream'], PHP_URL_HOST);
$remoteUser = $remotePath[1];
$remoteRepo = basename($remotePath[2], '.git');
$originPath = explode('/', parse_url($this->_options['github']['origin'], PHP_URL_PATH));
$originUser = $originPath[1];
$originRepo = basename($originPath[2], '.git');
$uri = 'https://api.' . $remoteHost . '/repos/' . $remoteUser . '/' . $remoteRepo . '/pulls';
$data = array(
'title' => $title,
'head' => $originUser . ':' . $branch,
//'base' => $remoteUser . ':' . 'master', // github changed the way this works (04/2014)
'base' => 'master',
);
if ($body) {
$data['body'] = $body;
}
$data = json_encode($data);
$http = new Zend_Http_Client();
$http->setUri($uri);
$http->setAuth($this->_options['github']['username'], $this->_options['github']['password']);
$http->setMethod(Zend_Http_Client::POST);
$http->setRawData($data);
$http->request();
if ($http->getLastResponse()->getStatus() == 201) {
$response = json_decode($http->getLastResponse()->getBody(), true);
$pullRequestNumber = $response['number'];
return $http->getLastResponse()->getHeader('Location');
}
if ($branch != 'master') {
print_r($http->getLastResponse()); exit;
throw new Exception('An error occurred while creating the pull request.');
} else {
// let's send an email off to admins, just so this is noted, but should be ok to ignore
// this error should happen only when processing mulitiple major name changes at same time, adding to existing pull request
NotifyEmailModel::NotifyAdmins("An error occured when trying to create a bulletin pull request. This error is expected when a pull request for the given branch already exists, it will automatically add files/changes to this existing pull request. This error should be safe to ignore, but we are notifying you anyway just in case.\n\n".print_r($http->getLastResponse(),true));
// ignore error on master, pull request already exists, it will automatically add to existing pull request
return $http->getLastResponse()->getHeader('Location');
}
}
public function getFilePathForSection(Bulletin_SectionModel $section)
{
return $this->getFilePathForCollegeMajorYear(
$section->getCollegeLong(),
$section->getMajor(),
$section->getYear()
);
}
public function getParentCommitForSection(Bulletin_SectionModel $section)
{
$command = 'merge-base '
. escapeshellarg('request-' . $section->getRequest()) . ' '
. 'master '
;
if ($this->_git_exec($command, $data) == 0) {
return $data;
}
throw new Exception('No parent commit could be found!');
}
public function getFilePathForCollegeMajorYear($college, $major, $year)
{
$year = intval($year);
$xhtmlFiles = array();
foreach ($this->getFileList() as $yearDir => $sections) {
if ($year != $yearDir) {
continue;
}
foreach ($sections as $section => $files) {
foreach ($files as $file => $name) {
if (substr($file, -6) != '.xhtml') {
continue;
}
$xhtmlFiles[$section][$name] = $yearDir . DIRECTORY_SEPARATOR . $section . DIRECTORY_SEPARATOR . $file;
}
}
}
require_once 'UNL/Autoload.php';
spl_autoload_register('Unl_Autoload');
require_once $this->_repoPath . '/data/' . $year . '/edition.config.inc.php';
$nameMap = array_flip(UNL_UndergraduateBulletin_Major_Description::getEpubToTitleMap());
if ($major) {
if (array_key_exists($major, $nameMap)) {
$major = $nameMap[$major];
}
if ($college == 'Other University Programs') {
$path = $xhtmlFiles['other'][$major];
} else {
$path = $xhtmlFiles['majors'][$major];
}
} else {
$path = $xhtmlFiles['colleges'][$college . ' Main Page'];
}
return $path;
}
/**
* Checks if a pull request has been merged
* @param $requestNumber Int Pull request number to check
* @return bool TRUE if merged, FALSE otherwise
*/
public function pullRequestHasBeenMerged($requestNumber)
{
$remotePath = explode('/', parse_url($this->_options['github']['upstream'], PHP_URL_PATH));
$remoteHost = parse_url($this->_options['github']['upstream'], PHP_URL_HOST);
$remoteUser = $remotePath[1];
$remoteRepo = basename($remotePath[2], '.git');
$requestNumber = intval($requestNumber);
$uri = 'https://api.' . $remoteHost . '/repos/' . $remoteUser . '/' . $remoteRepo . '/pulls/' . $requestNumber;
$http = new Zend_Http_Client();
$http->setUri($uri);
$http->setMethod(Zend_Http_Client::GET);
$http->request();
if ($http->getLastResponse()->getStatus() == 200) {
$response = json_decode($http->getLastResponse()->getBody(), true);
$status = $response['state'];
if ($status === "closed") {
return true;
}
}
return false;
}
}