aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStevan Radaković <stevan.radakovic@linaro.org>2013-03-31 22:31:14 +0200
committerStevan Radaković <stevan.radakovic@linaro.org>2013-03-31 22:31:14 +0200
commite5cd40c554aabc4bb8d240c3232e9fda90ab59f6 (patch)
tree9335d823afbc5f5f5561de4c91b227d8709e8c91
parentf6e9d7be791b269967311ba281b5705361112669 (diff)
parenta6b33ab7a88ea7acffd5d612ebbacc053f54edfb (diff)
Merge from upstream.
-rw-r--r--.hgtags2
-rwxr-xr-xREADME.rst102
-rwxr-xr-xdocs/changelog.rst43
-rw-r--r--docs/installation_win.rst300
-rwxr-xr-xdocs/setup.rst180
-rw-r--r--docs/upgrade.rst36
-rw-r--r--docs/usage/general.rst38
-rw-r--r--docs/usage/troubleshooting.rst30
-rw-r--r--rhodecode/__init__.py2
-rw-r--r--rhodecode/controllers/admin/repos.py6
-rw-r--r--rhodecode/controllers/feed.py4
-rw-r--r--rhodecode/controllers/login.py10
-rw-r--r--rhodecode/controllers/settings.py6
-rw-r--r--rhodecode/lib/auth.py25
-rw-r--r--rhodecode/lib/base.py14
-rw-r--r--rhodecode/lib/celerylib/tasks.py66
-rw-r--r--rhodecode/lib/cleanup.py3
-rw-r--r--rhodecode/lib/helpers.py10
-rw-r--r--rhodecode/lib/hooks.py7
-rw-r--r--rhodecode/lib/middleware/simplegit.py8
-rw-r--r--rhodecode/lib/middleware/simplehg.py7
-rw-r--r--rhodecode/lib/utils.py15
-rw-r--r--rhodecode/lib/utils2.py16
-rw-r--r--rhodecode/lib/vcs/utils/helpers.py3
-rw-r--r--rhodecode/model/comment.py7
-rwxr-xr-xrhodecode/model/db.py66
-rw-r--r--rhodecode/model/forms.py4
-rw-r--r--rhodecode/model/repo.py6
-rw-r--r--rhodecode/model/repos_group.py33
-rw-r--r--rhodecode/model/scm.py8
-rw-r--r--rhodecode/model/user.py55
-rw-r--r--rhodecode/model/validators.py105
-rw-r--r--rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html2
-rw-r--r--rhodecode/templates/email_templates/password_reset.html15
-rw-r--r--rhodecode/tests/__init__.py18
-rw-r--r--rhodecode/tests/functional/test_login.py2
-rw-r--r--rhodecode/tests/models/common.py8
-rw-r--r--rhodecode/tests/models/test_repos_groups.py139
-rw-r--r--rhodecode/tests/models/test_user_permissions_on_groups.py5
-rwxr-xr-xrhodecode/tests/scripts/test_vcs_operations.py2
-rw-r--r--setup.py4
41 files changed, 889 insertions, 523 deletions
diff --git a/.hgtags b/.hgtags
index 6cffccc8..38b29057 100644
--- a/.hgtags
+++ b/.hgtags
@@ -61,3 +61,5 @@ d998cc84cf726798486a438763053f0e1dc1b646 v1.4.2
a5f0bc867edc88be23eb808693e5393a97d4c54a v1.5.0
3259dc7caea48687eab018ee646ae6ad7e7ef377 v1.5.1
efe23d6c178c11d575a0214181276a3452776e48 v1.5.2
+1a498b11f1540f5b94b6f6009298f5dc3eaad9e9 v1.5.3
+3447862ad8c9ceba85857774c526e39fde3a2281 v1.5.4
diff --git a/README.rst b/README.rst
index f25f0e3f..57f49e95 100755
--- a/README.rst
+++ b/README.rst
@@ -5,18 +5,18 @@ RhodeCode
About
-----
-``RhodeCode`` is a fast and powerful management tool for Mercurial_ and GIT_
-with a built in push/pull server and full text search and code-review.
-It works on http/https and has a built in permission/authentication system with
+``RhodeCode`` is a fast and powerful management tool for Mercurial_ and GIT_
+with a built in push/pull server, full text search and code-review.
+It works on http/https and has a built in permission/authentication system with
the ability to authenticate via LDAP or ActiveDirectory. RhodeCode also provides
simple API so it's easy integrable with existing external systems.
-RhodeCode is similar in some respects to github_ or bitbucket_,
+RhodeCode is similar in some respects to github_ or bitbucket_,
however RhodeCode can be run as standalone hosted application on your own server.
-It is open source and donation ware and focuses more on providing a customized,
-self administered interface for Mercurial_ and GIT_ repositories.
-RhodeCode works on \*nix systems and Windows it is powered by a vcs_ library
-that Lukasz Balcerzak and Marcin Kuzminski created to handle multiple
+It is open source and donation ware and focuses more on providing a customized,
+self administered interface for Mercurial_ and GIT_ repositories.
+RhodeCode works on \*nix systems and Windows it is powered by a vcs_ library
+that Lukasz Balcerzak and Marcin Kuzminski created to handle multiple
different version control systems.
RhodeCode uses `PEP386 versioning <http://www.python.org/dev/peps/pep-0386/>`_
@@ -29,7 +29,7 @@ Stable releases of RhodeCode are best installed via::
Or::
- pip install rhodecode
+ pip install rhodecode
Detailed instructions and links may be found on the Installation page.
@@ -51,7 +51,7 @@ Source code
-----------
The latest sources can be obtained from official RhodeCode instance
-https://secure.rhodecode.org
+https://secure.rhodecode.org
MIRRORS:
@@ -68,56 +68,59 @@ https://github.com/marcinkuzminski/rhodecode
RhodeCode Features
------------------
-- Has its own middleware to handle mercurial_ protocol requests.
- Each request can be logged and authenticated.
-- Runs on threads unlike hgweb. You can make multiple pulls/pushes simultaneous.
- Supports http/https and LDAP
-- Full permissions (private/read/write/admin) for each repository, additional
- explicit forking and repository permissions.
-- Have built in users groups for easier permission management
+- Has its own middleware to handle mercurial_ and git_ protocol requests.
+ Each request is authenticated and logged together with IP address.
+- Build for speed and performance. You can make multiple pulls/pushes simultaneous.
+ Proven to work with 1000s of repositories and users
+- Supports http/https, LDAP, AD, proxy-pass authentication.
+- Full permissions (private/read/write/admin) together with IP restrictions for each repository,
+ additional explicit forking and repository creation permissions.
+- Users groups for easier permission management
- Repository groups let you group repos and manage them easier.
-- Users can fork other users repo. RhodeCode have also compare view to see
- combined changeset for all changeset made within single push.
+- Users can fork other users repos, and compare them at any time.
+- Integrates easily with other systems, with custom created mappers you can connect it to almost
+ any issue tracker, and with an JSON-RPC API you can make much more
- Build in commit-api let's you add, edit and commit files right from RhodeCode
- interface using simple editor or upload form for binaries.
-- Powerfull pull-request driven review system with inline commenting, and
- changeset statuses, notification system.
-- Importing SVN repositories from remote locations into RhodeCode.
+ web interface using simple editor or upload binary files using simple form.
+- Powerfull pull-request driven review system with inline commenting,
+ changeset statuses, and notification system.
+- Importing and syncing repositories from remote locations for GIT_, Mercurial_ and SVN.
- Mako templates let's you customize the look and feel of the application.
-- Beautiful diffs, annotations and source code browsing all colored by pygments.
- Raw diffs are made in git-diff format, including GIT_ binary-patches
-- Mercurial_ branch graph and yui-flot powered graphs with zooming and statistics
+- Beautiful diffs, annotations and source code browsing all colored by pygments.
+ Raw diffs are made in git-diff format for both VCS systems, including GIT_ binary-patches
+- Mercurial_ and Git_ DAG graphs and yui-flot powered graphs with zooming and statistics
+ to track activity for repositories
- Admin interface with user/permission management. Admin activity journal, logs
pulls, pushes, forks, registrations and other actions made by all users.
-- Server side forks. It is possible to fork a project and modify it freely
- without breaking the main repository. You can even write Your own hooks
- and install them
-- rst and markdown README support for repositories
-- Full text search powered by Whoosh on the source files, and file names.
+- Server side forks. It is possible to fork a project and modify it freely
+ without breaking the main repository.
+- rst and markdown README support for repositories.
+- Full text search powered by Whoosh on the source files, commit messages, and file names.
Build in indexing daemons, with optional incremental index build
(no external search servers required all in one application)
-- Setup project descriptions and info inside built in db for easy, non
- file-system operations
-- Intelligent cache with invalidation after push or project change, provides
+- Setup project descriptions/tags and info inside built in db for easy, non
+ file-system operations.
+- Intelligent cache with invalidation after push or project change, provides
high performance and always up to date data.
-- Rss / atom feeds, gravatar support, download sources as zip/tar/gz
-- Optional async tasks for speed and performance using celery_
-- Backup scripts can do backup of whole app and send it over scp to desired
- location
+- RSS / Atom feeds, gravatar support, downloadable sources as zip/tar/gz
+- Optional async tasks for speed and performance using celery_
+- Backup scripts can do backup of whole app and send it over scp to desired
+ location
- Based on pylons / sqlalchemy / sqlite / whoosh / vcs
-
+
Incoming / Plans
----------------
-- Finer granular permissions per branch, repo group or subrepo
-- Pull requests with web based merges
-- Per line file history
-- Simple issue tracker
+- Finer granular permissions per branch, or subrepo
+- Web based merges for pull requests
+- Tracking history for each lines in files
+- Simple issue tracker
- SSH based authentication with server side key management
- Commit based built in wiki system
+- Gist server
- More statistics and graph (global annotation + some more statistics)
-- Other advancements as development continues (or you can of course make
+- Other advancements as development continues (or you can of course make
additions and or requests)
License
@@ -132,16 +135,15 @@ Getting help
Listed bellow are various support resources that should help.
.. note::
-
+
Please try to read the documentation before posting any issues, especially
the **troubleshooting section**
-
+
- Join the `Google group <http://groups.google.com/group/rhodecode>`_ and ask
any questions.
- Open an issue at `issue tracker <http://bitbucket.org/marcinkuzminski/rhodecode/issues>`_
-
- Join #rhodecode on FreeNode (irc.freenode.net)
or use http://webchat.freenode.net/?channels=rhodecode for web access to irc.
@@ -161,9 +163,9 @@ You may also build the documentation for yourself - go into ``docs/`` and run::
make html
(You need to have sphinx_ installed to build the documentation. If you don't
-have sphinx_ installed you can install it via the command:
+have sphinx_ installed you can install it via the command:
``easy_install sphinx``)
-
+
.. _virtualenv: http://pypi.python.org/pypi/virtualenv
.. _python: http://www.python.org/
.. _sphinx: http://sphinx.pocoo.org/
@@ -174,4 +176,4 @@ have sphinx_ installed you can install it via the command:
.. _git: http://git-scm.com/
.. _celery: http://celeryproject.org/
.. _Sphinx: http://sphinx.pocoo.org/
-.. _vcs: http://pypi.python.org/pypi/vcs \ No newline at end of file
+.. _vcs: http://pypi.python.org/pypi/vcs
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 0e0345f0..61ef497e 100755
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,6 +4,47 @@
Changelog
=========
+1.5.4 (**2013-03-13**)
+----------------------
+
+news
+++++
+
+
+fixes
++++++
+
+- fixed webtest dependency issues
+- fixed issues with celery tasks for password reset
+- fixed #763 gravatar helper function should fallback into default image
+ if email is empty
+- fixes #762 user global activation flag is also respected for LDAP created
+ accounts
+- use password obfuscate when clonning a remote repo with credentials inside
+- fixed issue with renaming repos group together with changing parents
+- disallow cloning from file:/// URIs
+- handle all cases with multiple IP addresses in proxy headers
+
+1.5.3 (**2013-02-12**)
+----------------------
+
+news
+++++
+
+- IP restrictions now also enabled for IPv6
+
+fixes
++++++
+
+- fixed issues with private checkbox not always working
+- fixed #746 unicodeDedode errors on feed controllers
+- fixes issue #756 cleanup repos didn't properly compose paths of repos to be cleaned up.
+- fixed cache invalidation issues together with vcs_full_cache option
+- repo scan should skip directories with starting with '.'
+- fixes for issue #731, update-repoinfo sometimes failed to update data when changesets
+ were initial commits
+- recursive mode of setting permission skips private repositories
+
1.5.2 (**2013-01-14**)
----------------------
@@ -932,4 +973,4 @@ fixes
- Disabled dirsize in file browser, it's causing nasty bug when dir renames
occure. After vcs is fixed it'll be put back again.
-- templating/css rewrites, optimized css. \ No newline at end of file
+- templating/css rewrites, optimized css.
diff --git a/docs/installation_win.rst b/docs/installation_win.rst
index 9ed6f9f0..9a7aae63 100644
--- a/docs/installation_win.rst
+++ b/docs/installation_win.rst
@@ -5,154 +5,196 @@ Step by step Installation for Windows
=====================================
-RhodeCode step-by-step install Guide for Windows
+RhodeCode step-by-step install Guide for Windows
-Target OS: Windows XP SP3 32bit English (Clean installation)
-+ All Windows Updates until 24-may-2012
+Target OS: Windows XP SP3 32bit English (Clean installation)
++ All Windows Updates until 24-may-2012
.. note::
-
+
This installation is for 32bit systems, for 64bit windows you might need
- to download proper 64bit version of "Windows Installer" and Win32py
- extensions
+ to download proper 64bit versions of the different packages(Windows Installer, Win32py extensions)
+ plus some extra tweaks.
+ These extra steps haven been marked as "64bit".
+ Tested on Windows Server 2008 R2 SP1, 9-feb-2013.
+ If you run into any 64bit related problems, please check these pages:
+ - http://blog.victorjabur.com/2011/06/05/compiling-python-2-7-modules-on-windows-32-and-64-using-msvc-2008-express/
+ - http://bugs.python.org/issue7511
Step1 - Install Visual Studio 2008 Express
------------------------------------------
-
-Optional: You can also install MingW, but VS2008 installation is easier
-Download "Visual C++ 2008 Express Edition with SP1" from:
-http://www.microsoft.com/visualstudio/en-us/products/2008-editions/express
-(if not found or relocated, google for "visual studio 2008 express" for
-updated link)
+Optional: You can also install MingW, but VS2008 installation is easier
+
+Download "Visual C++ 2008 Express Edition with SP1" from:
+http://www.microsoft.com/visualstudio/en-us/products/2008-editions/express
+(if not found or relocated, google for "visual studio 2008 express" for
+updated link)
+
+You can also download full ISO file for offline installation, just
+choose "All - Offline Install ISO image file" in the previous page and
+choose "Visual C++ 2008 Express" when installing.
+
+.. note::
+
+ Using other versions of Visual Studio will lead to random crashes.
+ You must use Visual Studio 2008!"
-You can also download full ISO file for offline installation, just
-choose "All - Offline Install ISO image file" in the previous page and
-choose "Visual C++ 2008 Express" when installing.
+.. note::
+ Silverlight Runtime and SQL Server 2008 Express Edition are not
+ required, you can uncheck them
.. note::
+
+ 64bit: You also need to install the Microsoft Windows SDK for .NET 3.5 SP1 (.NET 4.0 won't work).
+ Download from: http://www.microsoft.com/en-us/download/details.aspx?id=3138
- Silverlight Runtime and SQL Server 2008 Express Edition are not
- required, you can uncheck them
+.. note::
+
+ 64bit: You also need to copy and rename a .bat file to make the Visual C++ compiler work.
+ I am not sure why this is not necessary for 32bit.
+ Copy C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat to C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\amd64\vcvarsamd64.bat
Step2 - Install Python
----------------------
Install Python 2.x.y (x >= 5) x86 version (32bit). DO NOT USE A 3.x version.
-Download Python 2.x.y from:
-http://www.python.org/download/
+Download Python 2.x.y from:
+http://www.python.org/download/
+
+Choose "Windows Installer" (32bit version) not "Windows X86-64
+Installer". While writing this guide, the latest version was v2.7.3.
+Remember the specific major and minor version installed, because it will
+be needed in the next step. In this case, it is "2.7".
-Choose "Windows Installer" (32bit version) not "Windows X86-64
-Installer". While writing this guide, the latest version was v2.7.3.
-Remember the specific major and minor version installed, because it will
-be needed in the next step. In this case, it is "2.7".
+.. note::
+ 64bit: Just download and install the 64bit version of python.
Step3 - Install Win32py extensions
----------------------------------
-
-Download pywin32 from:
-http://sourceforge.net/projects/pywin32/files/
-- Click on "pywin32" folder
-- Click on the first folder (in this case, Build 217, maybe newer when you try)
-- Choose the file ending with ".win32-py2.x.exe" -> x being the minor
- version of Python you installed (in this case, 7)
- When writing this guide, the file was:
- http://sourceforge.net/projects/pywin32/files/pywin32/Build%20217/pywin32-217.win32-py2.7.exe/download
+Download pywin32 from:
+http://sourceforge.net/projects/pywin32/files/
+
+- Click on "pywin32" folder
+- Click on the first folder (in this case, Build 217, maybe newer when you try)
+- Choose the file ending with ".win32-py2.x.exe" -> x being the minor
+ version of Python you installed (in this case, 7)
+ When writing this guide, the file was:
+ http://sourceforge.net/projects/pywin32/files/pywin32/Build%20217/pywin32-217.win32-py2.7.exe/download
+ .. note::
+
+ 64bit: Download and install the 64bit version.
+ At the time of writing you can find this at:
+ http://sourceforge.net/projects/pywin32/files/pywin32/Build%20218/pywin32-218.win-amd64-py2.7.exe/download
Step4 - Python BIN
------------------
-Add Python BIN folder to the path
+Add Python BIN folder to the path
-You have to add the Python folder to the path, you can do it manually
-(editing "PATH" environment variable) or using Windows Support Tools
-that came preinstalled in Vista/7 and can be installed in Windows XP.
+You have to add the Python folder to the path, you can do it manually
+(editing "PATH" environment variable) or using Windows Support Tools
+that came preinstalled in Vista/7 and can be installed in Windows XP.
-- Using support tools on WINDOWS XP:
- If you use Windows XP you can install them using Windows XP CD and
- navigating to \SUPPORT\TOOLS. There, execute Setup.EXE (not MSI).
+- Using support tools on WINDOWS XP:
+ If you use Windows XP you can install them using Windows XP CD and
+ navigating to \SUPPORT\TOOLS. There, execute Setup.EXE (not MSI).
Afterwards, open a CMD and type::
-
- SETX PATH "%PATH%;[your-python-path]" -M
- Close CMD (the path variable will be updated then)
+ SETX PATH "%PATH%;[your-python-path]" -M
+
+ Close CMD (the path variable will be updated then)
-- Using support tools on WINDOWS Vista/7:
+- Using support tools on WINDOWS Vista/7:
Open a CMD and type::
- SETX PATH "%PATH%;[your-python-path]" /M
+ SETX PATH "%PATH%;[your-python-path]" /M
- Please substitute [your-python-path] with your Python installation path.
- Typically: C:\\Python27
+ Please substitute [your-python-path] with your Python installation path.
+ Typically: C:\\Python27
Step5 - RhodeCode folder structure
----------------------------------
-Create a RhodeCode folder structure
+Create a RhodeCode folder structure
-This is only a example to install RhodeCode, you can of course change
-it. However, this guide will follow the proposed structure, so please
-later adapt the paths if you change them. My recommendation is to use
-folders with NO SPACES. But you can try if you are brave...
+This is only a example to install RhodeCode, you can of course change
+it. However, this guide will follow the proposed structure, so please
+later adapt the paths if you change them. My recommendation is to use
+folders with NO SPACES. But you can try if you are brave...
Create the following folder structure::
- C:\RhodeCode
- C:\RhodeCode\Bin
- C:\RhodeCode\Env
- C:\RhodeCode\Repos
+ C:\RhodeCode
+ C:\RhodeCode\Bin
+ C:\RhodeCode\Env
+ C:\RhodeCode\Repos
Step6 - Install virtualenv
---------------------------
-Install Virtual Env for Python
+Install Virtual Env for Python
-Navigate to: http://www.virtualenv.org/en/latest/index.html#installation
-Right click on "virtualenv.py" file and choose "Save link as...".
-Download to C:\\RhodeCode (or whatever you want)
-(the file is located at
-https://raw.github.com/pypa/virtualenv/master/virtualenv.py)
+Navigate to: http://www.virtualenv.org/en/latest/index.html#installation
+Right click on "virtualenv.py" file and choose "Save link as...".
+Download to C:\\RhodeCode (or whatever you want)
+(the file is located at
+https://raw.github.com/pypa/virtualenv/master/virtualenv.py)
-Create a virtual Python environment in C:\\RhodeCode\\Env (or similar). To
-do so, open a CMD (Python Path should be included in Step3), navigate
-where you downloaded "virtualenv.py", and write::
+Create a virtual Python environment in C:\\RhodeCode\\Env (or similar). To
+do so, open a CMD (Python Path should be included in Step3), navigate
+where you downloaded "virtualenv.py", and write::
- python virtualenv.py C:\RhodeCode\Env
+ python virtualenv.py C:\RhodeCode\Env
-(--no-site-packages is now the default behaviour of virtualenv, no need
-to include it)
+(--no-site-packages is now the default behaviour of virtualenv, no need
+to include it)
Step7 - Install RhodeCode
-------------------------
-Finally, install RhodeCode
+Finally, install RhodeCode
+
+Close previously opened command prompt/s, and open a Visual Studio 2008
+Command Prompt (**IMPORTANT!!**). To do so, go to Start Menu, and then open
+"Microsoft Visual C++ 2008 Express Edition" -> "Visual Studio Tools" ->
+"Visual Studio 2008 Command Prompt"
+
+.. note::
+
+ 64bit: For 64bit you need to modify the shortcut that is used to start the
+ Visual Studio 2008 Command Prompt. Use right-mouse click to open properties.
+
+Change commandline from::
+
+%comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"" x86
+
+to::
+
+%comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"" amd64
-Close previously opened command prompt/s, and open a Visual Studio 2008
-Command Prompt (**IMPORTANT!!**). To do so, go to Start Menu, and then open
-"Microsoft Visual C++ 2008 Express Edition" -> "Visual Studio Tools" ->
-"Visual Studio 2008 Command Prompt"
In that CMD (loaded with VS2008 PATHs) type::
-
- cd C:\RhodeCode\Env\Scripts (or similar)
- activate
-The prompt will change into "(Env) C:\\RhodeCode\\Env\\Scripts" or similar
-(depending of your folder structure). Then type::
+ cd C:\RhodeCode\Env\Scripts (or similar)
+ activate
+
+The prompt will change into "(Env) C:\\RhodeCode\\Env\\Scripts" or similar
+(depending of your folder structure). Then type::
- pip install rhodecode
+ pip install rhodecode
-(long step, please wait until fully complete)
+(long step, please wait until fully complete)
Some warnings will appear, don't worry as they are normal.
@@ -161,90 +203,90 @@ Step8 - Configuring RhodeCode
-----------------------------
-steps taken from http://packages.python.org/RhodeCode/setup.html
+steps taken from http://packages.python.org/RhodeCode/setup.html
-You have to use the same Visual Studio 2008 command prompt as Step7, so
-if you closed it reopen it following the same commands (including the
+You have to use the same Visual Studio 2008 command prompt as Step7, so
+if you closed it reopen it following the same commands (including the
"activate" one). When ready, just type::
-
- cd C:\RhodeCode\Bin
- paster make-config RhodeCode production.ini
-Then, you must edit production.ini to fit your needs (ip address, ip
-port, mail settings, database, whatever). I recommend using NotePad++
-(free) or similar text editor, as it handles well the EndOfLine
-character differences between Unix and Windows
-(http://notepad-plus-plus.org/)
+ cd C:\RhodeCode\Bin
+ paster make-config RhodeCode production.ini
-For the sake of simplicity lets run it with the default settings. After
-your edits (if any), in the previous Command Prompt, type::
-
- paster setup-rhodecode production.ini
+Then, you must edit production.ini to fit your needs (ip address, ip
+port, mail settings, database, whatever). I recommend using NotePad++
+(free) or similar text editor, as it handles well the EndOfLine
+character differences between Unix and Windows
+(http://notepad-plus-plus.org/)
-(this time a NEW database will be installed, you must follow a different
-step to later UPGRADE to a newer RhodeCode version)
+For the sake of simplicity lets run it with the default settings. After
+your edits (if any), in the previous Command Prompt, type::
-The script will ask you for confirmation about creating a NEW database,
-answer yes (y)
-The script will ask you for repository path, answer C:\\RhodeCode\\Repos
-(or similar)
-The script will ask you for admin username and password, answer "admin"
-+ "123456" (or whatever you want)
-The script will ask you for admin mail, answer "admin@xxxx.com" (or
-whatever you want)
+ paster setup-rhodecode production.ini
-If you make some mistake and the script does not end, don't worry, start
-it again.
+(this time a NEW database will be installed, you must follow a different
+step to later UPGRADE to a newer RhodeCode version)
+
+The script will ask you for confirmation about creating a NEW database,
+answer yes (y)
+The script will ask you for repository path, answer C:\\RhodeCode\\Repos
+(or similar)
+The script will ask you for admin username and password, answer "admin"
++ "123456" (or whatever you want)
+The script will ask you for admin mail, answer "admin@xxxx.com" (or
+whatever you want)
+
+If you make some mistake and the script does not end, don't worry, start
+it again.
Step9 - Running RhodeCode
-------------------------
-In the previous command prompt, being in the C:\\RhodeCode\\Bin folder,
+In the previous command prompt, being in the C:\\RhodeCode\\Bin folder,
just type::
-
- paster serve production.ini
-Open yout web server, and go to http://127.0.0.1:5000
+ paster serve production.ini
-It works!! :-)
+Open yout web server, and go to http://127.0.0.1:5000
-Remark:
-If it does not work first time, just Ctrl-C the CMD process and start it
-again. Don't forget the "http://" in Internet Explorer
+It works!! :-)
+
+Remark:
+If it does not work first time, just Ctrl-C the CMD process and start it
+again. Don't forget the "http://" in Internet Explorer
What this Guide does not cover:
-- Installing Celery
+- Installing Celery
- Running RhodeCode as Windows Service. You can investigate here:
-
- - http://pypi.python.org/pypi/wsgisvc
- - http://ryrobes.com/python/running-python-scripts-as-a-windows-service/
- - http://wiki.pylonshq.com/display/pylonscookbook/How+to+run+Pylons+as+a+Windows+service
+
+ - http://pypi.python.org/pypi/wsgisvc
+ - http://ryrobes.com/python/running-python-scripts-as-a-windows-service/
+ - http://wiki.pylonshq.com/display/pylonscookbook/How+to+run+Pylons+as+a+Windows+service
- Using Apache. You can investigate here:
- - https://groups.google.com/group/rhodecode/msg/c433074e813ffdc4
+ - https://groups.google.com/group/rhodecode/msg/c433074e813ffdc4
Upgrading
=========
-
-Stop running RhodeCode
+
+Stop running RhodeCode
Open a CommandPrompt like in Step7 (VS2008 path + activate) and type::
-
- easy_install -U rhodecode
- cd \RhodeCode\Bin
-{ backup your production.ini file now} ::
+ easy_install -U rhodecode
+ cd \RhodeCode\Bin
+
+{ backup your production.ini file now} ::
- paster make-config RhodeCode production.ini
+ paster make-config RhodeCode production.ini
(check changes and update your production.ini accordingly) ::
-
+
paster upgrade-db production.ini (update database)
-Full steps in http://packages.python.org/RhodeCode/upgrade.html \ No newline at end of file
+Full steps in http://packages.python.org/RhodeCode/upgrade.html
diff --git a/docs/setup.rst b/docs/setup.rst
index 24e9fa56..504c8c6c 100755
--- a/docs/setup.rst
+++ b/docs/setup.rst
@@ -8,14 +8,14 @@ Setup
Setting up RhodeCode
--------------------
-First, you will need to create a RhodeCode configuration file. Run the
+First, you will need to create a RhodeCode configuration file. Run the
following command to do this::
-
+
paster make-config RhodeCode production.ini
- This will create the file `production.ini` in the current directory. This
- configuration file contains the various settings for RhodeCode, e.g proxy
- port, email settings, usage of static files, cache, celery settings and
+ configuration file contains the various settings for RhodeCode, e.g proxy
+ port, email settings, usage of static files, cache, celery settings and
logging.
@@ -30,36 +30,36 @@ the following command::
This will prompt you for a "root" path. This "root" path is the location where
RhodeCode will store all of its repositories on the current machine. After
-entering this "root" path ``setup-rhodecode`` will also prompt you for a username
-and password for the initial admin account which ``setup-rhodecode`` sets
+entering this "root" path ``setup-rhodecode`` will also prompt you for a username
+and password for the initial admin account which ``setup-rhodecode`` sets
up for you.
setup process can be fully automated, example for lazy::
paster setup-rhodecode production.ini --user=marcink --password=secret --email=marcin@rhodecode.org --repos=/home/marcink/my_repos
-
-- The ``setup-rhodecode`` command will create all of the needed tables and an
- admin account. When choosing a root path you can either use a new empty
+
+- The ``setup-rhodecode`` command will create all of the needed tables and an
+ admin account. When choosing a root path you can either use a new empty
location, or a location which already contains existing repositories. If you
- choose a location which contains existing repositories RhodeCode will simply
- add all of the repositories at the chosen location to it's database.
+ choose a location which contains existing repositories RhodeCode will simply
+ add all of the repositories at the chosen location to it's database.
(Note: make sure you specify the correct path to the root).
- Note: the given path for mercurial_ repositories **must** be write accessible
- for the application. It's very important since the RhodeCode web interface
- will work without write access, but when trying to do a push it will
+ for the application. It's very important since the RhodeCode web interface
+ will work without write access, but when trying to do a push it will
eventually fail with permission denied errors unless it has write access.
You are now ready to use RhodeCode, to run it simply execute::
-
+
paster serve production.ini
-
-- This command runs the RhodeCode server. The web app should be available at the
- 127.0.0.1:5000. This ip and port is configurable via the production.ini
+
+- This command runs the RhodeCode server. The web app should be available at the
+ 127.0.0.1:5000. This ip and port is configurable via the production.ini
file created in previous step
-- Use the admin account you created above when running ``setup-rhodecode``
+- Use the admin account you created above when running ``setup-rhodecode``
to login to the web app.
-- The default permissions on each repository is read, and the owner is admin.
+- The default permissions on each repository is read, and the owner is admin.
Remember to update these if needed.
- In the admin panel you can toggle ldap, anonymous, permissions settings. As
well as edit more advanced options on users and repositories
@@ -70,10 +70,10 @@ functionality. To do this simply execute::
paster make-rcext production.ini
This will create `rcextensions` package in the same place that your `ini` file
-lives. With `rcextensions` it's possible to add additional mapping for whoosh,
+lives. With `rcextensions` it's possible to add additional mapping for whoosh,
stats and add additional code into the push/pull/create/delete repo hooks.
For example for sending signals to build-bots such as jenkins.
-Please see the `__init__.py` file inside `rcextensions` package
+Please see the `__init__.py` file inside `rcextensions` package
for more details.
@@ -86,11 +86,11 @@ parallel with RhodeCode. (Repository access via ssh is a standard "out of
the box" feature of mercurial_ and you can use this to access any of the
repositories that RhodeCode is hosting. See PublishingRepositories_)
-RhodeCode repository structures are kept in directories with the same name
+RhodeCode repository structures are kept in directories with the same name
as the project. When using repository groups, each group is a subdirectory.
This allows you to easily use ssh for accessing repositories.
-In order to use ssh you need to make sure that your web-server and the users
+In order to use ssh you need to make sure that your web-server and the users
login accounts have the correct permissions set on the appropriate directories.
(Note that these permissions are independent of any permissions you have set up
using the RhodeCode web interface.)
@@ -108,17 +108,17 @@ Note: In an advanced setup, in order for your ssh access to use the same
permissions as set up via the RhodeCode web interface, you can create an
authentication hook to connect to the rhodecode db and runs check functions for
permissions against that.
-
+
Setting up Whoosh full text search
----------------------------------
Starting from version 1.1 the whoosh index can be build by using the paster
command ``make-index``. To use ``make-index`` you must specify the configuration
-file that stores the location of the index. You may specify the location of the
-repositories (`--repo-location`). If not specified, this value is retrieved
-from the RhodeCode database. This was required prior to 1.2. Starting from
-version 1.2 it is also possible to specify a comma separated list of
-repositories (`--index-only`) to build index only on chooses repositories
+file that stores the location of the index. You may specify the location of the
+repositories (`--repo-location`). If not specified, this value is retrieved
+from the RhodeCode database. This was required prior to 1.2. Starting from
+version 1.2 it is also possible to specify a comma separated list of
+repositories (`--index-only`) to build index only on chooses repositories
skipping any other found in repos location
You may optionally pass the option `-f` to enable a full index rebuild. Without
@@ -126,24 +126,24 @@ the `-f` option, indexing will run always in "incremental" mode.
For an incremental index build use::
- paster make-index production.ini
+ paster make-index production.ini
For a full index rebuild use::
- paster make-index production.ini -f
+ paster make-index production.ini -f
building index just for chosen repositories is possible with such command::
-
+
paster make-index production.ini --index-only=vcs,rhodecode
In order to do periodical index builds and keep your index always up to date.
-It's recommended to do a crontab entry for incremental indexing.
+It's recommended to do a crontab entry for incremental indexing.
An example entry might look like this::
-
- /path/to/python/bin/paster make-index /path/to/rhodecode/production.ini
-
+
+ /path/to/python/bin/paster make-index /path/to/rhodecode/production.ini
+
When using incremental mode (the default) whoosh will check the last
modification date of each file and add it to be reindexed if a newer file is
available. The indexing daemon checks for any removed files and removes them
@@ -157,19 +157,19 @@ Setting up LDAP support
-----------------------
RhodeCode starting from version 1.1 supports ldap authentication. In order
-to use LDAP, you have to install the python-ldap_ package. This package is
+to use LDAP, you have to install the python-ldap_ package. This package is
available via pypi, so you can install it by running
using easy_install::
easy_install python-ldap
-
+
using pip::
pip install python-ldap
.. note::
- python-ldap requires some certain libs on your system, so before installing
+ python-ldap requires some certain libs on your system, so before installing
it check that you have at least `openldap`, and `sasl` libraries.
LDAP settings are located in admin->ldap section,
@@ -232,12 +232,12 @@ Connection Security : required
No encryption
Plain non encrypted connection
-
+
LDAPS connection
- Enable ldaps connection. It will likely require `Port`_ to be set to
- a different value (standard LDAPS port is 636). When LDAPS is enabled
+ Enable ldaps connection. It will likely require `Port`_ to be set to
+ a different value (standard LDAPS port is 636). When LDAPS is enabled
then `Certificate Checks`_ is required.
-
+
START_TLS on LDAP connection
START TLS connection
@@ -245,7 +245,7 @@ Connection Security : required
Certificate Checks : optional
How SSL certificates verification is handled - this is only useful when
- `Enable LDAPS`_ is enabled. Only DEMAND or HARD offer full SSL security
+ `Enable LDAPS`_ is enabled. Only DEMAND or HARD offer full SSL security
while the other options are susceptible to man-in-the-middle attacks. SSL
certificates can be installed to /etc/openldap/cacerts so that the
DEMAND or HARD options can be used with self-signed certificates or
@@ -305,7 +305,7 @@ LDAP Search Scope : required
.. _Login Attribute:
-Login Attribute : required
+Login Attribute : required
The LDAP record attribute that will be matched as the USERNAME or
ACCOUNT used to connect to RhodeCode. This will be added to `LDAP
Filter`_ for locating the User object. If `LDAP Filter`_ is specified as
@@ -423,7 +423,7 @@ reverse-proxy setup with basic auth::
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule .* - [E=RU:%1]
RequestHeader set X-Forwarded-User %{RU}e
- </Location>
+ </Location>
In order for RhodeCode to start using the forwarded username, you should set
the following in the [app:main] section of your .ini file::
@@ -450,27 +450,27 @@ uncomment following variables in the ini file::
`url_pat` is the regular expression that will fetch issues from commit messages.
Default regex will match issues in format of #<number> eg. #300.
-
-Matched issues will be replace with the link specified as `issue_server_link`
+
+Matched issues will be replace with the link specified as `issue_server_link`
{id} will be replaced with issue id, and {repo} with repository name.
-Since the # is striped `issue_prefix` is added as a prefix to url.
-`issue_prefix` can be something different than # if you pass
+Since the # is striped `issue_prefix` is added as a prefix to url.
+`issue_prefix` can be something different than # if you pass
ISSUE- as issue prefix this will generate an url in format::
-
- <a href="https://myissueserver.com/example_repo/issue/300">ISSUE-300</a>
+
+ <a href="https://myissueserver.com/example_repo/issue/300">ISSUE-300</a>
Hook management
---------------
Hooks can be managed in similar way to this used in .hgrc files.
To access hooks setting click `advanced setup` on Hooks section of Mercurial
-Settings in Admin.
+Settings in Admin.
There are 4 built in hooks that cannot be changed (only enable/disable by
checkboxes on previos section).
-To add another custom hook simply fill in first section with
+To add another custom hook simply fill in first section with
<name>.<hook_type> and the second one with hook path. Example hooks
-can be found at *rhodecode.lib.hooks*.
+can be found at *rhodecode.lib.hooks*.
Changing default encoding
@@ -488,7 +488,7 @@ Setting Up Celery
-----------------
Since version 1.1 celery is configured by the rhodecode ini configuration files.
-Simply set use_celery=true in the ini file then add / change the configuration
+Simply set use_celery=true in the ini file then add / change the configuration
variables inside the ini file.
Remember that the ini files use the format with '.' not with '_' like celery.
@@ -501,9 +501,9 @@ In order to start using celery run::
.. note::
- Make sure you run this command from the same virtualenv, and with the same
+ Make sure you run this command from the same virtualenv, and with the same
user that rhodecode runs.
-
+
HTTPS support
-------------
@@ -511,7 +511,7 @@ There are two ways to enable https:
- Set HTTP_X_URL_SCHEME in your http server headers, than rhodecode will
recognize this headers and make proper https redirections
-- Alternatively, change the `force_https = true` flag in the ini configuration
+- Alternatively, change the `force_https = true` flag in the ini configuration
to force using https, no headers are needed than to enable https
@@ -526,39 +526,49 @@ Sample config for nginx using proxy::
#server 127.0.0.1:5001;
#server 127.0.0.1:5002;
}
-
+
server {
- listen 80;
- server_name hg.myserver.com;
+ listen 443;
+ server_name rhodecode.myserver.com;
access_log /var/log/nginx/rhodecode.access.log;
error_log /var/log/nginx/rhodecode.error.log;
+ ssl on;
+ ssl_certificate rhodecode.myserver.com.crt;
+ ssl_certificate_key rhodecode.myserver.com.key;
+
+ ssl_session_timeout 5m;
+
+ ssl_protocols SSLv3 TLSv1;
+ ssl_ciphers DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:EDH-RSA-DES-CBC3-SHA:AES256-SHA:DES-CBC3-SHA:AES128-SHA:RC4-SHA:RC4-MD5;
+ ssl_prefer_server_ciphers on;
+
# uncomment if you have nginx with chunking module compiled
# fixes the issues of having to put postBuffer data for large git
- # pushes
+ # pushes
#chunkin on;
#error_page 411 = @my_411_error;
#location @my_411_error {
# chunkin_resume;
#}
-
+
# uncomment if you want to serve static files by nginx
#root /path/to/installation/rhodecode/public;
-
+
location / {
try_files $uri @rhode;
}
-
+
location @rhode {
proxy_pass http://rc;
include /etc/nginx/proxy.conf;
}
- }
-
+ }
+
Here's the proxy.conf. It's tuned so it will not timeout on long
pushes or large pushes::
-
+
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Url-Scheme $scheme;
@@ -573,7 +583,7 @@ pushes or large pushes::
proxy_send_timeout 7200;
proxy_read_timeout 7200;
proxy_buffers 8 32k;
-
+
Also, when using root path with nginx you might set the static files to false
in the production.ini file::
@@ -595,24 +605,24 @@ Here is a sample configuration file for apache using proxy::
<VirtualHost *:80>
ServerName hg.myserver.com
ServerAlias hg.myserver.com
-
+
<Proxy *>
Order allow,deny
Allow from all
</Proxy>
-
+
#important !
#Directive to properly generate url (clone url) for pylons
ProxyPreserveHost On
-
+
#rhodecode instance
ProxyPass / http://127.0.0.1:5000/
ProxyPassReverse / http://127.0.0.1:5000/
-
+
#to enable https use line below
#SetEnvIf X-Url-Scheme https HTTPS=1
-
- </VirtualHost>
+
+ </VirtualHost>
Additional tutorial
@@ -628,7 +638,7 @@ Apache subdirectory part::
ProxyPass http://127.0.0.1:5000/<someprefix>
ProxyPassReverse http://127.0.0.1:5000/<someprefix>
SetEnvIf X-Url-Scheme https HTTPS=1
- </Location>
+ </Location>
Besides the regular apache setup you will need to add the following line
into [app:main] section of your .ini file::
@@ -639,7 +649,7 @@ Add the following at the end of the .ini file::
[filter:proxy-prefix]
use = egg:PasteDeploy#prefix
- prefix = /<someprefix>
+ prefix = /<someprefix>
then change <someprefix> into your choosen prefix
@@ -675,12 +685,12 @@ Here is a sample excerpt from an Apache Virtual Host configuration file::
WSGIPassAuthorization On
.. note::
- when running apache as root please add: `user=www-data group=www-data`
+ when running apache as root please add: `user=www-data group=www-data`
into above configuration
.. note::
- RhodeCode cannot be runned in multiprocess mode in apache, make sure
- you don't specify `processes=num` directive in the config
+ Running RhodeCode in multiprocess mode in apache is not supported,
+ make sure you don't specify `processes=num` directive in the config
Example wsgi dispatch script::
@@ -688,13 +698,13 @@ Example wsgi dispatch script::
import os
os.environ["HGENCODING"] = "UTF-8"
os.environ['PYTHON_EGG_CACHE'] = '/home/web/rhodecode/.egg-cache'
-
+
# sometimes it's needed to set the curent dir
- os.chdir('/home/web/rhodecode/')
+ os.chdir('/home/web/rhodecode/')
import site
site.addsitedir("/home/web/rhodecode/pyenv/lib/python2.6/site-packages")
-
+
from paste.deploy import loadapp
from paste.script.util.logging_config import fileConfig
@@ -722,4 +732,4 @@ Some example init.d scripts can be found in init.d directory::
.. _mercurial-server: http://www.lshift.net/mercurial-server.html
.. _PublishingRepositories: http://mercurial.selenic.com/wiki/PublishingRepositories
.. _Issues tracker: https://bitbucket.org/marcinkuzminski/rhodecode/issues
-.. _google group rhodecode: http://groups.google.com/group/rhodecode \ No newline at end of file
+.. _google group rhodecode: http://groups.google.com/group/rhodecode
diff --git a/docs/upgrade.rst b/docs/upgrade.rst
index 4e6ea19c..12c8c334 100644
--- a/docs/upgrade.rst
+++ b/docs/upgrade.rst
@@ -8,12 +8,12 @@ Upgrading from PyPI (aka "Cheeseshop")
---------------------------------------
.. note::
- Firstly, it is recommended that you **always** perform a database and
+ Firstly, it is recommended that you **always** perform a database and
configuration backup before doing an upgrade.
-
- (These directions will use '{version}' to note that this is the version of
- Rhodecode that these files were used with. If backing up your RhodeCode
- instance from version 1.3.6 to 1.4.0, the ``production.ini`` file would be
+
+ (These directions will use '{version}' to note that this is the version of
+ Rhodecode that these files were used with. If backing up your RhodeCode
+ instance from version 1.3.6 to 1.4.0, the ``production.ini`` file would be
backed up to ``production.ini.1-3-6``.)
@@ -34,7 +34,7 @@ installed Rhodecode in::
pip freeze
-will list all packages installed in the current environment. If Rhodecode
+will list all packages installed in the current environment. If Rhodecode
isn't listed, change virtual environments to your venv location::
source /opt/rhodecode-venv/bin/activate
@@ -50,11 +50,11 @@ Or::
Then run the following command from the installation directory::
-
+
paster make-config RhodeCode production.ini
-
+
This will display any changes made by the new version of RhodeCode to your
-current configuration. It will try to perform an automerge. It's recommended
+current configuration. It will try to perform an automerge. It's recommended
that you re-check the content after the automerge.
.. note::
@@ -62,7 +62,7 @@ that you re-check the content after the automerge.
caused by missing params added in new versions.
-It is also recommended that you rebuild the whoosh index after upgrading since
+It is also recommended that you rebuild the whoosh index after upgrading since
the new whoosh version could introduce some incompatible index changes. Please
Read the changelog to see if there were any changes to whoosh.
@@ -70,12 +70,20 @@ Read the changelog to see if there were any changes to whoosh.
The final step is to upgrade the database. To do this simply run::
paster upgrade-db production.ini
-
+
This will upgrade the schema and update some of the defaults in the database,
-and will always recheck the settings of the application, if there are no new
+and will always recheck the settings of the application, if there are no new
options that need to be set.
-You may find it helpful to clear out your log file so that new errors are
+
+.. note::
+ DB schema upgrade library has some limitations and can sometimes fail if you try to
+ upgrade from older major releases. In such case simply run upgrades sequentially, eg.
+ upgrading from 1.2.X to 1.5.X should be done like that: 1.2.X. > 1.3.X > 1.4.X > 1.5.X
+ You can always specify what version of RhodeCode you want to install for example in pip
+ `pip install RhodeCode==1.3.6`
+
+You may find it helpful to clear out your log file so that new errors are
readily apparent::
echo > rhodecode.log
@@ -92,7 +100,7 @@ Or::
If you're using Celery, make sure you restart all instances of it after
upgrade.
-.. _virtualenv: http://pypi.python.org/pypi/virtualenv
+.. _virtualenv: http://pypi.python.org/pypi/virtualenv
.. _python: http://www.python.org/
.. _mercurial: http://mercurial.selenic.com/
.. _celery: http://celeryproject.org/
diff --git a/docs/usage/general.rst b/docs/usage/general.rst
index e2991145..bbe101f3 100644
--- a/docs/usage/general.rst
+++ b/docs/usage/general.rst
@@ -11,9 +11,17 @@ Repository deleting
Currently when admin/owner deletes a repository, RhodeCode does not physically
delete a repository from filesystem, it renames it in a special way so it's
not possible to push,clone or access repository. It's worth a notice that,
-even if someone will be given administrative access to RhodeCode and will
+even if someone will be given administrative access to RhodeCode and will
delete a repository You can easy restore such action by restoring `rm__<date>`
-from the repository name, and internal repository storage (.hg/.git)
+from the repository name, and internal repository storage (.hg/.git). There
+is also a special command for cleaning such archived repos::
+
+ paster cleanup-repos --older-than=30d production.ini
+
+This command will scan for archived repositories that are older than 30d,
+display them and ask if you want to delete them (there's a --dont-ask flag also)
+If you host big amount of repositories with forks that are constantly deleted
+it's recommended that you run such command via crontab.
Follow current branch in file view
----------------------------------
@@ -31,7 +39,7 @@ Checkboxes in compare view allow users to view combined compare view. You can
only show the range between the first and last checkbox (no cherry pick).
Clicking more than one checkbox will activate a link in top saying
`Show selected changes <from-rev> -> <to-rev>` clicking this will bring
-compare view
+compare view. In this view also it's possible to switch to combined compare.
Compare view is also available from the journal on pushes having more than
one changeset
@@ -44,21 +52,21 @@ Due to complicated nature of repository grouping, often urls of repositories
can change.
example::
-
+
#before
http://server.com/repo_name
# after insertion to test_group group the url will be
http://server.com/test_group/repo_name
-
+
This can be an issue for build systems and any other hardcoded scripts, moving
-repository to a group leads to a need for changing external systems. To
-overcome this RhodeCode introduces a non changable replacement url. It's
+repository to a group leads to a need for changing external systems. To
+overcome this RhodeCode introduces a non changable replacement url. It's
simply an repository ID prefixed with `_` above urls are also accessible as::
http://server.com/_<ID>
-
+
Since ID are always the same moving the repository will not affect such url.
-the _<ID> syntax can be used anywhere in the system so urls with repo_name
+the _<ID> syntax can be used anywhere in the system so urls with repo_name
for changelogs, files and other can be exchanged with _<ID> syntax.
@@ -71,7 +79,7 @@ on errors the mails will have a detailed traceback of error.
Mails are also sent for code comments. If someone comments on a changeset
-mail is sent to all participants, the person who commited the changeset
+mail is sent to all participants, the person who commited the changeset
(if present in RhodeCode), and to all people mentioned with @mention system.
@@ -96,12 +104,12 @@ Currently it support following options:
.. note::
-
- - *`svn -> hg` cloning requires `hgsubversion` library to be installed.*
+
+ * `svn -> hg` cloning requires `hgsubversion` library to be installed.*
If you need to clone repositories that are protected via basic auth, you
-might pass the url with stored credentials inside eg.
-`http://user:passw@remote.server/repo, RhodeCode will try to login and clone
+might pass the url with stored credentials inside eg.
+`http://user:passw@remote.server/repo`, RhodeCode will try to login and clone
using given credentials. Please take a note that they will be stored as
-plaintext inside the database. RhodeCode will remove auth info when showing the
+plaintext inside the database. RhodeCode will remove auth info when showing the
clone url in summary page.
diff --git a/docs/usage/troubleshooting.rst b/docs/usage/troubleshooting.rst
index c19d1ad4..e038bf07 100644
--- a/docs/usage/troubleshooting.rst
+++ b/docs/usage/troubleshooting.rst
@@ -7,23 +7,23 @@ Troubleshooting
:Q: **Missing static files?**
:A: Make sure either to set the `static_files = true` in the .ini file or
- double check the root path for your http setup. It should point to
+ double check the root path for your http setup. It should point to
for example:
/home/my-virtual-python/lib/python2.6/site-packages/rhodecode/public
-
-|
+
+|
:Q: **Can't install celery/rabbitmq?**
:A: Don't worry RhodeCode works without them too. No extra setup is required.
Try out great celery docs for further help.
|
-
+
:Q: **Long lasting push timeouts?**
:A: Make sure you set a longer timeouts in your proxy/fcgi settings, timeouts
are caused by https server and not RhodeCode.
-
-|
+
+|
:Q: **Large pushes timeouts?**
:A: Make sure you set a proper max_body_size for the http server. Very often
@@ -46,7 +46,7 @@ Troubleshooting
:Q: **How i use hooks in RhodeCode?**
:A: It's easy if they are python hooks just use advanced link in hooks section
in Admin panel, that works only for Mercurial. If you want to use githooks,
- just install proper one in repository eg. create file in
+ just install proper one in repository eg. create file in
`/gitrepo/hooks/pre-receive`. You can also use RhodeCode-extensions to
connect to callback hooks, for both Git and Mercurial.
@@ -55,7 +55,19 @@ Troubleshooting
:Q: **RhodeCode is slow for me, how can i make it faster?**
:A: See the :ref:`performance` section
-For further questions search the `Issues tracker`_, or post a message in the
+|
+
+:Q: **UnicodeDecodeError on Apache mod_wsgi**
+:A: Please read: https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/modwsgi/#if-you-get-a-unicodeencodeerror
+
+|
+
+:Q: **Requests hanging on Windows**
+:A: Please try out with disabled Antivirus software, there are some known problems with Eset Anitivirus. Make sure
+ you have installed latest windows patches (especially KB2789397)
+
+
+For further questions search the `Issues tracker`_, or post a message in the
`google group rhodecode`_
.. _virtualenv: http://pypi.python.org/pypi/virtualenv
@@ -67,4 +79,4 @@ For further questions search the `Issues tracker`_, or post a message in the
.. _mercurial-server: http://www.lshift.net/mercurial-server.html
.. _PublishingRepositories: http://mercurial.selenic.com/wiki/PublishingRepositories
.. _Issues tracker: https://bitbucket.org/marcinkuzminski/rhodecode/issues
-.. _google group rhodecode: http://groups.google.com/group/rhodecode \ No newline at end of file
+.. _google group rhodecode: http://groups.google.com/group/rhodecode
diff --git a/rhodecode/__init__.py b/rhodecode/__init__.py
index 03d8d5df..1fc5e69a 100644
--- a/rhodecode/__init__.py
+++ b/rhodecode/__init__.py
@@ -26,7 +26,7 @@
import sys
import platform
-VERSION = (1, 5, 2)
+VERSION = (1, 5, 4)
try:
from rhodecode.lib import get_current_revision
diff --git a/rhodecode/controllers/admin/repos.py b/rhodecode/controllers/admin/repos.py
index ac9372e0..bc681ff9 100644
--- a/rhodecode/controllers/admin/repos.py
+++ b/rhodecode/controllers/admin/repos.py
@@ -231,8 +231,10 @@ class ReposController(BaseController):
#override the choices with extracted revisions !
choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
c.landing_revs_choices = choices
-
- _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
+ repo = Repository.get_by_repo_name(repo_name)
+ _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
+ 'repo_group': repo.group.get_dict() \
+ if repo.group else {}},
repo_groups=c.repo_groups_choices,
landing_revs=c.landing_revs_choices)()
try:
diff --git a/rhodecode/controllers/feed.py b/rhodecode/controllers/feed.py
index 0e1f9361..283096da 100644
--- a/rhodecode/controllers/feed.py
+++ b/rhodecode/controllers/feed.py
@@ -36,7 +36,7 @@ from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController
from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
from rhodecode.model.db import CacheInvalidation
-from rhodecode.lib.utils2 import safe_int, str2bool
+from rhodecode.lib.utils2 import safe_int, str2bool, safe_unicode
log = logging.getLogger(__name__)
@@ -113,7 +113,7 @@ class FeedController(BaseRepoController):
desc_msg.append('\n\n')
desc_msg.append(diff_processor.as_raw())
desc_msg.append('</pre>')
- return desc_msg
+ return map(safe_unicode, desc_msg)
def atom(self, repo_name):
"""Produce an atom-1.0 feed via feedgenerator module"""
diff --git a/rhodecode/controllers/login.py b/rhodecode/controllers/login.py
index da9c07f8..583d3004 100644
--- a/rhodecode/controllers/login.py
+++ b/rhodecode/controllers/login.py
@@ -126,20 +126,16 @@ class LoginController(BaseController):
@HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
'hg.register.manual_activate')
def register(self):
- c.auto_active = False
- for perm in User.get_by_username('default').user_perms:
- if perm.permission.permission_name == 'hg.register.auto_activate':
- c.auto_active = True
- break
+ c.auto_active = 'hg.register.auto_activate' in User.get_by_username('default')\
+ .AuthUser.permissions['global']
if request.POST:
-
register_form = RegisterForm()()
try:
form_result = register_form.to_python(dict(request.POST))
form_result['active'] = c.auto_active
UserModel().create_registration(form_result)
- h.flash(_('You have successfully registered into rhodecode'),
+ h.flash(_('You have successfully registered into RhodeCode'),
category='success')
Session().commit()
return redirect(url('login_home'))
diff --git a/rhodecode/controllers/settings.py b/rhodecode/controllers/settings.py
index 3e850481..ca509ab5 100644
--- a/rhodecode/controllers/settings.py
+++ b/rhodecode/controllers/settings.py
@@ -107,9 +107,11 @@ class SettingsController(BaseRepoController):
#override the choices with extracted revisions !
choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
c.landing_revs_choices = choices
-
+ repo = Repository.get_by_repo_name(repo_name)
_form = RepoSettingsForm(edit=True,
- old_data={'repo_name': repo_name},
+ old_data={'repo_name': repo_name,
+ 'repo_group': repo.group.get_dict() \
+ if repo.group else {}},
repo_groups=c.repo_groups_choices,
landing_revs=c.landing_revs_choices)()
try:
diff --git a/rhodecode/lib/auth.py b/rhodecode/lib/auth.py
index 151a720e..e2cf7189 100644
--- a/rhodecode/lib/auth.py
+++ b/rhodecode/lib/auth.py
@@ -34,6 +34,7 @@ from decorator import decorator
from pylons import config, url, request
from pylons.controllers.util import abort, redirect
from pylons.i18n.translation import _
+from sqlalchemy.orm.exc import ObjectDeletedError
from rhodecode import __platform__, is_windows, is_unix
from rhodecode.model.meta import Session
@@ -225,6 +226,8 @@ def authenticate(username, password):
'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
'email': get_ldap_attr('ldap_attr_email'),
+ 'active': 'hg.register.auto_activate' in User\
+ .get_by_username('default').AuthUser.permissions['global']
}
# don't store LDAP password since we don't need it. Override
@@ -253,6 +256,8 @@ def login_container_auth(username):
'name': username,
'lastname': None,
'email': None,
+ 'active': 'hg.register.auto_activate' in User\
+ .get_by_username('default').AuthUser.permissions['global']
}
user = UserModel().create_for_container_auth(username, user_attrs)
if not user:
@@ -447,8 +452,13 @@ class AuthUser(object):
user_ips = user_ips.options(FromCache("sql_cache_short",
"get_user_ips_%s" % user_id))
for ip in user_ips:
- _set.add(ip.ip_addr)
- return _set or set(['0.0.0.0/0'])
+ try:
+ _set.add(ip.ip_addr)
+ except ObjectDeletedError:
+ # since we use heavy caching sometimes it happens that we get
+ # deleted objects here, we just skip them
+ pass
+ return _set or set(['0.0.0.0/0', '::/0'])
def set_available_permissions(config):
@@ -990,6 +1000,13 @@ def check_ip_access(source_ip, allowed_ips=None):
log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
if isinstance(allowed_ips, (tuple, list, set)):
for ip in allowed_ips:
- if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
- return True
+ try:
+ if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
+ return True
+ # for any case we cannot determine the IP, don't crash just
+ # skip it and log as error, we want to say forbidden still when
+ # sending bad IP
+ except Exception:
+ log.error(traceback.format_exc())
+ continue
return False
diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py
index 873ece86..1144b8b9 100644
--- a/rhodecode/lib/base.py
+++ b/rhodecode/lib/base.py
@@ -43,15 +43,17 @@ def _get_ip_addr(environ):
ip = environ.get(proxy_key2)
if ip:
- # HTTP_X_FORWARDED_FOR can have mutliple ips inside
- # the left-most being the original client, and each successive proxy
- # that passed the request adding the IP address where it received the
- # request from.
- if ',' in ip:
- ip = ip.split(',')[0].strip()
return ip
ip = environ.get(def_key, '0.0.0.0')
+
+ # HEADERS can have mutliple ips inside
+ # the left-most being the original client, and each successive proxy
+ # that passed the request adding the IP address where it received the
+ # request from.
+ if ',' in ip:
+ ip = ip.split(',')[0].strip()
+
return ip
diff --git a/rhodecode/lib/celerylib/tasks.py b/rhodecode/lib/celerylib/tasks.py
index 0a72f0f0..1e7d678e 100644
--- a/rhodecode/lib/celerylib/tasks.py
+++ b/rhodecode/lib/celerylib/tasks.py
@@ -247,72 +247,6 @@ def get_commits_stats(repo_name, ts_min_y, ts_max_y):
log.info('LockHeld')
return 'Task with key %s already running' % lockkey
-@task(ignore_result=True)
-@dbsession
-def send_password_link(user_email):
- from rhodecode.model.notification import EmailNotificationModel
-
- log = get_logger(send_password_link)
- DBS = get_session()
-
- try:
- user = User.get_by_email(user_email)
- if user:
- log.debug('password reset user found %s' % user)
- link = url('reset_password_confirmation', key=user.api_key,
- qualified=True)
- reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
- body = EmailNotificationModel().get_email_tmpl(reg_type,
- **{'user':user.short_contact,
- 'reset_url':link})
- log.debug('sending email')
- run_task(send_email, user_email,
- _("password reset link"), body)
- log.info('send new password mail to %s' % user_email)
- else:
- log.debug("password reset email %s not found" % user_email)
- except:
- log.error(traceback.format_exc())
- return False
-
- return True
-
-@task(ignore_result=True)
-@dbsession
-def reset_user_password(user_email):
- from rhodecode.lib import auth
-
- log = get_logger(reset_user_password)
- DBS = get_session()
-
- try:
- try:
- user = User.get_by_email(user_email)
- new_passwd = auth.PasswordGenerator().gen_password(8,
- auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
- if user:
- user.password = auth.get_crypt_password(new_passwd)
- user.api_key = auth.generate_api_key(user.username)
- DBS.add(user)
- DBS.commit()
- log.info('change password for %s' % user_email)
- if new_passwd is None:
- raise Exception('unable to generate new password')
- except:
- log.error(traceback.format_exc())
- DBS.rollback()
-
- run_task(send_email, user_email,
- 'Your new password',
- 'Your new RhodeCode password:%s' % (new_passwd))
- log.info('send new password mail to %s' % user_email)
-
- except:
- log.error('Failed to update user password')
- log.error(traceback.format_exc())
-
- return True
-
@task(ignore_result=True)
@dbsession
diff --git a/rhodecode/lib/cleanup.py b/rhodecode/lib/cleanup.py
index d4572c58..0c59da58 100644
--- a/rhodecode/lib/cleanup.py
+++ b/rhodecode/lib/cleanup.py
@@ -91,7 +91,8 @@ class CleanupCommand(BasePasterCommand):
for dn, dirs, f in os.walk(safe_str(repos_location)):
for loc in dirs:
if REMOVED_REPO_PAT.match(loc):
- to_remove.append([loc, self._extract_date(loc)])
+ to_remove.append([os.path.join(dn, loc),
+ self._extract_date(loc)])
#filter older than (if present)!
now = datetime.datetime.now()
diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py
index 2183ae69..2f073611 100644
--- a/rhodecode/lib/helpers.py
+++ b/rhodecode/lib/helpers.py
@@ -750,14 +750,14 @@ HasRepoPermissionAny, HasRepoPermissionAll
def gravatar_url(email_address, size=30):
from pylons import url # doh, we need to re-import url to mock it later
-
- if (not str2bool(config['app_conf'].get('use_gravatar')) or
- not email_address or email_address == 'anonymous@rhodecode.org'):
+ _def = 'anonymous@rhodecode.org'
+ use_gravatar = str2bool(config['app_conf'].get('use_gravatar'))
+ email_address = email_address or _def
+ if (not use_gravatar or not email_address or email_address == _def):
f = lambda a, l: min(l, key=lambda x: abs(x - a))
return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
- if(str2bool(config['app_conf'].get('use_gravatar')) and
- config['app_conf'].get('alternative_gravatar_url')):
+ if use_gravatar and config['app_conf'].get('alternative_gravatar_url'):
tmpl = config['app_conf'].get('alternative_gravatar_url', '')
parsed_url = urlparse.urlparse(url.current(qualified=True))
tmpl = tmpl.replace('{email}', email_address)\
diff --git a/rhodecode/lib/hooks.py b/rhodecode/lib/hooks.py
index 916b49ab..ff0a9781 100644
--- a/rhodecode/lib/hooks.py
+++ b/rhodecode/lib/hooks.py
@@ -382,7 +382,12 @@ def handle_git_receive(repo_path, revs, env, hook_type='post'):
for k, v in extras.items():
baseui.setconfig('rhodecode_extras', k, v)
- repo = repo.scm_instance
+ if hook_type == 'pre':
+ repo = repo.scm_instance
+ else:
+ #post push shouldn't use the cached instance never
+ repo = repo.scm_instance_no_cache
+
repo.ui = baseui
if hook_type == 'pre':
diff --git a/rhodecode/lib/middleware/simplegit.py b/rhodecode/lib/middleware/simplegit.py
index e35f95ae..63a6543c 100644
--- a/rhodecode/lib/middleware/simplegit.py
+++ b/rhodecode/lib/middleware/simplegit.py
@@ -228,11 +228,7 @@ class SimpleGit(BaseVCSController):
self.__inject_extras(repo_path, baseui, extras)
try:
- # invalidate cache on push
- if action == 'push':
- self._invalidate_cache(repo_name)
self._handle_githooks(repo_name, action, baseui, environ)
-
log.info('%s action on GIT repo "%s" by "%s" from %s' %
(action, repo_name, username, ip_addr))
app = self.__make_app(repo_name, repo_path, extras)
@@ -243,6 +239,10 @@ class SimpleGit(BaseVCSController):
except Exception:
log.error(traceback.format_exc())
return HTTPInternalServerError()(environ, start_response)
+ finally:
+ # invalidate cache on push
+ if action == 'push':
+ self._invalidate_cache(repo_name)
def __make_app(self, repo_name, repo_path, extras):
"""
diff --git a/rhodecode/lib/middleware/simplehg.py b/rhodecode/lib/middleware/simplehg.py
index 5d2ef692..12b73d72 100644
--- a/rhodecode/lib/middleware/simplehg.py
+++ b/rhodecode/lib/middleware/simplehg.py
@@ -191,9 +191,6 @@ class SimpleHg(BaseVCSController):
self.__inject_extras(repo_path, baseui, extras)
try:
- # invalidate cache on push
- if action == 'push':
- self._invalidate_cache(repo_name)
log.info('%s action on HG repo "%s" by "%s" from %s' %
(action, repo_name, username, ip_addr))
app = self.__make_app(repo_path, baseui, extras)
@@ -207,6 +204,10 @@ class SimpleHg(BaseVCSController):
except Exception:
log.error(traceback.format_exc())
return HTTPInternalServerError()(environ, start_response)
+ finally:
+ # invalidate cache on push
+ if action == 'push':
+ self._invalidate_cache(repo_name)
def __make_app(self, repo_name, baseui, extras):
"""
diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py
index 24a3ffa6..a0c8fb03 100644
--- a/rhodecode/lib/utils.py
+++ b/rhodecode/lib/utils.py
@@ -171,7 +171,7 @@ def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
raise
-def get_repos(path, recursive=False):
+def get_repos(path, recursive=False, skip_removed_repos=True):
"""
Scans given path for repos and return (name,(type,path)) tuple
@@ -181,6 +181,7 @@ def get_repos(path, recursive=False):
# remove ending slash for better results
path = path.rstrip(os.sep)
+ log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
def _get_repos(p):
if not os.access(p, os.W_OK):
@@ -189,6 +190,15 @@ def get_repos(path, recursive=False):
if os.path.isfile(os.path.join(p, dirpath)):
continue
cur_path = os.path.join(p, dirpath)
+
+ # skip removed repos
+ if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
+ continue
+
+ #skip .<somethin> dirs
+ if dirpath.startswith('.'):
+ continue
+
try:
scm_info = get_scm(cur_path)
yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
@@ -203,6 +213,9 @@ def get_repos(path, recursive=False):
return _get_repos(path)
+#alias for backward compat
+get_filesystem_repos = get_repos
+
def is_valid_repo(repo_name, base_path, scm=None):
"""
diff --git a/rhodecode/lib/utils2.py b/rhodecode/lib/utils2.py
index 890effce..46c022fc 100644
--- a/rhodecode/lib/utils2.py
+++ b/rhodecode/lib/utils2.py
@@ -182,7 +182,7 @@ def safe_int(val, default=None):
try:
val = int(val)
- except ValueError:
+ except (ValueError, TypeError):
val = default
return val
@@ -557,11 +557,15 @@ def fix_PATH(os_=None):
def obfuscate_url_pw(engine):
- from sqlalchemy.engine import url
- url = url.make_url(engine)
- if url.password:
- url.password = 'XXXXX'
- return str(url)
+ _url = engine or ''
+ from sqlalchemy.engine import url as sa_url
+ try:
+ _url = sa_url.make_url(engine)
+ if _url.password:
+ _url.password = 'XXXXX'
+ except:
+ pass
+ return str(_url)
def get_server_url(environ):
diff --git a/rhodecode/lib/vcs/utils/helpers.py b/rhodecode/lib/vcs/utils/helpers.py
index 6c1d50cf..20b1bf1b 100644
--- a/rhodecode/lib/vcs/utils/helpers.py
+++ b/rhodecode/lib/vcs/utils/helpers.py
@@ -80,7 +80,7 @@ def get_scms_for_path(path):
continue
dirname = os.path.join(path, 'rm__.' + key)
if os.path.isdir(dirname):
- return [None]
+ return result
# We still need to check if it's not bare repository as
# bare repos don't have working directories
try:
@@ -131,6 +131,7 @@ def get_highlighted_code(name, code, type='terminal'):
content = code
return content
+
def parse_changesets(text):
"""
Returns dictionary with *start*, *main* and *end* ids.
diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py
index 6ff96099..c17a55fa 100644
--- a/rhodecode/model/comment.py
+++ b/rhodecode/model/comment.py
@@ -88,7 +88,6 @@ class ChangesetCommentsModel(BaseModel):
if revision:
cs = repo.scm_instance.get_changeset(revision)
desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
- author_email = cs.author_email
comment.revision = revision
elif pull_request:
pull_request = self.__get_pull_request(pull_request)
@@ -122,7 +121,11 @@ class ChangesetCommentsModel(BaseModel):
# get the current participants of this changeset
recipients = ChangesetComment.get_users(revision=revision)
# add changeset author if it's in rhodecode system
- recipients += [User.get_by_email(author_email)]
+ cs_author = User.get_from_cs_author(cs.author)
+ if not cs_author:
+ #use repo owner if we cannot extract the author correctly
+ cs_author = repo.user
+ recipients += [cs_author]
email_kwargs = {
'status_change': status_change,
}
diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py
index 7a2924e4..38be5dfb 100755
--- a/rhodecode/model/db.py
+++ b/rhodecode/model/db.py
@@ -399,6 +399,15 @@ class User(Base, BaseModel):
def is_admin(self):
return self.admin
+ @property
+ def AuthUser(self):
+ """
+ Returns instance of AuthUser for this user
+ """
+ from rhodecode.lib.auth import AuthUser
+ return AuthUser(user_id=self.user_id, api_key=self.api_key,
+ username=self.username)
+
def __unicode__(self):
return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
self.user_id, self.username)
@@ -454,6 +463,26 @@ class User(Base, BaseModel):
return ret
+ @classmethod
+ def get_from_cs_author(cls, author):
+ """
+ Tries to get User objects out of commit author string
+
+ :param author:
+ """
+ from rhodecode.lib.helpers import email, author_name
+ # Valid email in the attribute passed, see if they're in the system
+ _email = email(author)
+ if _email:
+ user = cls.get_by_email(_email, case_insensitive=True)
+ if user:
+ return user
+ # Maybe we can match by username?
+ _author = author_name(author)
+ user = cls.get_by_username(_author, case_insensitive=True)
+ if user:
+ return user
+
def update_lastlogin(self):
"""Update user lastlogin"""
self.last_login = datetime.datetime.now()
@@ -542,7 +571,7 @@ class UserIpMap(Base, BaseModel):
@classmethod
def _get_ip_range(cls, ip_addr):
from rhodecode.lib import ipaddr
- net = ipaddr.IPv4Network(ip_addr)
+ net = ipaddr.IPNetwork(address=ip_addr)
return [str(net.network), str(net.broadcast)]
def __json__(self):
@@ -983,13 +1012,18 @@ class Repository(Base, BaseModel):
if isinstance(cs_cache, BaseChangeset):
cs_cache = cs_cache.__json__()
- if cs_cache != self.changeset_cache:
- last_change = cs_cache.get('date') or self.last_change
+ if (cs_cache != self.changeset_cache
+ or not self.last_change
+ or not self.changeset_cache):
+ _default = datetime.datetime.fromtimestamp(0)
+ last_change = cs_cache.get('date') or self.last_change or _default
log.debug('updated repo %s with new cs cache %s' % (self, cs_cache))
self.updated_on = last_change
self.changeset_cache = cs_cache
Session().add(self)
Session().commit()
+ else:
+ log.debug('Skipping repo:%s already with latest changes' % self)
@property
def tip(self):
@@ -1066,6 +1100,10 @@ class Repository(Base, BaseModel):
CacheInvalidation.set_invalidate(repo_name=self.repo_name)
@LazyProperty
+ def scm_instance_no_cache(self):
+ return self.__get_instance()
+
+ @LazyProperty
def scm_instance(self):
import rhodecode
full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
@@ -1249,15 +1287,13 @@ class RepoGroup(Base, BaseModel):
return cnt + children_count(self)
- def recursive_groups_and_repos(self):
- """
- Recursive return all groups, with repositories in those groups
- """
+ def _recursive_objects(self, include_repos=True):
all_ = []
def _get_members(root_gr):
- for r in root_gr.repositories:
- all_.append(r)
+ if include_repos:
+ for r in root_gr.repositories:
+ all_.append(r)
childs = root_gr.children.all()
if childs:
for gr in childs:
@@ -1267,6 +1303,18 @@ class RepoGroup(Base, BaseModel):
_get_members(self)
return [self] + all_
+ def recursive_groups_and_repos(self):
+ """
+ Recursive return all groups, with repositories in those groups
+ """
+ return self._recursive_objects()
+
+ def recursive_groups(self):
+ """
+ Returns all children groups for this group including children of children
+ """
+ return self._recursive_objects(include_repos=False)
+
def get_new_name(self, group_name):
"""
returns new full group name based on parent and new name
diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py
index d7c9d38f..75eda30f 100644
--- a/rhodecode/model/forms.py
+++ b/rhodecode/model/forms.py
@@ -176,7 +176,7 @@ def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
filter_extra_fields = False
repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
v.SlugifyName())
- repo_group = All(v.CanWriteGroup(),
+ repo_group = All(v.CanWriteGroup(old_data),
v.OneOf(repo_groups, hideList=True))
repo_type = v.OneOf(supported_backends)
repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
@@ -205,7 +205,7 @@ def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()
filter_extra_fields = False
repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
v.SlugifyName())
- repo_group = All(v.CanWriteGroup(),
+ repo_group = All(v.CanWriteGroup(old_data),
v.OneOf(repo_groups, hideList=True))
repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
repo_private = v.StringBoolean(if_missing=False)
diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py
index 18db88ce..edbbd180 100644
--- a/rhodecode/model/repo.py
+++ b/rhodecode/model/repo.py
@@ -32,7 +32,7 @@ from datetime import datetime
from rhodecode.lib.vcs.backends import get_backend
from rhodecode.lib.compat import json
from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
- remove_prefix
+ remove_prefix, obfuscate_url_pw
from rhodecode.lib.caching_query import FromCache
from rhodecode.lib.hooks import log_create_repository, log_delete_repository
@@ -43,7 +43,6 @@ from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import HasRepoPermissionAny
-
log = logging.getLogger(__name__)
@@ -624,7 +623,8 @@ class RepoModel(BaseModel):
raise Exception('This path %s is a valid group' % repo_path)
log.info('creating repo %s in %s @ %s' % (
- repo_name, safe_unicode(repo_path), clone_uri
+ repo_name, safe_unicode(repo_path),
+ obfuscate_url_pw(clone_uri)
)
)
backend = get_backend(alias)
diff --git a/rhodecode/model/repos_group.py b/rhodecode/model/repos_group.py
index d4162b39..2ec9d1d6 100644
--- a/rhodecode/model/repos_group.py
+++ b/rhodecode/model/repos_group.py
@@ -175,6 +175,10 @@ class ReposGroupModel(BaseModel):
repos_group=obj, user=user, perm=perm
)
elif isinstance(obj, Repository):
+ #we do this ONLY IF repository is non-private
+ if obj.private:
+ return
+
# we set group permission but we have to switch to repo
# permission
perm = perm.replace('group.', 'repository.')
@@ -199,6 +203,7 @@ class ReposGroupModel(BaseModel):
% (repos_group, recursive))
for obj in repos_group.recursive_groups_and_repos():
+ #obj is an instance of a group or repositories in that group
if not recursive:
obj = repos_group
@@ -239,27 +244,37 @@ class ReposGroupModel(BaseModel):
# change properties
repos_group.group_description = form_data['group_description']
- repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
repos_group.group_parent_id = form_data['group_parent_id']
repos_group.enable_locking = form_data['enable_locking']
+
+ repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
new_path = repos_group.full_path
-
self.sa.add(repos_group)
- # iterate over all members of this groups and set the locking !
+ # iterate over all members of this groups and do fixes
+ # set locking if given
+ # if obj is a repoGroup also fix the name of the group according
+ # to the parent
+ # if obj is a Repo fix it's name
# this can be potentially heavy operation
for obj in repos_group.recursive_groups_and_repos():
#set the value from it's parent
obj.enable_locking = repos_group.enable_locking
+ if isinstance(obj, RepoGroup):
+ new_name = obj.get_new_name(obj.name)
+ log.debug('Fixing group %s to new name %s' \
+ % (obj.group_name, new_name))
+ obj.group_name = new_name
+ elif isinstance(obj, Repository):
+ # we need to get all repositories from this new group and
+ # rename them accordingly to new group path
+ new_name = obj.get_new_name(obj.just_name)
+ log.debug('Fixing repo %s to new name %s' \
+ % (obj.repo_name, new_name))
+ obj.repo_name = new_name
self.sa.add(obj)
- # we need to get all repositories from this new group and
- # rename them accordingly to new group path
- for r in repos_group.repositories:
- r.repo_name = r.get_new_name(r.just_name)
- self.sa.add(r)
-
self.__rename_group(old_path, new_path)
return repos_group
diff --git a/rhodecode/model/scm.py b/rhodecode/model/scm.py
index 4490293e..b21b11c8 100644
--- a/rhodecode/model/scm.py
+++ b/rhodecode/model/scm.py
@@ -46,7 +46,7 @@ from rhodecode import BACKENDS
from rhodecode.lib import helpers as h
from rhodecode.lib.utils2 import safe_str, safe_unicode
from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
-from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
+from rhodecode.lib.utils import get_filesystem_repos, make_ui, \
action_logger, REMOVED_REPO_PAT
from rhodecode.model import BaseModel
from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
@@ -224,10 +224,6 @@ class ScmModel(BaseModel):
repos = {}
for name, path in get_filesystem_repos(repos_path, recursive=True):
- # skip removed repos
- if REMOVED_REPO_PAT.match(name) or path[0] is None:
- continue
-
# name need to be decomposed and put back together using the /
# since this is internal storage separator for rhodecode
name = Repository.normalize_repo_name(name)
@@ -247,7 +243,7 @@ class ScmModel(BaseModel):
repos[name] = klass(path[1])
except OSError:
continue
-
+ log.debug('found %s paths with repositories' % (len(repos)))
return repos
def get_repos(self, all_repos=None, sort_key=None, simple=False):
diff --git a/rhodecode/model/user.py b/rhodecode/model/user.py
index 453c6472..956d4a29 100644
--- a/rhodecode/model/user.py
+++ b/rhodecode/model/user.py
@@ -42,6 +42,7 @@ from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
UserEmailMap, UserIpMap
from rhodecode.lib.exceptions import DefaultUserException, \
UserOwnsReposException
+from rhodecode.model.meta import Session
log = logging.getLogger(__name__)
@@ -316,11 +317,61 @@ class UserModel(BaseModel):
def reset_password_link(self, data):
from rhodecode.lib.celerylib import tasks, run_task
- run_task(tasks.send_password_link, data['email'])
+ from rhodecode.model.notification import EmailNotificationModel
+ user_email = data['email']
+ try:
+ user = User.get_by_email(user_email)
+ if user:
+ log.debug('password reset user found %s' % user)
+ link = url('reset_password_confirmation', key=user.api_key,
+ qualified=True)
+ reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
+ body = EmailNotificationModel().get_email_tmpl(reg_type,
+ **{'user': user.short_contact,
+ 'reset_url': link})
+ log.debug('sending email')
+ run_task(tasks.send_email, user_email,
+ _("password reset link"), body, body)
+ log.info('send new password mail to %s' % user_email)
+ else:
+ log.debug("password reset email %s not found" % user_email)
+ except:
+ log.error(traceback.format_exc())
+ return False
+
+ return True
def reset_password(self, data):
from rhodecode.lib.celerylib import tasks, run_task
- run_task(tasks.reset_user_password, data['email'])
+ from rhodecode.lib import auth
+ user_email = data['email']
+ try:
+ try:
+ user = User.get_by_email(user_email)
+ new_passwd = auth.PasswordGenerator().gen_password(8,
+ auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
+ if user:
+ user.password = auth.get_crypt_password(new_passwd)
+ user.api_key = auth.generate_api_key(user.username)
+ Session().add(user)
+ Session().commit()
+ log.info('change password for %s' % user_email)
+ if new_passwd is None:
+ raise Exception('unable to generate new password')
+ except:
+ log.error(traceback.format_exc())
+ Session().rollback()
+
+ run_task(tasks.send_email, user_email,
+ _('Your new password'),
+ _('Your new RhodeCode password:%s') % (new_passwd))
+ log.info('send new password mail to %s' % user_email)
+
+ except:
+ log.error('Failed to update user password')
+ log.error(traceback.format_exc())
+
+ return True
def fill_data(self, auth_user, user_id=None, api_key=None):
"""
diff --git a/rhodecode/model/validators.py b/rhodecode/model/validators.py
index 61ed0583..18825bb6 100644
--- a/rhodecode/model/validators.py
+++ b/rhodecode/model/validators.py
@@ -14,12 +14,14 @@ from formencode.validators import (
NotEmpty, IPAddress, CIDR
)
from rhodecode.lib.compat import OrderedSet
+from rhodecode.lib import ipaddr
from rhodecode.lib.utils import repo_name_slug
+from rhodecode.lib.utils2 import safe_int
from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User,\
ChangesetStatus
from rhodecode.lib.exceptions import LdapImportError
from rhodecode.config.routing import ADMIN_PREFIX
-from rhodecode.lib.auth import HasReposGroupPermissionAny
+from rhodecode.lib.auth import HasReposGroupPermissionAny, HasPermissionAny
# silence warnings and pylint
UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
@@ -415,6 +417,8 @@ def ValidCloneUri():
svnremoterepo(ui, url).capabilities
elif url.startswith('git+http'):
raise NotImplementedError()
+ else:
+ raise Exception('clone from URI %s not allowed' % (url))
elif repo_type == 'git':
from rhodecode.lib.vcs.backends.git.repository import GitRepository
@@ -426,6 +430,8 @@ def ValidCloneUri():
raise NotImplementedError()
elif url.startswith('hg+http'):
raise NotImplementedError()
+ else:
+ raise Exception('clone from URI %s not allowed' % (url))
class _validator(formencode.validators.FancyValidator):
messages = {
@@ -467,7 +473,7 @@ def ValidForkType(old_data={}):
return _validator
-def CanWriteGroup():
+def CanWriteGroup(old_data=None):
class _validator(formencode.validators.FancyValidator):
messages = {
'permission_denied': _(u"You don't have permissions "
@@ -476,13 +482,58 @@ def CanWriteGroup():
def validate_python(self, value, state):
gr = RepoGroup.get(value)
- if not HasReposGroupPermissionAny(
- 'group.write', 'group.admin'
- )(gr.group_name, 'get group of repo form'):
+ gr_name = gr.group_name if gr else None # None means ROOT location
+ val = HasReposGroupPermissionAny('group.write', 'group.admin')
+ can_create_repos = HasPermissionAny('hg.admin', 'hg.create.repository')
+ forbidden = not val(gr_name, 'can write into group validator')
+ value_changed = old_data['repo_group'].get('group_id') != safe_int(value)
+ if value_changed: # do check if we changed the value
+ #parent group need to be existing
+ if gr and forbidden:
+ msg = M(self, 'permission_denied', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(repo_type=msg)
+ )
+ ## check if we can write to root location !
+ elif gr is None and can_create_repos() is False:
+ msg = M(self, 'permission_denied_root', state)
+ raise formencode.Invalid(msg, value, state,
+ error_dict=dict(repo_type=msg)
+ )
+
+ return _validator
+
+
+def CanCreateGroup(can_create_in_root=False):
+ class _validator(formencode.validators.FancyValidator):
+ messages = {
+ 'permission_denied': _(u"You don't have permissions "
+ "to create a group in this location")
+ }
+
+ def to_python(self, value, state):
+ #root location
+ if value in [-1, "-1"]:
+ return None
+ return value
+
+ def validate_python(self, value, state):
+ gr = RepoGroup.get(value)
+ gr_name = gr.group_name if gr else None # None means ROOT location
+
+ if can_create_in_root and gr is None:
+ #we can create in root, we're fine no validations required
+ return
+
+ forbidden_in_root = gr is None and can_create_in_root is False
+ val = HasReposGroupPermissionAny('group.admin')
+ forbidden = not val(gr_name, 'can create group validator')
+ if forbidden_in_root or forbidden:
msg = M(self, 'permission_denied', state)
raise formencode.Invalid(msg, value, state,
- error_dict=dict(repo_type=msg)
+ error_dict=dict(group_parent_id=msg)
)
+
return _validator
@@ -532,7 +583,7 @@ def ValidPerms(type_='repo'):
'g': 'users_group'
}[k[0]]
if member == 'default':
- if value.get('private'):
+ if value.get('repo_private'):
# set none for default when updating to
# private repo
v = EMPTY_PERM
@@ -711,35 +762,31 @@ def NotReviewedRevisions(repo_id):
def ValidIp():
class _validator(CIDR):
messages = dict(
- badFormat=_('Please enter a valid IP address (a.b.c.d)'),
- illegalOctets=_('The octets must be within the range of 0-255'
- ' (not %(octet)r)'),
+ badFormat=_('Please enter a valid IPv4 or IpV6 address'),
illegalBits=_('The network size (bits) must be within the range'
' of 0-32 (not %(bits)r)'))
+ def to_python(self, value, state):
+ v = super(_validator, self).to_python(value, state)
+ v = v.strip()
+ net = ipaddr.IPNetwork(address=v)
+ if isinstance(net, ipaddr.IPv4Network):
+ #if IPv4 doesn't end with a mask, add /32
+ if '/' not in value:
+ v += '/32'
+ if isinstance(net, ipaddr.IPv6Network):
+ #if IPv6 doesn't end with a mask, add /128
+ if '/' not in value:
+ v += '/128'
+ return v
+
def validate_python(self, value, state):
try:
- # Split into octets and bits
- if '/' in value: # a.b.c.d/e
- addr, bits = value.split('/')
- else: # a.b.c.d
- addr, bits = value, 32
- # Use IPAddress validator to validate the IP part
- IPAddress.validate_python(self, addr, state)
- # Bits (netmask) correct?
- if not 0 <= int(bits) <= 32:
- raise formencode.Invalid(
- self.message('illegalBits', state, bits=bits),
- value, state)
- # Splitting faild: wrong syntax
+ addr = value.strip()
+ #this raises an ValueError if address is not IpV4 or IpV6
+ ipaddr.IPNetwork(address=addr)
except ValueError:
raise formencode.Invalid(self.message('badFormat', state),
value, state)
- def to_python(self, value, state):
- v = super(_validator, self).to_python(value, state)
- #if IP doesn't end with a mask, add /32
- if '/' not in value:
- v += '/32'
- return v
return _validator
diff --git a/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html b/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html
index 17bcfcad..8b292f1a 100644
--- a/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html
+++ b/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html
@@ -71,7 +71,7 @@
<tr>
<td colspan="6">
${h.checkbox('recursive',value="True", label=_('apply to children'))}
- <span class="help-block">${_('Set or revoke permission to all children of that group, including repositories and other groups')}</span>
+ <span class="help-block">${_('Set or revoke permission to all children of that group, including non-private repositories and other groups')}</span>
</td>
</tr>
</table>
diff --git a/rhodecode/templates/email_templates/password_reset.html b/rhodecode/templates/email_templates/password_reset.html
index 3978f3ec..936f9941 100644
--- a/rhodecode/templates/email_templates/password_reset.html
+++ b/rhodecode/templates/email_templates/password_reset.html
@@ -1,12 +1,11 @@
## -*- coding: utf-8 -*-
<%inherit file="main.html"/>
-${_('Hello')} ${user}
-
-${_('We received a request to create a new password for your account.')}
-
-${_('You can generate it by clicking following URL')}:
-
+<h4>${_('Hello %s') % user}</h4>
+<div>${_('We received a request to create a new password for your account.')}</div>
+<div>${_('You can generate it by clicking following URL')}:</div>
+<pre>
${reset_url}
-
-${_("If you didn't request new password please ignore this email.")}
+</pre>
+<br/>
+${_("If you did not request new password please ignore this email.")}
diff --git a/rhodecode/tests/__init__.py b/rhodecode/tests/__init__.py
index f5b3232e..b419d805 100644
--- a/rhodecode/tests/__init__.py
+++ b/rhodecode/tests/__init__.py
@@ -47,7 +47,8 @@ __all__ = [
'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO',
'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO',
'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO',
- 'GIT_REMOTE_REPO', 'SCM_TESTS', '_get_repo_create_params'
+ 'GIT_REMOTE_REPO', 'SCM_TESTS', '_get_repo_create_params',
+ '_get_group_create_params'
]
# Invoke websetup with the current config file
@@ -182,3 +183,18 @@ def _get_repo_create_params(**custom):
defs.update({'repo_name_full': defs['repo_name']})
return defs
+
+
+def _get_group_create_params(**custom):
+ defs = dict(
+ group_name=None,
+ group_description='DESC',
+ group_parent_id=None,
+ perms_updates=[],
+ perms_new=[],
+ enable_locking=False,
+ recursive=False
+ )
+ defs.update(custom)
+
+ return defs
diff --git a/rhodecode/tests/functional/test_login.py b/rhodecode/tests/functional/test_login.py
index 2b6258eb..a119cc24 100644
--- a/rhodecode/tests/functional/test_login.py
+++ b/rhodecode/tests/functional/test_login.py
@@ -215,7 +215,7 @@ class TestLoginController(TestController):
'lastname': lastname,
'admin': True}) # This should be overriden
self.assertEqual(response.status, '302 Found')
- self.checkSessionFlash(response, 'You have successfully registered into rhodecode')
+ self.checkSessionFlash(response, 'You have successfully registered into RhodeCode')
ret = self.Session().query(User).filter(User.username == 'test_regular4').one()
self.assertEqual(ret.username, username)
diff --git a/rhodecode/tests/models/common.py b/rhodecode/tests/models/common.py
index 377a05ee..017e9255 100644
--- a/rhodecode/tests/models/common.py
+++ b/rhodecode/tests/models/common.py
@@ -25,10 +25,11 @@ def _make_group(path, desc='desc', parent_id=None,
return gr
-def _make_repo(name, repos_group=None, repo_type='hg'):
+def _make_repo(name, repos_group=None, repo_type='hg', private=False):
return RepoModel().create_repo(name, repo_type, 'desc',
TEST_USER_ADMIN_LOGIN,
- repos_group=repos_group)
+ repos_group=repos_group,
+ private=private)
def _destroy_project_tree(test_u1_id):
@@ -67,6 +68,7 @@ def _create_project_tree():
|__[g0_3] 1 repo
|
|_<g0/g0_3/g0_3_r1>
+ |_<g0/g0_3/g0_3_r2_private>
"""
test_u1 = UserModel().create_or_update(
@@ -84,6 +86,8 @@ def _create_project_tree():
g0_2_r2 = _make_repo('g0/g0_2/g0_2_r2', repos_group=g0_2)
g0_3 = _make_group('g0_3', parent_id=g0)
g0_3_r1 = _make_repo('g0/g0_3/g0_3_r1', repos_group=g0_3)
+ g0_3_r2_private = _make_repo('g0/g0_3/g0_3_r1_private', repos_group=g0_3,
+ private=True)
return test_u1
diff --git a/rhodecode/tests/models/test_repos_groups.py b/rhodecode/tests/models/test_repos_groups.py
index 14af83f9..fa016816 100644
--- a/rhodecode/tests/models/test_repos_groups.py
+++ b/rhodecode/tests/models/test_repos_groups.py
@@ -21,6 +21,33 @@ def _make_group(path, desc='desc', parent_id=None,
return gr
+def _update_group(id_, group_name, desc='desc', parent_id=None):
+ form_data = _get_group_create_params(group_name=group_name,
+ group_desc=desc,
+ group_parent_id=parent_id)
+ gr = ReposGroupModel().update(id_, form_data)
+ return gr
+
+
+def _make_repo(name, **kwargs):
+ form_data = _get_repo_create_params(repo_name=name, **kwargs)
+ cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
+ r = RepoModel().create(form_data, cur_user)
+ return r
+
+
+def _update_repo(name, **kwargs):
+ form_data = _get_repo_create_params(**kwargs)
+ if not 'repo_name' in kwargs:
+ form_data['repo_name'] = name
+ if not 'perms_new' in kwargs:
+ form_data['perms_new'] = []
+ if not 'perms_updates' in kwargs:
+ form_data['perms_updates'] = []
+ r = RepoModel().update(name, **form_data)
+ return r
+
+
class TestReposGroups(unittest.TestCase):
def setUp(self):
@@ -32,7 +59,7 @@ class TestReposGroups(unittest.TestCase):
Session().commit()
def tearDown(self):
- print 'out'
+ Session.remove()
def __check_path(self, *path):
"""
@@ -48,21 +75,9 @@ class TestReposGroups(unittest.TestCase):
def __delete_group(self, id_):
ReposGroupModel().delete(id_)
- def __update_group(self, id_, path, desc='desc', parent_id=None):
- form_data = dict(
- group_name=path,
- group_description=desc,
- group_parent_id=parent_id,
- perms_updates=[],
- perms_new=[],
- enable_locking=False,
- recursive=False
- )
- gr = ReposGroupModel().update(id_, form_data)
- return gr
-
def test_create_group(self):
g = _make_group('newGroup')
+ Session().commit()
self.assertEqual(g.full_path, 'newGroup')
self.assertTrue(self.__check_path('newGroup'))
@@ -73,23 +88,27 @@ class TestReposGroups(unittest.TestCase):
def test_same_subgroup(self):
sg1 = _make_group('sub1', parent_id=self.g1.group_id)
+ Session().commit()
self.assertEqual(sg1.parent_group, self.g1)
self.assertEqual(sg1.full_path, 'test1/sub1')
self.assertTrue(self.__check_path('test1', 'sub1'))
ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
+ Session().commit()
self.assertEqual(ssg1.parent_group, sg1)
self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
def test_remove_group(self):
sg1 = _make_group('deleteme')
+ Session().commit()
self.__delete_group(sg1.group_id)
self.assertEqual(RepoGroup.get(sg1.group_id), None)
self.assertFalse(self.__check_path('deteteme'))
sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
+ Session().commit()
self.__delete_group(sg1.group_id)
self.assertEqual(RepoGroup.get(sg1.group_id), None)
@@ -97,24 +116,26 @@ class TestReposGroups(unittest.TestCase):
def test_rename_single_group(self):
sg1 = _make_group('initial')
+ Session().commit()
- new_sg1 = self.__update_group(sg1.group_id, 'after')
+ new_sg1 = _update_group(sg1.group_id, 'after')
self.assertTrue(self.__check_path('after'))
self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
def test_update_group_parent(self):
sg1 = _make_group('initial', parent_id=self.g1.group_id)
+ Session().commit()
- new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
+ new_sg1 = _update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
self.assertTrue(self.__check_path('test1', 'after'))
self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
- new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
+ new_sg1 = _update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
self.assertTrue(self.__check_path('test3', 'after'))
self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
- new_sg1 = self.__update_group(sg1.group_id, 'hello')
+ new_sg1 = _update_group(sg1.group_id, 'hello')
self.assertTrue(self.__check_path('hello'))
self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
@@ -123,23 +144,17 @@ class TestReposGroups(unittest.TestCase):
g1 = _make_group('g1')
g2 = _make_group('g2')
-
+ Session().commit()
# create new repo
- form_data = _get_repo_create_params(repo_name='john')
- cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
- r = RepoModel().create(form_data, cur_user)
-
+ r = _make_repo('john')
+ Session().commit()
self.assertEqual(r.repo_name, 'john')
-
# put repo into group
- form_data = form_data
- form_data['repo_group'] = g1.group_id
- form_data['perms_new'] = []
- form_data['perms_updates'] = []
- RepoModel().update(r.repo_name, **form_data)
+ r = _update_repo('john', repo_group=g1.group_id)
+ Session().commit()
self.assertEqual(r.repo_name, 'g1/john')
- self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
+ _update_group(g1.group_id, 'g1', parent_id=g2.group_id)
self.assertTrue(self.__check_path('g2', 'g1'))
# test repo
@@ -155,7 +170,7 @@ class TestReposGroups(unittest.TestCase):
self.assertEqual(g2.full_path, 't11/t22')
self.assertTrue(self.__check_path('t11', 't22'))
- g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
+ g2 = _update_group(g2.group_id, 'g22', parent_id=None)
Session().commit()
self.assertEqual(g2.group_name, 'g22')
@@ -163,3 +178,65 @@ class TestReposGroups(unittest.TestCase):
self.assertEqual(g2.full_path, 'g22')
self.assertFalse(self.__check_path('t11', 't22'))
self.assertTrue(self.__check_path('g22'))
+
+ def test_rename_top_level_group_in_nested_setup(self):
+ g1 = _make_group('L1')
+ Session().commit()
+ g2 = _make_group('L2', parent_id=g1.group_id)
+ Session().commit()
+ g3 = _make_group('L3', parent_id=g2.group_id)
+ Session().commit()
+
+ r = _make_repo('L1/L2/L3/L3_REPO', repo_group=g3.group_id)
+ Session().commit()
+
+ ##rename L1 all groups should be now changed
+ _update_group(g1.group_id, 'L1_NEW')
+ Session().commit()
+ self.assertEqual(g1.full_path, 'L1_NEW')
+ self.assertEqual(g2.full_path, 'L1_NEW/L2')
+ self.assertEqual(g3.full_path, 'L1_NEW/L2/L3')
+ self.assertEqual(r.repo_name, 'L1_NEW/L2/L3/L3_REPO')
+
+ def test_change_parent_of_top_level_group_in_nested_setup(self):
+ g1 = _make_group('R1')
+ Session().commit()
+ g2 = _make_group('R2', parent_id=g1.group_id)
+ Session().commit()
+ g3 = _make_group('R3', parent_id=g2.group_id)
+ Session().commit()
+
+ g4 = _make_group('R1_NEW')
+ Session().commit()
+
+ r = _make_repo('R1/R2/R3/R3_REPO', repo_group=g3.group_id)
+ Session().commit()
+ ##rename L1 all groups should be now changed
+ _update_group(g1.group_id, 'R1', parent_id=g4.group_id)
+ Session().commit()
+ self.assertEqual(g1.full_path, 'R1_NEW/R1')
+ self.assertEqual(g2.full_path, 'R1_NEW/R1/R2')
+ self.assertEqual(g3.full_path, 'R1_NEW/R1/R2/R3')
+ self.assertEqual(r.repo_name, 'R1_NEW/R1/R2/R3/R3_REPO')
+
+ def test_change_parent_of_top_level_group_in_nested_setup_with_rename(self):
+ g1 = _make_group('X1')
+ Session().commit()
+ g2 = _make_group('X2', parent_id=g1.group_id)
+ Session().commit()
+ g3 = _make_group('X3', parent_id=g2.group_id)
+ Session().commit()
+
+ g4 = _make_group('X1_NEW')
+ Session().commit()
+
+ r = _make_repo('X1/X2/X3/X3_REPO', repo_group=g3.group_id)
+ Session().commit()
+
+ ##rename L1 all groups should be now changed
+ _update_group(g1.group_id, 'X1_PRIM', parent_id=g4.group_id)
+ Session().commit()
+ self.assertEqual(g1.full_path, 'X1_NEW/X1_PRIM')
+ self.assertEqual(g2.full_path, 'X1_NEW/X1_PRIM/X2')
+ self.assertEqual(g3.full_path, 'X1_NEW/X1_PRIM/X2/X3')
+ self.assertEqual(r.repo_name, 'X1_NEW/X1_PRIM/X2/X3/X3_REPO')
diff --git a/rhodecode/tests/models/test_user_permissions_on_groups.py b/rhodecode/tests/models/test_user_permissions_on_groups.py
index 6acf50c5..a034dcc8 100644
--- a/rhodecode/tests/models/test_user_permissions_on_groups.py
+++ b/rhodecode/tests/models/test_user_permissions_on_groups.py
@@ -101,7 +101,10 @@ def test_user_permissions_on_group_with_recursive_mode():
_check_expected_count(items, repo_items, expected_count(group, True))
for name, perm in repo_items:
- yield check_tree_perms, name, perm, group, 'repository.write'
+ if name == 'g0/g0_3/g0_3_r1_private':
+ yield check_tree_perms, name, perm, group, 'repository.none'
+ else:
+ yield check_tree_perms, name, perm, group, 'repository.write'
for name, perm in items:
yield check_tree_perms, name, perm, group, 'group.write'
diff --git a/rhodecode/tests/scripts/test_vcs_operations.py b/rhodecode/tests/scripts/test_vcs_operations.py
index 1ff86130..6fafc76e 100755
--- a/rhodecode/tests/scripts/test_vcs_operations.py
+++ b/rhodecode/tests/scripts/test_vcs_operations.py
@@ -112,7 +112,7 @@ def _add_files_and_push(vcs, DEST, **kwargs):
i, 'Marcin Kuźminski <marcin@python-blog.com>', added_file
)
elif vcs == 'git':
- cmd = """git ci -m 'commited new %s' --author '%s' %s """ % (
+ cmd = """git commit -m 'commited new %s' --author '%s' %s """ % (
i, 'Marcin Kuźminski <marcin@python-blog.com>', added_file
)
Command(cwd).execute(cmd)
diff --git a/setup.py b/setup.py
index 7c79cb17..fe803b88 100644
--- a/setup.py
+++ b/setup.py
@@ -34,6 +34,9 @@ is_windows = __platform__ in _get_meta_var('PLATFORM_WIN', _metadata)
requirements = [
"waitress==0.8.1",
+ "webob==1.0.8",
+ "webtest==1.4.3",
+ "Pylons==1.0.0",
"Beaker==1.6.4",
"SQLAlchemy==0.7.9",
"Mako==0.7.3",
@@ -50,6 +53,7 @@ if sys.version_info < (2, 6):
if sys.version_info < (2, 7):
requirements.append("unittest2")
+ requirements.append("argparse")
if is_windows:
requirements.append("mercurial==2.4.2")