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 @@
{{ repository.description }}
+ {% else %} +There is no repository description file. Please, create one to remove this message.
+ {% endif %}