From 5122e91b49071915ff78823d55ff9f84f36aba88 Mon Sep 17 00:00:00 2001 From: Klaus Silveira Date: Sat, 1 Jun 2013 12:51:25 -0300 Subject: [PATCH] Major refactoring of the codebase, specially in the directory scanning parts. I have removed the directory management features from Gitter and ported them to GitList, since they are application-specific logic. I was unhappy with the way directories and multiple directories were handled, so i altered almost everything. The tests are OK, but the coverage is not good. This means that i'll need som help from all GitList users that are actively testing the development version with multiple directories. I have removed the directory caching feature for now, it will be re-implemented soon. --- .gitignore | 1 + boot.php | 8 -- composer.json | 2 +- composer.lock | 31 +++-- config.ini-example | 19 +-- index.php | 7 +- src/GitList/Application.php | 22 ++-- src/GitList/Config.php | 16 +-- src/GitList/Controller/BlobController.php | 4 +- src/GitList/Controller/CommitController.php | 8 +- src/GitList/Controller/MainController.php | 8 +- src/GitList/Controller/TreeController.php | 6 +- src/GitList/Git/Client.php | 136 ++++++++++++++++---- src/GitList/Provider/GitServiceProvider.php | 2 - src/GitList/Util/Routing.php | 18 +-- tests/InterfaceTest.php | 31 ++--- views/index.twig | 4 + 17 files changed, 193 insertions(+), 130 deletions(-) diff --git a/.gitignore b/.gitignore index e9b0612..4321e1b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ node_modules config.ini cache.properties composer.phar +phpunit.xml diff --git a/boot.php b/boot.php index dd33f5b..967301c 100644 --- a/boot.php +++ b/boot.php @@ -1,13 +1,5 @@ =2.0,<3.0" }, "suggest": { - "symfony/css-selector": "2.2.*" + "symfony/css-selector": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.4-dev" } }, "autoload": { @@ -1261,7 +1265,7 @@ ], "description": "Symfony DomCrawler Component", "homepage": "http://symfony.com", - "time": "2013-04-01 08:07:30" + "time": "2013-05-27 14:49:42" }, { "name": "symfony/finder", @@ -1487,8 +1491,7 @@ ], "minimum-stability": "dev", "stability-flags": { - "silex/silex": 20, - "klaussilveira/gitter": 20 + "silex/silex": 20 }, "platform": [ diff --git a/config.ini-example b/config.ini-example index c5f8fcb..81e0688 100644 --- a/config.ini-example +++ b/config.ini-example @@ -1,26 +1,19 @@ [git] client = '/usr/bin/git' ; Your git executable path -repositories = '/var/www/projects/' ; Path to your repositories default_branch = 'master' ; Default branch when HEAD is detached +repositories[] = '/home/git/repositories/' ; Path to your repositories + ; If you wish to add more repositories, just add a new line - -;Windows Users +; WINDOWS USERS ;client = '"C:\Program Files (x86)\Git\bin\git.exe"' ; Your git executable path -;repositories = 'C:\Path\to\Repos\' ; Path to your repositories - - -; If you want to specify multiple paths, use the array syntax instead: -; -;repositories[] = '/var/www/projects/' -;repositories[] = '/var/www/subdir/more_projects/' -;repositories[] = '/home/user/even_more_projects/' - +;repositories[] = 'C:\Path\to\Repos\' ; Path to your repositories ; You can hide repositories from GitList, just copy this for each repository you want to hide -; hidden[] = '/var/www/projects/BetaTest' +; hidden[] = '/home/git/repositories/BetaTest' [app] debug = false +cache = true ; If you need to specify custom filetypes for certain extensions, do this here [filetypes] diff --git a/index.php b/index.php index 5d59466..5394796 100644 --- a/index.php +++ b/index.php @@ -14,12 +14,13 @@ if (php_sapi_name() == 'cli-server' && file_exists(substr($_SERVER['REQUEST_URI' return false; } +if (!is_writable(__DIR__ . DIRECTORY_SEPARATOR . 'cache')) { + die(sprintf('The "%s" folder must be writable for GitList to run.', __DIR__ . DIRECTORY_SEPARATOR . 'cache')); +} + require 'vendor/autoload.php'; -// Load configuration $config = GitList\Config::fromFile('config.ini'); - $app = require 'boot.php'; - $app->run(); diff --git a/src/GitList/Application.php b/src/GitList/Application.php index acebc36..30c061c 100644 --- a/src/GitList/Application.php +++ b/src/GitList/Application.php @@ -36,23 +36,20 @@ class Application extends SilexApplication // Register services $this->register(new TwigServiceProvider(), array( 'twig.path' => $this->getViewPath(), - 'twig.options' => array('cache' => $this->getCachePath() . 'views'), + 'twig.options' => $config->get('app', 'cache') ? + array('cache' => $this->getCachePath() . 'views') : array(), )); $repositories = $config->get('git', 'repositories'); - $repositoryCache = $config->get('app', 'cached_repos'); - if (false === $repositoryCache || empty($repositoryCache)) { - $repositoryCache = $this->getCachePath() . 'repos.json'; - } $this->register(new GitServiceProvider(), array( - 'git.client' => $config->get('git', 'client'), - 'git.repos' => $repositories, - 'cache.repos' => $repositoryCache, - 'ini.file' => "config.ini", - 'git.hidden' => $config->get('git', 'hidden') ? - $config->get('git', 'hidden') : array(), - 'git.default_branch' => $config->get('git', 'default_branch') ? $config->get('git', 'default_branch') : 'master', + 'git.client' => $config->get('git', 'client'), + 'git.repos' => $repositories, + 'ini.file' => "config.ini", + 'git.hidden' => $config->get('git', 'hidden') ? + $config->get('git', 'hidden') : array(), + 'git.default_branch' => $config->get('git', 'default_branch') ? + $config->get('git', 'default_branch') : 'master', )); $this->register(new ViewUtilServiceProvider()); @@ -100,4 +97,3 @@ class Application extends SilexApplication return $this->path . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR; } } - diff --git a/src/GitList/Config.php b/src/GitList/Config.php index e011870..fab9338 100644 --- a/src/GitList/Config.php +++ b/src/GitList/Config.php @@ -13,19 +13,15 @@ class Config } $data = parse_ini_file($file, true); - - # Ensure that repositories item is an array - if (!is_array($data['git']['repositories'])) { - $data['git']['repositories'] = array($data['git']['repositories']); - } + $config = new static($data); + $config->validateOptions(); - return new static($data); + return $config; } - public function __construct($data) + public function __construct($data = array()) { $this->data = $data; - $this->validateOptions(); } public function get($section, $option) @@ -59,10 +55,6 @@ class Config { $repositories = $this->get('git', 'repositories'); - if (!is_array($repositories)) { - return; - } - $atLeastOneOk = false; $atLeastOneWrong = false; diff --git a/src/GitList/Controller/BlobController.php b/src/GitList/Controller/BlobController.php index a228999..fc5ec90 100644 --- a/src/GitList/Controller/BlobController.php +++ b/src/GitList/Controller/BlobController.php @@ -13,7 +13,7 @@ class BlobController implements ControllerProviderInterface $route = $app['controllers_factory']; $route->get('{repo}/blob/{commitishPath}', function ($repo, $commitishPath) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); list($branch, $file) = $app['util.routing'] ->parseCommitishPathParam($commitishPath, $repo); @@ -46,7 +46,7 @@ class BlobController implements ControllerProviderInterface ->bind('blob'); $route->get('{repo}/raw/{commitishPath}', function ($repo, $commitishPath) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); list($branch, $file) = $app['util.routing'] ->parseCommitishPathParam($commitishPath, $repo); diff --git a/src/GitList/Controller/CommitController.php b/src/GitList/Controller/CommitController.php index e017160..86dc409 100644 --- a/src/GitList/Controller/CommitController.php +++ b/src/GitList/Controller/CommitController.php @@ -13,7 +13,7 @@ class CommitController implements ControllerProviderInterface $route = $app['controllers_factory']; $route->get('{repo}/commits/{commitishPath}', function ($repo, $commitishPath) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if ($commitishPath === null) { $commitishPath = $repository->getHead(); @@ -53,7 +53,7 @@ class CommitController implements ControllerProviderInterface ->bind('commits'); $route->post('{repo}/commits/{branch}/search', function (Request $request, $repo, $branch = '') use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $query = $request->get('query'); $commits = $repository->searchCommitLog($request->get('query')); @@ -79,7 +79,7 @@ class CommitController implements ControllerProviderInterface ->bind('searchcommits'); $route->get('{repo}/commit/{commit}', function ($repo, $commit) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $commit = $repository->getCommit($commit); $branch = $repository->getHead(); @@ -93,7 +93,7 @@ class CommitController implements ControllerProviderInterface ->bind('commit'); $route->get('{repo}/blame/{commitishPath}', function ($repo, $commitishPath) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); list($branch, $file) = $app['util.routing'] ->parseCommitishPathParam($commitishPath, $repo); diff --git a/src/GitList/Controller/MainController.php b/src/GitList/Controller/MainController.php index 25a8b4b..3c8919c 100644 --- a/src/GitList/Controller/MainController.php +++ b/src/GitList/Controller/MainController.php @@ -23,15 +23,12 @@ class MainController implements ControllerProviderInterface $route->get('/refresh', function(Request $request) use ($app ) { - $app['git']->deleteCached(); - # Go back to calling page return $app->redirect($request->headers->get('Referer')); })->bind('refresh'); - $route->get('{repo}/stats/{branch}', function($repo, $branch) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if ($branch === null) { $branch = $repository->getHead(); @@ -54,7 +51,7 @@ class MainController implements ControllerProviderInterface ->bind('stats'); $route->get('{repo}/{branch}/rss/', function($repo, $branch) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if ($branch === null) { $branch = $repository->getHead(); @@ -77,4 +74,3 @@ class MainController implements ControllerProviderInterface return $route; } } - diff --git a/src/GitList/Controller/TreeController.php b/src/GitList/Controller/TreeController.php index 3a24d0d..384b537 100644 --- a/src/GitList/Controller/TreeController.php +++ b/src/GitList/Controller/TreeController.php @@ -14,7 +14,7 @@ class TreeController implements ControllerProviderInterface $route = $app['controllers_factory']; $route->get('{repo}/tree/{commitishPath}/', $treeController = function ($repo, $commitishPath = '') use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if (!$commitishPath) { $commitishPath = $repository->getHead(); } @@ -48,7 +48,7 @@ class TreeController implements ControllerProviderInterface ->bind('tree'); $route->post('{repo}/tree/{branch}/search', function (Request $request, $repo, $branch = '', $tree = '') use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); if (!$branch) { $branch = $repository->getHead(); } @@ -74,7 +74,7 @@ class TreeController implements ControllerProviderInterface # Intentionally before next statement, because order appears # to be important, and the other statement got precedence previously. $route->get('{repo}/{format}ball/{branch}', function($repo, $format, $branch) use ($app) { - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $tree = $repository->getBranchTree($branch); diff --git a/src/GitList/Git/Client.php b/src/GitList/Git/Client.php index 4129a7b..36b5cf3 100644 --- a/src/GitList/Git/Client.php +++ b/src/GitList/Git/Client.php @@ -6,17 +6,95 @@ use Gitter\Client as BaseClient; class Client extends BaseClient { - protected $default_branch; + protected $defaultBranch; + protected $hidden; public function __construct($options = null) { - parent::__construct($options); + parent::__construct($options['path']); + $this->setDefaultBranch($options['default_branch']); + $this->setHidden($options['hidden']); + } - if (!isset($options['default_branch'])) { - $options['default_branch'] = 'master'; + public function getRepositoryFromName($paths, $repo) + { + $repositories = $this->getRepositories($paths); + $path = $repositories[$repo]['path']; + + return $this->getRepository($path); + } + + /** + * Searches for valid repositories on the specified path + * + * @param array $paths Array of paths where repositories will be searched + * @return array Found repositories, containing their name, path and description + */ + public function getRepositories($paths) + { + $allRepositories = array(); + + foreach ($paths as $path) { + $repositories = $this->recurseDirectory($path); + + if (empty($repositories)) { + throw new \RuntimeException('There are no GIT repositories in ' . $path); + } + + $allRepositories = array_merge($allRepositories, $repositories); } - $this->setDefaultBranch($options['default_branch']); + $allRepositories = array_unique($allRepositories, SORT_REGULAR); + asort($allRepositories); + + return $allRepositories; + } + + private function recurseDirectory($path) + { + $dir = new \DirectoryIterator($path); + + $repositories = array(); + + foreach ($dir as $file) { + if ($file->isDot()) { + continue; + } + + if (strrpos($file->getFilename(), '.') === 0) { + continue; + } + + if ($file->isDir()) { + $isBare = file_exists($file->getPathname() . '/HEAD'); + $isRepository = file_exists($file->getPathname() . '/.git/HEAD'); + + if ($isRepository || $isBare) { + if (in_array($file->getPathname(), $this->getHidden())) { + continue; + } + + if ($isBare) { + $description = $file->getPathname() . '/description'; + } else { + $description = $file->getPathname() . '/.git/description'; + } + + if (file_exists($description)) { + $description = file_get_contents($description); + } else { + $description = null; + } + + $repositories[$file->getFilename()] = array('name' => $file->getFilename(), 'path' => $file->getPathname(), 'description' => $description); + continue; + } else { + $repositories = array_merge($repositories, $this->recurseDirectory($file->getPathname())); + } + } + } + + return $repositories; } /** @@ -26,7 +104,7 @@ class Client extends BaseClient */ protected function setDefaultBranch($branch) { - $this->default_branch = $branch; + $this->defaultBranch = $branch; return $this; } @@ -36,14 +114,35 @@ class Client extends BaseClient */ public function getDefaultBranch() { - return $this->default_branch; + return $this->defaultBranch; } /** - * Creates a new repository on the specified path + * Get hidden repository list * - * @param string $path Path where the new repository will be created - * @return Repository Instance of Repository + * @return array List of repositories to hide + */ + protected function getHidden() + { + return $this->hidden; + } + + /** + * Set the hidden repository list + * + * @param array $hidden List of repositories to hide + */ + protected function setHidden($hidden) + { + $this->hidden = $hidden; + + return $this; + } + + /** + * Overloads the parent::createRepository method for the correct Repository class instance + * + * {@inheritdoc} */ public function createRepository($path, $bare = null) { @@ -57,25 +156,16 @@ class Client extends BaseClient } /** - * Opens a specified repository - * - * @param array $repos Array of items describing configured repositories - * @param string $repo Name of repository we are currently handling - * @return Repository Instance of Repository + * Overloads the parent::getRepository method for the correct Repository class instance + * + * {@inheritdoc} */ - public function getRepository($repos, $repo) + public function getRepository($path) { - $repotmp = $this->getRepositoryCached($repos, $repo); - $path = $repotmp->getPath(); - if (!file_exists($path) || !file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) { throw new \RuntimeException('There is no GIT repository at ' . $path); } - if (in_array($path, $this->getHidden())) { - throw new \RuntimeException('You don\'t have access to this repository'); - } - return new Repository($path, $this); } } diff --git a/src/GitList/Provider/GitServiceProvider.php b/src/GitList/Provider/GitServiceProvider.php index c4e0808..44f2cfa 100644 --- a/src/GitList/Provider/GitServiceProvider.php +++ b/src/GitList/Provider/GitServiceProvider.php @@ -21,7 +21,6 @@ class GitServiceProvider implements ServiceProviderInterface $options['path'] = $app['git.client']; $options['hidden'] = $app['git.hidden']; $options['ini.file'] = $app['ini.file']; - $options['cache.repos'] = $app['cache.repos']; $options['default_branch'] = $app['git.default_branch']; return new Client($options); @@ -32,4 +31,3 @@ class GitServiceProvider implements ServiceProviderInterface { } } - diff --git a/src/GitList/Util/Routing.php b/src/GitList/Util/Routing.php index e12d283..c687c3c 100644 --- a/src/GitList/Util/Routing.php +++ b/src/GitList/Util/Routing.php @@ -24,7 +24,7 @@ class Routing public function parseCommitishPathParam($commitishPath, $repo) { $app = $this->app; - $repository = $app['git']->getRepository($app['git.repos'], $repo); + $repository = $app['git']->getRepositoryFromName($app['git.repos'], $repo); $commitish = null; $path = null; @@ -105,7 +105,7 @@ class Routing $self = $this; $quotedPaths = array_map( function ($repo) use ($app, $self) { - $repoName = $repo['name'] ; + $repoName = $repo['name']; //Windows if ($self->isWindows()){ $repoName = str_replace('\\', '\\\\',$repoName); @@ -131,12 +131,14 @@ class Routing public function isWindows() { - switch(PHP_OS){ - case 'WIN32': - case 'WINNT': - case 'Windows': return true; - default : return false; - } + switch (PHP_OS) { + case 'WIN32': + case 'WINNT': + case 'Windows': + return true; + default: + return false; + } } /** diff --git a/tests/InterfaceTest.php b/tests/InterfaceTest.php index 74f751f..43b8391 100644 --- a/tests/InterfaceTest.php +++ b/tests/InterfaceTest.php @@ -2,13 +2,12 @@ use Silex\WebTestCase; use Symfony\Component\Filesystem\Filesystem; -use Gitter\Client; +use GitList\Git\Client; class InterfaceTest extends WebTestCase { protected static $tmpdir; protected static $gitPath; - protected static $cached_repos; public static function setUpBeforeClass() { @@ -31,13 +30,11 @@ class InterfaceTest extends WebTestCase $options['path'] = getenv('GIT_CLIENT') ?: '/usr/bin/git'; $options['hidden'] = array(self::$tmpdir . '/hiddenrepo'); + $options['default_branch'] = 'master'; $options['ini.file'] = "config.ini"; - $cached_dir = self::$tmpdir . DIRECTORY_SEPARATOR . 'cache'; - $fs->mkdir($cached_dir); - self::$cached_repos = $cached_dir . DIRECTORY_SEPARATOR . 'repos.json'; - - $options['cache.repos'] = self::$cached_repos; + $cacheDir = self::$tmpdir . DIRECTORY_SEPARATOR . 'cache'; + $fs->mkdir($cacheDir); $git = new Client($options); @@ -45,7 +42,7 @@ class InterfaceTest extends WebTestCase // GitTest repository fixture $git->createRepository(self::$tmpdir . 'GitTest'); - $repository = $git->getRepositoryCached(self::$tmpdir, 'GitTest'); + $repository = $git->getRepository(self::$tmpdir . 'GitTest'); file_put_contents(self::$tmpdir . 'GitTest/README.md', "## GitTest\nGitTest is a *test* repository!"); file_put_contents(self::$tmpdir . 'GitTest/test.php', "setConfig('user.name', 'Luke Skywalker'); @@ -58,7 +55,7 @@ class InterfaceTest extends WebTestCase // foobar repository fixture $git->createRepository(self::$tmpdir . 'foobar'); - $repository = $git->getRepositoryCached(self::$tmpdir, 'foobar'); + $repository = $git->getRepository(self::$tmpdir . 'foobar'); file_put_contents(self::$tmpdir . 'foobar/bar.json', "{\n\"name\": \"foobar\"\n}"); file_put_contents(self::$tmpdir . 'foobar/.git/description', 'This is a test repo!'); @@ -77,7 +74,7 @@ class InterfaceTest extends WebTestCase $nested_dir = self::$tmpdir . 'nested/'; $fs->mkdir($nested_dir); $git->createRepository($nested_dir . 'NestedRepo'); - $repository = $git->getRepositoryCached($nested_dir, 'NestedRepo'); + $repository = $git->getRepository($nested_dir . 'NestedRepo'); file_put_contents($nested_dir . 'NestedRepo/.git/description', 'This is a NESTED test repo!'); file_put_contents($nested_dir . 'NestedRepo/README.txt', 'NESTED TEST REPO README'); $repository->setConfig('user.name', 'Luke Skywalker'); @@ -93,7 +90,7 @@ class InterfaceTest extends WebTestCase // master-less repository fixture $git->createRepository(self::$tmpdir . 'develop'); - $repository = $git->getRepositoryCached(self::$tmpdir, 'develop'); + $repository = $git->getRepository(self::$tmpdir . 'develop'); $repository->setConfig('user.name', 'Luke Skywalker'); $repository->setConfig('user.email', 'luke@rebel.org'); file_put_contents(self::$tmpdir . 'develop/README.md', "## develop\ndevelop is a *test* repository!"); @@ -110,25 +107,23 @@ class InterfaceTest extends WebTestCase // Detached HEAD repository fixture $git->createRepository(self::$tmpdir . 'detached-head'); - $repository = $git->getRepositoryCached(self::$tmpdir, 'detached-head'); -# $repository = $git->getRepository(self::$tmpdir . '/detached-head'); + $repository = $git->getRepository(self::$tmpdir . 'detached-head'); $repository->setConfig('user.name', 'Luke Skywalker'); $repository->setConfig('user.email', 'luke@rebel.org'); file_put_contents(self::$tmpdir . 'detached-head/README.md', "## detached head\ndetached-head is a *test* repository!"); $repository->addAll(); $repository->commit("First commit"); $repository->checkout('HEAD'); - - $git->deleteCached(); } public function createApplication() { - $config = GitList\Config::fromFile('config.ini'); - $config->set('app', 'cached_repos', self::$cached_repos); - $config->set('git', 'repositories', self::$tmpdir); + $config = new GitList\Config; $config->set('app', 'debug', true); + $config->set('app', 'debug', false); $config->set('git', 'client', self::$gitPath); + $config->set('git', 'default_branch', 'master'); + $config->set('git', 'repositories', array(self::$tmpdir)); $app = require 'boot.php'; return $app; diff --git a/views/index.twig b/views/index.twig index db725ac..e78a35c 100644 --- a/views/index.twig +++ b/views/index.twig @@ -13,7 +13,11 @@
+ {% if repository.description %}

{{ repository.description }}

+ {% else %} +

There is no repository description file. Please, create one to remove this message.

+ {% endif %}
{% endfor %}